2009-05-24 7 views

ответ

127

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

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

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

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

+2

Я очень ценю быстрый ответ. К сожалению, я думаю, что я все еще слишком много новичок, чтобы полностью понять это :-( –

+4

Является ли мой расширенный ответ пролить свет? –

+0

Да ... спасибо! –

2

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

+0

Правда, но всегда стоит помнить, что дескриптор обычно не является адресом памяти, а один пользовательский код не должен разыгрывать его. – sharptooth

24

РУЧКА в программировании Win32 - это токен, представляющий ресурс, управляемый ядром Windows. Ручка может быть в окне, файле и т. Д.

Ручки - это просто способ идентификации ресурса частиц, с которым вы хотите работать, используя API Win32.

Так, например, если вы хотите, чтобы создать окно, и показать его на экране вы можете сделать следующее:

// Create the window 
HWND hwnd = CreateWindow(...); 
if (!hwnd) 
    return; // hwnd not created 

// Show the window. 
ShowWindow(hwnd, SW_SHOW); 

В приведенном выше примере HWND означает «дескриптор окна».

Если вы привыкли к объектно-ориентированному языку, вы можете придумать РУЧКУ как экземпляр класса без каких-либо методов, состояние которых может быть изменено только другими функциями. В этом случае функция ShowWindow изменяет состояние окна HANDLE.

Для получения дополнительной информации см. Handles and Data Types.

+0

Объекты, на которые делается ссылка через «РУЧКУ» ADT, управляются ядром. Другие типы дескрипторов, которые вы называете ('HWND' и т. Д.), С другой стороны, являются объектами USER. Они не управляются ядром Windows. – IInspectable

+0

@Инспективные догадки, которыми управляют материалы User32.dll? –

78

A HANDLE - уникальный для контекста идентификатор. Конкретно, я имею в виду, что дескриптор, полученный из одного контекста, не обязательно может использоваться в любом другом произвольном контексте, который также работает на HANDLE.

Например, GetModuleHandle возвращает уникальный идентификатор загружаемого модуля. Возвращенный дескриптор может использоваться в других функциях, которые принимают дескрипторы модулей. Он не может быть отдан функциям, которым требуются другие типы ручек. Например, вы не могли бы получить дескриптор, возвращенный с GetModuleHandle, до HeapDestroy и ожидать, что он сделает что-то разумное.

Сам HANDLE сам по себе является интегральным типом. Обычно, но необязательно, это указатель на некоторый базовый тип или расположение памяти. Например, HANDLE, возвращаемый GetModuleHandle, фактически является указателем на адрес базовой виртуальной памяти модуля. Но нет правила, указывающего, что ручки должны быть указателями. Ручка также может быть просто простым целым числом (которое может быть использовано некоторыми Win32 API в качестве индекса в массиве).

HANDLE s преднамеренно непрозрачные изображения, которые обеспечивают инкапсуляцию и абстракцию из внутренних ресурсов Win32. Таким образом, API Win32 может потенциально изменить базовый тип за ручкой, без какого-либо влияния на код пользователя (по крайней мере, это идея).

Рассмотрите эти три различные внутренние реализации API Win32, которые я только что составил, и предположим, что 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 структуры

Оба эти последствия могут быть нежелательными.

Второй пример скрывает эту внутреннюю деталь от кода пользователя, возвращая только 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 (смотрите его at MSDN). Он обеспечивает непрозрачную стену между кодом пользователя и внутренними представлениями библиотеки Win32, что повышает переносимость между версиями Windows кода, использующего Win32 API.

+3

Преднамеренное или нет, вы правы - понятие, безусловно, непрозрачное (по крайней мере для меня :-) –

+2

Я расширил свой первоначальный ответ некоторыми конкретными примерами. Надеюсь, это сделает концепцию немного более прозрачной. –

+0

Очень полезное расширение ... Спасибо! –

4

Так что на самом базовом уровне рукояти любого рода является указателем на указатель или

#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 был передан по значению (сделать копию и дать, что функции) для обув, то Printf напечатает исходное значение 1.

Теперь, если мы обновляем Foo к:

void foo(Object * bar) 
{ 
    LargeObj lo = new LargeObj(); 
    bar->val++; 
} 

Существует вероятность того, что Printf напечатает обновленное значение 2. Но есть вероятность того, что Foo будет вызывать некоторую форму повреждения памяти или исключения.

Причина в том, что, пока вы используете указатель для передачи obj в функцию, которую вы также выделяете 2 мегабайта памяти, это может заставить ОС перемещать память вокруг обновления местоположения obj. Поскольку вы передали указатель по значению, если obj перемещается, ОС обновляет указатель, но не копирует в функцию и потенциально вызывает проблемы.

Окончательное обновление обув из:

void foo(Object **bar){ 
    LargeObj lo = LargeObj(); 
    Object * b = &bar; 
    b->val++; 
} 

Это всегда будет печатать обновленное значение.

См., Когда компилятор выделяет память для указателей, он маркирует их как неподвижные, поэтому любое перетасование памяти, вызванное большим объектом, выделяемым значением, переданным функции, укажет на правильный адрес, чтобы узнать окончательный местоположение в памяти для обновления.

Любые конкретные типы HANDLE (hWnd, FILE и т. Д.) Являются специфичными для домена и указывают на определенный тип структуры для защиты от повреждения памяти.

+1

Это ошибочные аргументы; подсистема распределения памяти C не может просто отклонять указатели по желанию. В противном случае никакая программа на C или C++ никогда не была бы доказуемо правильной; худшая любая программа достаточной сложности была бы явно неверна по определению. Кроме того, двойное косвенное действие не помогает, если прямая ссылка на память перемещается вокруг программы, если указатель фактически не является абстракцией от реальной памяти, что сделало бы это * дескриптором *. –

+1

Операционная система Macintosh (в версиях до 9 или 8) сделала именно то, что указано выше. Если вы выделили какой-либо системный объект, вы бы часто получали от него ручку, оставив ОС свободно перемещать объект. С ограниченным объемом памяти первых Mac, которые были весьма важны. – Rhialto

6

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

3

Ручка как первичное значение ключа записи в базе данных.

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

+1

Я не думаю, что вы можете утверждать, что ручка уникальна. Он может быть уникальным для Windows Station Windows, но он не гарантированно будет уникальным, если несколько пользователей одновременно обращаются к одной и той же системе. То есть, несколько пользователей могут вернуть значение дескриптора, которое является численно идентичным, но в контексте Windows-станции пользователя они сопоставляются с разными вещами ... – Nick

1

Объект представляет собой структуру данных, которая представляет системный ресурс, такой как файл, поток или графическое изображение. Приложение не может напрямую обращаться к данным объекта или к системному ресурсу, который представляет объект. Вместо этого приложение должно получить дескриптор объекта, который он может использовать для проверки или изменения системного ресурса. Каждый дескриптор имеет запись во внутренней таблице. Эти записи содержат адреса ресурсов и средства для идентификации типа ресурса.

Смежные вопросы