Antworten:
Es ist ein abstrakter Referenzwert für eine Ressource, häufig Speicher oder eine geöffnete Datei oder eine Pipe.
Richtig , in Windows, (und in der Regel bei der Berechnung) ein Handgriff ist eine Abstraktion , die eine reale Speicheradresse von dem API Benutzer verbirgt, das System transparent , um das Programm zu reorganisieren physische Speicher ermöglicht. Das Auflösen eines Handles in einen Zeiger sperrt den Speicher, und das Loslassen des Handles macht den Zeiger ungültig. Stellen Sie sich in diesem Fall einen Index für eine Zeigertabelle vor. Sie verwenden den Index für die System-API-Aufrufe, und das System kann den Zeiger in der Tabelle nach Belieben ändern.
Alternativ kann ein realer Zeiger als Handle angegeben werden, wenn der API-Writer beabsichtigt, dass der Benutzer der API von den Besonderheiten isoliert wird, auf die die zurückgegebene Adresse verweist. In diesem Fall muss berücksichtigt werden, dass sich das, worauf das Handle zeigt, jederzeit ändern kann (von API-Version zu Version oder sogar von Aufruf zu Aufruf der API, die das Handle zurückgibt). Das Handle sollte daher einfach als undurchsichtiger Wert behandelt werden sinnvoll nur an die API.
Ich sollte hinzufügen, dass in jedem modernen Betriebssystem sogar die sogenannten "realen Zeiger" undurchsichtige Handles im virtuellen Speicherbereich des Prozesses sind, wodurch das Betriebssystem den Speicher verwalten und neu anordnen kann, ohne die Zeiger innerhalb des Prozesses ungültig zu machen .
A HANDLE
ist eine kontextspezifische eindeutige Kennung. Mit kontextspezifisch meine ich, dass ein aus einem Kontext erhaltenes Handle nicht unbedingt in einem anderen aribträren Kontext verwendet werden kann, der auch auf HANDLE
s funktioniert .
Gibt beispielsweise GetModuleHandle
eine eindeutige Kennung an ein aktuell geladenes Modul zurück. Das zurückgegebene Handle kann in anderen Funktionen verwendet werden, die Modulhandles akzeptieren. Es kann nicht für Funktionen vergeben werden, die andere Arten von Handles erfordern. Zum Beispiel könnten Sie einem Handle, das von GetModuleHandle
to zurückgegeben wird, nicht geben HeapDestroy
und erwarten, dass es etwas Sinnvolles tut.
Das HANDLE
selbst ist nur ein integraler Typ. Normalerweise, aber nicht unbedingt, ist es ein Zeiger auf einen zugrunde liegenden Typ oder Speicherort. Beispielsweise ist das HANDLE
zurückgegebene von GetModuleHandle
tatsächlich ein Zeiger auf die virtuelle Basisspeicheradresse des Moduls. Es gibt jedoch keine Regel, die besagt, dass Handles Zeiger sein müssen. Ein Handle kann auch nur eine einfache Ganzzahl sein (die möglicherweise von einer Win32-API als Index für ein Array verwendet werden kann).
HANDLE
s sind absichtlich undurchsichtige Darstellungen, die die Kapselung und Abstraktion von internen Win32-Ressourcen ermöglichen. Auf diese Weise können die Win32-APIs möglicherweise den zugrunde liegenden Typ hinter einem HANDLE ändern, ohne dass dies Auswirkungen auf den Benutzercode hat (zumindest ist dies die Idee).
Betrachten Sie diese drei verschiedenen internen Implementierungen einer Win32-API, die ich gerade erstellt habe, und nehmen Sie an, dass dies Widget
eine ist 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);
}
Das erste Beispiel zeigt die internen Details der API: Der Benutzercode erkennt, dass GetWidget
ein Zeiger auf a zurückgegeben wird struct Widget
. Dies hat einige Konsequenzen:
Widget
Struktur definiertWidget
Struktur ändernDiese beiden Konsequenzen können unerwünscht sein.
Das zweite Beispiel verbirgt dieses interne Detail vor dem Benutzercode, indem nur zurückgegeben wird void *
. Der Benutzercode benötigt keinen Zugriff auf den Header, der die Widget
Struktur definiert .
Das dritte Beispiel ist genau das gleiche wie das zweite, aber wir nennen stattdessen einfach das void *
a HANDLE
. Vielleicht hält dies den Benutzercode davon ab, genau herauszufinden, worauf die void *
Punkte zeigen.
Warum diese Schwierigkeiten durchmachen? Betrachten Sie dieses vierte Beispiel einer neueren Version derselben API:
typedef void * HANDLE;
HANDLE GetWidget (std::string name)
{
NewImprovedWidget *w;
w = findImprovedWidget(name);
return reinterpret_cast<HANDLE>(w);
}
Beachten Sie, dass die Benutzeroberfläche der Funktion mit dem dritten Beispiel oben identisch ist. Dies bedeutet, dass Benutzercode diese neue Version der API ohne Änderungen weiterhin verwenden kann, obwohl sich die Implementierung "hinter den Kulissen" geändert hat, um NewImprovedWidget
stattdessen die Struktur zu verwenden.
Die Handles in diesem Beispiel sind eigentlich nur ein neuer, vermutlich freundlicherer Name für void *
, und genau das HANDLE
ist a in der Win32-API (siehe MSDN ). Es bietet eine undurchsichtige Wand zwischen dem Benutzercode und den internen Darstellungen der Win32-Bibliothek, die die Portabilität von Code, der die Win32-API verwendet, zwischen Windows-Versionen erhöht.
handle
anstelle von void *
ist, Benutzercode davon abzuhalten , genau herauszufinden, worauf die Leere * hinweist . Hab ich recht?
Ein HANDLE in der Win32-Programmierung ist ein Token, das eine Ressource darstellt, die vom Windows-Kernel verwaltet wird. Ein Handle kann sich auf ein Fenster, eine Datei usw. beziehen.
Handles sind lediglich eine Möglichkeit, eine Partikelressource zu identifizieren, mit der Sie mithilfe der Win32-APIs arbeiten möchten.
Wenn Sie beispielsweise ein Fenster erstellen und auf dem Bildschirm anzeigen möchten, können Sie Folgendes tun:
// Create the window
HWND hwnd = CreateWindow(...);
if (!hwnd)
return; // hwnd not created
// Show the window.
ShowWindow(hwnd, SW_SHOW);
Im obigen Beispiel bedeutet HWND "ein Handle für ein Fenster".
Wenn Sie an eine objektorientierte Sprache gewöhnt sind, können Sie sich einen HANDLE als eine Instanz einer Klasse ohne Methoden vorstellen, deren Status nur durch andere Funktionen geändert werden kann. In diesem Fall ändert die ShowWindow- Funktion den Status des Fenstergriffs.
Weitere Informationen finden Sie unter Handles und Datentypen .
HANDLE
ADT verwiesen wird, werden vom Kernel verwaltet. Die anderen von Ihnen benannten Handle-Typen ( HWND
usw.) sind USER-Objekte. Diese werden nicht vom Windows-Kernel verwaltet.
Ein Handle ist eine eindeutige Kennung für ein von Windows verwaltetes Objekt. Es ist wie ein Zeiger , aber kein Zeiger in dem Sinne, dass es keine Adresse ist, die durch Benutzercode dereferenziert werden könnte, um Zugriff auf einige Daten zu erhalten. Stattdessen soll ein Handle an eine Reihe von Funktionen übergeben werden, die Aktionen für das vom Handle identifizierte Objekt ausführen können.
Auf der einfachsten Ebene ist ein GRIFF jeder Art ein Zeiger auf einen Zeiger oder
#define HANDLE void **
Nun, warum Sie es verwenden möchten
Nehmen wir ein Setup:
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);
}
Da obj als Wert (Kopie erstellen und der Funktion übergeben) an foo übergeben wurde, gibt printf den ursprünglichen Wert 1 aus.
Nun, wenn wir foo aktualisieren auf:
void foo(Object * bar)
{
LargeObj lo = new LargeObj();
bar->val++;
}
Es besteht die Möglichkeit, dass printf den aktualisierten Wert 2 ausgibt. Es besteht jedoch auch die Möglichkeit, dass foo eine Form von Speicherbeschädigung oder -ausnahme verursacht.
Der Grund dafür ist, dass Sie, während Sie jetzt einen Zeiger verwenden, um obj an die Funktion zu übergeben, der Sie auch 2 Megabyte Speicher zuweisen, das Betriebssystem möglicherweise den Speicher verschieben, um den Speicherort von obj zu aktualisieren. Da Sie den Zeiger als Wert übergeben haben, aktualisiert das Betriebssystem beim Verschieben von obj den Zeiger, jedoch nicht die Kopie in der Funktion, was möglicherweise zu Problemen führt.
Ein letztes Update zu foo von:
void foo(Object **bar){
LargeObj lo = LargeObj();
Object * b = &bar;
b->val++;
}
Dadurch wird immer der aktualisierte Wert gedruckt.
Wenn der Compiler Speicher für Zeiger zuweist, markiert er diese als unbeweglich, sodass jedes erneute Mischen des Speichers, das durch das Zuweisen des an die Funktion übergebenen Werts an das große Objekt verursacht wird, auf die richtige Adresse verweist, um die endgültige Position im Speicher zu ermitteln aktualisieren.
Alle bestimmten Arten von HANDLEs (hWnd, FILE usw.) sind domänenspezifisch und verweisen auf eine bestimmte Art von Struktur, um vor Speicherbeschädigung zu schützen.
Ein Handle ist wie ein Primärschlüsselwert eines Datensatzes in einer Datenbank.
edit 1: Nun, warum das Downvote, ein Primärschlüssel einen Datenbankdatensatz eindeutig identifiziert und ein Handle im Windows-System ein Fenster, eine geöffnete Datei usw. eindeutig identifiziert. Das sage ich.
Stellen Sie sich das Fenster in Windows als eine Struktur vor, die es beschreibt. Diese Struktur ist ein interner Teil von Windows und Sie müssen die Details nicht kennen. Stattdessen stellt Windows ein typedef für den Zeiger auf die Struktur für diese Struktur bereit. Das ist der "Griff", mit dem Sie das Fenster festhalten können.