您的位置:首页 > 其它

平台调用中的数据封送处理

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,例如:

操作系统

版本号

Windows 8

6.2

Windows 7

6.1

Windows Server 2008 R2

6.1

Windows Server 2008

6.0

Windows Vista

6.0

Windows Server 2003 R2

5.2

Windows Server 2003

5.2

Windows XP

5.1

Windows 2000

5.0

五、封送类的处理

下面直接通过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类型在托管代码中定义.然而本专题只是对数据封送做一个入门的介绍, 要真真掌握数据封送处理还要考虑很多其他的因素,这个就需要大家在平时工作中积累的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: