Quando si generano le estensioni gestite per c ++ DLL progetti, viene visualizzato gli avvisi del linker

Traduzione articoli Traduzione articoli
Identificativo articolo: 814472 - Visualizza i prodotti a cui si riferisce l?articolo.
Espandi tutto | Chiudi tutto

In questa pagina

Sintomi

Viene visualizzato uno dei seguenti messaggi di errore in fase di compilazione o in fase di collegamento:
Errore degli strumenti del linker LNK2001
'simbolo esterno non risolto 'simbolo' '

Avviso LNK4210 strumenti del linker
"La sezione .CRT esiste; potrebbe essere non gestita Inizializza statico o teminators'You ricevere avvisi del linker quando si generano le estensioni gestite per c ++ DLL progetti

Avviso LNK4243 strumenti del linker
'DLL contenente oggetti compilati con /clr non Ŕ collegato con /NOENTRY; immagine potrebbe non essere eseguita correttamente'.
Questi avvisi possono verificarsi durante le seguenti circostanze:
  • Quando si compila il collegamento di oggetti con il / clr passare.
  • Quando si sta creando uno dei seguenti progetti: modello di servizio Web ASP.NET, libreria di classi template; o modello di libreria di controlli Windows.
  • Dopo aver aggiunto il codice che utilizza variabili globali o classi native (vale a dire non __gc o __value) con i membri dati statici. Ad esempio, la libreria ATL (ActiveX Template Library), Microsoft Foundation Classes (MFC) e le classi di runtime C (CRT)
Nota : puoi ricevere gli errori LNK2001 e LNK4210 con i progetti che sono non interessato dal problema descritto in questo articolo. Tuttavia, il progetto in modo definito Ŕ interessato dal problema descritto in questo articolo se risolvendo un LNK2001 o avviso LNK4210 conduce a un messaggio di avviso LNK4243 o se il collegamento del progetto genera un avviso LNK4243.

Cause

I seguenti progetti vengono creati per impostazione predefinita come libreria di collegamento dinamico (DLL) senza alcun collegamento alle librerie native (ad esempio CRT, ATL o MFC) e senza classi native con i membri dati statici o variabili globali:
  • Modello di servizio Web ASP.NET
  • Modello di libreria di classi
  • Modello di libreria di controlli Windows
Se si aggiunge codice che utilizza le variabili globali o classi native con i membri dati statici (ad esempio, le librerie CRT, MFC e ATL utilizzano variabili globali) verranno visualizzati messaggi di errore del linker in fase di compilazione. In questo caso, Ŕ necessario aggiungere codice per inizializzare manualmente le variabili statiche. Per ulteriori informazioni su come effettuare questa operazione, vedere la sezione "Risoluzione" di questo articolo.

Per comoditÓ, in questo articolo si riferisce alla variabili globali e ai membri dati statici di classi native come "elementi statici" o "variabili statiche" impostato su da questo punto in avanti.

Questo problema Ŕ causato il problema di caricamento DLL mista. Le DLL miste (vale a dire DLL contenenti codice nativo e gestito) possono verificarsi gli scenari di blocco critico (deadlock) in alcune circostanze quando vengono caricati nello spazio di indirizzi del processo, soprattutto quando il sistema Ŕ in condizioni di stress. I messaggi di errore del linker menzionati in precedenza sono stati abilitati nel linker per assicurarsi che i clienti siano tenere in considerazione la possibilitÓ di blocco critico (deadlock) e le soluzioni descritte in questo documento. Per una descrizione dettagliata della DLL mista problemi di caricamento, vedere il white paper seguenti:
Misto caricamento DLL problema
http://msdn2.microsoft.com/en-us/library/aa290048(vs.71).aspx

Risoluzione

Estensioni gestite per c ++ i progetti vengono creati come DLL, per impostazione predefinita non si collega alle librerie di C/C ++ native, ad esempio la raccolta di componenti runtime C (CRT), ATL o MFC e non utilizzano tutte le variabili statiche. Inoltre, le impostazioni del progetto specificano che le DLL devono essere collegate con il / NOENTRY opzione Ŕ attivata.

Questo avviene perchÚ il collegamento con un punto di ingresso provoca l'esecuzione durante DllMain , che non Ŕ sicuro di codice gestito (per il set limitato di elementi Ŕ possibile eseguire durante l'ambito, vedere DllMain ).

Una DLL senza un punto di ingresso compia alcuna particolare per inizializzare variabili statiche, tranne per tipi molto semplice, ad esempio numeri interi. Non si dispone in genere le variabili statiche un / NOENTRY DLL.

Le librerie ATL, MFC e CRT che tutti si basano su variabili statiche, pertanto non Ŕ inoltre possibile utilizzare queste raccolte all'interno di queste DLL senza prima le modifiche.

Se la DLL a modalitÓ mista Ŕ necessario utilizzare elementi statici o le librerie che dipendono da elementi statici (ad esempio ATL, MFC o CRT), Ŕ necessario modificare la DLL in modo che gli elementi statici vengono inizializzati manualmente.

