Informazioni sulla funzione helper di caricamento ritardato

La funzione helper per il caricamento ritardato supportato dal linker è ciò che carica effettivamente la DLL in fase di esecuzione. È possibile modificare la funzione helper per personalizzarne il comportamento. Anziché usare la funzione helper fornita in delayimp.lib, scrivere la propria funzione e collegarla al programma. Una funzione helper serve tutte le DLL caricate in ritardo.

È possibile specificare la propria versione della funzione helper se si vuole eseguire un'elaborazione specifica in base ai nomi della DLL o delle importazioni.

La funzione helper esegue queste azioni:

  • Controlla l'handle archiviato nella libreria per verificare se è già stato caricato

  • Chiamate LoadLibrary per tentare di caricare la DLL

  • Chiamate GetProcAddress per tentare di ottenere l'indirizzo della procedura

  • Torna al caricamento di caricamento ritardato di importazione per chiamare il punto di ingresso ora caricato

La funzione helper può richiamare un hook di notifica nel programma dopo ognuna delle azioni seguenti:

  • All'avvio della funzione helper

  • Appena prima LoadLibrary viene chiamato nella funzione helper

  • Appena prima GetProcAddress viene chiamato nella funzione helper

  • Se la chiamata a LoadLibrary nella funzione helper ha esito negativo

  • Se la chiamata a GetProcAddress nella funzione helper ha esito negativo

  • Dopo aver completato l'elaborazione della funzione helper

Ognuno di questi punti hook può restituire un valore che modifica in qualche modo la normale elaborazione della routine helper, ad eccezione del ritorno al caricamento di caricamento ritardato di importazione.

Il codice helper predefinito è disponibile in delayhlp.cpp e delayimp.h nella directory MSVC include . Viene compilato delayimp.lib nella directory MSVC lib per l'architettura di destinazione. Sarà necessario includere questa libreria nelle compilazioni, a meno che non si scriva la propria funzione helper.

Ritardare le convenzioni di chiamata dell'helper di caricamento, i parametri e il tipo restituito

Il prototipo per la routine helper di caricamento ritardato è:

FARPROC WINAPI __delayLoadHelper2(
    PCImgDelayDescr pidd,
    FARPROC * ppfnIATEntry
);

Parametri

pidd
Puntatore const a un ImgDelayDescr oggetto che contiene gli offset di vari dati correlati all'importazione, un timestamp per le informazioni di associazione e un set di attributi che forniscono ulteriori informazioni sul contenuto del descrittore. Attualmente è presente un solo attributo, dlattrRva, che indica che gli indirizzi nel descrittore sono indirizzi virtuali relativi. Per altre informazioni, vedere le dichiarazioni in delayimp.h.

I puntatori nel descrittore di ritardo (ImgDelayDescr in delayimp.h) usano indirizzi virtuali relativi (RVA) per funzionare come previsto nei programmi a 32 bit e a 64 bit. Per usarli, convertire di nuovo questi RVA in puntatori usando la funzione PFromRva, disponibile in delayhlp.cpp. È possibile usare questa funzione in ognuno dei campi del descrittore per convertirli di nuovo in puntatori a 32 bit o a 64 bit. La funzione helper di caricamento ritardo predefinita è un modello valido da usare come esempio.

Per la definizione della PCImgDelayDescr struttura, vedere Struttura e definizioni costanti.

ppfnIATEntry
Puntatore a uno slot nella tabella degli indirizzi di importazione del caricamento ritardato (IAT). Si tratta dello slot aggiornato con l'indirizzo della funzione importata. La routine helper deve archiviare lo stesso valore restituito in questa posizione.

Valori restituiti previsti

Se la funzione helper ha esito positivo, restituisce l'indirizzo della funzione importata.

