Condividi tramite


Punto di ingresso DllMain

Punto di ingresso facoltativo in una libreria a collegamento dinamico (DLL). Quando il sistema avvia o termina un processo o un thread, chiama la funzione del punto di ingresso per ogni DLL caricata usando il primo thread del processo. Il sistema chiama anche la funzione del punto di ingresso per una DLL quando viene caricata o scaricata usando le funzioni LoadLibrary e FreeLibrary .

Esempio

BOOL WINAPI DllMain(
    HINSTANCE hinstDLL,  // handle to DLL module
    DWORD fdwReason,     // reason for calling function
    LPVOID lpvReserved )  // reserved
{
    // Perform actions based on the reason for calling.
    switch( fdwReason ) 
    { 
        case DLL_PROCESS_ATTACH:
         // Initialize once for each new process.
         // Return FALSE to fail DLL load.
            break;

        case DLL_THREAD_ATTACH:
         // Do thread-specific initialization.
            break;

        case DLL_THREAD_DETACH:
         // Do thread-specific cleanup.
            break;

        case DLL_PROCESS_DETACH:
        
            if (lpvReserved != nullptr)
            {
                break; // do not do cleanup if process termination scenario
            }
            
         // Perform any necessary cleanup.
            break;
    }
    return TRUE;  // Successful DLL_PROCESS_ATTACH.
}

Questo è un esempio della libreria di collegamento dinamico Entry-Point funzione.

Avviso

Esistono limiti significativi sulle operazioni che è possibile eseguire in modo sicuro in un punto di ingresso dll. Vedere Procedure consigliate generali per API Windows specifiche che non sono sicure da chiamare in DllMain. Se è necessario qualcosa di diverso dall'inizializzazione più semplice, eseguire questa operazione in una funzione di inizializzazione per la DLL. È possibile richiedere alle applicazioni di chiamare la funzione di inizializzazione dopo l'esecuzione di DllMain e prima di chiamare qualsiasi altra funzione nella DLL.

Sintassi

BOOL WINAPI DllMain(
  _In_ HINSTANCE hinstDLL,
  _In_ DWORD     fdwReason,
  _In_ LPVOID    lpvReserved
);

Parametri

hinstDLL [in]

Handle per il modulo DLL. Il valore è l'indirizzo di base della DLL. L'HINSTANCE di una DLL è uguale a HMODULE della DLL, quindi hinstDLL può essere usato nelle chiamate a funzioni che richiedono un handle di modulo.

fdwReason [in]

Codice motivo che indica il motivo per cui viene chiamata la funzione del punto di ingresso della DLL. Questo parametro può avere uno dei valori seguenti.

Valore Significato
DLL_PROCESS_ATTACH
1
La DLL viene caricata nello spazio degli indirizzi virtuali del processo corrente in seguito all'avvio del processo o in seguito a una chiamata a LoadLibrary. Le DLL possono usare questa opportunità per inizializzare tutti i dati dell'istanza o per usare la funzione TlsAlloc per allocare un indice tls (Thread Local Storage).
Il parametro lpvReserved indica se la DLL viene caricata in modo statico o dinamico.
DLL_PROCESS_DETACH
0
La DLL viene scaricata dallo spazio indirizzi virtuale del processo chiamante perché è stata caricata in modo non riuscito o il conteggio dei riferimenti ha raggiunto zero (i processi hanno terminato o chiamato FreeLibrary una volta per ogni volta che ha chiamato LoadLibrary).
Il parametro lpvReserved indica se la DLL viene scaricata in seguito a una chiamata FreeLibrary , a un errore di caricamento o alla terminazione del processo.
La DLL può usare questa opportunità per chiamare la funzione TlsFree per liberare tutti gli indici TLS allocati usando TlsAlloc e per liberare i dati locali del thread.
Si noti che il thread che riceve la notifica di DLL_PROCESS_DETACH non è necessariamente lo stesso thread che ha ricevuto la notifica di DLL_PROCESS_ATTACH .
DLL_THREAD_ATTACH
2
Il processo corrente sta creando un nuovo thread. In questo caso, il sistema chiama la funzione del punto di ingresso di tutte le DLL attualmente collegate al processo. La chiamata viene effettuata nel contesto del nuovo thread. Le DLL possono usare questa opportunità per inizializzare uno slot TLS per il thread. Un thread che chiama la funzione del punto di ingresso dll con DLL_PROCESS_ATTACH non chiama la funzione del punto di ingresso DLL con DLL_THREAD_ATTACH.
Si noti che la funzione del punto di ingresso di una DLL viene chiamata con questo valore solo dai thread creati dopo che la DLL viene caricata dal processo. Quando una DLL viene caricata tramite LoadLibrary, i thread esistenti non chiamano la funzione del punto di ingresso della DLL appena caricata.
DLL_THREAD_DETACH
3
Un thread viene chiuso in modo pulito. Se la DLL ha archiviato un puntatore alla memoria allocata in uno slot TLS, è consigliabile usare questa opportunità per liberare la memoria. Il sistema chiama la funzione del punto di ingresso di tutte le DLL attualmente caricate con questo valore. La chiamata viene effettuata nel contesto del thread di uscita.

