Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Při vytváření knihovny DLL (Dynamic-link Library) pomocí sady Visual Studio ve výchozím nastavení obsahuje linker knihovnu modulu runtime Visual C++ (VCRuntime). VCRuntime obsahuje kód potřebný k inicializaci a ukončení spustitelného souboru C/C++. Při propojení do knihovny DLL poskytuje kód VCRuntime interní vstupní funkci _DllMainCRTStartup, která zpracovává zprávy operačního systému Windows týkající se připojení nebo odpojení knihovny DLL od procesu nebo vlákna. Funkce _DllMainCRTStartup provádí základní úlohy, jako je nastavení zabezpečení vyrovnávací paměti zásobníku, inicializace a ukončení knihovny modulu runtime jazyka C (CRT) a volání konstruktorů a destruktorů pro statické a globální objekty.
_DllMainCRTStartup také volá funkce háku pro jiné knihovny, jako je WinRT, MFC a ATL, k provedení vlastní inicializace a ukončení. Bez této inicializace by crt a další knihovny a vaše statické proměnné zůstaly v neinicializovaném stavu. Stejné interní inicializační a ukončovací rutiny VCRuntime jsou volány bez ohledu na to, zda vaše knihovna DLL používá staticky propojenou CRT nebo dynamicky propojenou knihovnu CRT DLL.
Výchozí vstupní bod knihovny DLL _DllMainCRTStartup
Ve Windows můžou všechny knihovny DLL obsahovat volitelnou vstupní funkci, která se obvykle nazývá DllMain, která se volá pro inicializaci i ukončení. Díky tomu můžete podle potřeby přidělit nebo uvolnit jiné prostředky. Systém Windows volá funkci vstupního bodu ve čtyřech situacích: připojení procesu, odpojení procesu, připojení vlákna a odpojení vlákna. Při načtení knihovny DLL do adresního prostoru procesu, a to buď při načtení aplikace, nebo když aplikace požaduje knihovnu DLL za běhu, operační systém vytvoří samostatnou kopii dat knihovny DLL. Tomu se říká připojení procesu.
Připojení vlákna nastane, když proces, do kterého je knihovna DLL načtena, vytvoří nové vlákno.
Odpojení vlákna nastane, když se vlákno ukončí, a odpojení procesu nastane, když knihovna DLL již není potřebná a je uvolněna aplikací. Operační systém vytvoří samostatné volání vstupního bodu knihovny DLL pro každou z těchto událostí a předává argument důvodu pro každý typ události. Například, operační systém odešle DLL_PROCESS_ATTACH jako důvodový argument pro připojení procesu signálu.
Knihovna VCRuntime poskytuje funkci vstupního bodu, která se nazývá _DllMainCRTStartup, pro zpracování výchozích operací inicializace a ukončení. Při připojení _DllMainCRTStartup procesu funkce nastaví kontroly zabezpečení vyrovnávací paměti, inicializuje CRT a další knihovny, inicializuje informace o typu modulu runtime, inicializuje a volá konstruktory pro statická a nelokální data, inicializuje místní úložiště vláken, zvýší interní statický čítač pro každé připojení a potom zavolá uživatelem nebo knihovnou dodané DllMain. Při odpojení procesu funkce prochází těmito kroky obráceně. Volá DllMain, dekrementuje interní čítač, volá destruktory, funkce ukončení CRT a registrované funkce atexit, a upozorní všechny ostatní knihovny na ukončení. Když čítač přílohy přejde na nulu, vrátí FALSE funkce indikaci systému Windows, že knihovnu DLL lze uvolnit. Funkce _DllMainCRTStartup se také volá během připojení vlákna a odpojení vlákna. V těchto případech kód VCRuntime neprovádí samostatně žádnou další inicializaci nebo ukončení a pouze zavolá DllMain, aby zprávu předal. Pokud DllMain vrátí FALSE z připojení procesu, signalizuje selhání, _DllMainCRTStartup zavolá DllMain znovu a předá DLL_PROCESS_DETACH jako argument důvodu, pak projde zbytkem procesu ukončení.
Při vytváření knihoven DLL v prostředí Visual Studio je výchozí vstupní bod _DllMainCRTStartup zadaný VCRuntime automaticky propojen. Funkci vstupního bodu pro knihovnu DLL nemusíte zadávat pomocí možnosti linkeru /ENTRY (symbol vstupního bodu).
Poznámka:
I když je možné zadat jinou funkci vstupního bodu pro knihovnu DLL pomocí /ENTRYmožnosti : linker, nedoporučujeme ji, protože funkce vstupního bodu by musela duplikovat vše, co _DllMainCRTStartup dělá, ve stejném pořadí. VCRuntime poskytuje funkce, které umožňují duplikovat jeho chování. Například můžete volat __security_init_cookie v okamžiku připojení procesu na podporu možnosti /GS (kontrola zabezpečení zásobníku). Funkci _CRT_INIT můžete volat s předáním stejných parametrů jako funkce vstupního bodu k provedení zbývajících funkcí pro inicializaci nebo ukončení knihovny DLL.
Inicializovat knihovnu DLL
Vaše knihovna DLL může obsahovat inicializační kód, který se musí spustit při načtení této knihovny DLL. Aby bylo možné provádět vlastní inicializační a ukončovací funkce knihovny DLL, _DllMainCRTStartup volá funkci zvanou DllMain, kterou můžete poskytnout. Vaše DllMain musí mít podpis požadovaný pro vstupní bod knihovny DLL. Výchozí funkce vstupního bodu _DllMainCRTStartup volá DllMain pomocí stejných parametrů předaných systémem Windows. Pokud funkci nezadáte DllMain , Visual Studio vám ji ve výchozím nastavení poskytne a propojí ji tak, aby _DllMainCRTStartup vždy mělo co volat. To znamená, že pokud nepotřebujete inicializovat knihovnu DLL, nemusíte při jejím sestavování dělat nic zvláštního.
Toto je podpis použitý pro DllMain:
#include <windows.h>
extern "C" BOOL WINAPI DllMain (
HINSTANCE const instance, // handle to DLL module
DWORD const reason, // reason for calling function
LPVOID const reserved); // reserved
Některé knihovny zabalí DllMain funkci za vás. Například v běžné knihovně MFC DLL implementujte členské funkce InitInstance a ExitInstance objektu CWinApp pro provedení inicializace a ukončení, které vyžaduje vaše DLL. Další informace naleznete ve specifikaci Inicializace běžných knihoven MFC DLL.
Upozornění
Existují významná omezení toho, co můžete bezpečně dělat ve vstupním bodě knihovny DLL. Další informace o konkrétních rozhraních API systému Windows, která nejsou bezpečná pro volání, naleznete v DllMaintématu Obecné osvědčené postupy. Pokud potřebujete cokoli, kromě té nejjednodušší inicializace, proveďte to ve funkci inicializace pro knihovnu DLL. Po spuštění a před voláním jiných funkcí v knihovně DLL můžete vyžadovat, aby aplikace volaly inicializační funkci DllMain .
Inicializace běžných knihoven DLL (mimo MFC)
Chcete-li provést vlastní inicializaci v běžných knihovnách DLL (jiné než MFC), které používají vstupní bod zadaný VCRuntime _DllMainCRTStartup , musí zdrojový kód knihovny DLL obsahovat funkci s názvem DllMain. Následující kód představuje základní kostru, která ukazuje, jak může vypadat definice DllMain :
#include <windows.h>
extern "C" BOOL WINAPI DllMain (
HINSTANCE const instance, // handle to DLL module
DWORD const reason, // reason for calling function
LPVOID const reserved) // reserved
{
// Perform actions based on the reason for calling.
switch (reason)
{
case DLL_PROCESS_ATTACH:
// Initialize once for each new process.
// Return FALSE to fail DLL load.
break;
case DLL_THREAD_ATTACH:
// Do thread-specific initialization.
break;
case DLL_THREAD_DETACH:
// Do thread-specific cleanup.
break;
case DLL_PROCESS_DETACH:
// Perform any necessary cleanup.
break;
}
return TRUE; // Successful DLL_PROCESS_ATTACH.
}
Poznámka:
Starší dokumentace k sadě Windows SDK uvádí, že skutečný název funkce vstupního bodu knihovny DLL musí být zadán na příkazovém řádku linkeru s možností /ENTRY. Ve Visual Studio nemusíte použít možnost /ENTRY, pokud je název vaší funkce vstupního bodu DllMain. Pokud ve skutečnosti použijete /ENTRY možnost a pojmenujete funkci vstupního bodu něco jiného než DllMain, crT se neinicializuje správně, pokud funkce vstupního bodu nevyvolá stejná inicializační volání, která _DllMainCRTStartup provede.
Ruční inicializace CRT pomocí _CRT_INIT
Při vytváření knihovny DLL, která používá některou z knihoven modulu runtime jazyka C, zajistěte, aby byl CRT správně inicializován, buď:
- Inicializační funkce musí být pojmenovaná
DllMain()a vstupní bod musí být zadán pomocí možnosti linkeru-entry:_DllMainCRTStartup@12. Toto je výchozí chování při vytváření knihovny DLL (pouze pro x86, protože tato platforma používástdcall). - Vstupní bod knihovny DLL musí explicitně volat
_CRT_INIT()při připojení procesu a odpojení procesu. To je relevantní jenom v případě, že používáte/NOENTRYnebo máte vlastní vstupní bod. Pokud je to možné, nedoporučujeme používat/NOENTRYani vlastní vstupní bod, protože byste museli duplikovat veškerý inicializační a ukončovací kód, který_DllMainCRTStartupposkytuje, ve stejném pořadí.
To umožňuje knihovnám modulu runtime jazyka C správně přidělit a inicializovat data modulu runtime jazyka C, pokud je proces nebo vlákno připojené k knihovně DLL, aby správně vyčistily data modulu runtime jazyka C při odpojení procesu od knihovny DLL a aby globální objekty C++ v knihovně DLL byly správně vytvořené a destrukované.
Všechny ukázky sady Win32 SDK používají první metodu. Projděte si referenční informace pro Win32 a DllEntryPoint(), dokumentaci k Visual C++ pro DllMain().
DllMainCRTStartup() volá _CRT_INIT() a _CRT_INIT() volá DllMain() vaší aplikace, pokud existuje.
Pokud chcete použít druhou metodu, a místo použití DllMainCRTStartup() a DllMain() sami zavolat inicializační kód CRT, existují dvě techniky:
Pokud máte vlastní vstupní bod knihovny DLL, proveďte následující kroky v vstupním bodě:
a. Použijte tento prototyp (definovaný v
process.hpřípadě, že_DECL_DLLMAINje definován) pro_CRT_INIT():BOOL WINAPI _CRT_INIT(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved);Informace o
_CRT_INIT()návratových hodnotách najdete v dokumentaciDllEntryPoint; vrací se stejné hodnoty.b) Na
DLL_PROCESS_ATTACHaDLL_THREAD_ATTACHvolejte nejprve_CRT_INIT(), předtím než jsou volány jakékoli funkce modulu runtime jazyka C nebo jsou prováděny operace s plovoucí desetinnou čárkou.c) Volejte vlastní kód inicializace nebo ukončování procesu nebo vlákna.
d. Při
DLL_PROCESS_DETACHaDLL_THREAD_DETACHvolejte_CRT_INIT()jako poslední, po volání všech funkcí modulu runtime jazyka C a po dokončení všech operací s plovoucí desetinou čárkou.
Nezapomeňte předat _CRT_INIT() všechny parametry vstupního bodu; _CRT_INIT() očekává tyto parametry, takže věci nemusí spolehlivě fungovat, pokud jsou vynechány (zejména je nutné určit, fdwReason zda je potřeba inicializace nebo ukončení procesu).
Níže je vzorová funkce kostry vstupního bodu, která ukazuje, kdy a jak provést tato volání _CRT_INIT() ve vstupním bodu knihovny DLL.
BOOL WINAPI DllEntryPoint(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
{
if (fdwReason == DLL_PROCESS_ATTACH || fdwReason == DLL_THREAD_ATTACH)
if (!_CRT_INIT(hinstDLL, fdwReason, lpReserved))
return(FALSE);
if (fdwReason == DLL_PROCESS_DETACH || fdwReason == DLL_THREAD_DETACH)
if (!_CRT_INIT(hinstDLL, fdwReason, lpReserved))
return(FALSE);
return(TRUE);
}
Poznámka:
To není nutné, pokud používáte DllMain() a -entry:_DllMainCRTStartup@12.
Inicializovat běžné MFC DLLs
Vzhledem k tomu, že běžné knihovny MFC DLL mají CWinApp objekt, měly by provádět úlohy při inicializaci a ukončení ve stejném umístění jako aplikace MFC: ve funkcích InitInstance a ExitInstance členských funkcí třídy odvozené od CWinApp knihovny DLL. Vzhledem k tomu, že mfc poskytuje DllMain funkci, která je volána _DllMainCRTStartup pro DLL_PROCESS_ATTACH a DLL_PROCESS_DETACH, neměli byste psát vlastní DllMain funkci. Volání funkce DllMain, které poskytuje MFC, proběhne při načtení DLL a volání ExitInstance proběhne před jejím uvolněním.
Běžná knihovna MFC DLL může sledovat více vláken voláním TlsAlloc a TlsGetValue ve své InitInstance funkci. Tyto funkce umožňují knihovně DLL sledovat data specifická pro vlákna.
Ve vaší běžné MFC DLL, která dynamicky odkazuje na MFC, pokud používáte MFC OLE, MFC Databázi (nebo DAO), nebo MFC Sockets podporu, jsou ladicí rozšiřující MFC DLL knihovny MFCOverzeD.dll, MFCDverzeD.dll a MFCNverzeD.dll (kde verze je číslo verze) automaticky propojeny. Pro každou z těchto knihoven DLL, které používáte ve svých běžných MFC DLLCWinApp::InitInstance, je nutné volat jednu z následujících předdefinovaných inicializačních funkcí.
| Typ podpory MFC | Inicializační funkce pro volání |
|---|---|
| MFC OLE (MFCOverzeD.dll) | AfxOleInitModule |
| MFC databáze (MFCDverzeD.dll) | AfxDbInitModule |
| MFC sokety (MFCNverzeD.dll) | AfxNetInitModule |
Inicializovat DLL rozšíření MFC
Vzhledem k tomu, že knihovny DLL rozšíření MFC nemají odvozený CWinAppobjekt (stejně jako běžné knihovny MFC DLL), měli byste přidat inicializační a ukončovací kód do DllMain funkce, kterou generuje Průvodce knihovnou MFC DLL.
Průvodce poskytuje následující kód pro knihovny DLL rozšíření MFC. V kódu PROJNAME je zástupný symbol pro název projektu.
#include "pch.h" // For Visual Studio 2017 and earlier, use "stdafx.h"
#include <afxdllx.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
static AFX_EXTENSION_MODULE PROJNAMEDLL;
extern "C" int APIENTRY
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
TRACE0("PROJNAME.DLL Initializing!\n");
// MFC extension DLL one-time initialization
AfxInitExtensionModule(PROJNAMEDLL,
hInstance);
// Insert this DLL into the resource chain
new CDynLinkLibrary(Dll3DLL);
}
else if (dwReason == DLL_PROCESS_DETACH)
{
TRACE0("PROJNAME.DLL Terminating!\n");
}
return 1; // ok
}
Vytvoření nového CDynLinkLibrary objektu během inicializace umožňuje knihovně DLL rozšíření MFC exportovat CRuntimeClass objekty nebo prostředky do klientské aplikace.
Pokud budete používat knihovnu DLL rozšíření MFC z jedné nebo více běžných knihoven MFC DLL, musíte exportovat inicializační funkci, která vytvoří CDynLinkLibrary objekt. Tato funkce musí být volána z každé běžné knihovny MFC DLL, která používá rozšiřující knihovnu DLL MFC. Vhodným místem pro volání této inicializační funkce je ve členské funkci objektu odvozeného z běžné knihovny MFC DLL InitInstance před použitím jakékoli z exportovaných tříd nebo funkcí knihovny rozšíření MFC CWinApp.
Průvodce MFC DLL generuje volání, které zachycuje runtime třídy modulu (CRuntimeClass struktury) a jeho továrny objektů (COleObjectFactory objekty), aby mohly být použity při vytváření objektu CDynLinkLibrary. Měli byste zkontrolovat návratovou AfxInitExtensionModulehodnotu ; pokud je vrácena nulová hodnota z AfxInitExtensionModulefunkce , vrátit nulu z funkce DllMain .
Pokud je vaše rozšiřující DLL pro MFC explicitně propojena se spustitelným souborem (to znamená, že spustitelný soubor volá AfxLoadLibrary pro propojení s DLL), měli byste na DLL_PROCESS_DETACH přidat volání AfxTermExtensionModule. Tato funkce umožňuje knihovně MFC vyčistit knihovnu DLL rozšíření MFC, když se každý proces odpojí od knihovny DLL rozšíření MFC (což se stane, když se proces ukončí nebo když je knihovna DLL uvolněna v důsledku AfxFreeLibrary volání). Pokud je knihovna DLL rozšíření MFC implicitně propojená s aplikací, volání AfxTermExtensionModule není nutné.
Aplikace, které explicitně propojují knihovny DLL rozšíření MFC, musí při uvolnění knihovny DLL volat AfxTermExtensionModule . Měly by také používat AfxLoadLibrary a AfxFreeLibrary (místo funkcí Win32 LoadLibrary a FreeLibrary), pokud aplikace používá více vláken. Použití AfxLoadLibrary a AfxFreeLibrary zaručuje, že startovací a vypínací kód, který se spustí při načtení a uvolnění knihovny DLL rozšíření MFC, nepoškodí globální stav MFC.
Vzhledem k tomu, že MFCx0.dll je plně inicializován v okamžiku volání DllMain, můžete ve DllMain přidělit paměť a volat funkce MFC (na rozdíl od 16bitové verze MFC).
Rozšiřující knihovny DLL se mohou postarat o vícevláknové zpracování tím, že budou řešit případy DLL_THREAD_ATTACH a DLL_THREAD_DETACH v rámci funkce DllMain. Tyto případy se předávají DllMain , když se vlákna připojují a odpojují od knihovny DLL. Volání TlsAlloc při připojení knihovny DLL umožňuje této knihovně udržovat indexy místního úložiště vláken (TLS) pro každé připojené vlákno.
Soubor Afxdllx.h hlaviček obsahuje speciální definice pro struktury používané v rozšiřujících knihovnách DLL MFC, jako je definice pro AFX_EXTENSION_MODULE a CDynLinkLibrary. Tento hlavičkový soubor byste měli zahrnout do knihovny DLL s příponou MFC.
Poznámka:
Je důležité, abyste nedefinujte ani nezrušte definici žádná makra _AFX_NO_XXX v pch.h (stdafx.h ve Visual Studio 2017 a starších verzích). Tato makra existují pouze pro kontrolu, jestli konkrétní cílová platforma tuto funkci podporuje, nebo ne. Program můžete napsat a zkontrolovat tato makra (například #ifndef _AFX_NO_OLE_SUPPORT), ale program by neměl nikdy definovat nebo nedefinovat tato makra.
Ukázková inicializační funkce, která zpracovává vícevláknové zpracování, je součástí použití místního úložiště vláken v knihovně Dynamic-Link v sadě Windows SDK. Všimněte si, že ukázka obsahuje funkci vstupního bodu nazvanou LibMain, ale tuto funkci DllMain byste měli pojmenovat tak, aby fungovala s knihovnami modulu runtime MFC a C.
Viz také
Vytváření knihoven DLL jazyka C/C++ v sadě Visual Studio
Vstupní bod DllMain
Osvědčené postupy pro dynamické knihovny