Managed Extensions for C++ DLL 프로젝트를 빌드할 때 링커 경고가 발생한다

기술 자료 번역 기술 자료 번역
기술 자료: 814472 - 이 문서가 적용되는 제품 보기.
모두 확대 | 모두 축소

이 페이지에서

현상

컴파일 또는 링크 시 다음과 유사한 내용의 오류 메시지가 나타납니다.
링커 도구 오류 LNK2001
'"sysmbol" 외부 기호를 확인할 수 없습니다.'

링커 도구 경고 LNK4210
'.CRT 섹션이 있습니다. 처리되지 않은 정적 이니셜라이저 또는 종결자가 있을 수 있습니다. 'Managed Extensions for C++ DLL 프로젝트를 빌드할 때 링커 경고가 발생합니다.

링커 도구 경고 LNK4243
'/clr를 사용하여 컴파일된 개체를 포함한 DLL이 /NOENTRY와 링크되어 있지 않습니다. 이미지가 올바르게 실행되지 않을 수도 있습니다.'
이러한 경고는 다음과 같은 상황에서 발생합니다.
  • /clr 스위치로 연결 개체를 컴파일하는 경우
  • 다음 프로젝트 중 하나를 빌드하는 경우: ASP.NET 웹 서비스 템플릿, 클래스 라이브러리 템플릿 또는 Windows 컨트롤 라이브러리 템플릿
  • 정적 데이터 멤버를 포함한 기본 클래스 또는 전역 변수(__gc 또는 __value가 아님)를 사용하는 코드를 추가한 경우 (예: ATL(ActiveX Template Library), MFC(Microsoft Foundation Classes) 및 CRT(C 런타임) 클래스)
참고: 이 문서에 설명된 문제에 영향을 받지 않는 프로젝트에서 LNK2001 및 LNK4210 오류가 나타납니다. 그러나 이 프로젝트는 LNK2001 또는 LNK4210 경고를 해결하여 LNK4243 경고가 발생하거나 프로젝트를 연결하여 LNK4243 경고가 발생하는 경우에 이 문서에서 설명하는 문제에 의해 확실히 영향을 받습니다.

원인

다음 프로젝트는 기본적으로 기본 라이브러리(예: CRT, ATL 또는 MFC)에 연결하지 않고 정적 데이터 멤버를 포함한 기본 클래스 또는 전역 변수를 사용하지 않는 DLL(동적 연결 라이브러리)로 만들어집니다.
  • ASP.NET 웹 서비스 템플릿
  • 클래스 라이브러리 템플릿
  • Windows 컨트롤 라이브러리 템플릿
정적 데이터 멤버를 포함한 기본 클래스 또는 전역 변수를 사용하는 코드를 추가하면(예를 들어, ATL, MFC 및 CRT 라이브러리는 전역 변수 사용) 컴파일 시 링커 오류 메시지가 나타납니다. 이런 경우에는 정적 변수를 수동으로 초기화하는 코드를 추가해야 합니다. 이렇게 하는 방법에 대한 자세한 내용은 이 문서의 "해결 방법" 절을 참조하십시오.

편의를 위해 앞으로 계속 이 문서에서는 전역 변수 및 기본 클래스의 정적 데이터 멤버를 "정적" 또는 "정적 변수"로 참조합니다.

혼합된 DLL 로딩 문제로 인해 이 문제가 발생합니다. 혼합된 DLL(즉, 관리되는 코드 및 기본 코드 모두를 포함한 DLL)은 프로세스 주소 공간으로 로드되는 경우, 특히 시스템의 작업량이 많은 경우에 교착 상태가 발생할 수 있습니다. 고객이 잠재적 교착 상태 및 이 문서에 설명된 해결 방법을 알고 있는지 확인하도록 해당 링커에서 앞서 설명한 링커 오류 메시지를 설정했습니다. 혼합된 DLL 로딩 문제에 대한 자세한 내용은 다음 백서를 참조하십시오.
혼합된 DLL 로딩 문제(Mixed DLL Loading Problem)
http://msdn2.microsoft.com/en-us/library/aa290048(vs.71).aspx

해결 방법

기본적으로 DLL로 만든 Managed Extensions for C++ 프로젝트는 CRT(C 런타임) 라이브러리, ATL 또는 MFC 같은 기본 C/C++ 라이브러리에 연결되지 않으며 정적 변수를 사용하지 않습니다. 또한 프로젝트 설정에서 DLL이 /NOENTRY 옵션을 설정한 상태로 연결되도록 해야 합니다.

