.NET互操作技术杂谈

 关键字:COM.NET,互操作,封送

        多年来在程序设计领域微软技术一直引领着每个时代,在每一代的技术背后都有相关的动机,而这些动机及其实现细节往往是大部分的程序设计课程没有涉及的,但是对于我们理解相关的技术又十分重要,本文将对几种相关技术和隐藏在动机背后的细节做简要的阐述,算是抛砖引玉。

        COM技术于1993年首次发布,称得上是微软技术的一个里程碑,其意义在于使组件编程化得以实现。COM技术最初的思想起源于将类做成可重用的二进制组件,把类的实现和接口分离以便把类的实现封装到二进制防火墙的背后,而这道防火墙以VPTRVTBL的形式保证了这个防火墙的不变性。.NET技术则在语言层面上支持了基于组件的程序设计。相对于COM技术,.NET技术就是为基于组件的程序设计而生的,所以其编码效率和语言可读性要高出COM一个等级。但是由于1993年以后的十多年时间里,人们利用基于COM的组件程序设计技术开发了大量的高复杂度,高可用性的代码,并且他们运行良好,效率极高,如果用.NET技术重写这些代码简直就是得不偿失,加之在.NET的实现过程中并不是所有的Windows API都被重写,有些功能是.NET所不能完成的。面对这样的问题, .NET互操作技术应运而生,可见需求才是技术的原动力。

        .NET互操作技术主要分为3种,P/InvokeC++ InteropCOM Interop,其中P/Invoke 主要用于调用C库函数和Windows APIC++ Interop则主要用于Managed C++调用 C++类库和核心算法库,它甚至允许托管代码和非托管代码在同一个文件中。 COM Interop主要包括正向的RCW和反向的CCW。下面以一个简单的例子对互操作中比较重要的数据封送进行简单的介绍。

 如何封送字符串(P/Invoke方式)

假设非托管代码定义如下:

void _cdecl stringMarshal( const wchar_t* inString,  wchar_t* outString, int buffersize)

{

            If(NULL != inString)

{

            wcscpy_s(outString, buffersize, inString);

}

}

这段代码编译生成的文件名为:stringMarshal.dll

在托管代码中其托管定义如下:

 [DllImport

(“stringMarshal.dll”,

CharSet = CharSet.Unicode,

CallingConvention =CallingConvention.Cdecl)

]

public extern static void stringMarshal (string inString, StringBuilder outString, int bufferSize);

 

这里有几点需要注意:

1.  在声明函数时必须要用extern修饰符,目的是为了告诉编译器此函数是外部实现的,没有方法体,因此不需要在托管代码中搜索这个函数。

2.  在声明函数时必须要用static修饰符,原因是非托管的DLL导出的非托管方法都是可以直接调用的,无需对相关的类进行实例化,大部分情况下根本就不存在类。

3. 因为非托管代码中的字符串为wchar_t*类型,所以CharSet需要设置为CharSet.Unicode

4. 因为非托管代码的调用方式为_cdecl 所以托管部分的CallingConvention需要设置为CallingConvention.Cdecl,另外这种类型的调用方式是调用方负责处理堆栈,所以支持可变类型参数函数例如printf()的互操作。

5. 输入字符串需要封装为string是因为这个字符串属于固定字符串,互操作过程中不需要变化,而输出字符串则需要封装为StringBuilder,因为这种字符串默认为具有IN/OUT属性,其内容可变,而且当字符串经常需要变化时效率高。

 

托管代码中调用非托管代码方式如下:

 private static void TestStringMarshal ()

 {

            string inString = "Wally input test string.";

            int bufferSize = inString.Length;

            StringBuilder strbd = new StringBuilder(bufferSize);

            stringMarshal (inString, strbd, bufferSize + 1);

            Console.WriteLine("Wally Input string: {0}", inString);

            Console.WriteLine("Wally output string: {0}", strbd.ToString());

}

 

总结

        本文简要的介绍了.NET托管代码和本地非托管代码的互操作技术,并对数据封送的实现细节做了简单的说明,希望对大家的技术提高有所帮助,算是抛砖引玉,期待大家在这方面写出更多更好的文章。

原文地址:https://www.cnblogs.com/pugang/p/1717206.html