Você recebe avisos de vinculador ao criar Managed Extensions para C++ DLL projetos

Traduções deste artigo Traduções deste artigo
ID do artigo: 814472 - Exibir os produtos aos quais esse artigo se aplica.
Expandir tudo | Recolher tudo

Neste artigo

Sintomas

Você recebe uma das seguintes mensagens de erro em tempo de compilação ou em tempo de link:
Vinculador Tools Error LNK2001
'não resolvido externo símbolo "símbolo" '

Ferramentas de vinculador aviso LNK4210
' Seção .CRT existe; pode haver sem tratamento inicializa estática ou teminators'You receber avisos de vinculador quando você cria Managed Extensions para C++ DLL projetos

Ferramentas de vinculador aviso LNK4243
'DLL que contém objetos compilados com/CLR não está vinculado com /NOENTRY; imagem pode não funcionar corretamente'.
Esses avisos podem ocorrer durante as seguintes circunstâncias:
  • Quando você compilar vinculando os objetos com o / clr alternar.
  • Quando você estiver criando um dos seguintes projetos: modelo de serviço da Web ASP.NET, modelo de biblioteca de classe; ou Windows Control Library Template.
  • Quando você tiver adicionado o código que usa classes nativos (isto é, não __gc ou __value) ou variáveis globais com membros de dados estáticos. Por exemplo, a ActiveX Template Library (ATL), Microsoft Foundation Classes (MFC) e as classes C Run-Time (CRT)
Observação : você pode receber os erros LNK2001 e LNK4210 com projetos que são não afetados pelo problema descrito neste artigo. No entanto, definitivamente o projeto é afetado pelo problema descrito neste artigo se resolver um LNK2001 ou aviso LNK4210 leva a um aviso LNK4243 ou se vincular o projeto gera um aviso LNK4243.

Causa

Os projetos a seguir são criados por padrão como uma biblioteca de vínculo dinâmico (DLL) sem qualquer ligação para nativas bibliotecas (como a CRT, ATL ou MFC) e sem qualquer variáveis globais ou classes nativas com membros de dados estáticos:
  • Modelo de serviço da Web do ASP.NET
  • Modelo de biblioteca de classe
  • Windows Control Library Template
Se você adicionar código que usa variáveis globais ou classes nativas com membros de dados estáticos (por exemplo, os ATL, MFC e CRT as bibliotecas usar variáveis globais), você receberá mensagens de erro vinculador em tempo de compilação. Quando isso ocorre, você deve adicionar código para inicializar variáveis estáticas manualmente. Para obter mais informações sobre como fazer isso, consulte a seção "Resolução" deste artigo.

Para sua conveniência, este artigo se refere a variáveis globais e membros de dados estáticos de classes nativas como "statics" ou "variáveis estáticas" deste ponto em diante.

Esse problema é causado pelo problema de carregamento de DLL misto. DLLs mistas (isto é, DLLs que contêm código gerenciado e nativo) podem encontrar cenários de deadlock em algumas circunstâncias quando elas estiverem carregadas no espaço de endereço de processo, especialmente quando o sistema está sob carga excessiva. As mensagens de erro do vinculador mencionadas anteriormente foram ativadas no vinculador Certifique-se que os clientes estejam cientes do potencial de bloqueio e as soluções alternativas são descritas neste documento. Para obter uma descrição detalhada da DLL misto carregando o problema, consulte o white paper seguinte:
Misto carregamento de DLL problema
http://msdn2.microsoft.com/en-us/library/aa290048(vs.71).aspx

Resolução

Managed Extensions para C++ projetos que são criados como DLLs por padrão não vincular a bibliotecas de C/C ++ nativas, como a biblioteca C Runtime (CRT), ATL e MFC e não usam qualquer variáveis estáticas. Além disso, as configurações de projeto especificam que as DLLs devem ser vinculadas com o / /NOENTRY opção ativada.

