Receber avisos de linker quando criar extensões geridas para C++ DLL projectos

Traduções de Artigos Traduções de Artigos
Artigo: 814472 - Ver produtos para os quais este artigo se aplica.
Expandir tudo | Reduzir tudo

Nesta página

Sintomas

Recebe uma das seguintes mensagens de erro durante a compilação ou ligação altura:
Linker ferramentas erro LNK2001
'Símbolo externo não resolvido "símbolo" '

Ferramentas Linker aviso LNK4210
' Secção .CRT existe; podem existir não processada inicializa estática ou teminators'You receber Ligador de avisos quando criar extensões geridas para C++ DLL projectos

Ferramentas Linker aviso LNK4243
'DLL que contém objectos compilados com /clr não é associada /NOENTRY; imagem pode não funcionar correctamente'.
Estes avisos podem ocorrer durante as seguintes circunstâncias:
  • Quando compilar ligar objectos com o / clr mudar.
  • Quando estiver a criar um dos seguintes projectos: modelo de serviço Web ASP.NET; modelos de biblioteca de classe; ou modelo de biblioteca de controlo do Windows.
  • Quando tiver adicionado código que utiliza classes nativas (ou seja, não __gc ou __value) ou variáveis globais com membros de dados estáticos. Por exemplo, a biblioteca de modelo de ActiveX (ATL), Microsoft Foundation Classes (MFC) e as classes de tempo de execução C (CRT)
Nota : poderá receber os erros LNK2001 e LNK4210 com projectos são não afectados pelo problema descrito neste artigo. No entanto, o projecto definitivamente é afectado pelo problema descrito neste artigo se resolver um LNK2001 ou aviso LNK4210 conduz a um aviso LNK4243 ou se ligar o projecto gera um aviso LNK4243.

Causa

Os seguintes projectos são criados por predefinição, uma biblioteca de ligação dinâmica (DLL) sem qualquer ligação nativo bibliotecas (como CRT, ATL ou MFC) e sem qualquer nativas classes com membros de dados estáticos ou variáveis globais:
  • Modelo de serviço Web do ASP.NET
  • Modelo de biblioteca de classe
  • Modelo de biblioteca de controlo do Windows
Se adicionar o código que utilize variáveis globais ou classes nativas com membros de dados estáticos (por exemplo, as bibliotecas ATL MFC e CRT utilizar variáveis globais), receberá mensagens de erro linker durante a compilação. Quando isto ocorrer, tem de adicionar código manualmente inicializar as variáveis estáticas. Para obter mais informações sobre como efectuar este procedimento, consulte a secção "Resolução" deste artigo.

Para sua comodidade, neste artigo referido para membros de dados estáticos de classes nativos como "statics" ou "variáveis estáticas" e variáveis globais a partir deste ponto frente.

Este problema é causado pelo problema de carregamento de DLL misto. DLL mistas (ou seja, dll que contêm código gerido e nativo) podem encontrar cenários de bloqueio em algumas circunstâncias, quando são carregados no espaço de endereço processo, especialmente quando o sistema em importância. As mensagens de erro linker mencionadas anteriormente foram activadas no linker para garantir que os clientes estão em consideração o potencial de impasse e medidas descritas neste documento. Para obter uma descrição detalhada da DLL misto carregar problema, consulte a documentação técnica seguinte:
Misto carregamento da DLL problema
http://msdn2.microsoft.com/en-us/library/aa290048(vs.71).aspx

Resolução

Gerido extensões para projectos de C++ que são criados como DLLs por predefinição não ligar a bibliotecas de C/C ++ nativas como a biblioteca C run-time (CRT), ATL ou MFC e não utilizam as variáveis estáticas. Além disso, as definições de projecto especificam que as dll devem ser associadas a / NOENTRY opção activada.

Isto é efectuado uma vez que ligar com um ponto de entrada faz com que código gerido ser executado durante DllMain , que não é seguro (consulte DllMain para o conjunto limitado de coisas que pode fazer durante o respectivo âmbito).