lpvReserved [in]

Se fdwReason è DLL_PROCESS_ATTACH, lpvReserved è NULL per i caricamenti dinamici e non NULL per i caricamenti statici.

Se fdwReason è DLL_PROCESS_DETACH, lpvReserved è NULL se FreeLibrary è stato chiamato o il caricamento dll non è riuscito e non NULL se il processo termina.

Valore restituito

Quando il sistema chiama la funzione DllMain con il valore DLL_PROCESS_ATTACH , la funzione restituisce TRUE se ha esito positivo o FALSE se l'inizializzazione ha esito negativo. Se il valore restituito è FALSE quando Viene chiamato DllMain perché il processo usa la funzione LoadLibrary , LoadLibrary restituisce NULL. Il sistema chiama immediatamente la funzione del punto di ingresso con DLL_PROCESS_DETACH e scarica la DLL. Se il valore restituito è FALSE quando DllMain viene chiamato durante l'inizializzazione del processo, il processo termina con un errore. Per informazioni dettagliate sull'errore, chiamare GetLastError.

Quando il sistema chiama la funzione DllMain con qualsiasi valore diverso da DLL_PROCESS_ATTACH, il valore restituito viene ignorato.

Commenti

DllMain è un segnaposto per il nome della funzione definito dalla libreria. È necessario specificare il nome effettivo usato durante la compilazione della DLL. Per altre informazioni, vedere la documentazione inclusa negli strumenti di sviluppo.

Durante l'avvio iniziale del processo o dopo una chiamata a LoadLibrary, il sistema analizza l'elenco di DLL caricate per il processo. Per ogni DLL che non è già stata chiamata con il valore DLL_PROCESS_ATTACH , il sistema chiama la funzione del punto di ingresso della DLL. Questa chiamata viene eseguita nel contesto del thread che ha causato la modifica dello spazio degli indirizzi del processo, ad esempio il thread primario del processo o il thread che ha chiamato LoadLibrary. L'accesso al punto di ingresso viene serializzato dal sistema a livello di processo. I thread in DllMain contengono il blocco del caricatore in modo che non sia possibile caricare o inizializzare dinamicamente dll aggiuntive.

Se la funzione del punto di ingresso della DLL restituisce FALSE dopo una notifica di DLL_PROCESS_ATTACH , riceve una notifica di DLL_PROCESS_DETACH e la DLL viene scaricata immediatamente. Tuttavia, se il codice DLL_PROCESS_ATTACH genera un'eccezione, la funzione del punto di ingresso non riceverà la notifica di DLL_PROCESS_DETACH .

Esistono casi in cui la funzione del punto di ingresso viene chiamata per un thread di terminazione anche se la funzione del punto di ingresso non è mai stata chiamata con DLL_THREAD_ATTACH per il thread:

  • Il thread era il thread iniziale nel processo, quindi il sistema ha chiamato la funzione del punto di ingresso con il valore DLL_PROCESS_ATTACH .
  • Il thread era già in esecuzione quando è stata effettuata una chiamata alla funzione LoadLibrary , quindi il sistema non ha mai chiamato la funzione del punto di ingresso.

