Risposte:
È un valore di riferimento astratto per una risorsa, spesso memoria, un file aperto o una pipe.
Correttamente , in Windows, (e generalmente nell'informatica) un handle è un'astrazione che nasconde un indirizzo di memoria reale dall'utente API, consentendo al sistema di riorganizzare la memoria fisica in modo trasparente al programma. La risoluzione di una maniglia in un puntatore blocca la memoria e il rilascio della maniglia invalida il puntatore. In questo caso pensalo come un indice in una tabella di puntatori ... usi l'indice per le chiamate API di sistema e il sistema può cambiare il puntatore nella tabella a piacimento.
In alternativa un vero puntatore può essere dato come handle quando il writer API intende che l'utente dell'API sia isolato dalle specifiche di ciò a cui l'indirizzo restituito punta; in questo caso si deve considerare che ciò a cui punta l'handle può cambiare in qualsiasi momento (da versione API a versione o anche da chiamata a chiamata dell'API che restituisce l'handle) - l'handle deve quindi essere trattato semplicemente come un valore opaco significativo solo per l'API.
Vorrei aggiungere che in qualsiasi sistema operativo moderno, anche i cosiddetti "puntatori reali" sono ancora maniglie opache nello spazio di memoria virtuale del processo, che consente all'O / S di gestire e riorganizzare la memoria senza invalidare i puntatori all'interno del processo .
A HANDLE
è un identificatore univoco specifico del contesto. Per specifico contesto, intendo che un handle ottenuto da un contesto non può necessariamente essere utilizzato in qualsiasi altro contesto aribtrare che funziona anche su HANDLE
s.
Ad esempio, GetModuleHandle
restituisce un identificatore univoco a un modulo attualmente caricato. L'handle restituito può essere utilizzato in altre funzioni che accettano gli handle del modulo. Non può essere assegnato a funzioni che richiedono altri tipi di handle. Ad esempio, non si poteva dare un handle restituito da GetModuleHandle
ad HeapDestroy
e si aspettano di fare qualcosa di sensato.
Lo HANDLE
stesso è solo un tipo integrale. Di solito, ma non necessariamente, è un puntatore a un tipo o posizione di memoria sottostante. Ad esempio, HANDLE
restituito da GetModuleHandle
è in realtà un puntatore all'indirizzo di memoria virtuale di base del modulo. Ma non esiste una regola che affermi che gli handle devono essere puntatori. Un handle potrebbe anche essere un semplice numero intero (che potrebbe essere utilizzato da alcune API Win32 come indice in un array).
HANDLE
sono rappresentazioni volutamente opache che forniscono incapsulamento e astrazione dalle risorse interne di Win32. In questo modo, le API di Win32 potrebbero potenzialmente modificare il tipo sottostante dietro una MANIGLIA, senza che ciò influisca in alcun modo sul codice utente (almeno questa è l'idea).
Considera queste tre diverse implementazioni interne di un'API Win32 che ho appena creato e supponi che Widget
sia un 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);
}
Il primo esempio espone i dettagli interni sull'API: consente al codice utente di sapere che GetWidget
restituisce un puntatore a struct Widget
. Ciò ha un paio di conseguenze:
Widget
strutturaWidget
struttura restituitaEntrambe queste conseguenze potrebbero essere indesiderabili.
Il secondo esempio nasconde questo dettaglio interno dal codice utente, restituendo just void *
. Il codice utente non necessita dell'accesso all'intestazione che definisce la Widget
struttura.
Il terzo esempio è esattamente lo stesso come il secondo, ma basta chiamare l' void *
uno HANDLE
invece. Forse questo scoraggia il codice utente dal tentativo di capire esattamente a cosa void *
punta.
Perché attraversare questo problema? Considera questo quarto esempio di una versione più recente di questa stessa API:
typedef void * HANDLE;
HANDLE GetWidget (std::string name)
{
NewImprovedWidget *w;
w = findImprovedWidget(name);
return reinterpret_cast<HANDLE>(w);
}
Si noti che l'interfaccia della funzione è identica al terzo esempio sopra. Ciò significa che il codice utente può continuare a utilizzare questa nuova versione dell'API, senza alcuna modifica, anche se l'implementazione "dietro le quinte" è cambiata per utilizzare NewImprovedWidget
invece la struttura.
Gli handle in questo esempio sono in realtà solo un nuovo, presumibilmente più amichevole, nome void *
, che è esattamente ciò che HANDLE
è a nell'API Win32 (cercare MSDN ). Fornisce un muro opaco tra il codice utente e le rappresentazioni interne della libreria Win32 che aumenta la portabilità, tra le versioni di Windows, del codice che utilizza l'API Win32.
handle
posto di void *
è scoraggiare il codice utente dal tentativo di capire esattamente a cosa punta il vuoto * . Ho ragione?
Un HANDLE nella programmazione Win32 è un token che rappresenta una risorsa gestita dal kernel di Windows. Un handle può essere una finestra, un file, ecc.
Gli handle sono semplicemente un modo per identificare una risorsa particolato con cui si desidera lavorare utilizzando le API Win32.
Ad esempio, se si desidera creare una finestra e mostrarla sullo schermo, è possibile effettuare le seguenti operazioni:
// Create the window
HWND hwnd = CreateWindow(...);
if (!hwnd)
return; // hwnd not created
// Show the window.
ShowWindow(hwnd, SW_SHOW);
Nell'esempio sopra HWND significa "un handle per una finestra".
Se sei abituato a un linguaggio orientato agli oggetti puoi pensare a una MANIGLIA come un'istanza di una classe senza metodi il cui stato è modificabile solo da altre funzioni. In questo caso la funzione ShowWindow modifica lo stato di Window HANDLE.
Vedere Maniglie e tipi di dati per ulteriori informazioni.
HANDLE
ADT sono gestiti dal kernel. Gli altri tipi di handle nominati ( HWND
, ecc.) D'altra parte, sono oggetti USER. Quelli non sono gestiti dal kernel di Windows.
Un handle è un identificatore univoco per un oggetto gestito da Windows. È come un puntatore , ma non un puntatore nel senso che non è un indirizzo che potrebbe essere referenziato dal codice utente per ottenere l'accesso ad alcuni dati. Invece, un handle deve essere passato a un set di funzioni in grado di eseguire azioni sull'oggetto identificato dall'handle.
Quindi al livello più elementare una MANIGLIA di qualsiasi tipo è un puntatore a un puntatore o
#define HANDLE void **
Ora sul perché vorresti usarlo
Diamo una configurazione:
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);
}
Quindi, poiché obj è stato passato per valore (crea una copia e assegnalo alla funzione) a pippo, printf stamperà il valore originale di 1.
Ora se aggiorniamo foo a:
void foo(Object * bar)
{
LargeObj lo = new LargeObj();
bar->val++;
}
È possibile che printf stia stampando il valore aggiornato di 2. Ma esiste anche la possibilità che foo provochi una qualche forma di corruzione o eccezione della memoria.
Il motivo è che mentre stai usando un puntatore per passare obj alla funzione che stai allocando anche 2 Meg di memoria, questo potrebbe far spostare il sistema operativo aggiornando la posizione di obj. Dato che hai passato il puntatore in base al valore, se obj viene spostato, il sistema operativo aggiorna il puntatore ma non la copia nella funzione e potenzialmente causa problemi.
Un aggiornamento finale a pippo di:
void foo(Object **bar){
LargeObj lo = LargeObj();
Object * b = &bar;
b->val++;
}
Questo stamperà sempre il valore aggiornato.
Vedi, quando il compilatore alloca memoria per i puntatori, li contrassegna come immobili, quindi qualsiasi rimescolamento della memoria causato dall'oggetto di grandi dimensioni che viene allocato, il valore passato alla funzione punterà all'indirizzo corretto per trovare la posizione finale in memoria a aggiornare.
Qualsiasi tipo particolare di HANDLE (hWnd, FILE, ecc.) È specifico del dominio e punta a un certo tipo di struttura per proteggere dalla corruzione della memoria.
Un handle è come un valore chiave principale di un record in un database.
modifica 1: beh, perché il downvote, una chiave primaria identifica in modo univoco un record del database e un handle nel sistema Windows identifica in modo univoco una finestra, un file aperto, ecc. Ecco cosa sto dicendo.
Pensa alla finestra di Windows come a una struttura che la descrive. Questa struttura è una parte interna di Windows e non è necessario conoscerne i dettagli. Invece, Windows fornisce un typedef per il puntatore a struct per quella struttura. Questa è la "maniglia" con cui puoi aggrapparti alla finestra.,