คุณได้รับข้อความแสดงคำเตือน linker เมื่อคุณสร้างส่วนขยายที่มีจัดการสำหรับ c ++ DLL โครงการ

การแปลบทความ การแปลบทความ
หมายเลขบทความ (Article ID): 814472 - ผลิตภัณฑ์ที่เกี่ยวข้องในบทความนี้
ขยายทั้งหมด | ยุบทั้งหมด

เนื้อหาบนหน้านี้

อาการ

คุณได้รับข้อความแสดงข้อผิดพลาดต่อไปนี้อย่างใดอย่างหนึ่ง ณเวลาการคอมไพล์ หรือเวลาในการเชื่อมโยง:
linker LNK2001 ข้อผิดพลาดของเครื่องมือ
'ยังไม่ได้แก้ไขภายนอกสัญลักษณ์ "สัญลักษณ์" '

เครื่องมือ linker ที่คำเตือน LNK4210
' ส่วน.CRT อยู่ อาจ initializes คง unhandled หรือ teminators'You ได้รับคำเตือนของตัวเชื่อมโยงข้อมูลเมื่อคุณสร้างส่วนขยายที่มีจัดการสำหรับ c ++ DLL โครงการ

เครื่องมือ linker ที่คำเตือน LNK4243
'DLL ที่ประกอบด้วยออบเจ็กต์ที่คอมไพล์ ด้วย /clr ไม่ได้ถูกเชื่อมโยงกับ /NOENTRY รูปอาจไม่ทำงานอย่างถูกต้อง'
คำเตือนเหล่านี้อาจเกิดขึ้นในระหว่างการในสถานการณ์ต่อไปนี้:
  • เมื่อคุณคอมไพล์วัตถุที่มีการเชื่อมโยงนี้/clrสลับไป
  • เมื่อคุณกำลังสร้างโครงการที่มีต่อไปนี้อย่างใดอย่างหนึ่ง: แม่ แบบของการบริการเว็บ ASP.NET แม่ แบบของไลบรารีคลาส หรือแม่ แบบไลบรารีตัวควบคุมของ Windows
  • เมื่อคุณได้เพิ่มรหัส ที่ใช้ตัวแปรส่วนกลางหรือคลาสที่ท้องถิ่น (นั่นคือ ไม่ __gc หรือ __value) กับข้อมูลแบบคงที่สมาชิก ตัวอย่างเช่น ActiveX แม่แบบของไลบรารี (ATL), ระดับชั้นมูลฐาน Microsoft (MFC), และคลาส C เวลาเรียกใช้ (CRT)
หมายเหตุ:: คุณอาจได้รับข้อความแสดงข้อผิดพลาด LNK2001 และ LNK4210 กับโครงการที่มีไม่ได้รับผลกระทบจากปัญหาอธิบายไว้ในบทความนี้ อย่างไรก็ตาม โครงการแน่นอนคือได้รับผลกระทบจากปัญหาอธิบายไว้ในบทความนี้ ถ้าการแก้ไขคำเตือน LNK2001 หรือ LNK4210 แปลงไปยังคำเตือน LNK4243 หรือ หากการเชื่อมโยงโครงการสร้างคำเตือน LNK4243

สาเหตุ

โครงการที่มีต่อไปนี้ถูกสร้าง โดยค่าเริ่มต้นเป็นไลบรารีการเชื่อมโยงแบบไดนามิก (DLL) โดย linkage ใด ๆ ไปยังไลบรารีท้องถิ่น (เช่น CRT, ATL หรือ MFC), และไม่ มีคลาสที่ท้องถิ่นกับสมาชิกข้อมูลแบบคงที่หรือตัวแปรส่วนกลางใด ๆ:
  • แม่แบบการบริการของเว็บ asp.net
  • แม่แบบของไลบรารีคลาส
  • แม่แบบไลบรารีตัวควบคุมของ windows
ถ้าคุณเพิ่มรหัสที่ใช้ตัวแปรส่วนกลางหรือคลาสที่ท้องถิ่นกับสมาชิกข้อมูลสแตติก (ตัวอย่างเช่น ไลบรารี ATL, MFC และ CRT ใช้ตัวแปรส่วนกลาง), คุณจะได้รับข้อความแสดงข้อผิดพลาด linker เวลาในการคอมไพล์ เมื่อปัญหานี้เกิดขึ้น คุณต้องเพิ่มรหัสตัวแปรแบบคงที่เตรียมใช้งานด้วยตนเอง สำหรับข้อมูลเพิ่มเติมเกี่ยวกับวิธีการทำเช่นนี้ ให้ดูที่ส่วน "การแก้ปัญหา" ของบทความนี้

เพื่อความสะดวก บทความนี้อ้างถึงตัวแปรส่วนกลางและข้อมูลแบบคงที่สมาชิกของคลาสที่ดั้งเดิมเป็น "statics" หรือ "ตัวแปรแบบคงที่" จากจุดนี้ไปข้างหน้า

