Teilen über


Verstehen der Hilfsfunktion für verzögertes Laden

Die Hilfsfunktion für linkergestützte verzögertes Laden ist, was die DLL zur Laufzeit tatsächlich lädt. Sie können die Hilfsfunktion ändern, um das Verhalten anzupassen. Anstatt die bereitgestellte Hilfsfunktion in delayimp.libzu verwenden, schreiben Sie Ihre eigene Funktion, und verknüpfen Sie sie mit Ihrem Programm. Eine Hilfsfunktion dient allen verzögert geladenen DLLs.

Sie können Ihre eigene Version der Hilfsfunktion bereitstellen, wenn Sie eine bestimmte Verarbeitung basierend auf den Namen der DLL oder Importe durchführen möchten.

Die Hilfsfunktion führt die folgenden Aktionen aus:

  • Überprüft das gespeicherte Handle für die Bibliothek, um festzustellen, ob es bereits geladen wurde.

  • Aufrufe LoadLibrary zum Laden der DLL

  • Aufrufe GetProcAddress zum Abrufen der Adresse der Prozedur

  • Kehrt zum Verzögerten Importlade-Thunk zurück, um den jetzt geladenen Einstiegspunkt aufzurufen

Die Hilfsfunktion kann nach jeder der folgenden Aktionen zurück zu einem Benachrichtigungshaken in Ihrem Programm zurückkehren:

  • Wenn die Hilfsfunktion gestartet wird

  • Kurz bevor LoadLibrary in der Hilfsfunktion aufgerufen wird

  • Kurz bevor GetProcAddress in der Hilfsfunktion aufgerufen wird

  • Wenn der Aufruf LoadLibrary in der Hilfsfunktion fehlschlägt

  • Wenn der Aufruf GetProcAddress in der Hilfsfunktion fehlschlägt

  • Nach Abschluss der Verarbeitung der Hilfsfunktion

Jeder dieser Hookpunkte kann einen Wert zurückgeben, der die normale Verarbeitung der Hilfsroutine in irgendeiner Weise ändert, mit Ausnahme der Rückkehr zur Verzögerung import load thunk.

Der Standardhilfscode befindet sich in delayhlp.cpp und delayimp.h im MSVC-Verzeichnis include . Sie wird im MSVC-Verzeichnis lib für Ihre Zielarchitektur kompiliertdelayimp.lib. Sie müssen diese Bibliothek in Ihre Kompilierungen einschließen, es sei denn, Sie schreiben Ihre eigene Hilfsfunktion.

Verzögern von Ladeprogrammaufrufkonventionen, Parametern und Rückgabetyp

Der Prototyp für die Verzögerungsladehilfsroutine lautet:

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

Parameter

pidd
Ein const Zeiger auf einen ImgDelayDescr , der die Offsets verschiedener importbezogener Daten, einen Zeitstempel für Bindungsinformationen und einen Satz von Attributen enthält, die weitere Informationen zum Deskriptorinhalt bereitstellen. Derzeit gibt es nur ein Attribut, das angibt, dlattrRvadass die Adressen im Deskriptor relative virtuelle Adressen sind. Weitere Informationen finden Sie in den Deklarationen in delayimp.h.

Die Zeiger im Verzögerungsdeskriptor (ImgDelayDescr in delayimp.h) verwenden relative virtuelle Adressen (RVAs), um wie erwartet in 32-Bit- und 64-Bit-Programmen zu funktionieren. Um sie zu verwenden, konvertieren Sie diese RVAs mithilfe der Funktion PFromRvazurück in Zeiger, die in delayhlp.cpp. Sie können diese Funktion für jedes der Felder im Deskriptor verwenden, um sie wieder in 32-Bit- oder 64-Bit-Zeiger zu konvertieren. Die Standardmäßige Hilfsfunktion zum Laden von Verzögerung ist eine gute Vorlage, die als Beispiel verwendet werden kann.

