2008年(909)
分类:
2008-05-06 22:25:16
原文出处: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 转换为混合模式?
修改使用DLL的代码部分,进行手动初始化
转换成混合模式之后,你必须修改使用DLL的代码部分,根据你的DLL实现方式进行手动初始化:
用 __declspec(dllexport) 输出且调用者无法使用托管代码的 DLL 的修改方法:
// init.cpp // 在 using namespace System 指令头之前添加这些头文件, // 或者在没有using namespace System 指令头的 .cpp 文件中添加它们 #include将下面代码添加到 DLL .def 文件的 “exports” 部分:#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(); // 在这里什么也不要做。 }
DllEnsureInit PRIVATE DllForceTerm PRIVATE
如果没有这两行,那么当你有两个 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(); // ... 更多的初始化代码 }
// 代码段二 { // ... 终止代码 HMODULE hDll=::GetModuleHandle("mydll.dll"); If(!hDll) { // 退出,返回,再没有什么要做的了 } pfnForceTerm pfnDll=( pfnForceTerm) ::GetProcAddress(hDll, "DllForceTerm"); if(!pfnDll) { // 退出,返回,再没有什么要做的了 } pfnDll(); // ... 更多的终止代码 }
基于 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 =你的 DLL 包含调用者,该调用者使用托管代码以及 DLL 输出或者托管入口点,修改方式如下:__crt_dll_terminate(); return hr; } STDAPI DllUnregisterServer(void) { if ( !( __crt_dll_initialize() ) ) { return E_FAIL; } // 在这里调用注销代码 HRESULT hr = __crt_dll_terminate(); return hr; }
// ManagedWrapper.cpp // 这个代码验证当使用 /NOENTRY 链接选项时,DllMain 没有被 Loader 自动调用。 // 它也检查某些 CRT 初始化函数。 #include#include #include #include #include #include "_vcclrit.h" #using 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 */
#using#using "ijwdll.dll" using namespace System; int main() { int retval = 0; retval = ManagedWrapper::minitialize(); retval = ManagedWrapper::mterminate(); return retval; }