Il primo passaggio inizializzazione manuale consiste nell'assicurarsi di disattivare il codice di inizializzazione automatica, che Ŕ sicuro con le DLL miste e pu˛ causare il blocco critico (deadlock). Per disattivare il codice di inizializzazione, attenersi alla seguente procedura.

Rimuovere il punto di ingresso della DLL gestita

  1. Il collegamento con / NOENTRY . In Esplora soluzioni , fare clic con il pulsante destro del mouse sul nodo progetto e scegliere ProprietÓ . Nelle Pagine delle proprietÓ della finestra di dialogo fare clic su linker , fare clic su riga di comando e quindi aggiungere questa opzione per le Opzioni aggiuntive del campo.
  2. Collegamento msvcrt.lib . Nella casella finestra di dialogo Pagine delle proprietÓ , fare clic su linker , fare clic su input ., quindi aggiungere msvcrt.lib la proprietÓ Dipendenze aggiuntive .
  3. Rimuovere nochkclr.obj . Rimuovere nochkclr.obj nella pagina di input (stessa pagina as in il passaggio precedente), la proprietÓ Dipendenze aggiuntive .
  4. Collegamento in CRT. Nell'input di pagina (pagina stessa as in il passaggio precedente), aggiungere __DllMainCRTStartup@12 i Force Symbol References proprietÓ.

    Se si utilizza il prompt dei comandi, specificare le precedenti impostazioni del progetto con il seguente:
    LINK /NOENTRY msvcrt.lib /NODEFAULTLIB:nochkclr.obj /INCLUDE:__DllMainCRTStartup@12

Modificare i componenti che consente di utilizzare la DLL per Initializiation manuale

Dopo aver rimosso il punto di ingresso esplicita, Ŕ necessario modificare i componenti che utilizzano la DLL per l'inizializzazione manuale, a seconda del modo che la DLL viene implementata in cui:
  • La DLL viene immesso utilizzando esportazioni DLL ( __declspec (dllexport) ) e il consumer non Ŕ possibile utilizzare codice gestito se questi sono collegati in modo statico o dinamico alla DLL.
  • La DLL Ŕ una DLL COM.
  • I consumer della DLL utilizzare codice gestito e la DLL contiene entrambi esportazioni DLL o gestiti i punti di ingresso.

Modificare la DLL che si immette con DLL esportazioni e utenti che Ŕ Impossibile utilizzare codice gestito

Per modificare la DLL immesso utilizzando esportazioni di dll (__declspec(dllexport)) e gli utenti che non utilizzano codice gestito, attenersi alla seguente procedura:
  1. Aggiungere due nuove Esporta alla DLL, come illustrato nel codice riportato di seguito:
    // 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.
    }
    
    Nota In Visual c ++ 2005, in cui Ŕ necessario aggiungere l'opzione del compilatore supporto di common language runtime ( / clr: oldSyntax ) per compilare correttamente l'esempio di codice precedente. Per aggiungere il compilatore opzione supporto common language runtime, attenersi alla seguente procedura:
    1. Fare clic su progetto e quindi fare clic su proprietÓ ProjectName.

      Nota ProjectName Ŕ un segnaposto per il nome del progetto.
    2. Espandere la ProprietÓ di configurazione e quindi fare clic su Generale .
    3. Nel riquadro destro, fare clic su per selezionare supporto Common Language Runtime, vecchia sintassi (/ CLR: oldSyntax) in Common Language Runtime supporta le impostazioni del progetto.
    4. Fare clic su Applica , quindi fare clic su OK .
    Per ulteriori informazioni su common language runtime supporta opzioni del compilatore, visitare il seguente sito Web Microsoft Developer Network (MSDN):
    http://msdn2.microsoft.com/en-us/library/k8d11d4s.aspx
    Valida per l'intero articolo.
  2. La DLL pu˛ avere pi¨ consumer. Se Ŕ disponibile pi¨ consumer, aggiungere il codice riportato di seguito il file DEF DLL nella sezione Esporta:
    DllEnsureInit	PRIVATE
    DllForceTerm	PRIVATE
    
    se non si aggiunge queste righe, e se si dispone di due DLL esportare le funzioni, l'applicazione collegata alla DLL avrÓ gli errori di collegamento. In genere, le funzioni esportate hanno lo stesso nome. In caso multiconsumer, ogni consumer possono essere collegati in modo statico o dinamico alla DLL.
  3. Se il consumer Ŕ collegato in modo statico alla DLL, prima di utilizzare la DLL la prima volta o prima di utilizzare qualsiasi elemento che dipende da tale nell'applicazione, aggiungere la seguente chiamata:
    // 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. Dopo l'ultimo utilizzo della DLL dell'applicazione, aggiungere il codice riportato di seguito:
    // 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. Se il consumer Ŕ collegato in modo dinamico la DLL, inserire codice come segue:
    • Inserire il frammento 1 (vedere il passaggio 3) immediatamente dopo il primo LoadLibrary per la DLL.
    • Inserire un frammento 2 (vedere il passaggio 4) immediatamente prima dell' ultima FreeLibrary per la DLL.

