Відповіді:
Це абстрактне довідкове значення для ресурсу, часто пам'яті, відкритого файлу або труби.
Власне , в Windows (і взагалі в обчислювальній машині) ручка - це абстракція, яка приховує реальну адресу пам'яті від користувача API, що дозволяє системі прозоро реорганізувати фізичну пам'ять у програму. Розв’язання ручки в покажчику блокує пам'ять, а звільнення ручки недійсно вказує. У цьому випадку подумайте про це як на індекс у таблиці покажчиків ... ви використовуєте індекс для системних викликів API, і система може змінити вказівник у таблиці за бажанням.
Альтернативно, в якості ручки може бути заданий реальний покажчик, коли автор програми планує, щоб користувач API був ізольований від специфіки того, на що вказується адреса; в цьому випадку слід враховувати, що те, що вказує на ручку, може змінитися в будь-який час (від версії API до версії або навіть від виклику до виклику API, який повертає ручку) - тому ручка повинна трактуватися як просто непрозоре значення має значення лише для API.
Додам, що в будь-якій сучасній операційній системі навіть так звані «реальні покажчики» все ще залишаються непрозорими ручками у просторі віртуальної пам’яті, що дозволяє O / S керувати і переставляти пам’ять, не виключаючи покажчики в процесі .
A HANDLE
- це специфічний для контексту унікальний ідентифікатор. Під конкретним контекстом я маю на увазі, що ручка, отримана з одного контексту, не обов'язково може використовуватися в будь-якому іншому контексті, який також працює на HANDLE
s.
Наприклад, GetModuleHandle
повертає унікальний ідентифікатор до завантаженого модуля. Повернута ручка може використовуватися в інших функціях, які приймають ручки модулів. Він не може бути наданий функціям, які потребують інших типів ручок. Наприклад, ви не могли б дати ручку , повернуту від GetModuleHandle
до HeapDestroy
і очікувати , що робити що - то ділове.
Сам HANDLE
по собі є лише інтегральним типом. Зазвичай, але не обов'язково, це вказівник на деякий базовий тип або місце пам'яті. Наприклад, що HANDLE
повертається, GetModuleHandle
- це фактично вказівник на базову адресу віртуальної пам'яті модуля. Але не існує жодного правила, яке зазначає, що ручки повинні бути покажчиками. Ручка може бути просто простим цілим числом (яке, можливо, може бути використане деяким API Win32 як індекс в масив).
HANDLE
s - навмисно непрозорі уявлення, які забезпечують інкапсуляцію та абстрагування від внутрішніх ресурсів Win32. Таким чином, API Win32 може потенційно змінити базовий тип позаду HANDLE, не впливаючи на якийсь код користувача (принаймні, така ідея).
Розглянемо ці три різні внутрішні реалізації API Win32, які я щойно створив, і припустимо, що Widget
це a 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 *
а , 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
є a в API Win32 (подивіться це на MSDN ). Він забезпечує непрозору стіну між кодом користувача та внутрішніми представленнями бібліотеки Win32, що збільшує портативність між версіями Windows коду, що використовує API Win32.
handle
замість цього void *
- відштовхує код користувача від спроб з'ясувати, на що саме вказує пустота * . Я прав?
HANDLE в програмуванні Win32 - це маркер, який представляє ресурс, яким керує ядро Windows. Ручка може бути до вікна, файлу тощо.
Ручки - це просто спосіб визначення ресурсу часток, з яким потрібно працювати з використанням API Win32.
Наприклад, якщо ви хочете створити Вікно і показати його на екрані, ви можете зробити наступне:
// Create the window
HWND hwnd = CreateWindow(...);
if (!hwnd)
return; // hwnd not created
// Show the window.
ShowWindow(hwnd, SW_SHOW);
У наведеному вище прикладі HWND означає "ручка до вікна".
Якщо ви звикли до об'єктно-орієнтованої мови, ви можете розглядати HANDLE як екземпляр класу без методів, стан яких можна змінювати лише за допомогою інших функцій. У цьому випадку функція ShowWindow змінює стан WAND 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 переміщується, ОС оновлює вказівник, але не його копію у функції та потенційно спричиняє проблеми.
Остаточне оновлення до:
void foo(Object **bar){
LargeObj lo = LargeObj();
Object * b = &bar;
b->val++;
}
Це завжди буде надрукувати оновлене значення.
Дивіться, коли компілятор виділяє пам'ять для покажчиків, вона позначає їх як нерухомі, тому будь-яке повторне переміщення пам’яті, спричинене великим об'єктом, що виділяється, значення, передане функції, вказуватиме на правильну адресу, щоб з’ясувати остаточне місце в пам'яті для оновлення.
Будь-які конкретні типи HANDLE (hWnd, FILE тощо) є специфічними для домену і вказують на певний тип структури для захисту від пошкодження пам'яті.
Ручка - це як значення основного ключа запису в базі даних.
редагувати 1: добре, чому downvote, первинний ключ однозначно ідентифікує запис бази даних, а ручка в системі Windows однозначно ідентифікує вікно, відкритий файл тощо. Це те, що я говорю.
Подумайте про вікно в Windows як про структуру, яка описує його. Ця структура є внутрішньою частиною Windows, і вам не потрібно знати її деталі. Натомість Windows надає typedef для вказівника на stru для цієї структури. Це "ручка", за яку ви можете утриматись у вікні.,