Ответы:
Это абстрактное значение ссылки на ресурс, часто в память, открытый файл или канал.
Должным образом , в Windows (и вообще в вычислениях) дескриптор является абстракцией, которая скрывает реальный адрес памяти от пользователя API, позволяя системе прозрачно реорганизовать физическую память для программы. Преобразование дескриптора в указатель блокирует память, а освобождение дескриптора делает недействительным указатель. В этом случае воспринимайте его как индекс в таблице указателей ... вы используете индекс для системных вызовов API, и система может изменить указатель в таблице по своему усмотрению.
Альтернативно, реальный указатель может быть задан в качестве дескриптора, когда разработчик API намеревается изолировать пользователя API от особенностей того, на что указывает возвращаемый адрес; в этом случае следует учитывать, что то, на что указывает дескриптор, может измениться в любое время (от версии API к версии или даже от вызова к вызову API, возвращающего дескриптор) - поэтому дескриптор должен рассматриваться как просто непрозрачное значение смысл только для API.
Я должен добавить, что в любой современной операционной системе даже так называемые «реальные указатели» по-прежнему являются непрозрачными дескрипторами в пространстве виртуальной памяти процесса, что позволяет операционным системам управлять и переставлять память без аннулирования указателей в процессе. ,
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
возвращает указатель на a struct Widget
. Это имеет несколько последствий:
Widget
структуруWidget
структурыОба эти последствия могут быть нежелательными.
Второй пример скрывает эту внутреннюю деталь от пользовательского кода, возвращая только 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.
handle
вместо этого void *
- отговаривать пользовательский код от попыток точно определить, на что указывает пустота * . Я прав?
РУЧКА в программировании Win32 - это токен, представляющий ресурс, управляемый ядром Windows. Дескриптор может быть окном, файлом и т. Д.
Дескрипторы - это просто способ идентифицировать частичный ресурс, с которым вы хотите работать, используя Win32 API.
Например, если вы хотите создать окно и показать его на экране, вы можете сделать следующее:
// Create the window
HWND hwnd = CreateWindow(...);
if (!hwnd)
return; // hwnd not created
// Show the window.
ShowWindow(hwnd, SW_SHOW);
В приведенном выше примере HWND означает «дескриптор окна».
Если вы привыкли к объектно-ориентированному языку, вы можете думать о HANDLE как об экземпляре класса без методов, чье состояние может быть изменено только другими функциями. В этом случае ShowWindow функция изменяет состояние окна HANDLE.
Посмотрите Ручки и Типы данных для получения дополнительной информации.
HANDLE
ADT, управляются ядром. Другие типы дескрипторов, которые вы называете ( HWND
и т. Д.), С другой стороны, являются объектами USER. Они не управляются ядром Windows.
Дескриптор - это уникальный идентификатор объекта, управляемого Windows. Это как указатель , но не указатель в том смысле, что это не адрес, который может быть разыменован пользовательским кодом для получения доступа к некоторым данным. Вместо этого дескриптор должен быть передан в набор функций, которые могут выполнять действия над объектом, который идентифицирует дескриптор.
Таким образом, на самом базовом уровне РУЧКА любого рода - это указатель на указатель или
#define HANDLE void **
Теперь о том, почему вы хотели бы использовать его
Давайте сделаем настройку:
class Object{
int Value;
}
class LargeObj{
char * val;
LargeObj()
{
val = malloc(2048 * 1000);
}
}
void foo(Object bar){
LargeObj lo = new LargeObj();
bar.Value++;
}
void main()
{
Object obj = new Object();
obj.val = 1;
foo(obj);
printf("%d", obj.val);
}
Так как obj был передан по значению (сделайте копию и передайте это функции) в foo, printf выведет исходное значение 1.
Теперь, если мы обновим foo до:
void foo(Object * bar)
{
LargeObj lo = new LargeObj();
bar->val++;
}
Существует вероятность, что printf напечатает обновленное значение 2. Но также существует вероятность того, что foo вызовет некоторую форму повреждения памяти или исключения.
Причина в том, что теперь, когда вы используете указатель для передачи obj функции, которой вы также выделяете 2 мегабайта памяти, это может привести к тому, что ОС будет перемещать память вокруг обновления местоположения obj. Поскольку вы передали указатель по значению, при перемещении объекта obj ОС обновляет указатель, но не копию в функции, что может вызвать проблемы.
Последнее обновление для foo:
void foo(Object **bar){
LargeObj lo = LargeObj();
Object * b = &bar;
b->val++;
}
Это всегда будет печатать обновленное значение.
Видите, когда компилятор выделяет память для указателей, он помечает их как неподвижные, поэтому любая перестановка памяти, вызванная выделением большого объекта, значению, переданному функции, будет указывать на правильный адрес, чтобы узнать окончательное местоположение в памяти для Обновить.
Любые конкретные типы HANDLE (hWnd, FILE и т. Д.) Зависят от домена и указывают на определенный тип структуры для защиты от повреждения памяти.
Дескриптор подобен значению первичного ключа записи в базе данных.
правка 1: хорошо, почему downvote, первичный ключ однозначно идентифицирует запись в базе данных, а дескриптор в системе Windows однозначно идентифицирует окно, открытый файл и т. д. Это то, что я говорю.
Думайте об окне в Windows как о структуре, которая его описывает. Эта структура является внутренней частью Windows, и вам не нужно знать ее детали. Вместо этого Windows предоставляет typedef для указателя на структуру для этой структуры. Это «ручка», с помощью которой вы можете удерживать окно.,