Informationen zur Definition der PCImgDelayDescr Struktur finden Sie unter Struktur- und Konstantendefinitionen.

ppfnIATEntry
Ein Zeiger auf einen Steckplatz in der IAT-Tabelle (Delay Load Import Address Table). Es ist der Steckplatz, der mit der Adresse der importierten Funktion aktualisiert wird. Die Hilfsroutine muss denselben Wert speichern, den sie an diesen Speicherort zurückgibt.

Rückgabewerte erwartet

Wenn die Hilfsfunktion erfolgreich ist, wird die Adresse der importierten Funktion zurückgegeben.

Wenn die Funktion fehlschlägt, löst sie eine strukturierte Ausnahme aus und gibt 0 zurück. Es können drei Ausnahmetypen ausgelöst werden:

  • Ein ungültiger Parameter, was passiert, wenn die Attribute in pidd nicht korrekt angegeben werden. Behandeln Sie dies als nicht behebbaren Fehler.

  • LoadLibrary ist in der angegebenen DLL fehlgeschlagen.

  • Fehlschlagen von GetProcAddress.

Es liegt in Ihrer Verantwortung, diese Ausnahmen zu behandeln. Weitere Informationen finden Sie unter Fehlerbehandlung und Benachrichtigung.

Hinweise

Die Aufrufkonvention für die Hilfsfunktion ist __stdcall. Der Typ des Rückgabewerts ist nicht relevant, wird also FARPROC verwendet. Diese Funktion weist eine C-Verknüpfung auf, was bedeutet, dass sie bei extern "C" der Deklaration im C++-Code umbrochen werden muss. Das ExternC Makro kümmert sich um diesen Wrapper für Sie.

Damit Ihre Hilfsroutine als Benachrichtigungshaken verwendet werden kann, muss Ihr Code den entsprechenden Funktionszeiger angeben, der zurückgegeben werden soll. Der vom Linker generierte Thunkcode übernimmt dann diesen Rückgabewert als reales Ziel für den Importvorgang und springt direkt dorthin. Wenn Sie Ihre Hilfsroutine nicht als Benachrichtigungshaken verwenden möchten, speichern Sie den Rückgabewert der Hilfsfunktion in ppfnIATEntryder übergebenen Funktionszeigerposition.

Beispielhakenfunktion

Der folgende Code zeigt, wie Sie eine einfache Hook-Funktion implementieren.

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

Verzögerte Ladestruktur und Konstantendefinitionen

Die Standardmäßige Hilfsroutine zum Laden von Verzögerung verwendet mehrere Strukturen, um mit den Hook-Funktionen und während aller Ausnahmen zu kommunizieren. Diese Strukturen sind definiert in delayimp.h. Hier sind die Makros, TypeDefs, Benachrichtigungs- und Fehlerwerte, Informationsstrukturen und der Zeiger-zu-Hook-Funktionstyp, der an die Hooks übergeben wird:

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

Berechnen der erforderlichen Werte für das Verzögern des Ladevorgangs

Die Hilfsroutine zum Laden von Verzögerung muss zwei wichtige Informationen berechnen. Zur Hilfe gibt es zwei Inlinefunktionen delayhlp.cpp , um diese Informationen zu berechnen.

  • Der erste, IndexFromPImgThunkDataberechnet den Index des aktuellen Imports in die drei verschiedenen Tabellen (Importadressentabelle (IAT), gebundene Importadressentabelle (BIAT) und ungebundene Importadressentabelle (UIAT)).

  • Die zweite, CountOfImports, zählt die Anzahl der Importe in einem gültigen 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;
    }

Unterstützung des Entladens einer verzögert geladenen DLL