ปัญหานี้มีสาเหตุจากปัญหาในการโหลด DLL แบบผสม DLLs ผสม (นั่นคือ DLLs ที่ประกอบด้วยรหัสทั้งมีการจัดการ และดั้งเดิม) สามารถพบ deadlock สถานการณ์บางสถานการณ์เมื่อพวกเขาจะโหลดลงในพื้นที่ที่อยู่การประมวลผล โดยเฉพาะอย่างยิ่งเมื่อระบบอยู่ภายใต้ปัญหา ข้อผิดพลาด linker ที่กล่าวถึงก่อนหน้านี้ได้ถูกเปิดใช้งานใน linker การตรวจสอบให้แน่ใจว่า ลูกค้าตระหนักถึงโอกาส deadlock และการแก้ไขปัญหาที่อธิบายไว้ในเอกสารนี้ คำอธิบายโดยละเอียดของปัญหาที่กำลังโหลด DLL ผสม ดู whitepaper ที่ต่อไปนี้:
ผสมในการโหลด DLL ปัญหา
.aspx http://msdn2.microsoft.com/en-us/library/aa290048 (vs.71)

การแก้ไข

Managed Extensions for C++ projects that are created as DLLs by default do not link to native C/C++ libraries such as the C run-time (CRT) library, ATL, or MFC and do not use any static variables. Additionally, the project settings specify that the DLLs should be linked with the/NOENTRYตัวเลือกที่เปิดใช้งาน

This is done because linking with an entry point causes managed code to run duringDllMain, which is not safe (seeDllMainfor the limited set of things you can do during its scope).

A DLL without an entry point has no way to initialize static variables except for very simple types such as integers. You do not typically have static variables in a/NOENTRYDLL.

The ATL, MFC and CRT libraries all rely on static variables, so you also cannot use these libraries from within these DLLs without first making modifications.

If your mixed-mode DLL needs to use statics or libraries that depend on statics (such as ATL, MFC, or CRT), then you must modify your DLL so that the statics are manually initialized.

The first step to manual initialization is to make sure that you disable the automatic initialization code, which is unsafe with mixed DLLs and can cause deadlock. To disable the initialization code, follow the steps.

Remove the Entry Point of the Managed DLL

  1. Link with/NOENTRY. ในExplorer โซลูชัน, right-click the project node, clickคุณสมบัติ. ในการหน้าคุณสมบัติกล่องโต้ตอบ คลิกLinkerคลิกCommand Line, and then add this switch to theตัวเลือกเพิ่มเติมเขตข้อมูล:
  2. Linkmsvcrt.lib. ในการหน้าคุณสมบัติกล่องโต้ตอบ คลิกLinkerคลิกป้อนข้อมูล., and then addmsvcrt.libไปAdditional Dependenciesคุณสมบัติ
  3. เอาออกnochkclr.obj. ในการป้อนข้อมูลpage (same page as in the previous step), removenochkclr.objจากนั้นAdditional Dependenciesคุณสมบัติ
  4. Link in the CRT. ในการป้อนข้อมูลpage (same page as in the previous step), add__DllMainCRTStartup@12ไปForce Symbol Referencesคุณสมบัติ

    If you are using the command prompt, specify the above project settings with the following:
    LINK /NOENTRY msvcrt.lib /NODEFAULTLIB:nochkclr.obj /INCLUDE:__DllMainCRTStartup@12

Modify the Components that Consume the DLL for Manual Initializiation

After you remove the explicit entry point, you must modify components that consume the DLL for manual initialization, depending on the way that your DLL is implemented:
  • Your DLL is entered using DLL exports (__declspec(dllexport)), and your consumers cannot use managed code if they are linked statically or dynamically to your DLL.
  • Your DLL is a COM-based DLL.
  • Consumers of your DLL can use managed code, and your DLL contains either DLL exports or managed entry points.

Modify DLLs That You Enter By Using DLL Exports and Consumers That Cannot Use Managed Code

To modify DLLs that you enter by using dll exports (__declspec(dllexport)) and consumers that cannot use managed code, follow these steps:
  1. Add two new exports to your DLL, as shown in the following code:
    // 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.
    }
    
    หมายเหตุ:In Visual C++ 2005, you must add the common language runtime support compiler option (/clr:oldSyntax) to successfully compile the previous code sample. To add the common language runtime support compiler option, follow these steps:
    1. คลิกProjectแล้ว คลิกProjectNameคุณสมบัติ.

      หมายเหตุ:ProjectNameเป็นตัวยึดสำหรับชื่อของโครงการ
    2. ขยายคุณสมบัติการตั้งค่าคอนฟิกแล้ว คลิกทั่วไป.
    3. In the right pane, click to selectCommon Language Runtime Support, Old Syntax (/clr:oldSyntax)ในการการสนับสนุนการรันไทม์ภาษาที่พบโดยทั่วไปproject settings.
    4. คลิกนำไปใช้แล้ว คลิกตกลง.
    For more information about common language runtime support compiler options, visit the following Microsoft Developer Network (MSDN) Web site:
    http://msdn2.microsoft.com/en-us/library/k8d11d4s.aspx
    ขั้นตอนเหล่านี้นำไปใช้กับบทความทั้งหมด
  2. Your DLL can have several consumers. If it does have multiple consumers, add the following code to the DLL .def file in the exports section:
    DllEnsureInit	PRIVATE
    DllForceTerm	PRIVATE
    
    If you do not add these lines, and if you have two DLLs that export functions, the application that links to the DLL will have link errors. Typically, the exported functions have the same names. In a multiconsumer case, each consumer can be linked statically or dynamically to your DLL.
  3. If the consumer is statically linked to the DLL, before you use the DLL the first time, or before you use anything that depends on it in your application, add the following call:
    // 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. After the last use of the DLL in your application, add the following code:
    // 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. If the consumer is dynamically linked to the DLL, insert code as follows:
    • Insert snippet 1 (see step 3) immediately after the firstLoadLibraryfor the DLL .
    • Insert snippet 2 (see step 4) immediately before the lastFreeLibraryfor the DLL.

