A HANDLE
- уникальный контекстный уникальный идентификатор. Под контекстно-зависимым я подразумеваю, что дескриптор, полученный из одного контекста, не обязательно может быть использован в любом другом контексте aribtrary, который также работает на HANDLE
s.
Например, GetModuleHandle
возвращает уникальный идентификатор для загруженного в данный момент модуля. Возвращенный дескриптор может использоваться в других функциях, которые принимают дескрипторы модуля. Это не может быть дано функциям, которые требуют других типов ручек. Например, вы не можете дать дескриптор, возвращенный с GetModuleHandle
до HeapDestroy
, и ожидать, что он сделает что-то разумное.
Сам по себе HANDLE
является просто целым типом. Обычно, но не обязательно, это указатель на некоторый базовый тип или область памяти. Например, HANDLE
, возвращаемый GetModuleHandle
, фактически является указателем на базовый адрес виртуальной памяти модуля. Но нет правила, утверждающего, что дескрипторы должны быть указателями. Дескриптор также может быть простым целым числом (которое может использоваться некоторым Win32 API в качестве индекса в массиве).
HANDLE
являются намеренно непрозрачными представлениями, которые обеспечивают инкапсуляцию и абстрагирование от внутренних ресурсов Win32. Таким образом, Win32 API могли бы потенциально изменить базовый тип, стоящий за HANDLE, без какого-либо влияния на пользовательский код (по крайней мере, в этом идея).
Рассмотрим эти три различные внутренние реализации Win32 API, которые я только что составил, и предположим, что Widget
- это struct
.
Widget * GetWidget (std::string name)
{
Widget *w;
w = findWidget(name);
return w;
}
void * GetWidget (std::string name)
{
Widget *w;
w = findWidget(name);
return reinterpret_cast<void *>(w);
}
typedef void * HANDLE;
HANDLE GetWidget (std::string name)
{
Widget *w;
w = findWidget(name);
return reinterpret_cast<HANDLE>(w);
}
В первом примере раскрываются внутренние подробности об API: он позволяет пользовательскому коду знать, что GetWidget
возвращает указатель на struct Widget
. Это имеет несколько последствий:
- код пользователя должен иметь доступ к заголовочному файлу, который определяет
Widget
struct
- код пользователя может потенциально изменить внутренние части возвращенной
Widget
struct
Оба эти последствия могут быть нежелательными.
Второй пример скрывает эту внутреннюю деталь от кода пользователя, возвращая просто void *
. Код пользователя не нуждается в доступе к заголовку, который определяет структуру Widget
.
Третий пример точно такой же, как и второй, но вместо этого мы просто называем void *
a HANDLE
. Возможно, это препятствует тому, чтобы пользовательский код пытался точно определить, на что указывает void *
.
Зачем переживать эту проблему? Рассмотрим этот четвертый пример более новой версии того же API:
typedef void * HANDLE;
HANDLE GetWidget (std::string name)
{
NewImprovedWidget *w;
w = findImprovedWidget(name);
return reinterpret_cast<HANDLE>(w);
}
Обратите внимание, что интерфейс функции идентичен третьему примеру выше. Это означает, что пользовательский код может продолжать использовать эту новую версию API без каких-либо изменений, даже несмотря на то, что реализация «за кулисами» изменилась и теперь использует структуру NewImprovedWidget
.
Дескрипторы в этом примере на самом деле просто новое, предположительно более дружественное, имя для void *
, которое в точности соответствует HANDLE
в Win32 API (посмотрите в MSDN ). Он обеспечивает непрозрачную стену между пользовательским кодом и внутренними представлениями библиотеки Win32, что увеличивает переносимость между версиями Windows кода, использующего Win32 API.