Recibir las advertencias del vinculador al generar extensiones administradas para C++ DLL proyectos

Seleccione idioma Seleccione idioma
Id. de artículo: 814472 - Ver los productos a los que se aplica este artículo
Expandir todo | Contraer todo

En esta página

Síntomas

Recibirá uno de los siguientes mensajes de error en tiempo de compilación o en tiempo de vínculo:
Error de herramientas del vinculador LNK2001
'símbolo externo "símbolo sin resolver"'

Advertencia de las herramientas del vinculador LNK4210
' La sección .CRT existe; puede haber inicializa estático no controlada o teminators'You recibe advertencias del vinculador cuando generar extensiones administradas para C++ DLL proyectos

Advertencia LNK4243 herramientas del vinculador
'DLL que contiene objetos compilados con /clr no está vinculado con /NOENTRY; quizá la imagen no se ejecute correctamente'.
Estas advertencias pueden producirse durante las siguientes circunstancias:
  • Cuando se compila la vinculación de objetos con el / clr cambiar.
  • Cuando se está creando uno de los siguientes proyectos: plantilla de servicio de Web de ASP.NET; plantilla Biblioteca de clases; o plantilla de biblioteca de control de Windows.
  • Cuando haya agregado el código que utiliza variables globales o de clases nativas (es decir, no __gc o __value) con miembros de datos estáticos. Por ejemplo, ActiveX Template Library (ATL), Microsoft Foundation Classes (MFC) y las clases de tiempo de ejecución de C (CRT)
Nota : puede recibir los errores LNK2001 y LNK4210 con proyectos que no afectadas por el problema descrito en este artículo. Sin embargo, el proyecto definitivamente está afectado por el problema descrito en este artículo si resolver un error LNK2001 o advertencia LNK4210 conduce a una advertencia LNK4243 o si el proyecto de vinculación genera una advertencia LNK4243.

Causa

Los siguientes proyectos se crean de forma predeterminada como una biblioteca de vínculos dinámicos (DLL) sin ninguna vinculación a bibliotecas nativas (como el CRT, ATL o MFC), sin variables globales ni clases nativas con miembros de datos estáticos:
  • Plantilla servicio Web ASP.NET
  • Plantilla Biblioteca de clases
  • Plantilla de biblioteca de control de Windows
Si agrega código que utiliza variables globales o de clases nativas con miembros de datos estáticos (por ejemplo, las bibliotecas ATL, MFC y CRT utilizar variables globales), recibirá mensajes de error del vinculador en tiempo de compilación. Cuando esto ocurre, debe agregar código para inicializar las variables estáticas manualmente. Para obtener más información acerca de cómo hacerlo, consulte la sección "Solución" de este artículo.

Para mayor comodidad, este artículo hace referencia a variables globales y los miembros de clases nativas como "estáticos" o "variables estáticas" datos estáticos a partir de este punto.

Este problema se debe el problema de carga de DLL mixto. Las DLL mixtas (es decir, los DLL que contienen código nativo y administrado) pueden encontrarse con situaciones de interbloqueo en algunas circunstancias cuando se cargan en el espacio de dirección del proceso, especialmente cuando el sistema está bajo presión. Se habilitaron los mensajes de error del vinculador se ha mencionado anteriormente en el vinculador para asegurarse de que los clientes son conscientes de la posibilidad de interbloqueo y las soluciones que se describen en este documento. Para obtener una descripción detallada de problemas al cargar la DLL de mixta, consulte el artículo siguiente:
Mixto carga de DLL problema
http://msdn2.microsoft.com/en-us/library/aa290048(vs.71).aspx

Solución

Las extensiones administradas para proyectos de C++ se crean como archivos DLL de forma predeterminada no vincula a bibliotecas nativas de C/C ++ such as la biblioteca run-time C (CRT), ATL o MFC y no utilicen las variables estáticas. Además, la configuración de proyecto especifica que los archivos DLL deben vincularse con el / NOENTRY habilitada.

Esto se hizo porque vincular con un punto de entrada hace que ejecutar durante DllMain , que no es seguro el código administrado (consulte DllMain para el conjunto limitado de cosas que puede hacer durante su ámbito).

Un archivo DLL sin un punto de entrada no tiene forma para inicializar las variables estáticas excepto para tipos muy sencillos, como enteros. Normalmente no tienen variables estáticas de certificación en un / NOENTRY DLL.

