多年来在程序设计领域微软技术一直引领着每个时代在每一代的技术背后都有相关的动机而这些动机及其实现细节往往是大部分的程序设计课程没有涉及的但是对于我们理解相关的技术又十分重要本文将对几种相关技术和隐藏在动机背后的细节做简要的阐述算是抛砖引玉
COM技术于年首次发布称得上是微软技术的一个里程碑其意义在于使组件编程化得以实现COM技术最初的思想起源于将类做成可重用的二进制组件把类的实现和接口分离以便把类的实现封装到二进制防火墙的背后而这道防火墙以VPTR和VTBL的形式保证了这个防火墙的不变性……NET技术则在语言层面上支持了基于组件的程序设计相对于COM技术NET技术就是为基于组件的程序设计而生的所以其编码效率和语言可读性要高出COM一个等级但是由于年以后的十多年时间里人们利用基于COM的组件程序设计技术开发了大量的高复杂度高可用性的代码并且他们运行良好效率极高如果用NET技术重写这些代码简直就是得不偿失加之在NET的实现过程中并不是所有的Windows API都被重写有些功能是NET所不能完成的面对这样的问题 NET互操作技术应运而生可见需求才是技术的原动力
NET互操作技术主要分为种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)}这段代码编译生成的文件名为stringMarshaldll
在托管代码中其托管定义如下
[DllImport(stringMarshaldllCharSetCharSet = CharSetUnicodeCallingConventionCallingConvention =CallingConventionCdecl)
] public extern static void stringMarshal (string inString StringBuilder outString int bufferSize)这里有几点需要注意
在声明函数时必须要用extern修饰符目的是为了告诉编译器此函数是外部实现的没有方法体因此不需要在托管代码中搜索这个函数
在声明函数时必须要用static修饰符原因是非托管的DLL导出的非托管方法都是可以直接调用的无需对相关的类进行实例化大部分情况下根本就不存在类
因为非托管代码中的字符串为wchar_t*类型所以CharSet需要设置为CharSetUnicode
因为非托管代码的调用方式为_cdecl 所以托管部分的CallingConvention需要设置为CallingConventionCdecl另外这种类型的调用方式是调用方负责处理堆栈所以支持可变类型参数函数例如printf()的互操作
输入字符串需要封装为string是因为这个字符串属于固定字符串互操作过程中不需要变化而输出字符串则需要封装为StringBuilder因为这种字符串默认为具有IN/OUT属性其内容可变而且当字符串经常需要变化时效率高
托管代码中调用非托管代码方式如下
private static void TestStringMarshal ()
{ string inString = Wally input test stringint bufferSize = inStringLengthStringBuilder strbd = new StringBuilder(bufferSize)stringMarshal (inString strbd bufferSize + )ConsoleWriteLine(Wally Input string {} inString)ConsoleWriteLine(Wally output string {} strbdToString())}总结
本文简要的介绍了NET托管代码和本地非托管代码的互操作技术并对数据封送的实现细节做了简单的说明希望对大家的技术提高有所帮助算是抛砖引玉期待大家在这方面写出更多更好的文章