Se la funzione ha esito negativo, genera un'eccezione strutturata e restituisce 0. Possono essere generati tre tipi di eccezioni:

  • Parametro non valido, se gli attributi in pidd non sono specificati correttamente. Considera questa operazione come un errore irreversibile.

  • LoadLibrary: impossibile leggere la DLL specificata.

  • Errore di GetProcAddress.

È responsabilità dell'utente gestire queste eccezioni. Per altre informazioni, vedere Gestione degli errori e notifica.

Osservazioni:

La convenzione di chiamata per la funzione helper è __stdcall. Il tipo del valore restituito non è rilevante, quindi FARPROC viene usato. Questa funzione ha un collegamento C, il che significa che deve essere sottoposto a wrapping da extern "C" quando dichiarato nel codice C++. La ExternC macro si occupa automaticamente di questo wrapper.

Per usare la routine helper come hook di notifica, il codice deve specificare il puntatore alla funzione appropriato da restituire. Il codice thunk generato dal linker accetta quindi quel valore restituito come destinazione effettiva dell'importazione e passa direttamente ad essa. Se non si vuole usare la routine helper come hook di notifica, archiviare il valore restituito della funzione helper in ppfnIATEntry, la posizione del puntatore a funzione passata.

Funzione hook di esempio

Il codice seguente illustra come implementare una funzione hook di base.

FARPROC WINAPI delayHook(unsigned dliNotify, PDelayLoadInfo pdli)
{
    switch (dliNotify) {
        case dliStartProcessing :

            // If you want to return control to the helper, return 0.
            // Otherwise, return a pointer to a FARPROC helper function
            // that will be used instead, thereby bypassing the rest
            // of the helper.

            break;

        case dliNotePreLoadLibrary :

            // If you want to return control to the helper, return 0.
            // Otherwise, return your own HMODULE to be used by the
            // helper instead of having it call LoadLibrary itself.

            break;

        case dliNotePreGetProcAddress :

            // If you want to return control to the helper, return 0.
            // If you choose you may supply your own FARPROC function
            // address and bypass the helper's call to GetProcAddress.

            break;

        case dliFailLoadLib :

            // LoadLibrary failed.
            // If you don't want to handle this failure yourself, return 0.
            // In this case the helper will raise an exception
            // (ERROR_MOD_NOT_FOUND) and exit.
            // If you want to handle the failure by loading an alternate
            // DLL (for example), then return the HMODULE for
            // the alternate DLL. The helper will continue execution with
            // this alternate DLL and attempt to find the
            // requested entrypoint via GetProcAddress.

            break;

        case dliFailGetProc :

            // GetProcAddress failed.
            // If you don't want to handle this failure yourself, return 0.
            // In this case the helper will raise an exception
            // (ERROR_PROC_NOT_FOUND) and exit.
            // If you choose, you may handle the failure by returning
            // an alternate FARPROC function address.

            break;

        case dliNoteEndProcessing :

            // This notification is called after all processing is done.
            // There is no opportunity for modifying the helper's behavior
            // at this point except by longjmp()/throw()/RaiseException.
            // No return value is processed.

            break;

        default :

            return NULL;
    }

    return NULL;
}

/*
and then at global scope somewhere:

ExternC const PfnDliHook __pfnDliNotifyHook2 = delayHook;
ExternC const PfnDliHook __pfnDliFailureHook2 = delayHook;
*/

Struttura di caricamento ritardata e definizioni costanti

La routine helper di caricamento ritardo predefinita usa diverse strutture per comunicare con le funzioni hook e durante eventuali eccezioni. Queste strutture sono definite in delayimp.h. Ecco le macro, i typedef, i valori di notifica e di errore, le strutture informative e il tipo di funzione puntatore a hook passati agli hook:

#define _DELAY_IMP_VER  2

#if defined(__cplusplus)
#define ExternC extern "C"
#else
#define ExternC extern
#endif

typedef IMAGE_THUNK_DATA *          PImgThunkData;
typedef const IMAGE_THUNK_DATA *    PCImgThunkData;
typedef DWORD                       RVA;

