.NET框架下编程语言的互操作性

.NET架构让不同的编程语言如C#,Visual Basic.NET及C++等之间的相互调用成为了可能。在.NET框架下,所有高级语言都会先编译成中间语言(MSIL),这个阶段和Java源代码首先被编译成Java字节码类似;而从中间语言到本地机器码这个过程是在程序运行时完成的,也就是所谓的JIT技术。正是IL的出现,使得.NET具备了平台无关性和语言无关性两大主要特征。

下面演示如何在.NET框架下实现不同语言间的通信。以下三个代码段摘自PROGRAMMING MICROSOFT .NET一书Chapter 2的Dynamic Linking一节。

创建VB源文件simple.vb

Imports System Public Class SimpleMath Function Add (a As Integer, b As Integer) As Integer Return a + b End Function Function Subtract (a As Integer, b As Integer) As Integer Return a - b End Function End Class

创建C#源文件complex.cs

using System; public class ComplexMath { public int Square (int a) { return a * a; } }

创建C#测试文件demo.cs

using System; class MyApp { static void Main () { SimpleMath simple = new SimpleMath (); int sum = simple.Add (2, 2); Console.WriteLine ("2 + 2 = {0}", sum); ComplexMath complex = new ComplexMath (); int square = complex.Square (3); Console.WriteLine ("3 squared = {0}", square); } }

首先将两个类文件编译成netmodule模块供测试文件使用,这里将用到VS自带的命令行工具。

vbc /target:module simple.vb

csc /target:module complex.cs

在当前目录下会产生simple.netmodule和complex.netmodule两个文件。很显然,vbc是VB编译器,csc是C#编译器。编译选项/target:module告诉编译器生成模块,否则编译器默认生成可执行文件,即/target:exe, 然而上述文件中没有程序入口点导致报错。接下来编译测试程序:

csc /addmodule:simple.netmodule,complex.netmodule demo.cs

将会生成可执行程序demo.exe。测试程序中引用了module中定义的类,所以需要通过/addmodule告知编译器去哪里寻找它们的定义。注意/addmodule后需要跟一个文件列表,同时引用多个文件时需要用逗号将各文件名隔开。

最后执行demo程序,console正确输出结果。

这里我们用VB编写了SimpleMath类,又用C#编写了ComplexMath类,最后用C#程序对这两个类进行调用。在DOTNET框架下它们之间的协作不存在任何问题。

我们还可以采取创建程序集Assembly的方法来完成相同的功能。以上三个文件内容保持不变。

首先用相同命令生成simple.netmodule和complex.netmodule两个文件,然后我们将两个module进行打包,生成一个多文件程序集。为了简单起见,只生成私有程序集。为了部署到GAC中,还需要生成密钥,这里暂时不作考虑。

al /target:library /out:math.dll simple.netmodule complex.netmodule

执行完这行命令会发现当前目录多出一个名为math.dll的文件,它就是所说的程序集,我们可以在编译测试程序时对它进行引用。编译选项/target:library告诉编译器我需要输出为库文件,/out指定输出文件的文件名。

csc /reference:math.dll demo.cs

生成可执行文件demo.exe,程序同样能够完美运行。这里/reference:math.dll告诉编译器demo.cs对math.dll进行了引用,去掉它编译器会抱怨找不到所需类定义。注意/reference是对.dll进行引用,而/addmodule是对.obj和.netmodule这样的文件进行引用,两者不能混淆。

事实上,C#和Visual Basic.NET之间互操作是相当简单的,其原因在于C#本身就为DOTNET而生,而Visual Basic.NET在VB 6的基础上为能在新平台下工作进行了大幅改进。下面将可以看到,要让C#和C++协作起来,还是要花点功夫的。

假设我们有一个C++源文件,里面定义了一个简单的加法函数add。

int add(int a, int b) { return a + b; }

像这样的Native C++是不能在.NET framework下工作的,我们需要给它包装一下,将这个函数定义放到一个类里面去。除此之外,类定义的语法也有所变化。修改后的C++代码如下所示:

