博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

【转载来的】C# 2.0中P/Invoke支持的几处增强

Posted on 2008-05-29 16:54  wuhang  阅读(228)  评论(0编辑  收藏  举报

1、Array in stack

对于这样的struct:typedef struct { int XY[2]; } Point2D;

要在.NET为一个非托管函数传递这样一个结构体,原来得这样定义:

struct Point2D
{
 [MarshalAs(UnmanagedType.ByValArray, SizeConst=2)]
 public int[] XY;
}

  现在可以这么写(不过得用unsafe上下文):

unsafe struct Point2D
{
public fixed int XY[2];
}

 
不过这个功能还非常有限,不知道是出于什么原因考虑,只允许在strcut里面定义这样的数组,并且只能使用bool, byte,  short, int, long, char, sbyte, ushort, uint, ulong, float和double这样的 primitive类型。

也可以把数组作为局部变量分配在堆栈上,只是语法不太一样,那就是stackalloc关键字:int* fib = stackalloc int[100];,也要unsafe上下文。这可以提高不少效率。这是.NET 1.x就有的功能,只是似乎没人用这个

PS. 对于只允许使用primitive类型,我认为是没道理的,最起码应该允许所有值类型的栈内数组。设计者们为啥这么考虑呢?怕堆栈溢出?据我测试.NET的堆栈空间也是1M左右,大部分情况下这么大的栈空间都被浪费了。

2、Function pointer as a return value

在.NET 1.x做P/Invoke时,对于那些回调函数,可以使用Delegate类型的参数作为函数指针传入。但有些非托管函数的返回值也是个函数指针,此时.NET 1.1变得无能为力,要调用这个函数,你得用native代码再写个包装,总之很麻烦。

.NET 2.0的System.Runtime.InteropServices.Marshal类为此需求新增了两个方法:

public static Delegate GetDelegateForFunctionPointer (
IntPtr ptr,
Type t
);

public static IntPtr GetFunctionPointerForDelegate (
Delegate d
);

3、Marshal过程支持更多的类型

这是一个很细的问题,比如这样一个非托管struct:

typedef struct { Point2D XYZ[3]; } Point2DX3;

  对应到C#,你也许会这样写:

struct Point2D
{
 [MarshalAs(UnmanagedType.ByValArray, SizeConst=2)]
 public int[] XY;
}

struct Point2DX3
{
 [MarshalAs(UnmanagedType.ByValArray, SizeConst=3)]
 public Point2D[] XYZ;
}

  事实上这是不可行的。在.NET 1.x,结构体内嵌定长数组的类型必须是primitive类型,否则不能进行marshal过程。事实上调用Marshal.SizeOf时,会弹出异常说“无法得到大小”云云

.NET 2.0把这个问题给改了(与其说是个增强,还不如说是修正了这个bug),Marshal.SizeOf(typeof(Point2DX3))现在可以正常运行,输出24 == 3 * 2 * sizeof(int)。

让Marshal.SizeOf正常工作非常重要,得不到对象的大小,内存对齐都无法保证,marshal过程根本就不可行。

此外,正常工作的Marshal.SizeOf可以使这种“序列化”方式也总能正常工作(这在我以前的blog贴过,这里是Generic版本):

unsafe class BinarySerializer
{
public static byte[] Struct2Bytes<T>(T obj)
{
int size = Marshal.SizeOf(obj);
byte[] bytes = new byte[size];
fixed (byte* pb = &bytes[0])
{
Marshal.StructureToPtr(obj, (IntPtr)pb, true);
}
return bytes;
}

public static T Bytes2Struct<T>(byte[] bytes)
{
fixed (byte* pb = &bytes[0])
{
return (T)Marshal.PtrToStructure((IntPtr)pb, typeof(T));
}
}
}
和.NET内置的序列化机制相比,这个方案效率要高得多。和.NET内置的序列化机制相似,这里需要对象或结构体内部的所有成员都能使用 Marshal.SizeOf得到其大小,对应于“对象或结构体内部的所有成员都带有[Serializable]特性或者实现了 ISerializable接口”。

PS. 这个序列化方案效率虽高,但不如.NET内置的方案可靠。.NET的编译器可以检查一个 类型能否支持序列化,而这个方案得由程序员判断Marshal.SizeOf能否工作(比如Marshal.SizeOf绝对不会知道string类型实 际占用多少内存,也就是说类型实例的大小必须是固定的才行)。