Domanda

Che cosa è un "manico" quando si parla di risorse in Windows? Come funzionano?

È stato utile?

Soluzione

Si tratta di un valore di riferimento astratto a una risorsa, spesso di memoria o un file aperto, o un tubo.

correttamente , in Windows, (e in generale nel settore informatico) una maniglia è un'astrazione che nasconde un indirizzo di memoria vera e propria da parte dell'utente API, consentendo al sistema di riorganizzare la memoria fisica in modo trasparente al programma. Risolto un manico in un puntatore blocca la memoria, e rilasciando la maniglia invalida il puntatore. In questo caso pensa di esso come un indice in una tabella di puntatori ... si utilizza l'indice per le chiamate di sistema API, e il sistema può cambiare il puntatore nella tabella a volontà.

In alternativa un vero puntatore può essere dato come la maniglia quando lo scrittore API intende che l'utente del API essere isolata dalle specifiche di quello dell'indirizzo restituito indica; in questo caso occorre considerare che ciò che punti la maniglia per possono cambiare in qualsiasi momento (dalla versione API alla versione o anche da chiamata a chiamata delle API che restituisce l'handle) - la maniglia deve essere trattata come un semplice valore opaca significativa solo per l'API.

Vorrei aggiungere che in qualsiasi sistema operativo moderno, anche i cosiddetti "puntatori reali" sono ancora maniglie opachi nello spazio di memoria virtuale del processo, che consente l'O / S per gestire e riorganizzare la memoria senza invalidare i puntatori all'interno del processo.

Altri suggerimenti

Un HANDLE è un identificatore univoco contesto specifico. Da specifico contesto, intendo che una maniglia ricavata da un contesto non può necessariamente essere utilizzato in qualsiasi altro contesto aribtrary che funziona anche su HANDLEs.

Per esempio, GetModuleHandle restituisce un identificativo univoco a un modulo attualmente caricato. L'handle restituito può essere utilizzato in altre funzioni che accettano le maniglie dei moduli. Non può essere dato a funzioni che richiedono altri tipi di maniglie. Ad esempio, non si poteva dare un handle restituito da GetModuleHandle a HeapDestroy e si aspettano di fare qualcosa di sensato.

Il HANDLE stesso è solo un tipo intero. Di solito, ma non necessariamente, è un puntatore a qualche tipo sottostante o locazione di memoria. Ad esempio, il HANDLE restituito da GetModuleHandle è in realtà un puntatore all'indirizzo base della memoria virtuale del modulo. Ma non esiste una regola che indica che le maniglie devono essere puntatori. Una maniglia potrebbe anche essere solo un numero intero semplice (che potrebbe essere utilizzato da alcuni API Win32 come un indice in un array).

HANDLEs sono volutamente rappresentazioni opachi che forniscono l'incapsulamento e astrazione dalle risorse interne Win32. In questo modo, le API Win32 potrebbe potenzialmente modificare il tipo sottostante dietro una maniglia, senza codice utente impattante in qualsiasi modo (almeno questa è l'idea).

Considerate queste tre diverse implementazioni interne di un'API Win32 che ho appena inventato, e supponiamo che Widget è 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 circa l'API: permette il codice utente per sapere che GetWidget restituisce un puntatore ad un struct Widget. Questo ha un paio di conseguenze:

  • il codice utente deve avere accesso al file di intestazione che definisce lo struct Widget
  • il codice utente potrebbe potenzialmente modificare le parti interne della struct Widget restituito

Entrambe queste conseguenze possono essere indesiderabile.

Il secondo esempio nasconde questo dettaglio interno dal codice utente, restituendo solo void *. Il codice utente non deve accedere alla intestazione che definisce la struct Widget.

Il terzo esempio è esattamente lo stesso come il secondo, ma basta chiamare il void * un HANDLE invece. Forse questo scoraggia codice utente dal cercare di capire esattamente quali sono i punti di void *.

Perché passare attraverso questo guaio? Considerate 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 alla terza esempio precedente. Ciò significa che il codice utente può continuare a utilizzare questa nuova versione delle API, senza alcuna modifica, anche se l'attuazione "dietro le quinte" ha cambiato per utilizzare lo struct NewImprovedWidget invece.

