问微软最有价值专家:关于LNK4243警告

Muf 2004-03-01 10:32:19
LINK : warning LNK4243: 包含用 /clr 编译的对象的 DLL 在链接时没有使用 /NOENTRY;映像可能无法正确运行

其中,“可能无法正确运行”,指的是什么?
在什么情况下会无法正确运行?
...全文
137 3 打赏 收藏 转发到动态 举报
写回复
用AI写文章
3 条回复
切换为时间正序
请发表友善的回复…
发表回复
zwlpower 2004-07-06
  • 打赏
  • 举报
回复
C++ 托管扩展参考

将 C++ 托管扩展项目从纯中间语言转换为混合模式请参见
编译为 MSIL | 产生可验证的 C++ 托管扩展组件将 C++ 托管扩展项目从混合模式转换为纯中间语言项目在默认情况下,作为 DLL 创建的 C++ 托管扩展项目所包含的 Microsoft 中间语言 (MSIL) 代码不会与 C 运行时 (CRT) 库、ATL 或 MFC 等本机 C/C++ 库建立任何链接,也不会使用任何静态变量。它们的代码只用于公共语言运行库。
这样做的原因是:如果托管代码与一个入口点链接,则会导致它们在 DllMain 期间运行,而这样是不安全的(有关在 DllMain 范围内可以执行的操作限制,请参见 DllMain)。
如果一个 DLL 没有入口点,则无法初始化静态变量(像整数这样非常简单的类型除外)。通常情况下,在 /NOENTRY DLL 中不应有任何静态变量。
由于 ATL、MFC 和 CRT 库都依赖于静态库,因此,您也无法从此 DLL 内使用这些库。
如果混合模式的 DLL 需要使用静态变量或依靠静态变量的库(如 ATL、MFC 或 CRT),则必须修改该 DLL,使之具有显式的入口点。
若要修改 DLL,使之具有显式的入口点,需将托管 DLL 转换成混合模式。
将托管 DLL 转换成混合模式
使用 /NOENTRY 开关进行链接。在解决方案资源管理器中,右击该项目节点,然后单击“属性”。在项目的“属性页”对话框中,单击“链接器”,然后单击“命令行”。在“附加选项”字段中添加此开关。
链接 msvcrt.lib。在项目的“属性页”对话框中,单击“链接器”,然后单击“输入”。在“附加依赖项”属性中添加 msvcrt.lib。
移除 nochkclr.obj。在“输入”页(上一步的同一页)上,从“附加依赖项”属性中移除 nochkclr.obj。
在 CRT 中链接。在“输入”页(上一步的同一页)上,在“强制符号引用”属性中添加 __DllMainCRTStartup@12。
修改使用 DLL 的组件以进行手动初始化
在转换为混合模式后,必须修改使用 DLL 的组件以进行手动初始化,具体方法取决于实现 DLL 的方式:
DLL 是使用 DLL 导出 (__declspec(dllexport)) 输入的,使用者如果是静态或动态地链接到该 DLL,则无法使用托管代码。
DLL 是基于 COM 的 DLL。
DLL 的使用者可以使用托管代码,并且该 DLL 或者包含 DLL 导出,或者包含托管入口点。
修改通过 DLL 导出 (__declspec(dllexport)) 输入的并且使用者无法使用托管代码的 DLL
在 DLL 中添加两项新的导出:
// init.cpp
// Add these headers before the header with the using namespace System
// directive, or add them in a .cpp file that does not have a
// using namespace System directive.
#include <windows.h>
#include <_vcclrit.h>

// Call this function before you call anything in this DLL.
// It is safe to call from multiple threads, is not reference
// counted, and is reentrancy safe.

extern "C" __declspec(dllexport) void __stdcall 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.

