Question

Qu'est-ce qu'un « Handle » lors de l'examen des ressources dans Windows? Comment fonctionnent-ils?

Était-ce utile?

La solution

Il est une valeur de référence abstraite à une ressource, souvent mémoire ou un fichier ouvert, ou un tuyau.

correctement , dans Windows, (et généralement en informatique) une poignée est une abstraction qui cache une adresse réelle de la mémoire de l'utilisateur de l'API, ce qui permet au système de réorganiser la mémoire physique de manière transparente au programme. Résolution d'une poignée en un pointeur verrouille la mémoire, et en relâchant la poignée invalide le pointeur. Dans ce cas, pensez comme un index dans une table de pointeurs ... vous utilisez l'index pour les appels API du système, et le système peut changer le pointeur dans la table à volonté.

Sinon un vrai pointeur peut être donné que la poignée lorsque l'auteur de l'API entend que l'utilisateur de l'API être isolée des détails de ce que l'adresse est revenu à des points; dans ce cas, il faut considérer que ce que les points de poignée pour peut changer à tout moment (de la version de l'API à la version ou même d'un appel à l'API qui retourne la poignée) - la poignée doit donc être traitée comme une simple valeur opaque significative que à l'API.

Je dois ajouter que dans tout système d'exploitation moderne, même les soi-disant « vrais pointeurs » sont encore des poignées opaques dans l'espace de 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.

Autres conseils

A HANDLE est un identifiant unique propre au contexte. Je veux dire par du contexte, qu'une poignée obtenue d'un contexte ne peut pas nécessairement être utilisé dans tout autre contexte aribtrary qui fonctionne également sur HANDLEs.

Par exemple, GetModuleHandle renvoie un identifiant unique à un module chargé. La poignée de retour peut être utilisé dans d'autres fonctions qui acceptent les poignées de module. Il ne peut être donné aux fonctions qui nécessitent d'autres types de poignées. Par exemple, on ne peut pas donner une poignée de retour GetModuleHandle à HeapDestroy et attendre qu'il fasse quelque chose de sensé.

Le HANDLE lui-même est juste un type intégral. En général, mais pas nécessairement, il est un pointeur sur un certain type sous-jacent ou d'un emplacement de mémoire. Par exemple, le HANDLE retourné par GetModuleHandle est en fait un pointeur sur l'adresse de mémoire virtuelle base du module. Mais il n'y a pas de règle indiquant que les poignées doivent être des pointeurs. Une poignée pourrait aussi être juste un simple entier (qui pourrait éventuellement être utilisé par une API Win32 comme un index dans un tableau).

HANDLEs sont intentionnellement des représentations opaques qui fournissent l'encapsulation et l'abstraction des ressources Win32 internes. De cette façon, les API Win32 pourrait changer le type sous-jacent derrière une poignée, sans qu'il code d'utilisateur impactant de quelque manière que (au moins c'est l'idée).

Tenez compte de ces trois différentes implémentations internes d'une API Win32 que je viens de faire, et supposons que Widget est 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);
}

Le premier exemple expose les détails internes sur l'API: il permet au code utilisateur de savoir que GetWidget renvoie un pointeur sur un struct Widget. Cela a deux conséquences:

  • le code utilisateur doit avoir accès au fichier d'en-tête qui définit la struct Widget
  • le code utilisateur pourrait potentiellement modifier les parties internes de la structure de Widget retourné

Ces deux conséquences peuvent être indésirables.

Le second exemple se cache ce détail interne du code utilisateur, en retournant simplement void *. Le code utilisateur n'a pas besoin d'avoir accès à l'en-tête qui définit la struct Widget.

Le troisième exemple est exactement le même que le second, mais il suffit d'appeler le void * un HANDLE à la place. Peut-être que cela décourage le code utilisateur d'essayer de comprendre exactement ce que les points de void * à.

Pourquoi passer par ce trouble? 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);
}

