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 werden die verzögert geladenen Importe in der Importadressentabelle (IAT) neu Standard. 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.dlldie eine Funktion fnMyDllenthä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 Datei delayhlp.cppim MSVC-Verzeichnis include . 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 mithilfe DUMPBIN /DEPENDENTSvon . 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.cppdefinierte 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, CheckDelayExceptionum 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 /IMPORTSvon 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 der GetModuleHandle Verwendung, nachdem Sie die DLL geladen GetProcAddresshaben) 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 über LoadLibrarygeladen wird. Der dynamische TLS ist über TlsAlloc, TlsFree, TlsGetValue und TlsSetValue 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.libzu 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