Obdržíte upozornění linker projektů při vytváření spravovaných rozšíření C++ knihovny DLL

Překlady článku Překlady článku
ID článku: 814472 - Produkty, které se vztahují k tomuto článku.
Rozbalit všechny záložky | Minimalizovat všechny záložky

Na této stránce

Příznaky

Zobrazí se jedna z následujících chybových zpráv při kompilaci čas, nebo při propojení:
LNK2001 Chyba nástroje Linker
"nevyřešené externí symbolem "symbol" "

Propojovač nástroje Upozornění LNK4210
'.CRT oddíl existuje; Zde může být neošetřené statické inicializuje nebo teminators'You varovné Linker projektů při vytváření spravovaných rozšíření C++ knihovny DLL

Propojovač nástroje Upozornění LNK4243
"Knihovny DLL obsahující objekty zkompilována/clr není spojen s /NOENTRY; obraz se nemusí spustit správně ".
Tato upozornění může dojít během následujících okolnosti:
  • Při sestavování propojování objektů /CLR přepnete.
  • Když vytváříte, jeden z následujících projektů: PROSTŘEDÍ ASP.NET webové šablony služby; Šablona knihovny tříd; nebo ovládací prvek systému Windows Šablona knihovny.
  • Pokud jste přidali kód, který používá globální proměnné nebo nativní tříd (to znamená, že není __gc nebo __value) s členy statická data. Pro například ActiveX šablonu knihovny (ATL) Microsoft Foundation Classes (MFC) a třídy C Runtime (CRT)
Poznámka:: může dojít k chybám LNK2001 a LNK4210 s projekty které jsou není vliv problému popsaného v tomto článku. Však jednoznačně projektu je vliv problému popsaného v tomto článku, pokud řešení Upozornění LNK2001 nebo LNK4210 vede k LNK4243 varování, nebo je-li propojení aplikace Project generovat upozornění LNK4243.

Příčina

Tyto projekty jsou ve výchozím nastavení vytvořena jako dynamické Knihovna (DLL) bez jakékoli navázání nativní knihovny (například CRT, ATL nebo MFC) a bez globální proměnné nebo nativní tříd pomocí statické datové členy:
  • PROSTŘEDÍ ASP.Šablony služby NET
  • Knihovna šablony třídy
  • Šablona knihovny Windows ovládací prvek
Je-li přidat kód, který používá nativní tříd nebo globální proměnné s členy statických dat (například CRT, ATL a MFC knihovny použití globální proměnné), obdržíte chybové zprávy linker na kompilace. Když k tomu dojde, je třeba přidat kód ručně inicializovat statické proměnné. Pro Další informace o tomto postupu naleznete v části "Řešení" tohoto článek.

Pro pohodlí, tento článek se týká globální proměnné a statická data členů nativní tříd jako "Statistika" nebo "statické proměnné" z Tento bod vpřed.

Tento problém je způsoben smíšené DLL načítání došlo k potížím. Smíšené knihovny DLL (to znamená, DLL, které obsahují spravované i nativní kód) může dojít k zablokování scénáře za určitých okolností při načtení do adresového prostoru procesu zvláště když systém je pod napětím. Na Linker chybové zprávy, které již bylo zmíněno dříve byly povoleny ve vytvořeném propojovacím zda aby zákazníci byli vědomi možného zablokování a řešení, jsou popsány v tomto dokumentu. Podrobný popis smíšené knihovny DLL načítání problému, naleznete v následujícím dokumentu whitepaper:
Knihovna DLL nakládku Problém
http://msdn2.microsoft.com/en-us/library/aa290048 (vs.71) .aspx

Řešení

Řízená rozšíření pro C++ projekty, které jsou vytvořeny jako knihovny DLL ve výchozím nastavení propojení k nativní C/C++ knihovny jako C Runtime (CRT) knihovny ATL a MFC a neobsahují žádné statické proměnné. Navíc nastavení projektu určit, že knihovny DLL měla být spojena s / NOENTRY možnost povoleno.

To se provádí, protože propojení s vstupní bod způsobuje spravovaný kód při spuštění DllMain, který není bezpečný (viz DllMain pro omezenou sadu akcí, které lze provést během jeho rozsah).

Bez vstupní bod knihovny DLL nijak inicializovat staticky proměnné s výjimkou velmi jednoduché typy jako celá čísla. Nejsou obvykle provést mít statické proměnné v / NOENTRY KNIHOVNA DLL.

Všechny spoléhat na statické knihovny ATL, MFC a CRT proměnné, takže tyto knihovny v rámci těchto knihoven také nelze použít bez prvního provedení úprav.