진입점과의 링크로 인해 DllMain 동안 안전하지 않은 관리되는 코드가 실행될 수 있으므로 이렇게 해야 합니다. 해당 범위 내에서 수행할 수 있는 제한된 작업 세트를 보려면 DllMain을 참조하십시오.

진입점이 없는 DLL에는 정수 같은 매우 간단한 형식을 제외한 정적 변수를 초기화할 수 있는 방법이 없습니다. 대개 /NOENTRY DLL에는 정적 변수가 없습니다.

ATL, MFC 및 CRT 라이브러리는 모두 정적 변수에 의존하므로 먼저 수정하지 않고는 이러한 DLL의 라이브러리를 사용할 수 없습니다.

혼합 모드 DLL에서 정적 변수(예: ATL, MFC 또는 CRT)에 종속된 정적 변수 또는 라이브러리를 사용해야 하는 경우 DLL을 수정해야만 정적 변수를 수동으로 초기화할 수 있습니다.

혼합된 DLL에 안전하지 않으며 교착 상태를 야기시킬 수 있는 자동 초기화 코드를 해제하는 것이 수동으로 초기화하는 첫 번째 단계입니다. 초기화 코드를 해제하려면 다음 단계를 수행합니다.

관리되는 DLL의 진입점 제거

  1. /NOENTRY로 연결합니다. 솔루션 탐색기에서 프로젝트 노드를 마우스 오른쪽 단추로 누르고 속성을 누릅니다. 속성 페이지 대화 상자에서 링커, 명령줄을 차례로 누른 다음 추가 옵션 필드에 이 스위치를 추가합니다.
  2. msvcrt.lib를 연결합니다. 속성 페이지 대화 상자에서 링커, 입력을 차례로 누른 다음 msvcrt.lib추가 종속성 속성에 추가합니다.
  3. nochkclr.obj를 제거합니다. 입력 페이지(이전 단계에서와 같은 페이지)의 추가 종속성 속성에서 nochkclr.obj를 제거합니다.
  4. CRT에 연결합니다. 입력 페이지(이전 단계에서와 같은 페이지)에서 __DllMainCRTStartup@12강제 기호 참조 속성에 추가합니다.

    명령 프롬프트를 사용 중인 경우 다음과 같이 위의 프로젝트 설정을 지정합니다.
    LINK /NOENTRY msvcrt.lib /NODEFAULTLIB:nochkclr.obj /INCLUDE:__DllMainCRTStartup@12

수동 초기화를 위해 DLL을 사용하는 구성 요소 수정

명시적 진입점을 제거한 후 DLL이 구현되는 방법에 따라 수동 초기화를 위해 DLL을 사용하는 구성 요소를 수정해야 합니다.
  • 해당 DLL은 DLL 내보내기(__declspec(dllexport))를 사용하여 입력되고 해당 소비자는 정적 또는 동적으로 DLL에 연결되는 경우 관리되는 코드를 사용할 수 없습니다.
  • DLL은 COM 기반 DLL입니다.
  • DLL의 소비자는 관리되는 코드를 사용할 수 있고 이 DLL에는 DLL 내보내기 또는 관리되는 진입점이 포함되어 있습니다.

DLL 내보내기를 사용하여 입력하는 DLL 및 관리되는 코드를 사용할 수 없는 소비자 수정

