Sdílet prostřednictvím


Propojení spustitelného souboru s knihovnou DLL

Spustitelný soubor odkazuje na knihovnu DLL (nebo načte) jedním ze dvou způsobů:

  • Implicitní propojení, kde operační systém načte knihovnu DLL ve stejnou dobu jako spustitelný soubor, který ji používá. Spustitelný soubor klienta volá exportované funkce knihovny DLL stejným způsobem, jako kdyby byly funkce staticky propojené a obsažené ve spustitelném souboru. Implicitní propojení se někdy označuje jako dynamické dynamické propojení statického načítání nebo načítání.

  • Explicitní propojení, kde operační systém načte knihovnu DLL na vyžádání za běhu. Spustitelný soubor, který používá knihovnu DLL explicitním propojením, musí explicitně načíst a uvolnit knihovnu DLL. Musí také nastavit ukazatel funkce pro přístup ke každé funkci, která používá z knihovny DLL. Na rozdíl od volání funkcí v staticky propojené knihovně nebo implicitně propojené knihovně DLL musí spustitelný soubor klienta volat exportované funkce v explicitně propojené knihovně DLL prostřednictvím ukazatelů funkce. Explicitní propojení se někdy označuje jako dynamické načítání nebo dynamické propojení za běhu.

Spustitelný soubor může použít buď metodu propojení se stejnou knihovnou DLL. Tyto metody se navíc vzájemně nevylučují; jeden spustitelný soubor může implicitně propojit s knihovnou DLL a jiný se k němu může explicitně připojit.

Určení metody propojení, která se má použít

Ať už použijete implicitní propojení, nebo explicitní propojení, je rozhodnutí o architektuře, které musíte pro svou aplikaci provést. Každá metoda má výhody a nevýhody.

Implicitní propojení

Implicitní propojení nastane, když kód aplikace volá exportovanou funkci KNIHOVNY DLL. Pokud je zdrojový kód volajícího spustitelného souboru zkompilován nebo sestaven, volání funkce KNIHOVNY DLL vygeneruje odkaz na externí funkci v kódu objektu. Chcete-li tento externí odkaz vyřešit, musí aplikace propojit s knihovnou importu (soubor .lib) poskytovanou tvůrcem knihovny DLL.

Knihovna importu obsahuje pouze kód pro načtení knihovny DLL a implementaci volání funkcí v knihovně DLL. Vyhledání externí funkce v knihovně importu informuje linker, že kód této funkce je v knihovně DLL. Chcete-li vyřešit externí odkazy na knihovny DLL, linker jednoduše přidá informace do spustitelného souboru, který systému řekne, kde najít kód knihovny DLL při spuštění procesu.

Když systém spustí program, který obsahuje dynamicky propojené odkazy, použije informace ve spustitelném souboru programu k vyhledání požadovaných knihoven DLL. Pokud knihovna DLL nemůže najít, systém proces ukončí a zobrazí dialogové okno, které hlásí chybu. V opačném případě systém mapuje moduly DLL do adresního prostoru procesu.

Pokud některá z knihoven DLL obsahuje vstupní funkci pro inicializaci a ukončovací kód, například DllMain, operační systém tuto funkci zavolá. Jeden z parametrů předaných funkci vstupního bodu určuje kód, který označuje, že knihovna DLL se připojuje k procesu. Pokud funkce vstupního bodu nevrací hodnotu PRAVDA, systém proces ukončí a hlásí chybu.

Systém nakonec upraví spustitelný kód procesu tak, aby poskytoval počáteční adresy pro funkce knihovny DLL.

Stejně jako zbytek kódu programu zavaděč mapuje kód knihovny DLL do adresního prostoru procesu při spuštění procesu. Operační systém ho načte do paměti pouze v případě potřeby. V důsledku toho PRELOAD atributy kódu LOADONCALL používané soubory .def k řízení načítání v předchozích verzích Systému Windows již nemají význam.

Explicitní propojení

Většina aplikací používá implicitní propojení, protože se jedná o nejjednodušší metodu propojení, která se má použít. Existují však chvíle, kdy je nutné explicitní propojení. Tady je několik běžných důvodů použití explicitního propojení:

  • Aplikace nezná název knihovny DLL, kterou načte do doby běhu. Aplikace může například získat název knihovny DLL a exportované funkce z konfiguračního souboru při spuštění.

  • Proces, který používá implicitní propojení, je ukončen operačním systémem, pokud knihovna DLL nebyla nalezena při spuštění procesu. V této situaci není ukončen proces, který používá explicitní propojení, a může se pokusit o obnovení z chyby. Proces může například upozornit uživatele na chybu a nechat uživatele zadat jinou cestu ke knihovně DLL.

  • Proces, který používá implicitní propojení, se ukončí také v případě, že některá z knihoven DLL, která je propojena, má DllMain funkci, která selže. V této situaci se neukončí proces, který používá explicitní propojení.

  • Aplikace, která implicitně odkazuje na mnoho knihoven DLL, může být pomalé spuštění, protože Systém Windows načte všechny knihovny DLL při načítání aplikace. Aby se zlepšil výkon při spuštění, může aplikace použít pouze implicitní propojení knihoven DLL vyžadovaných okamžitě po načtení. Explicitní propojení může použít k načtení dalších knihoven DLL pouze v případě, že jsou potřeba.

  • Explicitní propojení eliminuje potřebu propojit aplikaci pomocí knihovny importu. Pokud změny v knihovně DLL způsobí, že se změní ordinaly exportu, aplikace nemusí znovu připojit, pokud volají GetProcAddress pomocí názvu funkce, a ne pořadové hodnoty. Aplikace, které používají implicitní propojení, se musí stále znovu připojit ke změněné knihovně importu.