public ref class Simple { public: int add(int a, int b) { return a + b; } };

实际上这是C++/CLI,它是对标准C++的扩展,使得托管代码和非托管代码能够无缝集成。简单点说,C++/CLI可以工作在.NET框架下,可以在CLR上运行。

另一个文件test.cs调用Simple类,以下是源代码:

using System; class Test { public static void Main() { Simple simple = new Simple(); Console.WriteLine(simple.add(3, 4)); } }

下面编译这两个文件。

cl /c /clr simple.cpp

当前目录生成simple.obj文件。/c告诉编译器只对源文件进行编译,而不进行链接;/clr启用Common Language Runtime支持,表明生成面向clr的托管代码。至于为什么VS的C/C++编译器要叫cl,我也不知道。

csc /addmodule:simple.obj /target:module test.cs

生成test.netmodule模块。现在应该知道/addmodule的意义了,我们在测试文件中引用了Simple类定义,只不过这里它定义在.obj文件中,而不是之前的.netmodule中。

最后可以生成可执行文件了,这里需要借助链接器。

link simple.obj test.netmodule /entry:Test.Main /subsystem:console /out:demo.exe

产生可执行文件demo.exe,结果输出7。

link命令看上去有点复杂。为了生成可执行文件demo.exe,需要链接两个文件,一个是simple.obj (C++编译而来),另一个是test.netmodule (由C#编译而来);/entry指明可执行程序的入口点函数,注意如果类Test外还有命名空间的话,比如wrapper,入口点函数需要写成wrapper.Test.Main;/subsystem指定子系统,链接器需要知道这是一个控制台应用程序(Console)还是一个窗口应用程序(Windows),这里我们告诉它是这是一个DOS控制台程序。

上面这个方法是我在网上搜到的,和前面让VB与C#通信的方法相比差别很大。直觉告诉我C++可以直接生成netmodule被C#调用,但让我颇为疑惑的是,如果用cl /LN /clr simple.cpp命令生成simple.netmodule,然后csc /out:demo.exe /addmodule: simple.netmodule test.cs生成demo无论如何也会抛出异常。倒腾了很久,终于找到了解决办法,cl命令中需要指定/clr:safe,也就是命令需要改成cl /LN /clr:safe simple.cpp问题就解决了!/LN和/clr需要一起使用,告知编译器生成MSIL模块。

程序是可以正常运行了,然而/clr和/clr:safe究竟对编译行为造成了什么影响呢?以下是MSDN的两段表述:

The /clr:safe compiler option generates verifiable assemblies, like those written in Visual Basic and C#, conforming to requirements that allow the common language runtime (CLR) to guarantee that the code does not violate current security settings. For example, if security settings prohibit a component from writing to disk, the CLR can determine if a verifiable component meets this criterion before executing any of the code.

Compiling by using /clr:safe is analogous to compiling by using /platform:anycpu in languages such as C#.

另外在对/clr的介绍中有这样一段注释,看得不是很明白,不知道之前抛出的异常与我用的64位OS有关。

When running a 32-bit .exe that was compiled /clr or /clr:pure on a 64-bit operating system, the application will be run under WOW64, which allows a 32-bit application to run by the 32-bit CLR on a 64-bit operating system. By default, an .exe compiled with /clr:safe will be run in the 64-bit CLR on a computer running a 64-bit operating system (on a 32-bit operating system the same .exe would run in the 32-bit CLR). However, it is possible that your safe application loads a 32-bit component. In that case, a safe image running under the operating system's 64-bit support will fail when it loads the 32-bit application (BadFormatException). To ensure that a safe image continues to run when it loads a 32-bit image on a 64-bit operating system, you must use /CLRIMAGETYPE to change the metadata (.corflags), marking it to be run under WOW64.

问题虽然暂时解决了,但是对.NET的类型安全和运行时安全验证还有不少疑惑。

你可能感兴趣的:(编程,.net,框架,Integer,语言,编译器)