在 Visual Studio .NET 2003 中初始化混合DLLs很麻烦,需要手工干预。但在 Visual Studio 2005 中,Visual C++ 和 CLR 团队设计了一种新的初始化模型,这种新模型更简单,更自动化。
Visual Studio .NET 2003 的根本问题在于将本地代码和托管代码一起置于单“池”中。DllMain 期间该池的运行是不安全的,托管代码根本就不能在这里运行。
新的模型将静态初始化汇集在两个单独的池中。一个池负责本地的静态初始化;另一个负责托管代码的初始化。
第一个池在 DllMainCRTStartup 期间进行初始化,就像本地代码那样。第二个池的初始化,我们加入了一个新的托管代码初始化阶段。当某个包含托管代码的模块被加载到应用程序域(AppDomain)时,CLR 将会在任何托管代码运行之前运行一个特殊的.cctor 函数(我们把它叫做“模块构造函数”)。
托管C运行时(msvcmrt.lib)包含一个模块构造函数,由它负责初始化第二个池。同时它还安装一个应用程序域的 unload 事件来运行托管静态析构函数和 exit 函数。
这种新模型意味着到托管代码的迁移或添加托管代码到现有的应用程序将变得更加容易,因为不需要再添加定制的初始化入口点。迁移期间,你可能还会碰到本地代码调用托管代码的情况。你的代码要可靠地启动和关闭,必须解决这个问题才行。否则会出现 C 运行时库的R6031错误,或者在调试器下运行时出现 CLR 警告。
为了在 Visual Studio 2005 Beta 2 或以后的版本中解决这个问题,使用调试器的调用堆栈(call stack)找到此烦人的函数,并将该函数要么移到某个头文件中,或者本地原文件中,以便将它编译成本地代码。你可能注意到了通过 #pragma managed 解决此类问题的建议。请尽量避免这种做法,用这种方法来解决这个问题非常难以凑效。尤其是在你具备自己的 DllMain 或 RawDllMain 时更是如此,此时必须保证将此函数放在本地代码集中。
如果你采纳了保罗描述的解决方案,从 Visual C++ 7.1 到 8.0 的迁移不会有太多麻烦。当你重新编译时,你会看到关于使用 _vcclrit.h 中函数的不满警告。此时删除对该文件的引用并进行手工初始化即可。为了完全恢复使用CRT,你还应该从编译开关中去掉 /Zl,从链接开关中去掉 /NOENTRY。
这是 Visual Studio 2005 中大量改动的地方之一,目的是方便将 C++ 应用程序迁移到托管代码使用环境。使用最新的生成产品,我们已经看到大量应用程序在相当短的时间内转移到了托管环境中。Visual Studio 2005 Beta 2 出炉后用用看吧,并把使用情况反馈给我们。
查看本文来源