Isso é feito porque vinculação com um ponto de entrada faz com que a execução durante DllMain , que não é seguro de código gerenciado (consulte DllMain para o conjunto limitado de coisas que você pode fazer durante seu escopo).

Uma DLL sem um ponto de entrada não tem como inicializar variáveis estáticas, exceto para tipos simples, como números inteiros. Não é geralmente necessário variáveis estáticas um / /NOENTRY DLL.

Todas as bibliotecas ATL e CRT e MFC contam com variáveis estáticas, portanto, você também não pode usar essas bibliotecas de dentro dessas DLLs sem primeira fazer modificações.

Se precisar usar bibliotecas que dependem de estatísticas (por exemplo, ATL, MFC ou CRT) ou statics sua DLL de modo misto, você deve modificar sua DLL para que o statics manualmente são inicializados.

A primeira etapa para inicialização manual é garantir que você desative o código de inicialização automática, que é não seguro com DLLs mistas e pode causar bloqueio. Para desativar o código de inicialização, execute as etapas.

Remover o ponto de entrada da DLL gerenciada

  1. Vincular com / /NOENTRY . Na Solution Explorer , clique com o botão direito no nó do projeto, clique em Propriedades . Em Páginas de propriedade caixa de diálogo, clique em vinculador , clique em linha de comando e adicione essa opção para as Opções adicionais de campo.
  2. Link msvcrt.lib . Na caixa de diálogo Property Pages , clique em vinculador , clique em entrada . e adicione msvcrt.lib a propriedade Dependencies adicionais .
  3. Remova nochkclr.obj . Na página de entrada (a mesma página como na etapa anterior), remove nochkclr.obj a propriedade Dependencies adicionais .
  4. Link no CRT. Na entrada página (a mesma página como na etapa anterior), adicione __DllMainCRTStartup@12 para Forçar referências de símbolo propriedade.

    Se você estiver usando o prompt de comando, especifique as acima configurações de projeto com o seguinte:
    LINK /NOENTRY msvcrt.lib /NODEFAULTLIB:nochkclr.obj /INCLUDE:__DllMainCRTStartup@12

Modificar os componentes que utilizar a DLL para Initializiation Manual

Depois de remover o ponto de entrada explícita, você deve modificar componentes que consomem a DLL para inicialização manual, dependendo da forma que sua DLL é implementada:
  • Sua DLL for inserida usando exportações DLL ( __declspec(dllexport) ) e os consumidores não podem usar código gerenciado se eles forem vinculados estaticamente ou dinamicamente a sua DLL.
  • Sua DLL é uma DLL baseada em COM.
  • Os consumidores da sua DLL podem usar código gerenciado, e sua DLL contém ambos exportações DLL ou gerenciado pontos de entrada.

Modificar DLLs que você digite usando DLL exportações e consumidores que não é possível usar código gerenciado