typedef struct ImgDelayDescr {
    DWORD           grAttrs;        // attributes
    RVA             rvaDLLName;     // RVA to dll name
    RVA             rvaHmod;        // RVA of module handle
    RVA             rvaIAT;         // RVA of the IAT
    RVA             rvaINT;         // RVA of the INT
    RVA             rvaBoundIAT;    // RVA of the optional bound IAT
    RVA             rvaUnloadIAT;   // RVA of optional copy of original IAT
    DWORD           dwTimeStamp;    // 0 if not bound,
                                    // O.W. date/time stamp of DLL bound to (Old BIND)
    } ImgDelayDescr, * PImgDelayDescr;

typedef const ImgDelayDescr *   PCImgDelayDescr;

enum DLAttr {                   // Delay Load Attributes
    dlattrRva = 0x1,                // RVAs are used instead of pointers
                                    // Having this set indicates a VC7.0
                                    // and above delay load descriptor.
    };

//
// Delay load import hook notifications
//
enum {
    dliStartProcessing,             // used to bypass or note helper only
    dliNoteStartProcessing = dliStartProcessing,

    dliNotePreLoadLibrary,          // called just before LoadLibrary, can
                                    //  override w/ new HMODULE return val
    dliNotePreGetProcAddress,       // called just before GetProcAddress, can
                                    //  override w/ new FARPROC return value
    dliFailLoadLib,                 // failed to load library, fix it by
                                    //  returning a valid HMODULE
    dliFailGetProc,                 // failed to get proc address, fix it by
                                    //  returning a valid FARPROC
    dliNoteEndProcessing,           // called after all processing is done, no
                                    //  bypass possible at this point except
                                    //  by longjmp()/throw()/RaiseException.
    };

typedef struct DelayLoadProc {
    BOOL                fImportByName;
    union {
        LPCSTR          szProcName;
        DWORD           dwOrdinal;
        };
    } DelayLoadProc;

typedef struct DelayLoadInfo {
    DWORD               cb;         // size of structure
    PCImgDelayDescr     pidd;       // raw form of data (everything is there)
    FARPROC *           ppfn;       // points to address of function to load
    LPCSTR              szDll;      // name of dll
    DelayLoadProc       dlp;        // name or ordinal of procedure
    HMODULE             hmodCur;    // the hInstance of the library we have loaded
    FARPROC             pfnCur;     // the actual function that will be called
    DWORD               dwLastError;// error received (if an error notification)
    } DelayLoadInfo, * PDelayLoadInfo;

typedef FARPROC (WINAPI *PfnDliHook)(
    unsigned        dliNotify,
    PDelayLoadInfo  pdli
    );

Calcolare i valori necessari per il caricamento ritardato

La routine helper di caricamento ritardato deve calcolare due informazioni critiche. Per semplificare, sono disponibili due funzioni inline in delayhlp.cpp per calcolare queste informazioni.

  • Il primo, IndexFromPImgThunkData, calcola l'indice dell'importazione corrente nelle tre diverse tabelle (tabella degli indirizzi di importazione (IAT), tabella degli indirizzi di importazione associata (BIAT) e tabella degli indirizzi di importazione non associata (UIAT)).

  • Il secondo, CountOfImports, conta il numero di importazioni in un IAT valido.

// utility function for calculating the index of the current import
// for all the tables (INT, BIAT, UIAT, and IAT).
__inline unsigned
IndexFromPImgThunkData(PCImgThunkData pitdCur, PCImgThunkData pitdBase) {
    return pitdCur - pitdBase;
    }

// utility function for calculating the count of imports given the base
// of the IAT. NB: this only works on a valid IAT!
__inline unsigned
CountOfImports(PCImgThunkData pitdBase) {
    unsigned        cRet = 0;
    PCImgThunkData  pitd = pitdBase;
    while (pitd->u1.Function) {
        pitd++;
        cRet++;
        }
    return cRet;
    }