Uma DLL sem um ponto de entrada não tem maneira inicializar variáveis estáticas, excepto para tipos de muito simples como números inteiros. Não tem normalmente variáveis estáticas num / NOENTRY DLL.

As bibliotecas MFC, ATL e o CRT todos os dependem variáveis estáticas, por isso também não pode utilizar estas bibliotecas a partir de dentro destas DLL sem primeira efectuar modificações.

Se a DLL de modo misto tem de utilizar statics ou bibliotecas que dependem statics (tal como ATL, MFC ou CRT), tem de modificar a DLL para que o statics manualmente são inicializados.

O primeiro passo para inicialização manual é para se certificar que desactive o código de inicialização automática, que é não seguro com DLLs mistas e pode causar um impasse. Para desactivar o código de inicialização, siga os passos.

Remover o ponto de entrada da DLL gerido

  1. Ligar a / NOENTRY . No Solution Explorer , clique com o botão direito do rato no nó de projecto, clique em Propriedades . Nas Páginas de propriedades da caixa de diálogo, clique em Ligador de , clique em linha de comandos e, em seguida, adicionar este parâmetro para as Opções adicionais do campo.
  2. Ligação msvcrt.lib . Na caixa de diálogo Páginas de propriedades , clique em Ligador de , clique em Introdução . e adicione msvcrt.lib a propriedade Dependências adicionais .
  3. Remova nochkclr.obj . Na página de Introdução de dados (mesma página como no passo anterior), remova nochkclr.obj da propriedade Dependências adicionais .
  4. Hiperligação de CRT. Em entrada page (página mesma como no passo anterior), adicione __DllMainCRTStartup@12 Force símbolo referências propriedade.

    Se estiver a utilizar a linha de comandos, especificar as definições de projecto acima com o seguinte:
    LINK /NOENTRY msvcrt.lib /NODEFAULTLIB:nochkclr.obj /INCLUDE:__DllMainCRTStartup@12

Modificar os componentes beneficiar a DLL para Initializiation Manual

Depois de remover o ponto de entrada explícita, tem de modificar componentes que consomem a DLL para inicialização manual, dependendo da forma que a DLL é implementado:
  • A DLL é introduzida com DLL exporta ( __declspec(dllexport) ) e os consumidores não podem utilizar código gerido se estas estiverem ligadas dinamicamente ou estaticamente para a DLL.
  • A DLL é uma DLL baseada em COM.
  • Os consumidores da sua DLL podem utilizar código gerido e DLL contém ambos exporta DLL ou geridas pontos de entrada.

Modificar as DLL que é introduza utilizando DLL exporta e consumidores que não é possível utilizar códigos geridos

Para modificar dll que introduziu utilizando dll exporta (__declspec(dllexport)) e consumidores que não é possível utilizar código gerido, siga estes passos:
  1. Adicionar dois novos exporta a DLL, conforme é ilustrado no seguinte código:
    // 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 no Visual C++ 2005, terá de adicionar a opção de compilador do suporte de tempo de execução idioma comum ( / clr:oldSyntax ) para compilar com êxito o anterior exemplo de código. Para adicionar a opção de compilador do suporte de tempo de execução idioma comum, siga estes passos:
    1. Clique em projecto e, em seguida, clique em propriedades ProjectName.

      Nota ProjectName é um marcador para o nome do projecto.
    2. Expanda Propriedades de configuração e, em seguida, clique em Geral .
    3. No painel direito, clique para seleccionar Runtime do idioma comum suporte, sintaxe antigo (/ clr:oldSyntax) no Common Language Runtime suporta definições do projecto.
    4. Clique em Aplicar e, em seguida, clique em OK .
    Para mais informações sobre o common language runtime suporta opções de compilador, visite o seguinte Web site da Microsoft Developer Network (MSDN):
    http://msdn2.microsoft.com/en-us/library/k8d11d4s.aspx
    Estes passos aplicam-se para o artigo completo.
  2. A DLL pode ter vários consumidores. Se tiver consumidores vários, adicionar o seguinte código ao ficheiro .def DLL na secção exporta:
    DllEnsureInit	PRIVATE
    DllForceTerm	PRIVATE
    
    se não adicionar estas linhas e se tiver dois dll que exportar funções, a aplicação que liga para a DLL terá erros de ligação. Normalmente, as funções exportadas têm os mesmos nomes. Num caso multiconsumer, cada consumidor pode ser ligado dinamicamente ou estaticamente para a DLL.
  3. Se o consumidor estaticamente estiver ligado a DLL, antes de utilizar a DLL na primeira vez ou antes de utilizar qualquer um que depende da aplicação, adicionar a chamada seguinte:
    // 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. Após a última utilização da DLL da aplicação, adicione o seguinte código:
    // 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 o consumidor dinamicamente estiver ligado a DLL, insira o código da seguinte forma:
    • Inserir fragmento 1 (consulte o passo 3) imediatamente após o primeiro LoadLibrary para a DLL.
    • Inserir fragmento 2 (consulte o passo 4) imediatamente antes do último FreeLibrary para a DLL.