Las bibliotecas ATL, MFC y CRT que todos se basan en variables estáticas, por lo que no puede utilizar también estas bibliotecas desde dentro de estos archivos DLL sin primera realizar modificaciones.

Si es necesario utilizar variables estáticas o bibliotecas que dependen de datos estáticos (como ATL, MFC o CRT) la DLL de modo mixto, debe modificar el archivo DLL para que las variables estáticas se inicializan manualmente.

El primer paso para inicialización manual es para asegurarse de que deshabilitar el código de inicialización automática, que es seguro con las DLL mixtas y puede causar un interbloqueo. Para deshabilitar el código de inicialización, siga los pasos.

Quitar el punto de entrada de la DLL administrada

  1. Vincular con / NOENTRY . En El Explorador de soluciones , haga clic con el botón secundario del mouse en el nodo del proyecto, haga clic en Propiedades . En las Páginas de propiedades de cuadro de diálogo, haga clic en Linker , haga clic en la línea de comandos y, a continuación, agregar este modificador para las Opciones adicionales de campo.
  2. Vínculo msvcrt.lib . En el cuadro de diálogo Páginas de propiedades , haga clic en Linker , haga clic en entrada . y agregue msvcrt.lib a la propiedad Dependencias adicionales .
  3. Quite nochkclr.obj . En la página de entrada (la misma página como en el paso anterior), quite nochkclr.obj de la propiedad Dependencias adicionales .
  4. Vínculo in the CRT. En la entrada de página (mismo como en el paso anterior), agregue __DllMainCRTStartup@12 Forzar referencias de símbolos de la propiedad.

    Si se utiliza el símbolo del sistema, especifique la anterior configuración del proyecto con la siguiente:
    LINK /NOENTRY msvcrt.lib /NODEFAULTLIB:nochkclr.obj /INCLUDE:__DllMainCRTStartup@12

Modificar los componentes que utilizan la DLL para Initializiation Manual

Después de quitar el punto de entrada explícita, debe modificar los componentes que consumen la DLL de inicialización manual, según la manera que el archivo DLL se implementa en:
  • El archivo DLL es entrada exportaciones de DLL ( __declspec (dllexport) ) y los consumidores no pueden utilizar código administrado si están vinculadas estática o dinámicamente al archivo DLL.
  • El archivo DLL es una DLL basada en COM.
  • Los consumidores del archivo DLL pueden utilizar código administrado y el archivo DLL contiene ambos exportaciones DLL o administrado puntos de entrada.

Modificar archivos DLL que se introduzca utilizando DLL exporta y consumidores que no utilizan código administrado

Para modificar las DLL que introduce con exportaciones de dll (__declspec(dllexport)) y consumidores que no se pueden utilizar código administrado, siga estos pasos:
  1. Agregue dos nuevas exporta al archivo DLL, como se muestra en el siguiente 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 en Visual C++ 2005, debe agregar la opción compatibilidad con compilador de common language runtime ( / clr: oldSyntax ) para compilar correctamente el ejemplo de código anterior. Para agregar la opción de compilador de compatibilidad de common language runtime, siga estos pasos:
    1. Haga clic en proyecto y, a continuación, haga clic en propiedades ProjectName.

      Nota ProjectName es un marcador de posición para el nombre del proyecto.
    2. Expanda Propiedades de configuración y, a continuación, haga clic en General .
    3. En el panel derecho, haga clic para seleccionar compatible con Common Language Runtime, sintaxis antigua (/ CLR: oldSyntax) en Common Language Runtime admite la configuración del proyecto.
    4. Haga clic en Aplicar y, a continuación, haga clic en Aceptar .
    Para obtener más información acerca de common language runtime admiten opciones de compilador, visite el siguiente sitio Web de Microsoft Developer Network (MSDN):
    http://msdn2.microsoft.com/en-us/library/k8d11d4s.aspx
    Estos pasos se aplican el artículo completo.
  2. El archivo DLL puede tener varios consumidores. Si tiene varios consumidores, agregue el código siguiente al archivo .def DLL en la sección de exportaciones:
    DllEnsureInit	PRIVATE
    DllForceTerm	PRIVATE
    
    si no lo agregue estas líneas, y si tiene dos DLL exportan funciones, la aplicación que vincula al archivo DLL tendrá errores de vínculo. Normalmente, las funciones exportadas tienen los mismos nombres. En un caso multiconsumer, cada consumidor puede vincularse estáticamente o dinámicamente al archivo DLL.
  3. Si el consumidor vinculado estáticamente a la DLL, antes de usar el archivo DLL de la primera vez o antes de utilizar cualquier cosa que depende de la aplicación, agregue la siguiente llamada:
    // 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. Después del último uso de la DLL en la aplicación, agregue el código siguiente:
    // 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. Si el consumidor está vinculado dinámicamente a la DLL, inserte código como sigue:
    • Insertar fragmento de código 1 (vea el paso 3) inmediatamente después de la primera LoadLibrary para el archivo DLL.
    • Insertar fragmento 2 (vea el paso 4) inmediatamente antes de la última FreeLibrary para el archivo DLL.

