Réponses:
C'est une valeur de référence abstraite à une ressource, souvent de la mémoire ou un fichier ouvert, ou un tube.
Correctement , sous Windows (et généralement en informatique), un handle est une abstraction qui cache une adresse mémoire réelle à l'utilisateur de l'API, permettant au système de réorganiser la mémoire physique de manière transparente au programme. La résolution d'une poignée en un pointeur verrouille la mémoire et la libération de la poignée invalide le pointeur. Dans ce cas, considérez-le comme un index dans une table de pointeurs ... vous utilisez l'index pour les appels d'API système, et le système peut changer le pointeur dans la table à volonté.
En variante, un pointeur réel peut être donné comme descripteur lorsque l'auteur de l'API a l'intention que l'utilisateur de l'API soit isolé des spécificités de ce vers quoi l'adresse renvoyée pointe; dans ce cas il faut considérer que ce vers quoi pointe le handle peut changer à tout moment (de version d'API à version ou même d'appel à appel de l'API qui renvoie le handle) - le handle doit donc être traité comme une simple valeur opaque significatif uniquement pour l'API.
Je dois ajouter que dans n'importe quel système d'exploitation moderne, même les soi-disant "vrais pointeurs" sont toujours des poignées opaques dans l'espace mémoire virtuelle du processus, ce qui permet à l'O / S de gérer et de réorganiser la mémoire sans invalider les pointeurs dans le processus .
A HANDLE
est un identifiant unique spécifique au contexte. Par contexte spécifique, je veux dire qu'un descripteur obtenu à partir d'un contexte ne peut pas nécessairement être utilisé dans un autre contexte aribtraire qui fonctionne également sur l' HANDLE
art.
Par exemple, GetModuleHandle
renvoie un identifiant unique à un module actuellement chargé. Le handle retourné peut être utilisé dans d'autres fonctions qui acceptent les handles de module. Il ne peut pas être attribué aux fonctions qui nécessitent d'autres types de poignées. Par exemple, vous ne pouvez pas donner un handle retourné de GetModuleHandle
à HeapDestroy
et vous attendre à ce qu'il fasse quelque chose de sensé.
Le HANDLE
lui-même n'est qu'un type intégral. Il s'agit généralement, mais pas nécessairement, d'un pointeur vers un type ou un emplacement mémoire sous-jacent. Par exemple, le HANDLE
renvoyé par GetModuleHandle
est en fait un pointeur vers l'adresse de mémoire virtuelle de base du module. Mais il n'y a pas de règle stipulant que les poignées doivent être des pointeurs. Un handle peut également être simplement un simple entier (qui pourrait éventuellement être utilisé par une API Win32 comme index dans un tableau).
HANDLE
Les s sont des représentations intentionnellement opaques qui fournissent l'encapsulation et l'abstraction des ressources Win32 internes. De cette façon, les API Win32 pourraient potentiellement changer le type sous-jacent derrière un HANDLE, sans que cela n'affecte le code utilisateur de quelque manière que ce soit (du moins c'est l'idée).
Considérez ces trois implémentations internes différentes d'une API Win32 que je viens de créer et supposez que Widget
c'est un fichier 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);
}
Le premier exemple expose les détails internes de l'API: il permet au code utilisateur de savoir qui GetWidget
renvoie un pointeur vers un struct Widget
. Cela a quelques conséquences:
Widget
structureWidget
structure retournéeCes deux conséquences peuvent être indésirables.
Le deuxième exemple masque ce détail interne du code utilisateur, en renvoyant juste void *
. Le code utilisateur n'a pas besoin d'accéder à l'en-tête qui définit la Widget
structure.
Le troisième exemple est exactement le même que le deuxième, mais nous appelons simplement le void *
a à la HANDLE
place. Cela décourage peut-être le code utilisateur d'essayer de comprendre exactement à quoi il void *
pointe.
Pourquoi traverser ce problème? Considérez ce quatrième exemple d'une version plus récente de cette même API:
typedef void * HANDLE;
HANDLE GetWidget (std::string name)
{
NewImprovedWidget *w;
w = findImprovedWidget(name);
return reinterpret_cast<HANDLE>(w);
}
Notez que l'interface de la fonction est identique au troisième exemple ci-dessus. Cela signifie que le code utilisateur peut continuer à utiliser cette nouvelle version de l'API, sans aucune modification, même si l'implémentation "en coulisses" a changé pour utiliser la NewImprovedWidget
structure à la place.
Les poignées de cet exemple ne sont en réalité qu'un nouveau nom, vraisemblablement plus convivial, void *
qui est exactement ce que a HANDLE
dans l'API Win32 (recherchez-le sur MSDN ). Il fournit un mur opaque entre le code utilisateur et les représentations internes de la bibliothèque Win32 qui augmente la portabilité, entre les versions de Windows, du code qui utilise l'API Win32.
handle
au lieu de void *
est décourage le code utilisateur d'essayer de comprendre exactement ce que le void * pointe . Ai-je raison?
Un HANDLE dans la programmation Win32 est un jeton qui représente une ressource gérée par le noyau Windows. Un handle peut être vers une fenêtre, un fichier, etc.
Les poignées sont simplement un moyen d'identifier une ressource particulaire avec laquelle vous souhaitez travailler à l'aide des API Win32.
Ainsi, par exemple, si vous souhaitez créer une fenêtre et l'afficher à l'écran, vous pouvez effectuer les opérations suivantes:
// Create the window
HWND hwnd = CreateWindow(...);
if (!hwnd)
return; // hwnd not created
// Show the window.
ShowWindow(hwnd, SW_SHOW);
Dans l'exemple ci-dessus, HWND signifie "un handle vers une fenêtre".
Si vous êtes habitué à un langage orienté objet, vous pouvez considérer un HANDLE comme une instance d'une classe sans méthode dont l'état n'est modifiable que par d'autres fonctions. Dans ce cas, la fonction ShowWindow modifie l'état de la poignée de fenêtre.
Voir Poignées et types de données pour plus d'informations.
HANDLE
ADT sont gérés par le noyau. Les autres types de descripteurs que vous nommez ( HWND
, etc.) sont en revanche des objets USER. Ceux-ci ne sont pas gérés par le noyau Windows.
Un handle est un identifiant unique pour un objet géré par Windows. C'est comme un pointeur , mais pas un pointeur dans le sens que ce n'est pas une adresse qui pourrait être déréférencée par le code utilisateur pour accéder à certaines données. Au lieu de cela, un handle doit être passé à un ensemble de fonctions qui peuvent effectuer des actions sur l'objet identifié par le handle.
Ainsi, au niveau le plus élémentaire, un HANDLE de toute sorte est un pointeur vers un pointeur ou
#define HANDLE void **
Maintenant, pourquoi vous voudriez l'utiliser
Prenons une configuration:
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);
}
Donc, comme obj a été passé par valeur (faites une copie et donnez-la à la fonction) à foo, le printf affichera la valeur d'origine de 1.
Maintenant, si nous mettons à jour foo vers:
void foo(Object * bar)
{
LargeObj lo = new LargeObj();
bar->val++;
}
Il y a une chance que le printf affiche la valeur mise à jour de 2. Mais il y a aussi la possibilité que foo provoque une forme de corruption de mémoire ou d'exception.
La raison est la suivante, alors que vous utilisez maintenant un pointeur pour passer obj à la fonction que vous allouez également 2 Mo de mémoire, cela pourrait amener le système d'exploitation à déplacer la mémoire en mettant à jour l'emplacement de obj. Puisque vous avez passé le pointeur par valeur, si obj est déplacé, le système d'exploitation met à jour le pointeur mais pas la copie dans la fonction et peut causer des problèmes.
Une dernière mise à jour de toto de:
void foo(Object **bar){
LargeObj lo = LargeObj();
Object * b = &bar;
b->val++;
}
Cela imprimera toujours la valeur mise à jour.
Voir, lorsque le compilateur alloue de la mémoire pour les pointeurs, il les marque comme immobiles, de sorte que tout remaniement de la mémoire causé par le grand objet alloué à la valeur transmise à la fonction pointera vers l'adresse correcte pour trouver l'emplacement final en mémoire vers mettre à jour.
Tous les types particuliers de HANDLE (hWnd, FILE, etc.) sont spécifiques au domaine et pointent vers un certain type de structure pour se protéger contre la corruption de la mémoire.
Un handle est comme une valeur de clé primaire d'un enregistrement dans une base de données.
edit 1: eh bien, pourquoi le downvote, une clé primaire identifie de manière unique un enregistrement de base de données, et une poignée dans le système Windows identifie de manière unique une fenêtre, un fichier ouvert, etc., c'est ce que je dis.
Considérez la fenêtre sous Windows comme une structure qui la décrit. Cette structure est une partie interne de Windows et vous n'avez pas besoin d'en connaître les détails. Au lieu de cela, Windows fournit un typedef pour le pointeur vers struct pour cette structure. C'est la "poignée" par laquelle vous pouvez saisir la fenêtre.,