Wenn eine verzögert geladene DLL geladen wird, überprüft die standardmäßige Verzögerungsladehilfe, ob die Verzögerungsladedeskriptoren einen Zeiger und eine Kopie der ursprünglichen Importadressentabelle (IAT) im pUnloadIAT Feld haben. Wenn ja, speichert der Hilfsprogramm einen Zeiger in einer Liste im Importverzögerungsdeskriptor. Mit diesem Eintrag kann die Hilfsfunktion die DLL anhand des Namens finden, um das Entladen dieser DLL explizit zu unterstützen.

Hier sind die zugehörigen Strukturen und Funktionen zum expliziten Entladen einer verzögert geladenen DLL:

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

Die UnloadInfo Struktur wird mit einer C++-Klasse implementiert, die LocalFreeLocalAlloc bzw. implementierungen verwendet operator new und operator deleteimplementiert. Diese Optionen werden in einer Standardmäßig verknüpften Liste gespeichert, die als Kopf der Liste verwendet __puiHead wird.

Wenn Sie aufrufen __FUnloadDelayLoadedDLL, wird versucht, den Namen zu finden, den Sie in der Liste der geladenen DLLs angeben. (Eine genaue Übereinstimmung ist erforderlich.) Wenn gefunden, wird die Kopie des IAT in pUnloadIAT über dem laufenden IAT kopiert, um die Thunk-Zeiger wiederherzustellen. Anschließend wird die Bibliothek mit FreeLibraryfreigegeben, der übereinstimmende UnloadInfo Datensatz wird von der Liste nicht verknüpft und gelöscht und TRUE zurückgegeben.

Bei dem Argument für die Funktion __FUnloadDelayLoadedDLL2 wird die Groß-/Kleinschreibung beachtet. Beispielsweise würden Sie Folgendes angeben:

__FUnloadDelayLoadedDLL2("user32.dll");

und nicht:

__FUnloadDelayLoadedDLL2("User32.DLL");

Ein Beispiel zum Entladen einer verzögert geladenen DLL finden Sie unter Explizites Entladen einer verzögerungsgeladenen DLL.

Entwickeln Ihrer eigenen Verzögerungsladehilfefunktion

Möglicherweise möchten Sie Ihre eigene Version der Hilfsroutine für das Verzögerte Laden bereitstellen. In Ihrer eigenen Routine können Sie eine bestimmte Verarbeitung basierend auf den Namen der DLL oder Importe durchführen. Es gibt zwei Möglichkeiten, ihren eigenen Code einzufügen: Codieren Sie Ihre eigene Hilfsfunktion, möglicherweise basierend auf dem bereitgestellten Code. Oder verbinden Sie den bereitgestellten Helfer, um Ihre eigene Funktion mithilfe der Benachrichtigungshaken aufzurufen.

Codieren Sie Ihr eigenes Hilfsprogramm

Das Erstellen Ihrer eigenen Hilfsroutine ist einfach. Sie können den vorhandenen Code als Leitfaden für Ihre neue Funktion verwenden. Ihre Funktion muss die gleichen Aufrufkonventionen wie der vorhandene Hilfsprogramm verwenden. Und wenn sie wieder an den linkergenerierten Thunks zurückkehrt, muss sie einen richtigen Funktionszeiger zurückgeben. Nachdem Sie Ihren Code erstellt haben, können Sie entweder den Aufruf erfüllen oder den Anruf aus dem Anruf herausholen, wie Sie möchten.

Verwenden des Benachrichtigungshakens für die Startverarbeitung

Es ist wahrscheinlich am einfachsten, einen neuen Zeiger auf eine vom Benutzer bereitgestellte Benachrichtigungshakfunktion bereitzustellen, die dieselben Werte wie der Standardhilfsprogramm für die dliStartProcessing Benachrichtigung verwendet. An diesem Punkt kann die Hook-Funktion im Wesentlichen zur neuen Hilfsfunktion werden, da eine erfolgreiche Rückkehr zum Standardhilfsprogramm alle weiteren Verarbeitungen im Standardhilfsprogramm umgeht.

Siehe auch

Linker-Unterstützung für verzögert geladene DLLs