extern "C" __declspec(dllexport) void __stdcall DllForceTerm(void)
{
// Do nothing else here. If you need extra terminate steps,
// use atexit.
__crt_dll_terminate();
// Do nothing else here.
}
在 DLL .def 文件的导出部分添加下列两行代码:
DllEnsureInit PRIVATE
DllForceTerm PRIVATE
如果不添加以上的代码行,那么,一旦出现两个需导出函数的 DLL,链接到该 DLL 的应用程序就会出现链接错误。通常情况是,导出的函数将具有相同的名称。
在出现多个使用者的情形中,每个使用者都可以静态或动态地链接到 DLL。
DLL 可以有多个使用者。
如果使用者是静态地链接到 DLL,那么,在应用程序中第一次使用该 DLL 或依赖它的任何对象的代码之前,添加下面的调用代码:
// Snippet 1

typedef void (__stdcall *pfnEnsureInit)(void);
typedef void (__stdcall *pfnForceTerm)(void);

{
// ... initialization code
HMODULE 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
}
在应用程序中最后一次使用该 DLL 的代码后面,添加以下代码:
// Snippet 2

{
// ... termination code
HMODULE 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
}
如果使用者是动态地链接到 DLL,则紧跟在 DLL 的第一个 LoadLibrary 的后面插入 snippet 1,并在 DLL 的最后一个 FreeLibrary 的紧前面插入 snippet 2。
修改基于 COM 的 DLL
按照如下代码所示,修改 DLL 导出函数 DllCanUnloadNow、DllGetClassObject、DllRegisterServer 和 DllUnregisterServer:
// Implementation of DLL Exports

STDAPI DllCanUnloadNow(void)
{
HRESULT hrReturn=S_FALSE;
// Function as usual
// At this point hrReturn is S_OK if you can unload
if(hrReturn == S_OK)
{
__crt_dll_terminate();
}
return hrReturn;
}

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
// Do nothing here
__crt_dll_initialize();
// Continue with DllGetClassObject as before
}

STDAPI DllRegisterServer(void)
{
if ( !( __crt_dll_initialize() ) )
{
return E_FAIL;
}
// Call your registration code here
HRESULT hr = <registration code>
__crt_dll_terminate();
return hr;
}

STDAPI DllUnregisterServer(void)
{
if ( !( __crt_dll_initialize() ) )
{
return E_FAIL;
}
// Call your unregistration code here
HRESULT hr = <unregistration code>
__crt_dll_terminate();
return hr;
}
修改包含使用托管代码的使用者和 DLL 导出或托管入口点的 DLL
实现一个具有静态成员的托管类以进行初始化和终止。在项目中添加一个 .cpp 文件,实现一个具有静态成员的托管类以进行初始化和终止:
// ManagedWrapper.cpp

// This code verifies that DllMain is not called by the Loader
// automatically 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 int minitialize() {
int retval = 0;
try {
__crt_dll_initialize();
} catch(System::Exception* e) {
Console::WriteLine(e);
retval = 1;
}
return retval;
}
static int mterminate() {
int retval = 0;
try {
__crt_dll_terminate();
} catch(System::Exception* e) {
Console::WriteLine(e);
retval = 1;
}
return retval;
}
};

BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID
lpvReserved) {
Console::WriteLine(S"DllMain is called...");
return TRUE;
} /* DllMain */
在引用该 DLL 之前以及完成它之后调用这些函数。在 main 方法中调用初始化和终止成员函数:
#using <mscorlib.dll>
#using "ijwdll.dll"

using namespace System;

int main() {
int retval = 0;
retval += ManagedWrapper::minitialize();
retval += ManagedWrapper::mterminate();
return retval;
}
zwlpower 2004-07-06
  • 打赏
  • 举报
回复
使用 /clr 开关编译的 DLL 包含对象没有使用 /NOENTRY 开关进行链接;映像可能不能正确运行

当 C++ 托管扩展 DLL 具有入口点时会出现此警告。

若要修复,必须使用在链接时使用 /NOENTRY 开关。有关更多信息,请参见将 C++ 托管扩展项目从纯中间语言转换为混合模式。

Muf 2004-06-25
  • 打赏
  • 举报
回复
waiting...