Para modificar DLL baseadas em com.

  • Modificar as funções de exportação DLL DllCanUnloadNow , DllGetClassObject , DllRegisterServer e DllUnregisterServer como demonstrado no seguinte código:
    //  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;
    }
    

Modificar DLL com consumidores com geridos código e DLL exporta ou geridos pontos de entrada

Para modificar DLL que contém os consumidores que utilizam o código gerido e dll exporta ou pontos de entrada gerido, siga estes passos:
  1. Implemente uma classe gerida com funções de membro estático para inicialização e a terminação. Adicionar um ficheiro .cpp ao projecto, implementar uma classe gerida com membros estáticos para inicialização e a terminação:
    // 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. Chamar estas funções antes referem-se para a DLL e quando tiver terminado de utilizar. Chamar funções de membro inicialização e a terminação de principal:
    // Main.cpp
    
    #using <mscorlib.dll>
    using namespace System;
    using namespace System::Reflection;
    #using "ijwdll.dll";
    
    int main() {
    	int retval = ManagedWrapper::minitialize();
        ManagedWrapper::mterminate();
    }
    

Utilizadores do Visual C++ .NET 2002

Nota : Apesar da mensagem de erro linker LNK4243 não existe na versão de produto Visual C++ .NET 2002, os utilizadores do Visual C++ .NET 2002 são aconselhados a seguir as directrizes mencionadas anteriormente quando desenvolver dll mistas.

A versão do produto Visual C++ .NET 2003 contém um cabeçalho adicional para efectuar a inicialização manual mais conveniente. Para tornar as soluções que constam deste artigo trabalho com o Visual C++ .NET 2002, tem de adicionar um ficheiro de cabeçalho ao projecto denominada _vcclrit.h com o seguinte texto:
/***
* _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);
}

Referências

Para obter mais informações, clique no número de artigo que se segue para visualizar o artigo na Microsoft Knowledge Base:
309694Erro: Excepção AppDomainUnloaded quando utiliza geridos extensões para componentes do Visual C++

Propriedades

Artigo: 814472 - Última revisão: 11 de maio de 2007 - Revisão: 7.3
A informação contida neste artigo aplica-se 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
Palavras-chave: 
kbmt kbcominterop kbmanaged kbdll kbijw kbprb KB814472 KbMtpt
Tradução automática
IMPORTANTE: Este artigo foi traduzido por um sistema de tradução automática (também designado por Machine translation ou MT), não tendo sido portanto revisto ou traduzido por humanos. A Microsoft tem artigos traduzidos por aplicações (MT) e artigos traduzidos por tradutores profissionais. O objectivo é simples: oferecer em Português a totalidade dos artigos existentes na base de dados do suporte. Sabemos no entanto que a tradução automática não é sempre perfeita. Esta pode conter erros de vocabulário, sintaxe ou gramática? erros semelhantes aos que um estrangeiro realiza ao falar em Português. A Microsoft não é responsável por incoerências, erros ou estragos realizados na sequência da utilização dos artigos MT por parte dos nossos clientes. A Microsoft realiza actualizações frequentes ao software de tradução automática (MT). Obrigado.
Clique aqui para ver a versão em Inglês deste artigo: 814472

Submeter comentários

 

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