Zde jsou dvě rizika explicitního propojení, o nichž je potřeba vědět:

  • Pokud knihovna DLL obsahuje funkci vstupního DllMain bodu, operační systém volá funkci v kontextu vlákna, které volal LoadLibrary. Funkce vstupního bodu není volána, pokud je knihovna DLL již připojena k procesu z důvodu předchozího volání LoadLibrary , které nemělo odpovídající volání FreeLibrary funkce. Explicitní propojení může způsobit problémy, pokud knihovna DLL používá DllMain funkci k inicializaci každého vlákna procesu, protože všechna vlákna, která již existují, když LoadLibrary (nebo AfxLoadLibrary) je volána, nejsou inicializována.

  • Pokud knihovna DLL deklaruje data statického rozsahu jako __declspec(thread), může způsobit chybu ochrany, pokud je explicitně propojena. Po načtení knihovny DLL voláním LoadLibraryzpůsobí chybu ochrany pokaždé, když kód odkazuje na tato data. (Statická data zahrnují globální i místní statické položky.) Proto byste při vytváření knihovny DLL neměli používat místní úložiště s vlákny. Pokud nemůžete, informujte uživatele knihovny DLL o potenciálních nástrahách dynamického načítání knihovny DLL. Další informace najdete v tématu Použití místního úložiště vláken v knihovně dynamic-link (Windows SDK).

Jak používat implicitní propojení

Chcete-li použít knihovnu DLL implicitním propojením, musí spustitelné soubory klienta získat tyto soubory od poskytovatele knihovny DLL:

  • Jeden nebo více souborů hlaviček (.h soubory), které obsahují deklarace exportovaných dat, funkcí a tříd C++ v knihovně DLL. Třídy, funkce a data exportovaná knihovnou DLL musí být všechny označené __declspec(dllimport) v souboru záhlaví. Další informace naleznete v tématu dllexport, dllimport.

  • Knihovna importu, která se má propojit se spustitelným souborem. Linker vytvoří knihovnu importu při sestavení knihovny DLL. Další informace najdete v tématu Soubory LIB jako vstup linkeru.

  • Skutečný soubor DLL.

Chcete-li použít data, funkce a třídy v knihovně DLL implicitním propojením, musí každý zdrojový soubor klienta obsahovat hlavičkové soubory, které je deklarují. Z hlediska kódování jsou volání exportovaných funkcí stejně jako jakékoli jiné volání funkce.

Chcete-li sestavit spustitelný soubor klienta, je nutné propojit knihovnu pro import knihovny DLL. Pokud používáte externí soubor pravidel nebo systém sestavení, zadejte knihovnu importu společně s ostatními soubory objektů nebo knihovnami, které propojíte.

Operační systém musí být schopen vyhledat soubor DLL při načtení volajícího spustitelného souboru. To znamená, že při instalaci aplikace musíte buď nasadit, nebo ověřit existenci knihovny DLL.

Chcete-li použít knihovnu DLL explicitním propojením, musí aplikace volat funkci, která explicitně načte knihovnu DLL za běhu. Pokud chcete explicitně propojit knihovnu DLL, musí aplikace:

  • Volání LoadLibraryEx nebo podobné funkce pro načtení knihovny DLL a získání popisovače modulu.

  • Volání GetProcAddress získat ukazatel funkce na každou exportovanou funkci, kterou aplikace volá. Vzhledem k tomu, že aplikace volají funkce knihovny DLL pomocí ukazatele, kompilátor negeneruje externí odkazy, takže není nutné propojit s knihovnou importu. Musíte však mít typedef nebo using příkaz, který definuje podpis volání exportovaných funkcí, které voláte.

  • Volání FreeLibrary při dokončení knihovny DLL.

Například volání této ukázkové funkce LoadLibrary pro načtení knihovny DLL s názvem MyDLL, volání GetProcAddress k získání ukazatele na funkci s názvem DLLFunc1, volání funkce a uložení výsledku a následné volání FreeLibrary pro uvolnění knihovny DLL.

#include "windows.h"

typedef HRESULT (CALLBACK* LPFNDLLFUNC1)(DWORD,UINT*);

HRESULT LoadAndCallSomeFunction(DWORD dwParam1, UINT * puParam2)
{
    HINSTANCE hDLL;               // Handle to DLL
    LPFNDLLFUNC1 lpfnDllFunc1;    // Function pointer
    HRESULT hrReturnVal;

    hDLL = LoadLibrary("MyDLL");
    if (NULL != hDLL)
    {
        lpfnDllFunc1 = (LPFNDLLFUNC1)GetProcAddress(hDLL, "DLLFunc1");
        if (NULL != lpfnDllFunc1)
        {
            // call the function
            hrReturnVal = lpfnDllFunc1(dwParam1, puParam2);
        }
        else
        {
            // report the error
            hrReturnVal = ERROR_DELAY_LOAD_FAILED;
        }
        FreeLibrary(hDLL);
    }
    else
    {
        hrReturnVal = ERROR_DELAY_LOAD_FAILED;
    }
    return hrReturnVal;
}

Na rozdíl od tohoto příkladu byste ve většině případů měli volat LoadLibrary a FreeLibrary pouze jednou v aplikaci pro danou knihovnu DLL. Platí to zejména v případě, že budete volat více funkcí v knihovně DLL nebo volat funkce knihovny DLL opakovaně.

O čem chcete vědět víc?

Viz také

Vytváření knihoven DLL jazyka C/C++ v sadě Visual Studio