Le maniglie in questi esempio sono in realtà solo un nuovo, presumibilmente più amichevole, nome per void *, che è esattamente ciò che un HANDLE è in API Win32 (lo guardi su a MSDN ). Esso fornisce una parete opaca tra il codice utente e rappresentazioni interne della biblioteca Win32 che aumenta la portabilità, tra le versioni di Windows, di codice che utilizza l'API Win32.

Una maniglia nella programmazione Win32 è un token che rappresenta una risorsa che è gestito dal kernel di Windows. Una maniglia può essere quello di una finestra, un file, ecc.

Le maniglie sono semplicemente un modo di identificare una risorsa particolato che si desidera lavorare utilizzando le API Win32.

Così, per esempio, se si vuole creare una finestra, e mostrarlo sullo schermo si potrebbe procedere come segue:

// Create the window
HWND hwnd = CreateWindow(...); 
if (!hwnd)
   return; // hwnd not created

// Show the window.
ShowWindow(hwnd, SW_SHOW);

Nell'esempio precedente HWND significa "un handle a una finestra".

Se siete abituati a un linguaggio orientato agli oggetti si può pensare a una maniglia come un'istanza di una classe senza metodi che stato è modificabile solo da altre funzioni. In questo caso il ShowWindow funzione modifica lo stato della maniglia della finestra.

Maniglie e tipi di dati per ulteriori informazioni.

Una maniglia è un identificatore univoco per un oggetto gestito da Windows. E ' come un puntatore , ma non un puntatore nel sence che non è un indirizzo che potrebbe essere referenziato da codice utente per ottenere l'accesso ad alcuni dati. Invece una maniglia deve essere passato ad una serie di funzioni che possono eseguire azioni sull'oggetto maniglia identifica.

Una maniglia è come un valore della chiave primaria di un record in un database.

modifica 1: bene, perché la downvote, una chiave primaria identifica in modo univoco un record di database, e una maniglia nel sistema di Windows identifica in modo univoco una finestra, un file aperto, ecc, questo è quello che sto dicendo

.

Quindi, al livello più elementare un manico di qualsiasi tipo è un puntatore a un puntatore o

#define HANDLE void **

Ora, il motivo per cui si vorrebbe usarlo

consente di dare una messa a punto:

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, a causa obj è stato passato per valore (fare una copia e dare che alla funzione) a foo, il printf stamperà il valore originale di 1.

Ora, se aggiorniamo foo a:

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

C'è una possibilità che il printf stampa il valore aggiornato di 2. Ma v'è anche la possibilità che foo causerà qualche forma di corruzione della memoria o eccezione.

Il motivo è questo, mentre ora si sta utilizzando un puntatore a passare obj alla funzione che si sta anche l'assegnazione del 2 MB di memoria, questo potrebbe causare il sistema operativo per spostare la memoria intorno aggiornamento della posizione di obj. Dal momento che si hanno superato il puntatore per valore, se obj viene spostato poi il sistema operativo aggiorna il puntatore, ma non la copia nella funzione e problemi potenzialmente causano.

Un aggiornamento finale per foo di:

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

Questo sarà sempre stampare il valore aggiornato.

Vedere, quando il compilatore alloca memoria per i puntatori li contrassegna come inamovibile, quindi qualsiasi rimescolamento della memoria causato dal gran oggetto alloca il valore passato alla funzione punterà all'indirizzo corretto per trovare la posizione finale in memoria per aggiornare.

Le eventuali particolari tipi di maniglie (hWnd, FILE, ecc) sono specifiche del dominio e puntare a un certo tipo di struttura per la protezione contro la corruzione della memoria.

Si pensi della finestra in Windows come una struttura che lo descrive. Questa struct è una parte interna di Windows e non è necessario conoscere i dettagli di esso. Invece, Windows fornisce un typedef per il puntatore a struct per quella struct. Questo è il "manico" con cui è possibile entrare in possesso sulla finestra.,

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top