Pokud potřebuje knihovny DLL v kombinovaném režimu Statistika nebo knihoven, které jsou závislé na Statistika (například ATL, MFC nebo CRT), potom knihovny DLL je třeba upravit tak, aby Statistika ručně inicializovat.

Prvním krokem k ruční inicializace je zajistit zakázat kód automatické inicializace, který nebezpečný s kombinovaným Knihovny DLL a mohou způsobit zablokování. Inicializace kód zakázat, postupujte kroky.

Odebrat vstupní bod knihovny DLL, spravované

  1. Propojit s / NOENTRY. V Aplikaci Solution Explorer, klepněte pravým tlačítkem myši projekt uzel, klepněte na tlačítko Vlastnosti. V Stránky vlastnostíDialogové okno, klepněte na tlačítko Linker, klepněte na tlačítko Příkazový řádek, a potom přidejte tento přepínač Další možnostipole.
  2. Odkaz Msvcrt.lib. V Stránky vlastností Dialogové okno, klepněte na tlačítkoLinker, klepněte na tlačítko Vstup. a potom přidejte Msvcrt.lib aby Další závislostiVlastnost.
  3. Odebrat nochkclr.obj. V Vstup stránka (stejné jako v předchozím krok), odebrání nochkclr.obj od Další závislosti Vlastnost.
  4. Odkaz CRT. V Vstup stránky (stejné stránky jako v předchozím kroku), přidání __DllMainCRTStartup@12 aby Síla Symbol odkazyVlastnost.

    Pokud použijete příkazový řádek, zadejte výše nastavení projektu následující:
    LINK /NOENTRY msvcrt.lib /NODEFAULTLIB:nochkclr.obj /INCLUDE:__DllMainCRTStartup@12

Změnit komponenty, které spotřebovávají knihovny DLL pro ruční Initializiation

Po odebrání explicitní vstupní bod, je nutné upravit součásti, které spotřebovávají knihovny DLL pro ruční inicializace, v závislosti na způsobu že je implementována knihovny DLL:
  • Knihovny DLL je zadán pomocí (vývoz) knihovny DLL__declspec(dllexport)), a vaši spotřebitelé nemohou používat spravovaný kód, pokud jsou spojeny staticky nebo dynamicky do knihovny DLL.
  • Knihovny DLL je založená na modelu COM DLL.
  • Pomocí knihovny DLL spotřebitelů spravovaný kód a knihovny DLL obsahuje buď vývozy knihovny DLL nebo spravované vstupních bodů.

Upravit soubory DLL, které zadáte pomocí knihovny DLL vývozů a Spotřebitelů, které nemohou používat spravovaný kód

Upravit soubory DLL, které zadáte pomocí knihovny dll vývozu (__declspec(dllexport)) a spotřebitelů, které nelze používat spravovaný kód, Tyto kroky:
  1. Přidat dvě nové vývozy knihovny DLL, jak je uvedeno v následujících kód:
    // 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.
    }
    
    Poznámka: Visual C++ 2005 musíte přidat běžné kompilátor možnost (podpora runtime jazyka/CLR:oldSyntax) úspěšně kompilovat předcházející ukázce kódu. Chcete-li přidat volba kompilátoru podporu runtime společný jazyk, postupujte takto:
    1. Klepněte na tlačítko Projekta klepněte na tlačítkoProjectName Vlastnosti.

      Poznámka: ProjectName je zástupný symbol pro název projektu.
    2. Rozbalit Vlastnosti konfiguracea klepněte na tlačítkoObecné.
    3. V pravém podokně klepnutím vyberte Společná podpora Runtime jazyka, staré syntaxe (/ clr:oldSyntax) vPodpora společného jazykového modulu Runtime nastavení projektu.
    4. Klepněte na tlačítkoPoužíta klepněte na tlačítko OK.
    Další informace o společný jazykový modul runtime Podpora volby kompilátoru, navštivte následující Web společnosti Microsoft Developer Network (MSDN):
    http://msdn2.microsoft.com/en-us/library/k8d11d4s.aspx
    Tyto kroky platí pro celý tento článek.
  2. Knihovny DLL může mít několik spotřebitelů. Pokud má více spotřebitelů, přidejte následující kód do souboru DLL DEF. ve vývozu oddíl:
    DllEnsureInit	PRIVATE
    DllForceTerm	PRIVATE
    
    Pokud přidáte tyto řádky a máte-li dvě knihovny DLL, které exportovat funkce a odkazy na knihovny DLL aplikace bude mít odkaz chyby. Exportovaná funkce obvykle mají stejné názvy. V případě multiconsumer jednotlivé spotřebitele lze propojit staticky nebo dynamicky knihovny DLL.
  3. V případě, že spotřebitel je staticky propojeny ke knihovně DLL, před použít čas první knihovny DLL nebo před použitím cokoli, která závisí na jeho v aplikaci přidejte následující volání:
    // 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
    }
    
  4. Po jeho posledním použití knihovny DLL v aplikaci přidat Následující kód:
    // 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
    }
    
  5. V případě, že spotřebitel je dynamicky spojen knihovny DLL, vložení Následující kód:
    • Vložit fragment 1 (viz krok 3) bezprostředně po první Funkce LoadLibrary pro knihovnu DLL.
    • Vložit fragment 2 (viz krok 4) těsně před poslední FreeLibrary pro knihovnu DLL.

