Sdílet prostřednictvím


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

Linker MSVC podporuje zpožděné načítání knihoven DLL. Tato funkce vás zbavuje nutnosti používat funkce LoadLibrary sady Windows SDK a GetProcAddress implementovat zpožděné načítání knihovny DLL.

Bez zpožděného načtení je LoadLibrary jediným způsobem, jak za běhu načíst knihovnu DLL, a GetProcAddress; operační systém načte knihovnu DLL při načtení spustitelného souboru nebo knihovny DLL.

Při zpožděném načtení, když implicitně propojit knihovnu DLL, linker poskytuje možnosti zpoždění načtení knihovny DLL, dokud program nevolá funkci v této knihovně DLL.

Aplikace může zpozdit načtení knihovny DLL pomocí možnosti linkeru /DELAYLOAD (delay load import) s pomocnou funkcí. (Microsoft poskytuje výchozí implementaci pomocné funkce.) Pomocná funkce načte knihovnu DLL na vyžádání za běhu voláním LoadLibrary a GetProcAddress za vás.

Zvažte zpoždění načtení knihovny DLL, pokud:

  • Program nemusí volat funkci v knihovně DLL.

  • Funkce v knihovně DLL nemusí být volána až do konce provádění programu.

Zpožděné načítání knihovny DLL lze zadat během sestavení projektu EXE nebo KNIHOVNY DLL. Projekt knihovny DLL, který zpožďuje načítání jednoho nebo více samotných knihoven DLL, by neměl volat vstupní bod načtený zpožděním v DllMainsouboru .

Určení knihoven DLL pro odložení načítání

Pomocí možnosti linkeru můžete určit, které knihovny DLL se mají zpozdit načtení /delayload:dllname . Pokud nemáte v úmyslu používat vlastní verzi pomocné funkce, musíte také propojit program s aplikacemi delayimp.lib (pro desktopové aplikace) nebo dloadhelper.lib (pro aplikace pro UPW).

Tady je jednoduchý příklad zpoždění načítání knihovny 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);
}

Sestavte verzi LADĚNÍ projektu. Krokujte kód pomocí ladicího programu a všimnete si, že user32.dll je načten pouze při volání MessageBox.

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

Možnost /delay:unload linkeru umožňuje vašemu kódu explicitně uvolnit knihovnu DLL, která byla zpožděna načtena. Ve výchozím nastavení zůstanou importy načtené zpožděním v tabulce IAT (Import Address Table). Pokud však použijete /delay:unload na příkazovém řádku linkeru, pomocná funkce podporuje explicitní uvolnění knihovny DLL voláním __FUnloadDelayLoadedDLL2 a resetuje IAT do původního formuláře. Nyní neplatné ukazatele se přepíší. IAT je pole ve ImgDelayDescr struktuře, které obsahuje adresu kopie původního IAT, pokud existuje.

Příklad uvolnění knihovny DLL načtené zpožděním

Tento příklad ukazuje, jak explicitně uvolnit knihovnu DLL, MyDll.dllkterá obsahuje funkci fnMyDll:

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

Důležité poznámky k uvolnění knihovny DLL načtené zpožděním:

  • Implementaci __FUnloadDelayLoadedDLL2 funkce najdete v souboru delayhlp.cpp, v adresáři MSVC include . Další informace najdete v tématu Vysvětlení pomocné funkce načítání zpoždění.

  • Parametr name__FUnloadDelayLoadedDLL2 funkce se musí přesně shodovat (včetně případu) toho, co knihovna importu obsahuje. (Tento řetězec je také v tabulce importu na obrázku.) Obsah knihovny importu můžete zobrazit pomocí příkazu DUMPBIN /DEPENDENTS. Pokud dáváte přednost porovnávání řetězců nerozlišující velká a malá písmena, můžete aktualizovat __FUnloadDelayLoadedDLL2 tak, aby používala některou z funkcí řetězce CRT nerozlišující velká a malá písmena nebo volání rozhraní API systému Windows.

Vytvoření vazby importů načtených zpožděním

Výchozím chováním linkeru je vytvoření tabulky IAT (Bindable import address table) pro knihovnu DLL načtené zpožděním. Pokud je knihovna DLL vázána, pomocná funkce se pokusí použít vázané informace místo volání GetProcAddress na každý odkazovaný import. Pokud se časové razítko nebo upřednostňovaná adresa neshoduje s datem v načtené knihovně DLL, pomocná funkce předpokládá, že tabulka vázaných adres importu je za aktuální. Pokračuje, jako kdyby IAT neexistuje.

Pokud nemáte v úmyslu svázat importy knihovny DLL načtené zpožděním, zadejte /delay:nobind na příkazovém řádku linkeru. Linker nevygeneruje vázanou tabulku adres importu, která šetří místo v souboru obrázku.

