Каков диапазон Windows HANDLE для 64-битного приложения?

В WinAPI тип HANDLE определяется как void*, поэтому в 64-битном приложении значение HANDLE может варьироваться от 0 до 18446744073709551615. Но так ли это на практике? В какой-либо документации указывается интегральный диапазон такого HANDLE?

Если, например, вы хотите сохранить этот HANDLE как int32_t в 32-битном приложении, который полностью прекрасен, но в 64-битном приложении возникают сомнения.

Ответ 1

MSDN заявляет:

Межпроцессное взаимодействие между 32-битными и 64-битными приложениями

64-разрядные версии Windows используют 32-разрядные дескрипторы для взаимодействия. При разделении дескриптора между 32-битными и 64-битными приложениями значимы только младшие 32 бита, поэтому можно укоротить дескриптор (при передаче его из 64-битного в 32-битный) или расширить дескриптор знака ( при переходе от 32-битного к 64-битному). К дескрипторам, которые могут совместно использоваться, относятся дескрипторы пользовательских объектов, таких как окна (HWND), дескрипторы объектов GDI, таких как перья и кисти (HBRUSH и HPEN), и дескрипторы именованных объектов, таких как мьютексы, семафоры и файловые дескрипторы.

Стоит также отметить этот комментарий, добавленный на этой странице:

Надлежащим способом совместного использования таких дескрипторов через границы процесса является расширение нуля 32-битных дескрипторов до 64-битных, или наоборот, усечение 64-битных дескрипторов до 32-битных, отбрасывая старшие биты.

Обратите внимание на различие между "расширяющим знак" дескриптором и "расширяющим ноль" дескриптором.

Редактировать: Судя по обсуждению, увиденному в удаленном ответе на этот вопрос, я предполагаю, что значение расширения знака 32-битного дескриптора для получения 64-битного дескриптора вместо расширения нуля заключается в сохранении надлежащего обращения с INVALID_HANDLE_VALUE значение для ручки.

Ответ 2

Мне хотелось бы знать, где это задокументировано, но мой коллега настаивает на том, что 64-битные HWND-ручки всегда подходят в 32-битных. Я никогда не видел случая, когда это неправда, но не может говорить о будущем или где он документирован. Что касается других дескрипторов, как, например, HTREEITEM.... Они полные 64-битные, и я был немного уверен в том, что они тоже подходят к 32 бит.

Ответ 3

Чтобы добавить к предыдущим правильным ответам, позвольте мне отметить, что HWND также является допустимым дескриптором для всех процессов. Любой другой дескриптор void * (например, HBRUSH, HBITMAP и т.д.) Может быть усеченным, поскольку только младшие 32 бита значимы, но он недопустим вне собственного процесса.

Для объектов GDI это может работать, потому что это на самом деле индексы (см. Https://docs.microsoft.com/en-us/previous-versions/ms810501(v=msdn.10))

Что здесь происходит, так это то, что дескрипторы объектов GDI внутренне реализуются как смещения в таблицу дескрипторов, которая находится на стороне клиента подсистемы Win32. (Помните, что клиент Win32 - это библиотека DLL, которая находится в адресном пространстве приложения на основе Win32 и вызывается приложением.) Другими словами, таблицы дескрипторов хранятся для каждого процесса, но они не помечены для процесса. Это означает, что дескриптор объекта, который принадлежит процессу A, может случайно совпадать с действительным дескриптором в контексте процесса B. Таким образом, вызов SelectObject из B может завершиться успешно, но B фактически выберет совершенно другой объект в контексте своего устройства или, что еще хуже, правильный. Выбор правильного объекта может быть хуже, потому что объекты могут совпадать по совпадению, так что вы думаете, что это работает, но приложение будет вести себя странно позже. Поэтому не передавайте дескрипторы объектам GDI между приложениями; они имеют совершенно разные значения в разных процессах.

HWND является документированным исключением из этого.