To Modify COM-based DLL

  • Modify the DLL export functionsDllCanUnloadNow,DllGetClassObject,DllRegisterServerและDllUnregisterServerตามที่แสดงในรหัสต่อไปนี้:
    //  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 ที่ประกอบด้วยลูกค้าที่ใช้จัดการรหัสและ Exports 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 และหลัง จากที่คุณทำเสร็จแล้วการใช้งาน เรียกสมาชิกเริ่มต้นและสิ้นสุดฟังก์ชันในหลัก:
    // Main.cpp
    
    #using <mscorlib.dll>
    using namespace System;
    using namespace System::Reflection;
    #using "ijwdll.dll";
    
    int main() {
    	int retval = ManagedWrapper::minitialize();
        ManagedWrapper::mterminate();
    }
    

ผู้ใช้ของ c ++ Visual .NET 2002

หมายเหตุ:: แม้ว่าการข้อความแสดงข้อผิดพลาดการ linker LNK4243 ไม่มีอยู่ในรุ่นผลิตภัณฑ์ของ 2002 .NET c ++ Visual ผู้ใช้ของ 2002 .NET c ++ Visual จะแนะนำให้ทำตามคำแนะนำกล่าวถึงก่อนหน้านี้เมื่อมีการพัฒนา DLLs ผสม

รุ่นของผลิตภัณฑ์ 2003 .NET c ++ Visual ประกอบด้วยหัวข้อเพิ่มเติมเพื่อทำการเตรียมใช้งานด้วยตนเองให้สะดวก เพื่อทำการแก้ไขปัญหาที่ระบุไว้ในการทำงานของบทความนี้มี 2002 .NET c ++ Visual คุณต้องเพิ่มส่วนหัวของแฟ้มโครงการของคุณที่เรียกว่า_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::
309694BUG: AppDomainUnloaded ยกเว้นเมื่อคุณใช้จัดการส่วนขยายสำหรับคอมโพเนนต์ c ++ Visual

คุณสมบัติ

หมายเลขบทความ (Article ID): 814472 - รีวิวครั้งสุดท้าย: 14 มกราคม 2554 - Revision: 4.0
ใช้กับ
  • Microsoft Visual C++ 2005 Express Edition
  • Microsoft Visual C++ .NET 2003 Standard Edition
  • Microsoft Visual C++ 2002 Standard Edition
  • Microsoft .NET Framework 1.1
  • Microsoft .NET Framework 1.0
Keywords: 
kbcominterop kbmanaged kbdll kbijw kbprb kbmt KB814472 KbMtth
แปลโดยคอมพิวเตอร์
ข้อมูลสำคัญ: บทความนี้แปลโดยซอฟต์แวร์การแปลด้วยคอมพิวเตอร์ของ Microsoft แทนที่จะเป็นนักแปลที่เป็นบุคคล Microsoft มีบทความที่แปลโดยนักแปลและบทความที่แปลด้วยคอมพิวเตอร์ เพื่อให้คุณสามารถเข้าถึงบทความทั้งหมดในฐานความรู้ของเรา ในภาษาของคุณเอง อย่างไรก็ตาม บทความที่แปลด้วยคอมพิวเตอร์นั้นอาจมีข้อบกพร่อง โดยอาจมีข้อผิดพลาดในคำศัพท์ รูปแบบการใช้ภาษาและไวยากรณ์ เช่นเดียวกับกรณีที่ชาวต่างชาติพูดผิดเมื่อพูดภาษาของคุณ Microsoft ไม่มีส่วนรับผิดชอบต่อความคลาดเคลื่อน ความผิดพลาดหรือความเสียหายที่เกิดจากการแปลเนื้อหาผิดพลาด หรือการใช้บทแปลของลูกค้า และ Microsoft มีการปรับปรุงซอฟต์แวร์การแปลด้วยคอมพิวเตอร์อยู่เป็นประจำ
ต่อไปนี้เป็นฉบับภาษาอังกฤษของบทความนี้:814472

ให้ข้อเสนอแนะ

 

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