Načtení všech importů pro knihovnu DLL se zpožděním

Funkce __HrLoadAllImportsForDll , která je definována v delayhlp.cpp, říká linkeru, aby načetl všechny importy z knihovny DLL, která byla zadána s možností linkeru /delayload .

Při načítání všech importů najednou můžete centralizovat zpracování chyb na jednom místě. Můžete se vyhnout strukturovanému zpracování výjimek kolem všech skutečných volání importu. Zároveň se vyhne situaci, kdy vaše aplikace selže částečně procesem: Například pokud se pomocnému kódu nepodaří načíst import, po úspěšném načtení dalších.

Volání __HrLoadAllImportsForDll nemění chování háků a zpracování chyb. Další informace naleznete v tématu Zpracování chyb a oznámení.

__HrLoadAllImportsForDll porovnává s názvem uloženým v samotné knihovně DLL malá a velká písmena.

Tady je příklad, který používá __HrLoadAllImportsForDll funkci volanou TryDelayLoadAllImports k pokusu o načtení pojmenované knihovny DLL. Používá funkci , CheckDelayExceptionk určení chování výjimky.

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

Výsledek TryDelayLoadAllImports můžete použít k určení, jestli voláte funkce importu, nebo ne.

Zpracování chyb a oznámení

Pokud váš program používá knihovny DLL s odloženým načtením, musí zpracovávat chyby robustním způsobem. Selhání, ke kterým dojde, když program běží, způsobí neošetřené výjimky. Další informace o zpracování chyb a oznámení o zpoždění načítání knihovny DLL naleznete v tématu Zpracování chyb a oznámení.

Importy se zpožděním načtení výpisu paměti

Importy načtené zpožděním lze vyřadit pomocí .DUMPBIN /IMPORTS Tyto importy se zobrazují s mírně odlišnými informacemi než standardní importy. Jsou odděleny do vlastního oddílu /imports seznamu a jsou explicitně označené jako importy načtené zpožděním. Pokud jsou na obrázku uvedeny informace o uvolnění, je uvedeno. Pokud jsou k dispozici informace o vazbě, je uvedeno časové razítko a datum cílové knihovny DLL spolu s vázané adresy importu.

Omezení knihoven DLL se zpožděním

Existuje několik omezení pro zpoždění načítání importů knihoven DLL.

  • Import dat není možné podporovat. Alternativním řešením je explicitně zpracovat import dat sami pomocí LoadLibrary (nebo pomocí GetModuleHandle , jakmile víte, že pomocník pro odložené načtení načetl knihovnu DLL) a GetProcAddress.

  • Zpoždění načítání Kernel32.dll se nepodporuje. Tato knihovna DLL musí být načtena, aby pomocné rutiny pro odložené načítání fungovaly.

  • Vazba přesměrovaných vstupních bodů se nepodporuje.

  • Proces může mít jiné chování, pokud je knihovna DLL načtena zpožděním místo načtení při spuštění. Je vidět, zda existují inicializace pro jednotlivé procesy, ke kterým dochází v vstupním bodě knihovny DLL s odloženým načtením. Mezi další případy patří statické tls (místní úložiště vlákna), deklarované pomocí __declspec(thread), který není zpracován při načtení knihovny DLL prostřednictvím LoadLibrary. Dynamické tls, using TlsAlloc, TlsFreeTlsGetValue, a TlsSetValue, je stále k dispozici pro použití v statických nebo pozdržované knihovny DLL.

  • Znovu inicializovat statické globální ukazatele funkce na importované funkce po prvním volání každé funkce. To je povinné, protože první použití ukazatele funkce odkazuje na thunk, nikoli načtenou funkci.

  • V současné době neexistuje způsob, jak zpozdit načítání pouze konkrétních procedur z knihovny DLL při použití normálního mechanismu importu.

  • Vlastní konvence volání (například použití kódů podmínek v architektuře x86) se nepodporují. Registry s plovoucí desetinou čárkou se také neukládají na žádné platformě. Dbejte na to, jestli vlastní pomocné rutiny nebo rutiny háku používají typy s plovoucí desetinou čárkou: Rutiny musí ukládat a obnovovat úplný stav s plovoucí desetinou čárkou na počítačích, které používají registrovat konvence volání s parametry s plovoucí desetinou čárkou. Při načítání knihovny DLL CRT buďte opatrní, zejména pokud voláte funkce CRT, které u zásobníku procesoru číselných dat (NDP) ve funkci nápovědy přebírají parametry s plovoucí desetinou čárkou.

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. Další informace najdete v tématu Vysvětlení pomocné funkcepro zpoždění a vývoj vlastní pomocné funkce.

Viz také

Vytváření knihoven DLL jazyka C/C++ v sadě Visual Studio
Referenční zdroje k linkeru MSVC