Para modificar DLL basada en COM

  • Modificar las funciones de exportación DLL, DllCanUnloadNow , DllGetClassObject , DllRegisterServer y DllUnregisterServer como se muestra en el siguiente 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 con consumidores que utilizan Managed Code and exportaciones DLL o administradas puntos de entrada

Para modificar el archivo DLL que contiene los consumidores que utilizan código administrado y dll exporta o puntos de entrada administrado, siga estos pasos:
  1. Implemente una clase administrada con funciones miembro estáticas para inicialización y finalización. Agregar un archivo .cpp al proyecto, la implementación de una clase administrada con miembros estáticos para la inicialización y finalización:
    // 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. Llame a estas funciones antes de que hacen referencia a la DLL y cuando termine de utilizarlo. Llamar a funciones el miembro de inicialización y finalización en main:
    // Main.cpp
    
    #using <mscorlib.dll>
    using namespace System;
    using namespace System::Reflection;
    #using "ijwdll.dll";
    
    int main() {
    	int retval = ManagedWrapper::minitialize();
        ManagedWrapper::mterminate();
    }
    

Usuarios de Visual C++ .NET 2002

Nota : aunque el vinculador mensaje de error LNK4243 no existe en la versión del producto Visual C++ .NET 2002, los usuarios de Visual C++ .NET 2002 se aconseja seguir las directrices mencionadas anteriormente al desarrollar las DLL mixtas.

La versión del producto Visual C++ .NET 2003 contiene un encabezado adicional para facilitar más la inicialización manual. Para realizar las soluciones enumeradas en este trabajo de artículo con Visual C++ .NET 2002, debe agregar un archivo de encabezado al proyecto llamado _vcclrit.h con el texto siguiente:
/***
* _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);
}

Referencias

Para obtener más información, haga clic en el número de artículo siguiente para verlo en Microsoft Knowledge Base:
309694Error: Las extensiones para componentes de C++ administrado de AppDomainUnloaded excepción cuando se utiliza

Propiedades

Id. de artículo: 814472 - Última revisión: viernes, 11 de mayo de 2007 - Versión: 7.3
La información de este artículo se refiere a:
  • Microsoft Visual C++ 2005 Express Edition
  • Microsoft Visual C++ .NET 2003 Standard
  • Microsoft Visual C++ .NET 2002 Standard
  • Microsoft .NET Framework 1.1
  • Microsoft .NET Framework 1.0
Palabras clave: 
kbmt kbcominterop kbmanaged kbdll kbijw kbprb KB814472 KbMtes
Traducción automática
IMPORTANTE: Este artículo ha sido traducido por un software de traducción automática de Microsoft (http://support.microsoft.com/gp/mtdetails) en lugar de un traductor humano. Microsoft le ofrece artículos traducidos por un traductor humano y artículos traducidos automáticamente para que tenga acceso en su propio idioma a todos los artículos de nuestra base de conocimientos (Knowledge Base). Sin embargo, los artículos traducidos automáticamente pueden contener errores en el vocabulario, la sintaxis o la gramática, como los que un extranjero podría cometer al hablar el idioma. Microsoft no se hace responsable de cualquier imprecisión, error o daño ocasionado por una mala traducción del contenido o como consecuencia de su utilización por nuestros clientes. Microsoft suele actualizar el software de traducción frecuentemente.
Haga clic aquí para ver el artículo original (en inglés): 814472

Enviar comentarios

 

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