Chcete-li upravit založená na modelu COM DLL

  • Změna funkce export DLL DllCanUnloadNow, DllGetClassObject, Funkce DllRegisterServer, a DllUnregisterServer jak je ukázáno v následujícím kódu:
    //  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;
    }
    

Upravit Knihovny DLL obsahující spotřebitelů, které používají spravovaný kód a vývozy knihovny DLL nebo spravované vstupních bodů

Úprava knihovny DLL obsahující spotřebitelů, které využívají spravovaný kód a Exportuje knihovny DLL nebo spravované vstupních bodů, postupujte takto:
  1. Implementovat spravované třídy s funkcí statický člen. Inicializace a ukončení. Přidání souboru cpp do projektu, provádění spravované třídy statické členy pro inicializaci a ukončení:
    // 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 */
    
  2. Tyto funkce volají před naleznete v knihovně DLL a po Dokončili jste jeho použití. Inicializace nebo ukončení člen volání hlavní funkce:
    // Main.cpp
    
    #using <mscorlib.dll>
    using namespace System;
    using namespace System::Reflection;
    #using "ijwdll.dll";
    
    int main() {
    	int retval = ManagedWrapper::minitialize();
        ManagedWrapper::mterminate();
    }
    

Uživatelé aplikace Visual C++.NET 2002

Poznámka:: Ačkoli linker chybová zpráva LNK4243 neexistuje v Visual C++.NET 2002 verze produktu, uživatelé aplikace Visual C++.NET 2002 je doporučeno. postupujte podle pokynů uvedených výše při vývoji smíšené knihoven DLL.

Visual C++.NET 2003 verze produktu obsahuje další záhlaví provést ruční inicializace pohodlnější. K řešení podle tohoto článku práce s Visual C++.NET 2002, je třeba přidat záhlaví soubor s názvem projektu _vcclrit.h následující text:
/***
* _vcclrit.h
*
*       Copyright (c) Microsoft Corporation. All rights reserved.
*
* Purpose:
*       This file defines the functions and variables used by user
*       to initialize CRT and the dll in IJW scenario.
*
****/

#pragma once

#ifdef __cplusplus
extern "C" {
#endif

extern IMAGE_DOS_HEADER __ImageBase;

BOOL WINAPI _DllMainCRTStartup(
        HANDLE  hDllHandle,
        DWORD   dwReason,
        LPVOID  lpreserved
        );
#ifdef __cplusplus
}
#endif

#ifdef _cplusplus
#define __USE_GLOBAL_NAMESPACE  ::
#else
#define __USE_GLOBAL_NAMESPACE
#endif

// Used to lock 
__declspec( selectany ) LONG  volatile __lock_handle = 0;

// Init called
__declspec(selectany) BOOL volatile __initialized = FALSE;

// Term called
__declspec( selectany ) BOOL volatile __terminated = FALSE;

__inline BOOL WINAPI __crt_dll_initialize()
{
    // Try to make the variable names unique, so that the variables 
    // do not even clash with macros.
    static BOOL volatile (__retval) = FALSE;
    static DWORD volatile (__lockThreadId) = 0xffffffff;
    DWORD volatile (__currentThreadId) = __USE_GLOBAL_NAMESPACE(GetCurrentThreadId)();
    int (__int_var)=0;
    
    // Take Lock; this is needed for multithreaded scenario. 
    // Additionally, the threads need to wait here to make sure that the dll 
    // is initialized when they get past this function.
    while ( __USE_GLOBAL_NAMESPACE(InterlockedExchange)( &(__lock_handle), 1) == 1 )
	{
        ++(__int_var);
        if ((__lockThreadId) == (__currentThreadId)) 
        {
            return TRUE;
        }
		__USE_GLOBAL_NAMESPACE(Sleep)( (__int_var)>1000?100:0 );

        // If you hang in this loop, this implies that your 
        // dllMainCRTStartup is hung on another thread. 
        // The most likely cause of this is a hang in one of your 
        // static constructors or destructors.
	}
    // Note: you do not really need any interlocked stuff here because the 
    // writes are always in the lock. Only reads are outside the lock.
    (__lockThreadId) = (__currentThreadId);
    __try {
        if ( (__terminated) == TRUE )
        {
            (__retval) = FALSE;
        }
        else if ( (__initialized) == FALSE )
        {
            (__retval) = (_DllMainCRTStartup)( ( HINSTANCE )( &__ImageBase ), DLL_PROCESS_ATTACH, 0 );
            (__initialized) = TRUE;
        }

    } __finally {
        // revert the __lockThreadId
        (__lockThreadId) = 0xffffffff;
        // Release Lock
       __USE_GLOBAL_NAMESPACE(InterlockedExchange)(&(__lock_handle),0);
    }
    return (__retval);
}

