NET 互操作(一) ——平台调用 前言:由于历史原因,.NET 出现之前,开发人员已经编写了大量经历严格测试的非托管 代码。它们以 C 库函数、C++类库已经 COM 组件的形式存在于应用程序和框架之中。但是, 由于在非托管和托管对象模型之间,数据类型、方法签名和错误处理机制存在很大差异,从而 是两种编程模型之间大门互用和移植便得很复杂。 因此,在相当长的时间内,开发人员(尤其是 PC
软件开发人员),必须面对.NET 与非托管 代码长期共存的局面。在很多情况下,重用已有的非托管代码成了最有效、可行的解决
方案。 总论:
NET 提供 3 类主要互操作技术: 平台调用技术(P/Invoke): 主要用于处理在托管代码中调用 C 库函数及 Win32 API 函数等 非托管函数的情形。 C++ Interop: 适用于在托管代码与 C++类库、核心算法库之间进行高效、灵活的互操作过 程。一方面托管代码可以通过包装类机制使用 C++类库,另一方面非托管代码可以通 过 包装模板机制使用托管对象。 COM Interop:该技术用于处理托管代码与 COM 之间的交互过程。托管代码通过运行库 可调用包装 (RCW) 使用非托管 COM 组件。 反过来, 非托管 COM 客 户端可以通过 COM 可调用包装(CCW)使用托管程序集。
? ?
?
托管环境
平台调用技术 P/Invoke
非托管环境
C库函数 Win32 API
.NET应用
程序 C++ Interop 托管包装类
C++类库 核心算法库
COM Interop RCW/CCW
COM组件
图 1:NET 提供 3 类主要互操作技术 由于不同的非托管对象,其
设计和运行机制等存在很多差异。因此,托管代码与这些非托管对 象进行交互操作时,在数据类型处理、错误处理机制、创建和销毁对象 的规则以及互操作方 法上,都需要根据不同的情况,分别进行不同的处理。 平台调用 一、 基础知识 平台调用技术(P/Invoke): 主要用于处理在托管代码中调用 C/C++库函数及 Win32 API 函 数等非托管函数的情形 一、基本要素
一个简单例子: C++声明: extern "C" __declspec(dllexport) int Multiply(int factorA, int factorB); 实现: int Multiply(int factorA, int factorB) { return factorA * factorB; } 托管代码(C#)调用此非托管声明: class Invoker { //声明非托管函数 [DllImport("Interop.dll",EntryPoint = "Multiply",CharSet = CharSet.Ansi)] static extern int Multiply(int factorA, int factorB); } 总结: (声明托管函数) 1.函数声明: extern 修饰符和 static 修饰符 2.DllImport 属性 (
常用) 指定动态库:指明平台要调用的 dll 名称,此项不可缺少。 指定入口点: EntryPoint 字段按名称或序号指定 DLL 函数, 可以使用与原 dll 中不同名称, 不填此项,默认为跟原函数名称一致。 指定字符集:CharSet 字段控制字符串封送处理并确定平
台调用在 DLL 中查找函数名的方 式。有窄版本 (ANSI) 和宽版本 (Unicode)。MSDN 如下说明: 成员名 称 Ansi Auto 说明 以多字节字符串的形式封送字符串。 针对目标操作
系统适当地自动封送字符串。在 Windows NT、Windows 2000、 Windows XP 和 Windows Server 2003 系列上默认值为 Unicode;在 Windows 98 和 Windows Me 上默认值为 Ansi。尽管公共语言运行库默认值为 Auto, 使用语言可重写此默认值。例如,默认情况下,C# 将所有方法和类型都标记 为 Ansi。 此值已过时,它与 CharSet.Ansi 具有相同的行为。
None
Unicode 以 Unicode 2 字节字符形式封送字符串。
指定调用约定:CallingConvention 字 段指定调用在非托管代码中实现的方法所需的调用约 定。动态链接库导出的一般有两种调用协议,__stdcall 和_cdecl。__cdecl 是 C/C++和 MFC 程序默认使用的调用约定:采用__cdecl 约定时,函数参数按照从右到左的顺序入栈,并且由 调用函数者把参数弹出栈以清理堆栈。因 此,实现可变参数的函数只能使用该调用约定。
__stdcall 调用约定用于调用 Win32 API 函数。采用