平台调用中的数据封送处理
2013-01-23 18:16
155 查看
.NET互操作性入门系列(三):平台调用中的数据封送处理
2013-01-23 11:03 by Learning hard, 529 阅读, 10 评论, 收藏, 编辑本专题概要
数据封送介绍
封送Win32数据类型
封送字符串的处理
封送结构体的处理
封送类的处理
小结
一、数据封送介绍
看到这个专题时,大家的第一个疑问肯定是——什么是数据封送呢?(这系列专题中采用假设朋友的提问方式来解说概念,就是希望大家带着问题去学习本专题内容,以及大家在平时的学习过程中也可以采用这个方式,个人觉得这个方式可以使自己学习效率有所提高,即使这样在学习的过程可能会显得慢了,但是这种方式会对你所看过的知识点会有一个更深的印象。远比看的很快,最后却发现记住的没多少强,在这里分享下这个学习方式,认为可以接受的朋友可以在平时的学习中可以尝试下的,如果觉得不好的话,相信大家肯定也会有自己更好的学习方式的。)对于这个问题的解释是,数据封送是——在托管代码中对非托管函数进行互操作时,需要通过方法的参数和返回值在托管内存和非托管内存之间传递数据的过程,数据封送处理的过程是由CLR(公共语言运行时)的封送处理服务(即封送拆送器)完成的。
封送拆送器主要进行3项任务:
将数据从托管类型转换为非托管类型,或从非托管类型转换为托管类型
将经过类型转换的数据从托管代码内存复制到非托管内存,或从非托管内存复制到托管内存
调用完成后,释放封送处理过程中分配的内存
二、封送Win32数据类型
对非托管代码进行互操作时,一定会有数据的封送处理。然而封送时需要处理的数据类型分为两种——可直接复制到本机结构中的类型(blittable)和非直接复制到本机结构中的类型(non-bittable)。下面就这两种数据类型分别做一个介绍。
2.1 可直接复制到本机结构中的类型
由于在托管代码和非托管代码中,数据类型在托管内存和非托管内存的表示形式不一样,因为这样的原因,所以我们需要对数据进行封送处理,以至于在托管代码中调用非托管函数时,把正确的传入参数传递给非托管函数和把正确的返回值返回给托管代码中。然而,并不是所有数据类型在两者内存的表现形式不一样的,这时候我们把在托管内存和非托管内存中有相同表现形式的数据类型称为——可直接复制到本机结构中的类型,这些数据类型不需要封送拆送器进行任何特殊的处理就可以在托管和非托管代码之间传递, 下面列出一些课直接复制到本机结构中的简单数据类型:
Windows 数据类型 | 非托管数据类型 | 托管数据类型 | 托管数据类型解释 |
BYTE/Uchar/UInt8 | unsigned char | System.Byte | 无符号8位整型 |
Sbyte/Char/Int8 | char | System.SByte | 有符号8位整型 |
Short/Int16 | short | System.Int16 | 有符号16位整型 |
USHORT/WORD/UInt16/WCHAR | unsigned short | System.UInt16 | 无符号16位整型 |
Bool/HResult/Int/Long | long/int | System.Int32 | 有符号32位整型 |
DWORD/ULONG/UINT | unsigned long/unsigned int | System.UInt32 | 无符号32位整型 |
INT64/LONGLONG | _int64 | System.Int64 | 有符号64位整型 |
UINT64/DWORDLONG/ULONGLONG | _uint64 | System.UInt64 | 无符号64位整型 |
INT_PTR/hANDLE/wPARAM | void*/int或_int64 | System.IntPtr | 有符号指针类型 |
HANDLE | void* | System.UIntPtr | 无符号指针类型 |
FLOAT | float | System.Single | 单精度浮点数 |
DOUBLE | double | System.Double | 双精度浮点数 |
(1) 数据元素都是可直接复制到本机结构中的一元数组,如整数数组,浮点数组等
(2)只包含可直接复制到本机结构中的格式化值类型
(3)成员变量全部都是可复制到本机结构中的类型且作为格式化类型封送的类
上面提到的格式化指的是——在类型定义时,成员的内存布局在声明时就明确指定的类型。在代码中用StructLayout属性修饰被指定的类型,并将StructLayout的LayoutKind属性设置为Sequential或Explicit,例如:
下面直接通过GetVersionEx函数进行封送类的处理的例子,具体代码如下:
using System; using System.ComponentModel; using System.Runtime.InteropServices; namespace 封送类的处理 { class Program { // 对GetVersionEx进行托管定义 // 由于类的定义中CSDVersion为String类型,String是非直接复制到本机结构类型, // 所以封送拆送器需要进行复制操作。 // 为了是非托管代码能够获得在托管代码中对象设置的初始值(指的是OSVersionInfoSize字段,调用函数前首先初始化该值), // 所以必须加上[In]属性;函数返回时,为了将结果复制到托管对象中,必须同时加上 [Out]属性 // 这里不能是用ref关键字,因为 OsVersionInfo是类类型,本来就是引用类型,如果加ref 关键字就是传入的为指针的指针了,这样就会导致调用失败 [DllImport("Kernel32", CharSet = CharSet.Unicode, EntryPoint = "GetVersionEx")] private static extern Boolean GetVersionEx_Struct([In, Out] OSVersionInfo osVersionInfo); // 获得操作系统信息 private static string GetOSVersion() { // 定义一个字符串存储操作系统信息 string versionName = string.Empty; // 初始化一个类对象 OSVersionInfo osVersionInformation = new OSVersionInfo(); // 调用Win32函数 Boolean result = GetVersionEx_Struct(osVersionInformation); if (!result) { // 如果调用失败,获得最后的错误码 int errorcode = Marshal.GetLastWin32Error(); Win32Exception win32Exc = new Win32Exception(errorcode); Console.WriteLine("调用失败的错误信息为: " + win32Exc.Message); // 调用失败时返回为空字符串 return string.Empty; } else { Console.WriteLine("调用成功"); switch (osVersionInformation.MajorVersion) { // 这里仅仅讨论 主版本号为6的情况,其他情况是一样讨论的 case 6: switch (osVersionInformation.MinorVersion) { case 0: if (osVersionInformation.ProductType == (Byte)0) { versionName = " Microsoft Windows Vista"; } else { versionName = "Microsoft Windows Server 2008"; // 服务器版本 } break; case 1: if (osVersionInformation.ProductType == (Byte)0) { versionName = " Microsoft Windows 7"; } else { versionName = "Microsoft Windows Server 2008 R2"; } break; case 2: versionName = "Microsoft Windows 8"; break; } break; default: versionName = "未知的操作系统"; break; } return versionName; } } static void Main(string[] args) { string OS = GetOSVersion(); Console.WriteLine("当前电脑安装的操作系统为:{0}", OS); Console.Read(); } } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] public class OSVersionInfo { public UInt32 OSVersionInfoSize = (UInt32)Marshal.SizeOf(typeof(OSVersionInfo)); public UInt32 MajorVersion = 0; public UInt32 MinorVersion = 0; public UInt32 BuildNumber = 0; public UInt32 PlatformId = 0; // 此属性用于表示将其封送成内联数组 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] public string CSDVersion = null; public UInt16 ServicePackMajor = 0; public UInt16 ServicePackMinor = 0; public UInt16 SuiteMask = 0; public Byte ProductType = 0; public Byte Reserved; } }
运行结果还是和上面使用结构体定义的一样,还是附上下图吧:
六、小结
本专题主要介绍了几种类型的数据封送处理, 对于封送处理的一句话概括就是——保证托管代码中定义的数据在内存中的布局与非托管代码中的内存布局相同,专题中也列出了一些简单类型在非托管代码和托管代码中定义的对应关系,对于一些没有列出来的指针类型或回调函数等可以使用万能的IntPtr类型在托管代码中定义.然而本专题只是对数据封送做一个入门的介绍, 要真真掌握数据封送处理还要考虑很多其他的因素,这个就需要大家在平时工作中积累的。
相关文章推荐
- .NET互操作性入门系列(三):平台调用中的数据封送处理
- C# 互操作性入门系列(三):平台调用中的数据封送处理
- C# 互操作性入门系列(三):平台调用中的数据封送处理
- Hadoop系列之一:大数据存储及处理平台产生的背景
- jquery调用后台方法返回json数据的处理
- Linux平台php命令行程序处理管道数据的方法
- OpenRS—— 开放式遥感数据处理与服务平台 OpenRS-Cloude:基于MapReduce的并行遥感处理系统
- 程序中,调用Bison和Flex结合的小例子(语法分析中处理数据)
- 140-基于双TI DSP TMS320C6670+XC7K480T的6UCPCI Express高速数据处理平台
- Enterprise Library3.1 使用数据访问模块时,调用Microsoft.Practices.EnterpriseLibrary.Data报出源文件与当前应用程序不一致和创建dataconfiguration的配置节处理程序出错
- 大海捞枕木:大数据处理平台的衍变
- J2ME平台A-RPG游戏地图数据处理
- Spark+Jupyter=在线文本数据处理逻辑测试平台
- JQuery调用webservice,返回的json数据和XML数据的处理方法
- kettle调用java代码处理数据
- 实时处理流数据平台-InfoSphere Streams简介
- 日均请求量百亿级数据处理平台的容器云实践
- 前端Js中利用Ajax异步调用后台处理数据