Você está offline; aguardando reconexão

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

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
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:
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): 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	PRIVATEDllForceTerm	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 1typedef 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 __cplusplusextern "C" {#endifextern 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++

Aviso: Este artigo foi traduzido automaticamente

Propriedades

ID do Artigo: 814472 - Última Revisão: 05/11/2007 03:25:59 - Revisão: 7.3

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

  • kbmt kbcominterop kbmanaged kbdll kbijw kbprb KB814472 KbMtpt
Comentários