Supporto del caricamento di una DLL caricata in ritardo

Quando viene caricata una DLL con caricamento ritardato, l'helper di caricamento ritardato predefinito verifica se i descrittori di caricamento ritardato hanno un puntatore e una copia della tabella degli indirizzi di importazione originale (IAT) nel pUnloadIAT campo. In tal caso, l'helper salva un puntatore in un elenco nel descrittore di ritardo di importazione. Questa voce consente alla funzione helper di trovare la DLL in base al nome, per supportare lo scaricamento esplicito della DLL.

Ecco le strutture e le funzioni associate per scaricare in modo esplicito una DLL con caricamento ritardato:

//
// Unload support from delayimp.h
//

// routine definition; takes a pointer to a name to unload

ExternC
BOOL WINAPI
__FUnloadDelayLoadedDLL2(LPCSTR szDll);

// structure definitions for the list of unload records
typedef struct UnloadInfo * PUnloadInfo;
typedef struct UnloadInfo {
    PUnloadInfo     puiNext;
    PCImgDelayDescr pidd;
    } UnloadInfo;

// from delayhlp.cpp
// the default delay load helper places the unloadinfo records in the
// list headed by the following pointer.
ExternC
PUnloadInfo __puiHead;

La UnloadInfo struttura viene implementata usando una classe C++ che usa LocalAlloc e LocalFree le implementazioni rispettivamente come operator new e operator delete. Queste opzioni vengono mantenute in un elenco collegato standard che usa __puiHead come capo dell'elenco.

Quando si chiama __FUnloadDelayLoadedDLL, tenta di trovare il nome specificato nell'elenco delle DLL caricate. È necessaria una corrispondenza esatta. Se viene trovata, la copia dell'IAT in pUnloadIAT viene copiata sopra la parte superiore dell'IAT in esecuzione per ripristinare i puntatori di thunk. La libreria viene quindi liberata usando FreeLibrary, il record corrispondente UnloadInfo viene scollegato dall'elenco ed eliminato e TRUE viene restituito.

L'argomento della funzione __FUnloadDelayLoadedDLL2 fa distinzione tra maiuscole e minuscole. Ad esempio, è necessario specificare:

__FUnloadDelayLoadedDLL2("user32.dll");

e non:

__FUnloadDelayLoadedDLL2("User32.DLL");

Per un esempio di scaricamento di una DLL con caricamento ritardato, vedere Scaricare in modo esplicito una DLL con caricamento ritardato.

Sviluppare una funzione helper di caricamento ritardata personalizzata

È possibile specificare la propria versione della routine helper di caricamento ritardato. Nella tua routine puoi eseguire un'elaborazione specifica in base ai nomi della DLL o delle importazioni. Esistono due modi per inserire il proprio codice: scrivere il codice della propria funzione helper, possibilmente in base al codice fornito. In alternativa, associare l'helper fornito per chiamare la propria funzione usando gli hook di notifica.

Codificare il proprio helper

La creazione di una routine helper personalizzata è semplice. È possibile usare il codice esistente come guida per la nuova funzione. La funzione deve usare le stesse convenzioni di chiamata dell'helper esistente. E, se torna ai batch generati dal linker, deve restituire un puntatore a funzione corretto. Dopo aver creato il codice, puoi soddisfare la chiamata o uscire dalla chiamata, ma ti piace.

Usare l'hook di notifica di avvio dell'elaborazione

È probabilmente più semplice fornire un nuovo puntatore a una funzione hook di notifica fornita dall'utente che accetta gli stessi valori dell'helper predefinito per la dliStartProcessing notifica. A questo punto, la funzione hook può essenzialmente diventare la nuova funzione helper, perché un ritorno corretto all'helper predefinito ignora tutta l'ulteriore elaborazione nell'helper predefinito.

Vedi anche

Supporto del linker per DLL caricate in ritardo