Per la modifica basata su COM DLL

  • Modificare le funzioni di esportazione DLL DllCanUnloadNow , DllGetClassObject , DllRegisterServer e DllUnregisterServer , come illustrato nel codice seguente:
    //  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;
    }
    

Modificare DLL che contiene utenti che utilizzano gestito codice ed esportazioni DLL o gestite punti di ingresso

Per modificare la DLL che contiene gli utenti che utilizzano codice gestito e la dll Esporta o punti di ingresso gestito, attenersi alla seguente procedura:
  1. Implementare una classe gestita con le funzioni membro statiche per l'inizializzazione e terminazione. Aggiungere un file cpp al progetto, che implementa una classe gestita con membri statici per l'inizializzazione e terminazione:
    // 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. Queste funzioni chiamate prima che si fa riferimento alla DLL e dopo aver utilizzato. Chiamare il membro di inizializzazione e terminazione di funzioni nelle principale:
    // Main.cpp
    
    #using <mscorlib.dll>
    using namespace System;
    using namespace System::Reflection;
    #using "ijwdll.dll";
    
    int main() {
    	int retval = ManagedWrapper::minitialize();
        ManagedWrapper::mterminate();
    }
    

Utenti di Visual c ++ .NET 2002

Nota : anche se il messaggio di errore del linker LNK4243 non esiste nella versione del prodotto Visual c ++ .NET 2002, gli utenti di Visual c ++ .NET 2002 si consiglia di seguire le istruzioni menzionate in precedenza quando si sviluppano le DLL miste.

La versione del prodotto di Visual c ++ .NET 2003 contiene un'intestazione aggiuntiva per rendere pi¨ convenienti inizializzazione manuale. Per rendere le soluzioni elencate in questo articolo di lavoro con Visual c ++ .NET 2002, Ŕ necessario aggiungere un file di intestazione al progetto chiamato _vcclrit.h con il seguente testo:
/***
* _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);
}

Riferimenti

Per ulteriori informazioni, fare clic sul numero dell'articolo della Microsoft Knowledge Base riportato di seguito riportato:
309694BUG: AppDomainUnloaded eccezione quando si utilizza gestite le estensioni per i componenti di Visual c ++

ProprietÓ

Identificativo articolo: 814472 - Ultima modifica: venerdý 11 maggio 2007 - Revisione: 7.3
Le informazioni in questo articolo si applicano a:
  • 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
Chiavi:á
kbmt kbcominterop kbmanaged kbdll kbijw kbprb KB814472 KbMtit
Traduzione automatica articoli
Il presente articolo Ŕ stato tradotto tramite il software di traduzione automatica di Microsoft e non da una persona. Microsoft offre sia articoli tradotti da persone fisiche sia articoli tradotti automaticamente da un software, in modo da rendere disponibili tutti gli articoli presenti nella nostra Knowledge Base nella lingua madre dell?utente. Tuttavia, un articolo tradotto in modo automatico non Ŕ sempre perfetto. Potrebbe contenere errori di sintassi, di grammatica o di utilizzo dei vocaboli, pi¨ o meno allo stesso modo di come una persona straniera potrebbe commettere degli errori parlando una lingua che non Ŕ la sua. Microsoft non Ŕ responsabile di alcuna imprecisione, errore o danno cagionato da qualsiasi traduzione non corretta dei contenuti o dell?utilizzo degli stessi fatto dai propri clienti. Microsoft, inoltre, aggiorna frequentemente il software di traduzione automatica.
Clicca qui per visualizzare la versione originale in inglese dell?articolo: 814472
LE INFORMAZIONI CONTENUTE NELLA MICROSOFT KNOWLEDGE BASE SONO FORNITE SENZA GARANZIA DI ALCUN TIPO, IMPLICITA OD ESPLICITA, COMPRESA QUELLA RIGUARDO ALLA COMMERCIALIZZAZIONE E/O COMPATIBILITA' IN IMPIEGHI PARTICOLARI. L'UTENTE SI ASSUME L'INTERA RESPONSABILITA' PER L'UTILIZZO DI QUESTE INFORMAZIONI. IN NESSUN CASO MICROSOFT CORPORATION E I SUOI FORNITORI SI RENDONO RESPONSABILI PER DANNI DIRETTI, INDIRETTI O ACCIDENTALI CHE POSSANO PROVOCARE PERDITA DI DENARO O DI DATI, ANCHE SE MICROSOFT O I SUOI FORNITORI FOSSERO STATI AVVISATI. IL DOCUMENTO PUO' ESSERE COPIATO E DISTRIBUITO ALLE SEGUENTI CONDIZIONI: 1) IL TESTO DEVE ESSERE COPIATO INTEGRALMENTE E TUTTE LE PAGINE DEVONO ESSERE INCLUSE. 2) I PROGRAMMI SE PRESENTI, DEVONO ESSERE COPIATI SENZA MODIFICHE, 3) IL DOCUMENTO DEVE ESSERE DISTRIBUITO INTERAMENTE IN OGNI SUA PARTE. 4) IL DOCUMENTO NON PUO' ESSERE DISTRIBUITO A SCOPO DI LUCRO.

Invia suggerimenti

 

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