分享
 
 
 

将C++托管扩展项目从纯粹的中间语言转换成混合模式

王朝vc·作者佚名  2006-01-17
窄屏简体版  字體: |||超大  

将C++托管扩展项目从纯粹的中间语言转换成混合模式

编译:Northtibet

原文出处:Converting Managed Extensions for C++ Projects from Pure Intermediate Language to Mixed Mode

创建 DLL 的 C++ 托管扩展项目默认包含 MSIL(微软中间语言)代码,这个代码并不与 C 运行时库(CRT),ATL 或 MFC

这样的本机 C/C++ 库链接,也不使用任何静态变量。其代码只面向公共语言运行时。

之所以要这么做是因为带有入口点的链接导致 DllMain 期间运行托管代码,这样不安全(参见 DllMain

相关文档,了解在它执行期间你不能做那些事情)。

不带 入口点的 DLL 无法初始化静态变量,非常简单的类型如整型除外。通常,在 /NOENTRY DLL 中,你不能有任何静态变量。

ATL,MFC 和 CRT 库都依赖于静态变量,所以你也不能在该 DLL 中使用这些库。

如果你的混合模式 DLL必须使用静态变量或者依赖静态变量的库(如:ATL,MFC 或 CRT),那么你必须修改你的 DLL,使之具备外在入口点。

为此,必须将托管 DLL 转换为混合模式。那么,

如何将将托管 DLL 转换为混合模式?

用 /NOENTRY 链接:在解决方案管理器中,在项目节点上单击右键并选择“属性”。在属性页对话框中选择“链接器”,然后选择

“命令行”。添加一个开关到“附加选项”编辑框。

链接 msvcrt.lib:在项目的属性页对话框中选择“连接器”,然后选择“输入”。将 msvcrt.lib 添加到“附加依赖项”。

删除 nochkclr.obj:在“输入”页面(与前一步骤相同的页面),从附加依赖项属性中删除 nochkclr.obj。

链接到 CRT:在“输入”页面(与前一步骤相同的页面),将 __DllMainCRTStartup@12 添加到“强制符号引用”属性中。

修改使用DLL的代码部分,进行手动初始化

转换成混合模式之后,你必须修改使用DLL的代码部分,根据你的DLL实现方式进行手动初始化:

你的 DLL 使用 __declspec(dllexport) 输出,并且 DLL 的使用者与 DLL

之间的链接不管是静态链接的还是动态的,那么 DLL 的使用者都无法使用托管代码。

你的 DLL 是基于 COM 的 DLL

你的 DLL 的调用者可以用托管代码,并且你的 DLL 包含 DLL 输出或托管入口点

用 __declspec(dllexport) 输出且调用者无法使用托管代码的 DLL 的修改方法:

向 DLL 添加两个新的输出:

// init.cpp

// 在 using namespace System 指令头之前添加这些头文件,

// 或者在没有using namespace System 指令头的 .cpp 文件中添加它们

#include <windows.h>

#include <_vcclrit.h>

// 在你调用任何该 DLL 中的东西之前调用该函数。

// 从多线程中调用才安全,并非引用安全,而是重入安全

extern "C" __declspec(dllexport) void __stdcall DllEnsureInit(void)

{

// 在这里什么也不要做,如果你需要额外的初始化步骤,

// 创建带有构造函数的静态对象,在构造函数中完成初始化。

__crt_dll_initialize();

// 在这里什么也不要做。

}

// 在整个进程彻底调用完该 DLL 后调用该函数。从多线程中调用才安全。

// 并非引用安全,而是重入安全。第一次调用将终止。

extern "C" __declspec(dllexport) void __stdcall DllForceTerm(void)

{

// 在这里什么也不要做,如果你需要额外的终止步骤,

// Do nothing else here. If you need extra terminate steps,

// 使用 atexit.

__crt_dll_terminate();

// 在这里什么也不要做。

}

将下面代码添加到 DLL .def 文件的 “exports” 部分:

DllEnsureInit PRIVATE

DllForceTerm PRIVATE

如果没有这两行,那么当你有两个 DLL 都输出函数时,链接到该 DLL

的应用程序将会出现链接错误。典型的错误是输出的函数名字相同。

在有多个DLL调用者时,每个调用者都可以和你 DLL 进行静态或动态链接。

你的 DLL 可以有多个调用者

如果调用者与该 DLL 静态链接,那么在应用程序中首次使用你的 DLL 或者依赖它的任何东西之前,要添加如下调用:

// 代码段一

typedef void (__stdcall *pfnEnsureInit)(void);

typedef void (__stdcall *pfnForceTerm)(void);

{

// ... 初始化代码

HMODULE hDll=::GetModuleHandle("mydll.dll");

If(!hDll)

{

// 退出,返回,再没有什么要做的了

}

pfnEnsureInit pfnDll=( pfnEnsureInit) ::GetProcAddress(hDll,

"DllEnsureInit");

if(!pfnDll)

{

// 退出,返回,再没有什么要做的了

}

pfnDll();

// ... 更多的初始化代码

}

在应用程序最后一次使用 DLL 之后,添加下面的代码:

// 代码段二

{

// ... 终止代码

HMODULE hDll=::GetModuleHandle("mydll.dll");

If(!hDll)

{

// 退出,返回,再没有什么要做的了

}

pfnForceTerm pfnDll=( pfnForceTerm) ::GetProcAddress(hDll,

"DllForceTerm");

if(!pfnDll)

{

// 退出,返回,再没有什么要做的了

}

pfnDll();

// ... 更多的终止代码

}

如果调用者是动态链接到此 DLL,将代码段一插入到第一次调用 LoadLibrary 之后,同时将代码段二插入到最后一次调用

FreeLibrary 之前。

基于 COM 的 DLL 的修改方法

修改 DllCanUnloadNow,DllGetClassObject,DllRegisterServer 和

DllUnregisterServer 输出函数的方法如下:

// 实现 DLL 输出

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)

{

// 在这里什么也不要做。

__crt_dll_initialize();

// 像从前那样继续 DllGetClassObject

}

STDAPI DllRegisterServer(void)

{

if ( !( __crt_dll_initialize() ) )

{

return E_FAIL;

}

// 在这里调用注册代码

HRESULT hr = <registration code>

__crt_dll_terminate();

return hr;

}

STDAPI DllUnregisterServer(void)

{

if ( !( __crt_dll_initialize() ) )

{

return E_FAIL;

}

// 在这里调用注销代码

HRESULT hr = <unregistration code>

__crt_dll_terminate();

return hr;

}

你的 DLL 包含调用者,该调用者使用托管代码以及 DLL 输出或者托管入口点,修改方式如下:

实现一个带有静态成员函数的托管类,静态成员函数用于初始化终止例程。往项目中添加一个 .cpp 文件:

// ManagedWrapper.cpp

// 这个代码验证当使用 /NOENTRY 链接选项时,DllMain 没有被 Loader 自动调用。

// 它也检查某些 CRT 初始化函数。

#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 之前和使用完 DLL 之后,在

main 中调用这些初始化例程和终止例程成员函数:

#using <mscorlib.dll>

#using "ijwdll.dll"

using namespace System;

int main() {

int retval = 0;

retval += ManagedWrapper::minitialize();

retval += ManagedWrapper::mterminate();

return retval;

}

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有