中国IT动力,最新最全的IT技术教程
最新100篇 | 推荐100篇 | 专题100篇 | 排行榜 | 搜索 | 在线API文档
首 页 | 程序开发 | 操作系统 | 软件应用 | 图形图象 | 网络应用 | 精文荟萃 | 教育认证 | 硬件维护 | 未整理篇 | 站长教程
ASP JS PHP工程 ASP.NET 网站建设 UML J2EESUN .NET VC VB VFP 网络维护 数据库 DB2 SQL2000 Oracle Mysql
服务器 Win2000 Office C DreamWeaver FireWorks Flash PhotoShop 上网宝典 CorelDraw 协议大全 网络安全 微软认证
硬件维护  CPU  主板  硬盘  内存  显卡  显示器  键盘鼠标  声卡音箱  打印机  机箱电源  BIOS  网卡  C#  Java  Delphi  vs.net2005
  当前位置:> 程序开发 > 编程语言 > Visual C++ > .NET平台
将C++托管扩展项目从纯粹的中间语言转换成混合模式
作者:未知 时间:2005-07-20 14:21 出处:VC知识库 责编:chinaitpower
              摘要:将C++托管扩展项目从纯粹的中间语言转换成混合模式

将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 转换为混合模式?

  1. 用 /NOENTRY 链接:在解决方案管理器中,在项目节点上单击右键并选择“属性”。在属性页对话框中选择“链接器”,然后选择 “命令行”。添加一个开关到“附加选项”编辑框。
  2. 链接 msvcrt.lib:在项目的属性页对话框中选择“连接器”,然后选择“输入”。将 msvcrt.lib 添加到“附加依赖项”。
  3. 删除 nochkclr.obj:在“输入”页面(与前一步骤相同的页面),从附加依赖项属性中删除 nochkclr.obj。
  4. 链接到 CRT:在“输入”页面(与前一步骤相同的页面),将 __DllMainCRTStartup@12 添加到“强制符号引用”属性中。

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

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

  • 你的 DLL 使用 __declspec(dllexport) 输出,并且 DLL 的使用者与 DLL 之间的链接不管是静态链接的还是动态的,那么 DLL 的使用者都无法使用托管代码。
  • 你的 DLL 是基于 COM 的 DLL
  • 你的 DLL 的调用者可以用托管代码,并且你的 DLL 包含 DLL 输出或托管入口点

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

  1. 向 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 进行静态或动态链接。

  2. 你的 DLL 可以有多个调用者
  3. 如果调用者与该 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();
    	
    	// ... 更多的初始化代码
    }        
  4. 在应用程序最后一次使用 DLL 之后,添加下面的代码:
    // 代码段二
    
    {
    	// ... 终止代码
    	HMODULE hDll=::GetModuleHandle("mydll.dll");
    	If(!hDll)
    	{
    		// 退出,返回,再没有什么要做的了
    	}
    	pfnForceTerm pfnDll=( pfnForceTerm) ::GetProcAddress(hDll, 
    		"DllForceTerm");
    	if(!pfnDll)
    	{
    		// 退出,返回,再没有什么要做的了
    	}
    	
    	pfnDll();
    	
    	// ... 更多的终止代码
    }        
  5. 如果调用者是动态链接到此 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 输出或者托管入口点,修改方式如下:

  1. 实现一个带有静态成员函数的托管类,静态成员函数用于初始化终止例程。往项目中添加一个 .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 */
    
  2. 在引用 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;
    }        
关闭本页
 
首页 | 投资与合作 | 服务条款 | 隐私政策 | 收藏本站 | 设为首页 | 新用户注册 | 免责声明 | 使用帮助
Copyright ©2005-2008 chinaitpower.com All rights reserved. www.chinaitpower.com 版权所有