dll 내보내기(__declspec(dllexport))를 사용하여 입력하는 DLL 및 관리되는 코드를 사용할 수 없는 소비자를 수정하려면 다음을 수행합니다.
  1. 다음 코드에서처럼 DLL에 두 개의 새 내보내기를 추가합니다.
    // 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.
    }
    
    참고 Visual C++ 2005에서는 공용 언어 런타임 지원 컴파일러 옵션(/clr:oldSyntax)을 추가해야 이전 코드 예제를 성공적으로 컴파일할 수 있습니다. 공용 언어 런타임 지원 컴파일러 옵션을 추가하려면 다음과 같이 하십시오.
    1. 프로젝트를 누르고 ProjectName 속성을 누릅니다.

      참고 ProjectName은 프로젝트 이름의 자리 표시자입니다.
    2. 구성 속성을 확장한 다음 일반을 누릅니다.
    3. 오른쪽 창의 공용 언어 런타임 지원 프로젝트 설정에서 공용 언어 런타임 지원, 이전 구문(/clr:oldSyntax)을 선택합니다.
    4. 적용을 누른 다음 확인을 누릅니다.
    공용 언어 런타임 지원 컴파일러 옵션에 대한 자세한 내용은 다음 MSDN (Microsoft Developer Network) 웹 사이트를 참조하십시오.
    http://msdn.microsoft.com/library/kor/default.asp?url=/library/KOR/vccore/html/vcrefeecomcompilation.asp
    위의 단계는 문서 전체에 적용됩니다.
  2. DLL에는 여러 소비자가 있을 수 있습니다. DLL에 여러 소비자가 없는 경우 내보내기 섹션에서 DLL .def 파일에 다음 코드를 추가합니다.
    DllEnsureInit	PRIVATE
    DllForceTerm	PRIVATE
    
    이 줄을 추가하지 않은 경우와 함수를 내보내는 DLL이 두 개 있는 경우에는 DLL로 연결하는 응용 프로그램에 링크 오류가 있습니다. 대개 내보낸 함수는 같은 이름을 갖습니다. 여러 소비자의 경우 각 소비자는 정적 또는 동적으로 DLL에 연결될 수 있습니다.
  3. 소비자가 정적으로 DLL에 연결되면 처음 DLL을 사용하기 전 또는 해당 응용 프로그램의 DLL에 종속된 것을 사용하기 전에 다음 호출을 추가합니다.
    // 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. 해당 응용 프로그램에서 DLL을 마지막으로 사용한 후 다음 코드를 추가합니다.
    // 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. 소비자가 동적으로 DLL에 연결되면 다음과 같이 코드를 삽입합니다.
    • DLL의 첫 번째 LoadLibrary 다음에 바로 snippet 1(3단계 참조)을 삽입합니다.
    • DLL의 마지막 FreeLibrary 전에 바로 snippet 2(4단계 참조)를 삽입합니다.

COM 기반 DLL 수정 방법

  • DLL 내보내기 함수 DllCanUnloadNow, DllGetClassObject, DllRegisterServerDllUnregisterServer를 다음 코드와 같이 수정합니다.
    //  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;
    }
    

관리되는 코드 및 DLL 내보내기 또는 관리되는 진입점을 사용하는 소비자를 포함한 DLL 수정

관리되는 코드 및 dll 내보내기 또는 관리되는 진입점을 사용하는 소비자를 포함한 DLL을 수정하려면 다음을 수행합니다.
  1. 초기화 및 종료를 위해 정적 멤버 함수를 포함하는 관리되는 클래스를 구현합니다. .cpp 파일을 해당 프로젝트에 추가하고, 초기화 및 종료를 위해 정적 멤버를 포함하는 관리되는 클래스를 구현합니다.
    // 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. DLL을 참조하기 전과 이 DLL을 사용한 후에 이러한 함수를 호출합니다. main에서 초기화 및 종료 구성원 함수를 호출합니다.
    // Main.cpp
    
    #using <mscorlib.dll>
    using namespace System;
    using namespace System::Reflection;
    #using "ijwdll.dll";
    
    int main() {
    	int retval = ManagedWrapper::minitialize();
        ManagedWrapper::mterminate();
    }
    

Visual C++ .NET 2002 사용자

참고: 링커 오류 메시지 LNK4243이 Visual C++ .NET 2002 제품 릴리스에서는 나타나지 않아도 혼합된 DLL을 개발할 때 Visual C++ .NET 2002 사용자는 앞에서 언급한 지침을 따르는 것이 좋습니다.

Visual C++ .NET 2003 제품 릴리스에는 좀더 편리하게 수동으로 초기화할 수 있도록 추가 머리글이 포함되어 있습니다. 이 문서에 나열된 솔루션을 Visual C++ .NET 2002에서 수행하려면 다음 텍스트가 포함된 머리글 파일을 _vcclrit.h라는 프로젝트에 추가해야 합니다.
/***
* _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);
}

참조

자세한 내용은 Microsoft 기술 자료의 다음 문서를 참조하십시오.
309694 BUG: Managed Extensions for C++ 구성 요소를 사용하는 경우 AppDomainUnloaded 예외가 발생한다




Microsoft 제품 관련 기술 전문가들과 온라인으로 정보를 교환하시려면 Microsoft 뉴스 그룹에 참여하시기 바랍니다.

속성

기술 자료: 814472 - 마지막 검토: 2007년 5월 11일 금요일 - 수정: 7.2
본 문서의 정보는 다음의 제품에 적용됩니다.
  • 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
키워드:?
kbdll kbprb kbijw kbcominterop kbmanaged KB814472

피드백 보내기

 

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