Para modificar DLLs que você inserir usando exportações de dll (__declspec(dllexport)) e consumidores que não é possível usar código gerenciado, execute estas etapas:
  1. Adicionar duas novas exporta a sua DLL, como mostrado no código a seguir:
    // 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.
    }
    
    Observação Visual C++ 2005, você deve adicionar a opção de compilador de suporte de tempo de execução idioma comum ( / CLR: oldSyntax ) para compilar o exemplo de código anterior com êxito. Para adicionar a opção de compilador de suporte de tempo de execução idioma comum, siga estas etapas:
    1. Clique em projeto e, em seguida, clique em propriedades ProjectName.

      Observação ProjectName é um espaço reservado para o nome do projeto.
    2. Expanda Configuration Properties e, em seguida, clique em Geral .
    3. No painel direito, clique para selecionar Common Language Runtime suporte, sintaxe antiga (/ CLR: oldSyntax) nas configurações de projeto oferece suporte a Common Language Runtime .
    4. Clique em Aplicar e em seguida, clique em OK .
    Para obter mais informações sobre common language runtime oferece suporte às opções de compilador, visite o seguinte site da Microsoft Developer Network (MSDN):
    http://msdn2.microsoft.com/en-us/library/k8d11d4s.aspx
    Estas etapas se aplicam o artigo inteiro.
  2. Sua DLL pode ter vários consumidores. Se ele tiver vários consumidores, adicione o código a seguir ao arquivo .def DLL na seção exportações:
    DllEnsureInit	PRIVATE
    DllForceTerm	PRIVATE
    
    se você não adicionar essas linhas, e se você tiver duas DLLs que exportam funções, o aplicativo que vincula para a DLL terá erros de link. Geralmente, as funções exportadas têm os mesmos nomes. Caso multiconsumer, cada consumidor pode ser vinculada estaticamente ou dinamicamente a sua DLL.
  3. Se o consumidor estiver vinculado estaticamente a DLL, antes de usar a DLL na primeira vez, ou antes de usar qualquer coisa que depende dele em seu aplicativo, adicione a seguinte chamada:
    // 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 o último uso da DLL em seu aplicativo, 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 é vinculado dinamicamente a DLL, insira o código da seguinte maneira:
    • Inserir trecho 1 (consulte a etapa 3) imediatamente após a primeira LoadLibrary para a DLL.
    • Inserir trecho 2 (consulte a etapa 4) imediatamente antes do último FreeLibrary para a DLL.

Para modificar a DLL baseado em COM

  • Modificar as funções de exportação DLL DllCanUnloadNow , DllGetClassObject , DllRegisterServer e DllUnregisterServer conforme demonstrado no código a seguir:
    //  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 que usam o Managed Code e exportações DLL ou gerenciado pontos de entrada

Para modificar a DLL que contém os consumidores que usam código gerenciado e dll exporta ou pontos de entrada gerenciado, execute estas etapas:
  1. Implemente uma classe gerenciada com funções de membro estático para inicialização e encerramento. Adicionar um arquivo .cpp ao seu projeto, implementação de uma classe gerenciada com membros estáticos para inicialização e encerramento:
    // 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. Chame essas funções antes de você se referir a DLL e depois que terminar de usá-lo. Chamar funções de membro inicialização e encerramento no principal:
    // Main.cpp
    
    #using <mscorlib.dll>
    using namespace System;
    using namespace System::Reflection;
    #using "ijwdll.dll";
    
    int main() {
    	int retval = ManagedWrapper::minitialize();
        ManagedWrapper::mterminate();
    }
    

Usuários do Visual C++ .NET 2002

Observação : Embora a mensagem de erro do vinculador LNK4243 não existe na versão de produto Visual C++ .NET 2002, os usuários do Visual C++ .NET 2002 são aconselhados a seguir as diretrizes mencionadas anteriormente, ao desenvolver DLLs mistas.

O lançamento do produto Visual C++ .NET 2003 contém um cabeçalho adicional para tornar a inicialização manual mais conveniente. Para tornar as soluções nesse trabalho artigo com Visual C++ .NET 2002, você deve adicionar um arquivo de cabeçalho para o projeto chamado _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 abaixo para ler o artigo na Base de dados de Conhecimento da Microsoft:
309694Erro: Exceção AppDomainUnloaded quando você usa gerenciado extensões para componentes do Visual C++

Propriedades

ID do artigo: 814472 - Última revisão: sexta-feira, 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 traduzido ou revisto por pessoas. A Microsoft possui artigos traduzidos por aplicações (MT) e artigos traduzidos por tradutores profissionais, com o objetivo de oferecer em português a totalidade dos artigos existentes na base de dados de suporte. No entanto, a tradução automática não é sempre perfeita, podendo conter erros de vocabulário, sintaxe ou gramática. A Microsoft não é responsável por incoerências, erros ou prejuízos ocorridos em decorrência da utilização dos artigos MT por parte dos nossos clientes. A Microsoft realiza atualizações freqüentes 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