Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Dit artikel bevat informatie over het oplossen van linkerwaarschuwingen bij het bouwen van beheerde extensies voor C++ DLL-projecten.
Oorspronkelijke productversie: Visual C++
Oorspronkelijk KB-nummer: 814472
Symptomen
U ontvangt een van de volgende foutberichten tijdens het compileren of op het moment van de koppeling:
Fout bij linkerhulpprogramma's LNK2001
'niet-opgelost extern symbool 'symbool'
Waarschuwing LNK4210 linkerhulpprogramma's
'. CRT-sectie bestaat; er kunnen niet-verwerkte statische initialisaties of teminators zijn'U ontvangt linkerwaarschuwingen wanneer u beheerde extensies voor C++ DLL-projecten bouwt
Waarschuwing LNK4243 linkerhulpprogramma's
'DLL met objecten die zijn gecompileerd met /clr is niet gekoppeld aan /NOENTRY; de installatiekopieën zijn mogelijk niet correct uitgevoerd.'
Deze waarschuwingen kunnen optreden in de volgende omstandigheden:
- Wanneer u het koppelen van objecten compileert met de schakeloptie /clr .
- Wanneer u een van de volgende projecten bouwt:
- ASP.NET-webservicesjabloon
- Sjabloon voor klassenbibliotheek
- Sjabloon voor Windows-besturingselementbibliotheek
- Wanneer u code hebt toegevoegd die gebruikmaakt van globale variabelen of systeemeigen klassen (dat wil
__gcniet of__value) met statische gegevensleden. Bijvoorbeeld de ActiveX-sjabloonbibliotheek (ATL), Microsoft Foundation-klassen (MFC) en de C-runtimeklassen (CRT).
Notitie
Mogelijk ontvangt u de LNK2001- en LNK4210 fouten met projecten die niet worden beïnvloed door het probleem dat in dit artikel wordt beschreven. Het project wordt echter zeker beïnvloed door het probleem dat in dit artikel wordt beschreven als het oplossen van een LNK2001 of LNK4210 waarschuwing leidt tot een LNK4243 waarschuwing of als het koppelen van het project een LNK4243 waarschuwing genereert.
Oorzaak
De volgende projecten worden standaard gemaakt als een DLL (Dynamic Link Library) zonder koppeling naar systeemeigen bibliotheken (zoals CRT, ATL of MFC), en zonder globale variabelen of systeemeigen klassen met statische gegevensleden:
- ASP.NET-webservicesjabloon
- Sjabloon voor klassenbibliotheek
- Sjabloon voor Windows-besturingselementbibliotheek
Als u code toevoegt die gebruikmaakt van globale variabelen of systeemeigen klassen met statische gegevensleden (bijvoorbeeld de ATL-, MFC- en CRT-bibliotheken gebruiken globale variabelen), worden er tijdens het compileren linker-foutberichten weergegeven. Wanneer dit gebeurt, moet u code toevoegen om de statische variabelen handmatig te initialiseren. Zie de sectie Oplossing van dit artikel voor meer informatie over hoe u dit doet.
Voor het gemak verwijst dit artikel naar globale variabelen en statische gegevensleden van systeemeigen klassen als statics of statische variabelen vanaf dit moment.
Dit probleem wordt veroorzaakt door het gemengde DLL-laadprobleem. Gemengde DLL's (d.i.v. DLL's die zowel beheerde als systeemeigen code bevatten) kunnen onder bepaalde omstandigheden impassescenario's tegenkomen wanneer ze in de adresruimte van het proces worden geladen, met name wanneer het systeem stress ondervindt. De eerder genoemde linker-foutberichten zijn ingeschakeld in de linker om ervoor te zorgen dat klanten op de hoogte zijn van het potentieel voor impasses en de tijdelijke oplossingen die in dit document worden beschreven.
Oplossing
Beheerde extensies voor C++-projecten die standaard als DLL's worden gemaakt, koppelen geen systeemeigen C/C++-bibliotheken, zoals de C-runtimebibliotheek (CRT), ATL of MFC en gebruiken geen statische variabelen. Daarnaast geven de projectinstellingen op dat de DLL's moeten worden gekoppeld aan de optie /NOENTRY ingeschakeld.
Dit wordt gedaan omdat het koppelen met een toegangspunt ervoor zorgt dat beheerde code wordt uitgevoerd, DllMainwat niet veilig is (zie DllMain voor de beperkte set dingen die u kunt doen tijdens het bereik).
Een DLL zonder invoerpunt kan geen statische variabelen initialiseren, met uitzondering van eenvoudige typen, zoals gehele getallen. Normaal gesproken hebt u geen statische variabelen in een /NOENTRY DLL.
De ATL-, MFC- en CRT-bibliotheken zijn allemaal afhankelijk van statische variabelen, dus u kunt deze bibliotheken ook niet gebruiken vanuit deze DLL's zonder eerst wijzigingen aan te brengen.
Als uw DLL-bestand in de gemengde modus statics of bibliotheken moet gebruiken die afhankelijk zijn van statische gegevens (zoals ATL, MFC of CRT), moet u uw DLL zodanig wijzigen dat de statics handmatig worden geïnitialiseerd.
De eerste stap bij het handmatig initialiseren is ervoor te zorgen dat u de automatische initialisatiecode uitschakelt, die onveilig is met gemengde DLL's en een impasse kan veroorzaken. Volg de stappen om de initialisatiecode uit te schakelen.
Het toegangspunt van het beheerde DLL-bestand verwijderen
Koppeling met /NOENTRY. Klik in Solution Explorer met de rechtermuisknop op het projectknooppunt en klik op Eigenschappen. Klik in het dialoogvenster Eigenschappenpagina's op Linker, klik op Opdrachtregel en voeg deze schakeloptie toe aan het veld Extra opties .
Link msvcrt.lib. Klik in het dialoogvenster Eigenschappenpagina's op Linker, klik op Invoer en voeg vervolgens msvcrt.lib toe aan de eigenschap Aanvullende afhankelijkheden.
Verwijder nochkclr.obj. Verwijder op de invoerpagina (dezelfde pagina als in de vorige stap) nochkclr.obj uit de eigenschap Aanvullende afhankelijkheden.
Koppeling in de CRT. Voeg op de invoerpagina (dezelfde pagina als in de vorige stap) __DllMainCRTStartup@12 toe aan de eigenschap Geforceerde symboolverwijzingen.
Als u de opdrachtprompt gebruikt, geeft u de bovenstaande projectinstellingen op met het volgende:
LINK /NOENTRY msvcrt.lib /NODEFAULTLIB:nochkclr.obj /INCLUDE:__DllMainCRTStartup@12
Onderdelen wijzigen die het DLL-bestand gebruiken voor handmatige initialisatie
Nadat u het expliciete toegangspunt hebt verwijderd, moet u onderdelen wijzigen die het DLL-bestand gebruiken voor handmatige initialisatie, afhankelijk van de manier waarop uw DLL wordt geïmplementeerd:
- Uw DLL wordt ingevoerd met DLL-exports (
__declspec(dllexport)) en uw gebruikers kunnen geen beheerde code gebruiken als ze statisch of dynamisch zijn gekoppeld aan uw DLL. - Uw DLL is een DLL op basis van COM.
- Consumenten van uw DLL kunnen beheerde code gebruiken en uw DLL bevat DLL-exports of beheerde toegangspunten.
DLL's wijzigen die u invoert met behulp van DLL-exports en consumenten die geen beheerde code kunnen gebruiken
Als u DLL's wilt wijzigen die u invoert met behulp van dll-exports (__declspec(dllexport)) en consumenten die geen beheerde code kunnen gebruiken, voert u de volgende stappen uit:
Voeg twee nieuwe exports toe aan uw DLL, zoals wordt weergegeven in de volgende code:
// init.cpp #include <windows.h> #include <_vcclrit.h> // Call this function before you call anything in this DLL. // It is safe to call from multiple threads; it is not reference // counted; and is reentrancy safe. __declspec(dllexport) void __cdecl DllEnsureInit(void) { // Do nothing else here. If you need extra initialization steps, // create static objects with constructors that perform initialization. __crt_dll_initialize(); // Do nothing else here. } // Call this function after this whole process is totally done // calling anything in this DLL. It is safe to call from multiple // threads; is not reference counted; and is reentrancy safe. // First call will terminate. __declspec(dllexport) void __cdecl DllForceTerm(void) { // Do nothing else here. If you need extra terminate steps, // use atexit. __crt_dll_terminate(); // Do nothing else here. }Voer de volgende stappen uit om de common Language Runtime-ondersteuningscompilatoroptie toe te voegen:
Klik op Project en klik vervolgens op ProjectName Properties.
Notitie
ProjectName is een tijdelijke aanduiding voor de naam van het project.
Vouw configuratie-eigenschappen uit en klik vervolgens op Algemeen.
Klik in het rechterdeelvenster om Common Language Runtime-ondersteuning, oude syntaxis (/clr:oldSyntax) te selecteren in de instellingen van het Common Language Runtime-ondersteuningsproject.
Klik achtereenvolgens op Toepassen en op OK.
Zie /clr (Common Language Runtime Compilation) voor meer informatie over algemene opties voor language runtime-ondersteuning voor compileren.
Deze stappen zijn van toepassing op het hele artikel.
Uw DLL kan verschillende gebruikers hebben. Als het meerdere gebruikers heeft, voegt u de volgende code toe aan het DLL-def-bestand in de sectie Exports:
DllEnsureInitPRIVATE DllForceTermPRIVATEAls u deze regels niet toevoegt en als u twee DLL's hebt die functies exporteren, bevat de toepassing die is gekoppeld aan het DLL-bestand koppelingsfouten. Normaal gesproken hebben de geëxporteerde functies dezelfde namen. In een multiconsumer-geval kan elke consument statisch of dynamisch aan uw DLL worden gekoppeld.
Als de consument statisch is gekoppeld aan het DLL-bestand, voordat u het DLL-bestand de eerste keer gebruikt, of voordat u iets gebruikt dat ervan afhankelijk is in uw toepassing, voegt u de volgende aanroep toe:
// Snippet 1 typedef void (__stdcall *pfnEnsureInit)(void); typedef void (__stdcall *pfnForceTerm)(void); { // ... initialization code HANDLE hDll=::GetModuleHandle("mydll.dll"); If(!hDll) { // Exit, return; there is nothing else to do. } pfnEnsureInit pfnDll=::( pfnEnsureInit) GetProcAddress(hDll, "DllEnsureInit"); if(!pfnDll) { // Exit, return; there is nothing else to do. } pfnDll(); // ... more initialization code }Voeg na het laatste gebruik van het DLL-bestand in uw toepassing de volgende code toe:
// Snippet 2 { // ... termination code HANDLE hDll=::GetModuleHandle("mydll.dll"); If(!hDll) { // exit, return; there is nothing else to do } pfnForceTerm pfnDll=::( pfnForceTerm) GetProcAddress(hDll, "DllForceTerm"); if(!pfnDll) { // exit, return; there is nothing else to do } pfnDll(); // ... more termination code }Als de consument dynamisch is gekoppeld aan het DLL-bestand, voegt u als volgt code in:
- Voeg fragment 1 in (zie stap 3) direct na de eerste LoadLibrary voor het DLL-bestand.
- Voeg fragment 2 in (zie stap 4) direct vóór de laatste FreeLibrary voor het DLL-bestand.
OP COM gebaseerde DLL's wijzigen
Wijzig de DLL-exportfuncties DllCanUnloadNow, DllGetClassObjecten DllRegisterServerDllUnregisterServer zoals wordt gedemonstreerd in de volgende code:
// Implementation of DLL Exports.
#include <_vcclrit.h>
STDAPI DllCanUnloadNow(void)
{
if ( _Module.GetLockCount() == 0 )
{
__crt_dll_terminate();
return S_OK;
}
else
{
return S_FALSE;
}
}
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
if ( !( __crt_dll_initialize()) )
{
return E_FAIL;
}
else
{
return _Module.GetClassObject(rclsid, riid, ppv);
}
}
STDAPI DllRegisterServer(void)
{
if ( !( __crt_dll_initialize()) )
{
return E_FAIL;
}
// Call your registration code here
HRESULT hr = _Module.RegisterServer(TRUE)
return hr;
}
STDAPI DllUnregisterServer(void)
{
HRESULT hr = S_OK;
__crt_dll_terminate();
// Call your unregistration code here
hr = _Module.UnregisterServer(TRUE);
return hr;
}
DLL wijzigen die consumenten bevat die beheerde code en DLL-exports of beheerde toegangspunten gebruiken
Voer de volgende stappen uit om dll-bestand te wijzigen dat consumenten bevat die beheerde code en dll-exports of beheerde toegangspunten gebruiken:
Implementeer een beheerde klasse met statische lidfuncties voor initialisatie en beëindiging. Voeg een .cpp-bestand toe aan uw project en implementeer een beheerde klasse met statische leden voor initialisatie en beëindiging:
// ManagedWrapper.cpp // This code verifies that DllMain is not automatically called // by the Loader when linked with /noentry. It also checks some // functions that the CRT initializes. #include <windows.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> #include "_vcclrit.h" #using <mscorlib.dll> using namespace System; public __gc class ManagedWrapper { public: static BOOL minitialize() { BOOL retval = TRUE; try { retval = __crt_dll_initialize(); } catch(System::Exception* e) { Console::WriteLine(e->Message); retval = FALSE; } return retval; } static BOOL mterminate() { BOOL retval = TRUE; try { retval = __crt_dll_terminate(); } catch(System::Exception* e) { Console::WriteLine(e->Message); retval = FALSE; } return retval; } }; BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID lpvReserved) { Console::WriteLine(S"DllMain is called..."); return TRUE; } /* DllMain */Roep deze functies aan voordat u naar het DLL-bestand verwijst en nadat u klaar bent met het gebruik ervan. De initialisatie- en beëindigingslidfuncties aanroepen in
main:// Main.cpp #using <mscorlib.dll> using namespace System; using namespace System::Reflection; #using "ijwdll.dll"; int main() { int retval = ManagedWrapper::minitialize(); ManagedWrapper::mterminate(); }