__inline BOOL WINAPI __crt_dll_terminate()
{
    static BOOL volatile (__retval) = TRUE;
    static DWORD volatile (__lockThreadId) = 0xffffffff;
    DWORD volatile (__currentThreadId) = __USE_GLOBAL_NAMESPACE(GetCurrentThreadId)();
    int (__int_var)=0;
    
    // Take Lock; this lock is needed to keep Terminate 
    // in sync with Initialize.
    while ( __USE_GLOBAL_NAMESPACE(InterlockedExchange)( &(__lock_handle), 1) == 1 )
	{
        ++(__int_var);
        if ((__lockThreadId) == (__currentThreadId)) 
        {
            return TRUE;
        }
		__USE_GLOBAL_NAMESPACE(Sleep)( (__int_var)>1000?100:0 );

        // If you hang in this loop, this implies that your 
        // dllMainCRTStartup is hung on another thread. The most likely 
        // cause of this is a hang in one of your static constructors 
        // or destructors.
    }
    // Note: you do not really need any interlocked stuff here because the 
    // writes are always in the lock. Only reads are outside the lock.
    (__lockThreadId) = (__currentThreadId);
    __try {
        if ( (__initialized) == FALSE )
        {
            (__retval) = FALSE;
        }
        else if ( (__terminated) == FALSE )
        {
            (__retval) = _DllMainCRTStartup( ( HINSTANCE )( &(__ImageBase) ), DLL_PROCESS_DETACH, 0 );
            (__terminated) = TRUE;
        }
    } __finally {
        // revert the __lockThreadId
        (__lockThreadId) = 0xffffffff;
        // Release Lock
       __USE_GLOBAL_NAMESPACE(InterlockedExchange)(&(__lock_handle),0);
    }
    return (__retval);
}

Odkazy

Další informace získáte klepnutím na následující číslo článku databáze Microsoft Knowledge Base:
309694Chyba: Výjimka AppDomainUnloaded při použití Řízená rozšíření pro součásti Visual C++

Vlastnosti

ID článku: 814472 - Poslední aktualizace: 20. května 2011 - Revize: 9.0
Informace v tomto článku jsou určeny pro produkt:
  • Microsoft Visual C++ 2005 Express Edition
  • Microsoft Visual C++ .NET 2003 Standard Edition
  • Microsoft Visual C++ .NET 2002 Standard Edition
  • Microsoft .NET Framework 1.1
  • Microsoft .NET Framework 1.0
Klíčová slova: 
kbcominterop kbmanaged kbdll kbijw kbprb kbmt KB814472 KbMtcs
Strojově přeložený článek
Důležité: Tento článek byl přeložen pomocí software společnosti Microsoft na strojový překlad, ne profesionálním překladatelem. Společnost Microsoft nabízí jak články přeložené překladatelem, tak články přeložené pomocí software na strojový překlad, takže všechny články ve Znalostní databázi (Knowledge Base) jsou dostupné v češtině. Překlad pomocí software na strojový překlad ale není bohužel vždy dokonalý. Obsahuje chyby ve skloňování slov, skladbě vět, nebo gramatice, podobně jako když cizinci dělají chyby při mluvení v češtině. Společnost Microsoft není právně zodpovědná za nepřesnosti, chyby nebo škody vzniklé chybami v překladu, nebo při použití nepřesně přeložených instrukcí v článku zákazníkem. Společnost Microsoft aktualizuje software na strojový překlad, aby byl počet chyb omezen na minimum.
Projděte si také anglickou verzi článku:814472

Dejte nám zpětnou vazbu

 

Contact us for more help

Contact us for more help
Connect with Answer Desk for expert help.
Get more support from smallbusiness.support.microsoft.com