背景:#EDF0F5 #FAFBE6 #FFF2E2 #FDE6E0 #F3FFE1 #DAFAF3 #EAEAEF 默认  
阅读新闻

C# 2.0中P/Invoke支持的几处增强

[日期:2007-11-19] 来源:博客堂  作者: [字体: ]

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类型实际占用多少内存,也就是说类型实例的大小必须是固定的才行)。

阅读:
录入:木鸟

推荐 】 【 打印
上一篇:P/Invoke使用Win32非托管函数
下一篇:.Net线程问题解答
相关新闻      
本文评论       全部评论
发表评论


点评: 字数
姓名:

  • 尊重网上道德,遵守中华人民共和国的各项有关法律法规
  • 承担一切因您的行为而直接或间接导致的民事或刑事法律责任
  • 本站管理人员有权保留或删除其管辖留言中的任意内容
  • 本站有权在网站内转载或引用您的评论
  • 参与本评论即表明您已经阅读并接受上述条款