Sdílet prostřednictvím


Vysvětlení pomocné funkce pro odložené načítání

Pomocná funkce pro zpožděné načítání podporované linkerem je to, co ve skutečnosti načte knihovnu DLL za běhu. Pomocnou funkci můžete upravit tak, aby přizpůsobila její chování. Místo použití dodané pomocné funkce v delayimp.lib, napište vlastní funkci a propojte ji s programem. Jedna pomocná funkce obsluhuje všechny knihovny DLL načtené zpožděním.

Pokud chcete provést konkrétní zpracování na základě názvů knihoven DLL nebo importů, můžete zadat vlastní verzi pomocné funkce.

Pomocná funkce provede tyto akce:

  • Zkontroluje uložený popisovač knihovny a zjistí, jestli už byl načten.

  • Volání LoadLibrary pokusu o načtení knihovny DLL

  • Volání GetProcAddress k pokusu o získání adresy procedury

  • Vrátí se do záznamu načtení importu zpoždění, který zavolá nyní načtený vstupní bod.

Pomocná funkce může za každou z následujících akcí volat zpět na hook oznámení ve vašem programu:

  • Při spuštění pomocné funkce

  • Těsně před LoadLibrary zavolání v pomocné funkci

  • Těsně před GetProcAddress zavolání v pomocné funkci

  • Pokud volání LoadLibrary v pomocné funkci selže

  • Pokud volání GetProcAddress v pomocné funkci selže

  • Po zpracování pomocné funkce

Každý z těchto bodů háku může vrátit hodnotu, která nějakým způsobem mění normální zpracování pomocné rutiny, s výjimkou návratu do bloku načítání zpoždění importu.

Výchozí pomocný kód najdete v delayhlp.cpp adresáři MSVC a delayimp.h v adresáři MSVC include . Je zkompilován do delayimp.lib adresáře MSVC lib pro vaši cílovou architekturu. Tuto knihovnu budete muset zahrnout do kompilací, pokud nenapíšete vlastní pomocnou funkci.

Zpoždění konvencí volání, parametrů a návratového typu pomocné rutiny načítání

Prototyp rutiny pomocné rutiny pro zpoždění zatížení je:

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

Parametry

pidd
const Ukazatel na ImgDelayDescr hodnotu, která obsahuje posuny různých dat souvisejících s importem, časové razítko pro informace vazby a sadu atributů, které poskytují další informace o obsahu popisovače. V současné době existuje pouze jeden atribut, který označuje, dlattrRvaže adresy v popisovači jsou relativní virtuální adresy. Další informace naleznete v deklarací v delayimp.h.

Ukazatele v popisovači zpoždění (ImgDelayDescr v delayimp.h) používají relativní virtuální adresy (RVA) k práci podle očekávání v 32bitové i 64bitové programy. Chcete-li je použít, převeďte tyto RVA zpět na ukazatele pomocí funkce PFromRvanalezené v delayhlp.cpp. Tuto funkci můžete použít u každého pole v popisovači a převést je zpět na 32bitové nebo 64bitové ukazatele. Výchozí pomocná funkce pro načtení zpoždění je dobrou šablonou, která se používá jako příklad.

Definice struktury naleznete v PCImgDelayDescr tématu Struktury a konstanty definice.

ppfnIATEntry
Ukazatel na slot v tabulce IAT (Delay Load Import Address Table). Je to slot, který se aktualizuje adresou importované funkce. Pomocná rutina musí uložit stejnou hodnotu, kterou vrací do tohoto umístění.

Očekávané návratové hodnoty

Pokud je pomocná funkce úspěšná, vrátí adresu importované funkce.

Pokud funkce selže, vyvolá strukturovanou výjimku a vrátí hodnotu 0. Mohou být vyvolány tři typy výjimek:

  • Neplatný parametr, ke kterému dochází v případě, že atributy nejsou pidd zadány správně. Považuje se za neopravitelnou chybu.

  • LoadLibrary v zadané knihovně DLL došlo k chybě.

  • Selhání .GetProcAddress

Za zpracování těchto výjimek je vaší zodpovědností. Další informace naleznete v tématu Zpracování chyb a oznámení.

Poznámky

Konvence volání pomocné funkce je __stdcall. Typ návratové hodnoty není relevantní, proto FARPROC se používá. Tato funkce má propojení jazyka C, což znamená, že je nutné ji zabalit extern "C" , když je deklarována v kódu jazyka C++. Makro ExternC se postará o tento obálku za vás.

