Что такое дескриптор Windows? - PullRequest
139 голосов
/ 24 мая 2009

Что такое «дескриптор» при обсуждении ресурсов в Windows? Как они работают?

Ответы [ 7 ]

148 голосов
/ 24 мая 2009

Это абстрактное ссылочное значение для ресурса, часто памяти, открытого файла или канала.

Правильно , в Windows (и вообще в вычислениях) дескриптор - это абстракция, которая скрывает реальный адрес памяти от пользователя API, позволяя системе прозрачно реорганизовать физическую память для программы. Преобразование дескриптора в указатель блокирует память, а освобождение дескриптора делает недействительным указатель. В этом случае воспринимайте его как индекс в таблице указателей ... вы используете индекс для системных вызовов API, и система может изменить указатель в таблице по желанию.

В качестве альтернативы реальный указатель может быть задан в качестве дескриптора, когда разработчик API намеревается изолировать пользователя API от специфики того, на что указывает возвращаемый адрес; в этом случае следует учитывать, что то, на что указывает дескриптор, может измениться в любое время (от версии API к версии или даже от вызова к вызову API, возвращающего дескриптор) - поэтому дескриптор должен рассматриваться как просто непрозрачное значение значение только API.

Я должен добавить, что в любой современной операционной системе даже так называемые «реальные указатели» по-прежнему являются непрозрачными дескрипторами в пространстве виртуальной памяти процесса, что позволяет операционным системам управлять и перестраивать память, не отменяя указатели в процессе.

88 голосов
/ 24 мая 2009

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.

34 голосов
/ 24 мая 2009

РУЧКА в 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.

См. Дескрипторы и типы данных для получения дополнительной информации.

7 голосов
/ 25 мая 2009

Дескриптор - это уникальный идентификатор объекта, управляемого Windows. Это как указатель , но не указатель в том смысле, что это не адрес, который может быть разыменован пользовательским кодом для получения доступа к некоторым данным. Вместо этого дескриптор должен быть передан в набор функций, которые могут выполнять действия над объектом, который идентифицирует дескриптор.

5 голосов
/ 18 сентября 2012

Дескриптор подобен значению первичного ключа записи в базе данных.

edit 1: хорошо, почему downvote, первичный ключ однозначно идентифицирует запись базы данных, а дескриптор в системе Windows однозначно идентифицирует окно, открытый файл и т. Д. Это то, что я говорю.

4 голосов
/ 25 мая 2009

Так что на самом базовом уровне РУЧКА любого рода - это указатель на указатель или

#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 и т. Д.) Зависят от домена и указывают на определенный тип структуры для защиты от повреждения памяти.

2 голосов
/ 24 мая 2009

Думайте об окне в Windows как о структуре, которая его описывает. Эта структура является внутренней частью Windows, и вам не нужно знать ее детали. Вместо этого Windows предоставляет typedef для указателя на структуру для этой структуры. Это «ручка», с помощью которой вы можете удерживать окно.,

...