При создании управляемых расширений для C++ DLL проектов получают предупреждения компоновщика

Переводы статьи Переводы статьи
Код статьи: 814472 - Vizualiza?i produsele pentru care se aplic? acest articol.
Развернуть все | Свернуть все

В этой статье

Проблема

Появляется одно из следующих сообщений об ошибке при компиляции время или во время компоновки.
Ошибка средств компоновщика LNK2001
"неразрешенных внешний символ «символ» "

Предупреждение средств компоновщика LNK4210
'.CRT существует раздел; там может быть необработанных выполняет инициализацию статических или teminators'You получать предупреждения компоновщика при построении управляемых расширений для C++ DLL проектов

LNK4243 Предупреждение средств компоновщика
"Библиотеки DLL, содержащей объекты скомпилированный с/clr не связан с/NOENTRY; образ может не запуститься правильно ".
Эти предупреждения могут возникнуть во время следующего обстоятельств:
  • При компиляции связывание объектов с / CLR коммутатор.
  • При создании одного из следующих проектов: ASP.NET веб-службы шаблона; Шаблон библиотеки классов; или элемент управления Windows Шаблон библиотеки.
  • После добавления кода, который использует глобальные переменные или собственные классы (то есть не __gc или __value) с статические члены данных. Для пример, ActiveX Template Library (ATL), классов (MFC) и классы времени выполнения C (CRT)
Примечание: могут возникнуть ошибки LNK2001 и LNK4210 с проектами которые не подвержены уязвимости, описанной в этой статье. Тем не менее определенно проекта является подвержены уязвимости, описанной в этой статье, при разрешении Предупреждение LNK2001 или LNK4210 приводит к LNK4243 предупреждение, или если компоновка проект создается предупреждение LNK4243.

Причина

По умолчанию создаются следующие проекты как динамический библиотеки (DLL) без любого выдвигать собственных библиотек (таких как CRT ATL или MFC) и без собственных классов с помощью статических или глобальных переменных члены данных:
  • ASP.NET веб-службы шаблона
  • Шаблон библиотеки классов
  • Шаблон библиотеки элементов управления Windows
Если добавить код, который использует глобальные переменные или собственных классов статические члены данных (например, ATL, MFC и CRT библиотеки использование с глобальные переменные), будет получать сообщения об ошибках компоновщика во время компиляции. Когда в этом случае необходимо добавить код для инициализации статических переменных вручную. Для Дополнительные сведения о том, как сделать это, обратитесь к разделу «Решение» этой в статье.

Для удобства в данной статье относится к глобальным переменным и статические члены данных собственных классов, как «помехи» или «статические переменные» конца.

Эта проблема вызвана смешанных библиотек DLL при загрузке проблема. Смешанные библиотеки (DLL, содержащих управляемого и машинного кода) могут возникнуть ситуации взаимоблокировки в некоторых случаях при загрузке в адресное пространство процесса особенно когда система находится под нагрузкой. В упомянутые выше сообщения об ошибке компоновщика были включены в компоновщик, убедитесь, что что известно клиентов может привести к взаимоблокировке и способы их устранения, описанные в данном документе. Подробное описание смешанных библиотек DLL Загрузка проблема, см. следующий документ:
Смешанные загрузку DLL Проблема
http://msdn2.Microsoft.com/en-us/library/aa290048 (vs.71) .aspx

Решение

Управляемые расширения для C++ проектов, которые создаются как библиотеки DLL по умолчанию связан с собственной библиотеки C/C++, такие как библиотеку времени выполнения C (CRT) Библиотека ATL и MFC и не используйте статические переменные. Кроме того настройки проекта определяют, что библиотеки 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 для ручной Initializiation

После удаления явной точки входа необходимо изменить компоненты, которые используют библиотеки DLL для ручной инициализации, в зависимости от способа что реализуется библиотеки DLL:
  • Библиотека DLL введены (DLL) экспортирует__declspec(dllexport)), и потребителей не могут использовать управляемый код, если они связаны статически или динамически с библиотекой DLL.
  • Библиотека DLL является библиотекой DLL COM-совместимый.
  • Потребители библиотеки DLL можно использовать управляемый код и библиотеки DLL. содержит либо экспорта библиотеки DLL или управляемой точки входа.