Pokud chcete jako háček oznámení použít pomocnou rutinu, musí váš kód zadat odpovídající ukazatel funkce, který se má vrátit. Kód thunk, který linker vygeneruje, pak vezme vrácenou hodnotu jako skutečný cíl importu a přeskočí přímo na něj. Pokud nechcete jako hák oznámení použít pomocnou rutinu, uložte návratovou hodnotu pomocné funkce do ppfnIATEntryumístění předaného ukazatele funkce.

Ukázková funkce háku

Následující kód ukazuje, jak implementovat základní funkci háku.

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;
*/

Zpoždění struktury zatížení a definice konstant

Výchozí obslužná rutina pro načítání zpoždění používá několik struktur ke komunikaci s funkcemi háku a během jakýchkoli výjimek. Tyto struktury jsou definovány v delayimp.h. Tady jsou makra, definice typedef, hodnoty oznámení a selhání, informační struktury a typ funkce pointer-to-hook předaný do háků:

#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
    );

Výpočet potřebných hodnot pro odložené načítání

Rutina pomocné rutiny načítání zpoždění potřebuje vypočítat dva důležité informace. Pro pomoc jsou k dispozici dvě vložené funkce delayhlp.cpp pro výpočet těchto informací.

  • Nejprve IndexFromPImgThunkDatavypočítá index aktuálního importu do tří různých tabulek (tabulka importu adres (IAT), vázaná tabulka adres importu (BIAT) a nevázaná tabulka adres importu (UIAT).

  • Druhý, CountOfImportspočítá počet importů v platnéM IAT.

// 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;
    }

Podpora uvolnění knihovny DLL načtené zpožděním

Když se načte knihovna DLL načtená zpoždění, výchozí pomocné rutiny pro načtení zpoždění zkontroluje, jestli mají popisovače zpoždění a kopii původní tabulky adresy importu pUnloadIAT (IAT) v poli. Pokud ano, pomocník uloží ukazatel do seznamu na popisovač zpoždění importu. Tato položka umožňuje pomocné funkci najít knihovnu DLL podle názvu, aby podporovala explicitní uvolnění této knihovny DLL.

Tady jsou přidružené struktury a funkce pro explicitní uvolnění knihovny DLL načtené zpožděním:

//
// 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;

Struktura UnloadInfo je implementována pomocí třídy C++, která používá LocalAlloc a LocalFree implementuje jako své operator new a operator deletev uvedeném pořadí. Tyto možnosti se uchovávají ve standardním propojeném seznamu, který se používá __puiHead jako hlava seznamu.

Při volání __FUnloadDelayLoadedDLLse pokusí najít název, který zadáte v seznamu načtených knihoven DLL. (Vyžaduje se přesná shoda.) Pokud se najde, zkopíruje se kopie IAT v pUnloadIAT horní části spuštěného IAT, aby se obnovily ukazatele thunk. Pak se knihovna uvolní pomocí FreeLibrary, odpovídající UnloadInfo záznam se odpojí ze seznamu a odstraní a TRUE vrátí se.

Argument funkce __FUnloadDelayLoadedDLL2 rozlišují malá a velká písmena. Zadáte například:

__FUnloadDelayLoadedDLL2("user32.dll");

a ne:

__FUnloadDelayLoadedDLL2("User32.DLL");

Příklad uvolnění knihovny DLL načtené zpožděním naleznete v tématu Explicitně uvolnění knihovny DLL se zpožděním.

Vývoj vlastní pomocné funkce pro načítání zpoždění

Můžete chtít zadat vlastní verzi pomocné rutiny pro odložení načítání. Ve své vlastní rutině můžete provádět konkrétní zpracování na základě názvů knihoven DLL nebo importů. Existují dva způsoby, jak vložit vlastní kód: Kód vlastní pomocné funkce, případně na základě dodaného kódu. Nebo připojte dodaného pomocníka, aby volal vlastní funkci pomocí oznámení.

Kódování vlastního pomocníka

Vytvoření vlastní pomocné rutiny je jednoduché. Existující kód můžete použít jako vodítko pro novou funkci. Vaše funkce musí používat stejné konvence volání jako stávající pomocník. A pokud se vrátí na bloky generované linkerem, musí vrátit správný ukazatel funkce. Jakmile vytvoříte kód, můžete hovor buď splnit, nebo se z hovoru dostat, ale rádi.

Použití háku oznámení pro zahájení zpracování

Pravděpodobně je nejjednodušší poskytnout nový ukazatel na funkci háku oznámení zadané uživatelem, která přebírá stejné hodnoty jako výchozí pomocník pro dliStartProcessing oznámení. V tomto okamžiku se funkce háku může v podstatě stát novou pomocnou funkcí, protože úspěšný návrat do výchozí pomocné rutiny obchází veškeré další zpracování ve výchozím pomocném pomocníku.

Viz také

Podpora linkerů pro knihovny DLL s odloženým načtením