Linkerunterstützung für verzögertes Laden von DLLs
Der MSVC-Linker unterstützt das verzögerte Laden von DLLs. Diese Funktion erleichtert Ihnen die Verwendung der Windows SDK-Funktionen LoadLibrary
und GetProcAddress
die Implementierung von DLL-verzögerten Ladevorgangs.
Ohne verzögerte Last wird die einzige Möglichkeit zum Laden einer DLL zur Laufzeit verwendet LoadLibrary
und GetProcAddress
; das Betriebssystem lädt die DLL, wenn die ausführbare Datei oder DLL verwendet wird, geladen wird.
Bei verzögerter Auslastung bietet der Linker beim impliziten Verknüpfen einer DLL Optionen, um die DLL-Auslastung zu verzögern, bis das Programm eine Funktion in dieser DLL aufruft.
Eine Anwendung kann das Laden einer DLL mithilfe der /DELAYLOAD
Linkeroption (Verzögerter Ladeimport) mit einer Hilfsfunktion verzögern. (Eine Standardmäßige Hilfsfunktionsimplementierung wird von Microsoft bereitgestellt.) Die Hilfsfunktion lädt die DLL bei Bedarf zur Laufzeit durch Aufrufen LoadLibrary
und GetProcAddress
für Sie.
Erwägen Sie das Verzögern des Ladens einer DLL, wenn:
Ihr Programm ruft möglicherweise keine Funktion in der DLL auf.
Eine Funktion in der DLL wird möglicherweise erst spät in der Ausführung Ihres Programms aufgerufen.
Das verzögerte Laden einer DLL kann während des Builds eines EXE- oder DLL-Projekts angegeben werden. Ein DLL-Projekt, das das Laden einer oder mehrerer DLLs selbst verzögert, sollte keinen verzögerungsgeladenen Einstiegspunkt aufrufen DllMain
.
Festlegen von DLLs für verzögertes Laden
Sie können angeben, welche DLLs das Laden verzögern sollen, indem Sie die /delayload:
dllname
Linkeroption verwenden. Wenn Sie nicht beabsichtigen, Ihre eigene Version einer Hilfsfunktion zu verwenden, müssen Sie ihr Programm auch mit delayimp.lib
(für Desktopanwendungen) oder dloadhelper.lib
(für UWP-Apps) verknüpfen.
Hier ist ein einfaches Beispiel für das Verzögern des Ladens einer DLL:
// cl t.cpp user32.lib delayimp.lib /link /DELAYLOAD:user32.dll
#include <windows.h>
// uncomment these lines to remove .libs from command line
// #pragma comment(lib, "delayimp")
// #pragma comment(lib, "user32")
int main() {
// user32.dll will load at this point
MessageBox(NULL, "Hello", "Hello", MB_OK);
}
Erstellen Sie die Debugversion des Projekts. Durchlaufen Sie den Code mithilfe des Debuggers, und Sie werden feststellen, dass nur geladen wird, user32.dll
wenn Sie den Aufruf ausführen MessageBox
.
Explizites Entladen einer verzögert geladenen DLL
Die /delay:unload
Linkeroption ermöglicht ihrem Code das explizite Entladen einer DLL, die verzögert wurde. Standardmäßig verbleiben die verzögert geladenen Importe in der Importadressentabelle (IAT). Wenn Sie jedoch in der Befehlszeile des Linkers verwenden /delay:unload
, unterstützt die Hilfsfunktion die explizite Entladung der DLL durch einen __FUnloadDelayLoadedDLL2
Aufruf und setzt das IAT auf das ursprüngliche Formular zurück. Die jetzt ungültigen Zeiger werden überschrieben. Das IAT ist ein Feld in der ImgDelayDescr
Struktur, das die Adresse einer Kopie des ursprünglichen IAT enthält, falls vorhanden.
Beispiel für das Entladen einer verzögert geladenen DLL
In diesem Beispiel wird gezeigt, wie Eine DLL explizit entladen wird, MyDll.dll
die eine Funktion fnMyDll
enthält:
// link with /link /DELAYLOAD:MyDLL.dll /DELAY:UNLOAD
#include <windows.h>
#include <delayimp.h>
#include "MyDll.h"
#include <stdio.h>
#pragma comment(lib, "delayimp")
#pragma comment(lib, "MyDll")
int main()
{
BOOL TestReturn;
// MyDLL.DLL will load at this point
fnMyDll();
//MyDLL.dll will unload at this point
TestReturn = __FUnloadDelayLoadedDLL2("MyDll.dll");
if (TestReturn)
printf_s("\nDLL was unloaded");
else
printf_s("\nDLL was not unloaded");
}
Wichtige Hinweise zum Entladen einer verzögert geladenen DLL:
Sie finden die Implementierung der Funktion in der
__FUnloadDelayLoadedDLL2
Dateidelayhlp.cpp
im MSVC-Verzeichnisinclude
. Weitere Informationen finden Sie unter Grundlegendes zur Hilfsfunktion zum Verzögern des Ladevorgangs.Der
name
Parameter der__FUnloadDelayLoadedDLL2
Funktion muss genau übereinstimmen (einschließlich Groß-/Kleinschreibung), was die Importbibliothek enthält. (Diese Zeichenfolge befindet sich auch in der Importtabelle im Bild.) Sie können den Inhalt der Importbibliothek mithilfeDUMPBIN /DEPENDENTS
von . Wenn Sie eine Zeichenfolgenübereinstimmung bei Groß-/Kleinschreibung bevorzugen, können Sie aktualisieren__FUnloadDelayLoadedDLL2
, um eine der Zeichenfolgenfunktionen der Groß-/Kleinschreibung oder einen Windows-API-Aufruf zu verwenden.
Binden von verzögert geladenen Importen
Das Standardmäßige Linkerverhalten besteht darin, eine bindungsfähige Importadressentabelle (IAT) für die verzögert geladene DLL zu erstellen. Wenn die DLL gebunden ist, versucht die Hilfsfunktion, die gebundenen Informationen zu verwenden, anstatt jedes der referenzierten Importe aufzurufen GetProcAddress
. Wenn entweder der Zeitstempel oder die bevorzugte Adresse nicht mit der in der geladenen DLL übereinstimmen, geht die Hilfsfunktion davon aus, dass die gebundene Importadressentabelle veraltet ist. Es wird fortgesetzt, als ob das IAT nicht vorhanden ist.
Wenn Sie niemals beabsichtigen, die verzögert geladenen Importe einer DLL zu binden, geben Sie dies /delay:nobind
in der Befehlszeile des Linkers an. Der Linker generiert nicht die gebundene Importadressentabelle, die Platz in der Bilddatei spart.
Laden aller Importe für eine verzögert geladene DLL
Die __HrLoadAllImportsForDll
In delayhlp.cpp
definierte Funktion weist den Linker an, alle Importe aus einer DLL zu laden, die mit der /delayload
Linkeroption angegeben wurde.
Wenn Sie alle Importe gleichzeitig laden, können Sie die Fehlerbehandlung an einer zentralen Stelle zentralisieren. Sie können die strukturierte Ausnahmebehandlung für alle tatsächlichen Aufrufe der Importe vermeiden. Außerdem wird eine Situation vermieden, in der Ihre Anwendung einen Teil eines Prozesses fehlschlägt: Wenn beispielsweise der Hilfscode einen Import nicht laden kann, nachdem andere erfolgreich geladen wurden.
Das Aufrufen __HrLoadAllImportsForDll
ändert das Verhalten von Hooks und Fehlerbehandlungen nicht. Weitere Informationen finden Sie unter Fehlerbehandlung und Benachrichtigung.
__HrLoadAllImportsForDll
Führt einen Vergleich zwischen Groß-/Kleinschreibung und dem Namen aus, der innerhalb der DLL selbst gespeichert ist.
Hier ist ein Beispiel, das in einer Funktion verwendet wird, die __HrLoadAllImportsForDll
aufgerufen TryDelayLoadAllImports
wird, um zu versuchen, eine benannte DLL zu laden. Es verwendet eine Funktion, CheckDelayException
um das Ausnahmeverhalten zu bestimmen.
int CheckDelayException(int exception_value)
{
if (exception_value == VcppException(ERROR_SEVERITY_ERROR, ERROR_MOD_NOT_FOUND) ||
exception_value == VcppException(ERROR_SEVERITY_ERROR, ERROR_PROC_NOT_FOUND))
{
// This example just executes the handler.
return EXCEPTION_EXECUTE_HANDLER;
}
// Don't attempt to handle other errors
return EXCEPTION_CONTINUE_SEARCH;
}
bool TryDelayLoadAllImports(LPCSTR szDll)
{
__try
{
HRESULT hr = __HrLoadAllImportsForDll(szDll);
if (FAILED(hr))
{
// printf_s("Failed to delay load functions from %s\n", szDll);
return false;
}
}
__except (CheckDelayException(GetExceptionCode()))
{
// printf_s("Delay load exception for %s\n", szDll);
return false;
}
// printf_s("Delay load completed for %s\n", szDll);
return true;
}
Sie können das Ergebnis TryDelayLoadAllImports
verwenden, um zu steuern, ob Sie die Importfunktionen aufrufen oder nicht.
Fehlerbehandlung und Benachrichtigung
Wenn Ihr Programm verzögert geladene DLLs verwendet, muss es Fehler robust behandeln. Fehler, die auftreten, während das Programm ausgeführt wird, führen zu unbehandelten Ausnahmen. Weitere Informationen zur DLL-Verzögerung beim Laden von Fehlern und Benachrichtigungen finden Sie unter Fehlerbehandlung und Benachrichtigung.
Sichern von verzögert geladenen Importen
Verzögert geladene Importe können mithilfe DUMPBIN /IMPORTS
von Verzögerungen gedumpt werden. Diese Importe zeigen sich mit etwas anderen Informationen als standardimporte. Sie sind in ihren eigenen Abschnitt der /imports
Liste unterteilt und werden explizit als verzögerte Importe bezeichnet. Wenn im Bild Informationen zum Entladen vorhanden sind, wird dies angegeben. Wenn Bindungsinformationen vorhanden sind, wird der Zeit- und Datumsstempel der Ziel-DLL zusammen mit den gebundenen Adressen der Importe angegeben.
Einschränkungen für DLLs mit Verzögerungsladevorgang
Es gibt mehrere Einschränkungen beim Verzögern des Ladens von DLL-Importen.
Importe von Daten können nicht unterstützt werden. Eine Problemumgehung besteht darin, den Datenimport explizit mithilfe
LoadLibrary
von (oder mithilfe derGetModuleHandle
Verwendung, nachdem Sie die DLL geladenGetProcAddress
haben) und verwenden.Das Verzögern des Ladens
Kernel32.dll
wird nicht unterstützt. Diese DLL muss geladen werden, damit die Hilfsroutinen zum Verzögern des Ladens funktionieren.Die Bindung weitergeleiteter Einstiegspunkte wird nicht unterstützt.
Ein Prozess kann ein anderes Verhalten aufweisen, wenn eine DLL verzögert geladen wird, anstatt beim Start geladen zu werden. Es kann angezeigt werden, ob pro Prozess Initialisierungen vorhanden sind, die im Einstiegspunkt der verzögert geladenen DLL auftreten. Andere Fälle sind statisches TLS (thread lokaler Speicher), deklariert mit
__declspec(thread)
, das nicht behandelt wird, wenn die DLL überLoadLibrary
geladen wird. Der dynamische TLS ist überTlsAlloc
,TlsFree
,TlsGetValue
undTlsSetValue
immer noch zur Verwendung in statischen oder verzögert geladenen DLLs verfügbar.Initialisieren Sie statische globale Funktionszeiger auf importierte Funktionen nach dem ersten Aufruf jeder Funktion erneut. Das ist erforderlich, da die erste Verwendung eines Funktionszeigers auf den Thunk verweist, nicht auf die geladene Funktion.
Es gibt derzeit keine Möglichkeit, das Laden nur bestimmter Prozeduren aus einer DLL zu verzögern, während der normale Importmechanismus verwendet wird.
Benutzerdefinierte Aufrufkonventionen (z. B. die Verwendung von Bedingungscodes für x86-Architekturen) werden nicht unterstützt. Außerdem werden die Gleitkommaregister nicht auf einer Plattform gespeichert. Achten Sie darauf, ob Ihre benutzerdefinierte Hilfsroutine oder Hook-Routinen Gleitkommatypen verwenden: Die Routinen müssen den vollständigen Gleitkommazustand auf Computern speichern und wiederherstellen, die Aufrufkonventionen mit Gleitkommaparametern verwenden. Achten Sie darauf, dass die CRT-DLL verzögert geladen wird, insbesondere wenn Sie CRT-Funktionen aufrufen, die Gleitkommaparameter in einem numerischen Datenprozessorstapel (NDP) in der Hilfefunktion verwenden.
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. Weitere Informationen finden Sie unter "Grundlegendes zur Verzögerten Ladehilfefunktion " und zum Entwickeln Ihrer eigenen Hilfsfunktion.
Siehe auch
Erstellen von C/C++-DLLs in Visual Studio
MSVC-Linkerreferenz