Изменение библиотеки DLL, введенные С помощью экспорта библиотеки DLL и Потребители, которые не могут использовать управляемый код

Изменение библиотеки DLL, введенные с помощью экспорта библиотеки dll (__declspec(dllexport)) и потребителей, которые не могут использовать управляемый код, выполните следующие действия:
  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. Нажмите кнопку Проект, а затем нажмите кнопкуИмя проекта Свойства.

      Примечание Имя проекта — Это имя проекта.
    2. Разверните узел Свойства конфигурации, а затем нажмите кнопкуОбщие.
    3. В области справа щелкните, чтобы выбрать Поддержка среды CLR, старый синтаксис (/ CLR: oldSyntax) В диалоговом окнеПоддержка среды CLR параметры проекта.
    4. Нажмите кнопкуПрименение, а затем нажмите кнопку ОК.
    Для получения дополнительных сведений о общеязыковая среда выполнения поддерживает параметры компилятора, посетите следующий веб-узел Microsoft Developer Network (MSDN):
    http://msdn2.Microsoft.com/en-us/library/k8d11d4s.aspx
    Эти действия применимы ко всей статьи.
  2. Библиотека DLL может иметь несколько пользователей. Если у него несколько пользователей, добавьте следующий код в DEF-файле библиотеки DLL в результате проведения раздел:
    DllEnsureInit	PRIVATE
    DllForceTerm	PRIVATE
    
    Если эти строки не добавляются и при наличии двух библиотек DLL, экспорт функции, приложение, которое содержит ссылки на библиотеки DLL будет иметь ошибки ссылки. Как правило экспортируемые функции имеют одинаковые имена. В multiconsumer случае Каждый получатель может быть связана статически или динамически с библиотекой DLL.
  3. Если потребитель статически связанная с библиотекой 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, Вставка код следующим образом:
    • Вставить фрагмент 1 (см. шаг 3) сразу после Первый Функции LoadLibrary для библиотеки DLL.
    • Вставить фрагмент 2 (см. шаг 4) непосредственно перед Фамилия FreeLibrary для библиотеки DLL.

Изменение библиотеки DLL на основе COM.

  • Изменение функции экспорта библиотеки DLL DllCanUnloadNow, 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, содержащей потребителей, использующих управляемый код и экспорта библиотеки 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:
    // 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 выпуска продукта, пользователям Visual C++.NET 2002 без промедления Следуйте указаниям, приведенным упоминалось ранее, при разработке смешанных библиотек DLL.

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);
}

Ссылки

Для получения дополнительных сведений щелкните следующий номер статьи базы знаний Майкрософт:
309694Ошибка: Исключение AppDomainUnloaded при использовании управляемых расширений для компонентов Visual C++

Свойства

Код статьи: 814472 - Последний отзыв: 14 июня 2011 г. - Revision: 4.0
Информация в данной статье относится к следующим продуктам.
  • 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
Ключевые слова: 
kbcominterop kbmanaged kbdll kbijw kbprb kbmt KB814472 KbMtru
Переведено с помощью машинного перевода
ВНИМАНИЕ! Перевод данной статьи был выполнен не человеком, а с помощью программы машинного перевода, разработанной корпорацией Майкрософт. Корпорация Майкрософт предлагает вам статьи, переведенные как людьми, так и средствами машинного перевода, чтобы у вас была возможность ознакомиться со статьями базы знаний KB на родном языке. Однако машинный перевод не всегда идеален. Он может содержать смысловые, синтаксические и грамматические ошибки, подобно тому как иностранец делает ошибки, пытаясь говорить на вашем языке. Корпорация Майкрософт не несет ответственности за неточности, ошибки и возможный ущерб, причиненный в результате неправильного перевода или его использования. Корпорация Майкрософт также часто обновляет средства машинного перевода.
Эта статья на английском языке: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