Quando una DLL viene scaricata da un processo in seguito a un caricamento non riuscito della DLL, alla chiusura del processo o a una chiamata a FreeLibrary, il sistema non chiama la funzione del punto di ingresso della DLL con il valore DLL_THREAD_DETACH per i singoli thread del processo. La DLL viene inviata solo una notifica di DLL_PROCESS_DETACH . Le DLL possono sfruttare questa opportunità per pulire tutte le risorse per tutti i thread noti alla DLL.

Quando si gestisce DLL_PROCESS_DETACH, una DLL deve liberare risorse come la memoria heap solo se la DLL viene scaricata dinamicamente (il parametro lpvReserved è NULL). Se il processo termina (il parametro lpvReserved è diverso da NULL), tutti i thread del processo, ad eccezione del thread corrente sono già usciti o sono stati terminati in modo esplicito da una chiamata alla funzione ExitProcess , che potrebbe lasciare alcune risorse di processo, ad esempio gli heap in uno stato incoerente. In questo caso, non è sicuro che la DLL pulisca le risorse. Al contrario, la DLL deve consentire al sistema operativo di recuperare la memoria.

Se si termina un processo chiamando TerminateProcess o TerminateJobObject, le DLL di tale processo non ricevono notifiche DLL_PROCESS_DETACH . Se si termina un thread chiamando TerminateThread, le DLL del thread non ricevono notifiche DLL_THREAD_DETACH .

La funzione del punto di ingresso deve eseguire solo semplici attività di inizializzazione o terminazione. Non deve chiamare la funzione LoadLibrary o LoadLibraryEx (o una funzione che chiama queste funzioni), perché ciò può creare cicli di dipendenza nell'ordine di caricamento dll. Ciò può comportare l'uso di una DLL prima che il sistema abbia eseguito il codice di inizializzazione. Analogamente, la funzione del punto di ingresso non deve chiamare la funzione FreeLibrary (o una funzione che chiama FreeLibrary) durante la terminazione del processo, perché ciò può comportare l'uso di una DLL dopo che il sistema ha eseguito il codice di terminazione.

Poiché Kernel32.dll è garantito che venga caricato nello spazio indirizzi del processo quando viene chiamata la funzione del punto di ingresso, la chiamata di funzioni in Kernel32.dll non comporta l'uso della DLL prima dell'esecuzione del codice di inizializzazione. Pertanto, la funzione del punto di ingresso può chiamare funzioni in Kernel32.dll che non caricano altre DLL. Ad esempio, DllMain può creare oggetti di sincronizzazione come sezioni critiche e mutex e usare TLS. Sfortunatamente, non esiste un elenco completo di funzioni sicure in Kernel32.dll.

La chiamata di funzioni che richiedono DLL diverse da Kernel32.dll può causare problemi difficili da diagnosticare. Ad esempio, la chiamata di funzioni User, Shell e COM può causare errori di violazione di accesso, perché alcune funzioni caricano altri componenti di sistema. Al contrario, le funzioni di chiamata come queste durante la terminazione possono causare errori di violazione di accesso perché il componente corrispondente potrebbe essere già stato scaricato o non inizializzato.

Poiché le notifiche DLL vengono serializzate, le funzioni del punto di ingresso non devono tentare di comunicare con altri thread o processi. I deadlock possono verificarsi di conseguenza.

Per informazioni sulle procedure consigliate per la scrittura di una DLL, vedere Procedure consigliate per la libreria a collegamento dinamico.

Se la DLL è collegata alla libreria di runtime C (CRT), il punto di ingresso fornito da CRT chiama i costruttori e i distruttori per gli oggetti C++ globali e statici. Pertanto, queste restrizioni per DllMain si applicano anche a costruttori e distruttori e a qualsiasi codice che viene chiamato da essi.

Prendere in considerazione la chiamata a DisableThreadLibraryCalls durante la ricezione di DLL_PROCESS_ATTACH, a meno che la DLL non sia collegata con la libreria di runtime C statica (CRT).

Requisiti

Requisito Valore
Client minimo supportato
Windows XP [solo app desktop]
Server minimo supportato
Windows Server 2003 [solo app desktop]
Intestazione
Process.h

Vedi anche

Funzione Entry-Point libreria di collegamenti dinamici

Funzioni della libreria di collegamento dinamico

Freelibrary

GetModuleFileName

LoadLibrary

Tlsalloc

TlsFree

DisableThreadLibraryCalls