Remarquez identique au troisième exemple ci-dessus l'interface de la fonction. Cela signifie que le code utilisateur peut continuer à utiliser cette nouvelle version de l'API, sans aucun changement, même si les « coulisses » la mise en œuvre a changé d'utiliser la struct NewImprovedWidget à la place.

Les poignées de ces exemples sont vraiment juste une nouvelle, sans doute plus convivial, le nom de void *, ce qui est exactement ce que HANDLE est dans l'API Win32 (le chercher à MSDN ). Il fournit une paroi opaque entre le code d'utilisateur et représentations internes de la bibliothèque Win32 qui augmente la portabilité, entre les versions de Windows, de code qui utilise l'API Win32.

Une poignée dans la programmation Win32 est un jeton qui représente une ressource qui est gérée par le noyau Windows. Une poignée peut être à une fenêtre, un fichier, etc.

Les poignées sont simplement un moyen d'identifier une ressource de particules que vous voulez travailler avec l'aide des API Win32.

Ainsi par exemple, si vous voulez créer une fenêtre et l'afficher sur l'écran, vous pouvez faire ce qui suit:

// 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 « une poignée à une fenêtre ».

Si vous êtes habitué à un langage orienté objet, vous pouvez penser à une poignée comme une instance d'une classe sans méthodes qui est l'état est uniquement modifiable par d'autres fonctions. Dans ce cas, le ShowWindow fonction modifie l'état de la poignée de fenêtre.

Voir Poignées et types de données pour plus d'informations.

Une poignée est un identifiant unique pour un objet géré par Windows. Il est comme un pointeur , mais pas un pointeur dans le qu'il est sence pas une adresse qui pourrait être déréférencé par code utilisateur pour accéder à certaines données. Au lieu de cela une poignée doit être transmise à un ensemble de fonctions qui peuvent effectuer des actions sur l'objet de la poignée identifie.

Une poignée est comme une valeur de clé primaire d'un enregistrement dans une base de données.

modifier 1: Pourquoi la 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

.

Ainsi, au niveau le plus élémentaire d'une poignée de toute sorte est un pointeur vers un pointeur ou

#define HANDLE void **

Maintenant, pourquoi vous voulez utiliser

Permet de prendre 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);
}

parce que obj a été adoptée par la valeur (faire une copie et que donner à la fonction) à foo, le printf imprime la valeur initiale de 1.

Maintenant, si nous mettons à jour foo à:

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

Il y a une chance que le printf affichera la valeur actualisée de 2. Mais il y a aussi la possibilité que foo provoquera une certaine forme de corruption de mémoire ou une exception.

La raison est pendant que vous utilisez actuellement un pointeur pour passer obj à la fonction que vous allouez également 2 Mo de mémoire, ce qui pourrait provoquer le système d'exploitation pour déplacer la mémoire autour de la mise à jour de l'emplacement obj. Puisque vous avez passé le pointeur en valeur, si obj est déplacé alors le système d'exploitation met à jour le pointeur, mais pas la copie dans la fonction et les problèmes potentiellement causant.

Une mise à jour finale de foo:

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

Ce sera toujours imprimer la valeur actualisée.

Vous voyez, quand le compilateur alloue de la mémoire pour les pointeurs il les marque comme immobile, de sorte que toute re-brassage de la mémoire provoquée par le grand objet étant affecté la valeur passée à la fonction pointera à l'adresse correcte pour connaître l'emplacement final dans la mémoire à mettre à jour.

Tous les types de handles (hWnd, fichier, etc.) sont du domaine spécifique et un point à un certain type de structure pour protéger contre la corruption de la mémoire.

Pensez à la fenêtre dans Windows comme étant un struct qui la décrit. Cette structure est une partie interne de Windows et vous n'avez pas besoin de connaître les détails. Au lieu de cela, Windows fournit un typedef pour pointeur sur struct pour cette struct. C'est la « poignée » par lequel vous pouvez mettre la main sur la fenêtre.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top