当年Visual Studio.NET 2003 C++编译器的面世,令无数C++语言爱好者都对此垂涎不已。
作者:谢启东编译 来源:天极开发 2007年11月16日
关键字:
新的Interop
在Visual Studio .NET 2003的所有基于 .NET Framework的语言当中,Visual C++ 2005提供了最好的interop功能。它可不像是纸上谈兵,如今具有了足够的能力来实现真实世界中的场景,一个最好的例子就是把Quake II移植到 .NET Framework平台上,而Visual C++ 2005则更加扩展了这项功能。
在本地和托管世界中,.NET有四种主要的方法可进行interop。对COM的interop可使用RCW(Runtime Callable Wrappers)和CCW(COM Callable Wrappers),而CLR主要负责类型调度(除了在极少数情况下,使用了定制的调度器),但这些调用是开销巨大的。因为接口极其复杂,所以必须加倍小心,否则将会导致严重的性能损失,而且还要保证这些包装器的底层组件是最新版本的。这就是说,当你试图引入大量本地COM代码时,只有对一些简单的情况而言,COM interop才非常有用。
第二种使用interop的方法是通过P/Invok,一般通过使用DLLImport属性,对需要引入的函数,在方法声明上指定此属性,根据声明是怎样指定的,从而决定调度怎样处理。然而,只有在一个DLL通过export属性有输出函数时,DLLImport才能起到作用。
如果想从本地代码中调用托管代码,可选择CLR宿主。在这种情况下,本地程序必须做好一切相关工作:设置宿主、绑定运行时、启动宿主、取回相应的AppDomain、设置调用上下文、定位所需的程序集和类、在相应的类上调用操作。这就是一个稳健的解决方案需要做的一切事情,很单调乏味,并且需要很多人工代码。
第四种,也可能是最容易和高效的一种方法,就是使用C++中的interop,通过设置/clr,编译器将生成MSIL代码——而不是本地机器码,其中包括了那些使用内联汇编的函数和CPU特定指令集,如SSE;/clr正是Quake II移植到 .NET平台的关键之处。在此不需要加入其他任何二进制代码,只是简单地包含一些相应的头文件,托管C++就能和本地C++互相调用,对开发者来说,不需做任何工作就能实现这一点,何乐而不为呢。实际上,编译器处理了相应转换器的创建,并在托管和本地两个世界中来回奔波。
对C++开发者来说,以上的结果还涉及到更多的方面,其中一个,就是在Visual Studio .NET 2002和Visual Studio .NET 2003中声名狼藉的混合DLL加载问题。如果你在加载器锁(loader lock)中运行本地代码,但又引用了程序集中一个未被加载的托管类型,此时,CLR会调用LoadLibrary来加载这个程序集。LoadLibrary会试图取得加载器锁,在这一点上,造成了程序死锁。而这个问题,在新版本的Visual Studio中,已经解决了。
虽然/clr给C++开发者带来了巨大的好处,但也有一些不利的方面。如之前所提到的,/clr会生成包含本地和托管代码的映像文件,这在有些时候会带来问题。首先,这些混合的映像文件不遵从CLI,它们有本地进入点,当超出托管边界时,会带来严重的转换性能损失。但最重要的是,本地进入点会对使用程序集和反射(reflection)的工具造成严重的影响。反射之前要先检查一个映像文件,此时必须先加载并运行程序集,只有在所有的初始化完成之后,反射才可能检查元数据,而此时却因为包含了本地进入点,造成反射不能正确地加载一个托管程序集。
此外,Visual Studio .NET 2003只在极少数情况下,能生成可验证代码,即使生成了,也需花一大番气力。然而,MSIL对不可验证指令提供了第一类的支持(指针算法、间接加载、访问本地堆),可验证代码也使你可参与到可信赖计算中,反过来,也极大地丰富了Visual Studio 2005的功能。单击部署基于局部可信赖计算,就像托管代码宿于SQL Server 2005中一样。Visual C++ 2005开发小组一个主要目标,就是使编译器可生成非混合及可验证映像,为此引入了两个新的编译器选项:/clr:pure和/clr:safe,但在研究这两个新选项之前,还需要弄清楚C++ interop是怎样工作的。