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.lib
zu 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 DLLAufrufe
GetProcAddress
zum Abrufen der Adresse der ProzedurKehrt 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 wirdKurz bevor
GetProcAddress
in der Hilfsfunktion aufgerufen wirdWenn der Aufruf
LoadLibrary
in der Hilfsfunktion fehlschlägtWenn der Aufruf
GetProcAddress
in der Hilfsfunktion fehlschlägtNach 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, dlattrRva
dass 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 PFromRva
zurü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 ppfnIATEntry
der ü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,
IndexFromPImgThunkData
berechnet 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 LocalFree
LocalAlloc
bzw. implementierungen verwendet operator new
und operator delete
implementiert. 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 FreeLibrary
freigegeben, 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.