1. 通过 P/Invoke 封送处理类型有哪些限制?
返回值
• 只能是小于或等于 32 位的值类型
• 没有浮点
• 参数
• 只支持封送处理直接复制到本机结构中的类型
• 可直接复制到本机结构中的类型 -> 托管和本机两种结构在内存中有相同的表示
• 非直接复制到本机结构中的类型 -> 需要进行内存转换
• 由于只有直接复制到本机结构中的类型,所以所有对象都是固定的,不会被复制
• 例外:在 VB.NET 中传递 String ByVal
• 这意味着您不能封送处理嵌套对象,因为它需要进行内存转换(非直接复制到本机结构中)
• 只能是小于或等于 32 位的值类型
• 值在堆栈中传递
• 例外:float32
• 引用
• 传递可直接复制到本机结构中的引用类型
• 将引用传递给值类型
• 这是传递 float32 的方法
• 可以传递值类型数组,本机获得指向第一个对象的指针,对象按照您期望的那样顺序排列
• String 较特殊,它传递字符数组 -> 不可变
• StringBuilder 较特殊,它传递字符数组 -> 可变(需要单独传递长度)
• 注:C# bool 只有 8 位,不等于 Win32 BOOL
• 对齐方式:默认编译器对齐(4 字节)
• Marshal.GetLastWin32Error 支持 GetLastError() 语义
• 不受支持:
• MarshalAs:不支持非直接复制到本机结构中的类型
• StructLayout:无法更改布局
• 委托 (Delegate)
• DateTime
• 只支持默认调用约定
2.如何将 byte[] 转换为 IntPtr?第一种是使用不安全的代码块来访问直接指向字节数组的指针:
unsafe { byte[] test = new byte[5]; fixed (byte* p = &test[0]) { *p = 0xff; } }也可以使用 GCHandle 来获得对象:
using System.Runtime.InteropServices; byte[] test = new byte[5]; GCHandle hObject = GCHandle.Alloc(test, GCHandleType.Pinned); IntPtr pObject = hObject.AddrOfPinnedObject(); if(hObject.IsAllocated) hObject.Free();
最后,可以这样实现:通过 LocalAlloc 创建内存块并将数据封送处理到该内存块:[DllImport("coredll.dll",SetLastError=true)] public static extern IntPtr LocalAlloc(uint uFlags, uint uBytes); [DllImport("coredll.dll",SetLastError=true)] public static extern IntPtr LocalFree(IntPtr hMem); [DllImport("coredll.dll",SetLastError=true)] public static extern IntPtr LocalReAlloc(IntPtr hMem, uint uBytes, uint fuFlags); public const uint LMEM_FIXED = 0; public const uint LMEM_MOVEABLE = 2; public const uint LMEM_ZEROINIT = 0x0040; byte[] test = new byte[5]; IntPtr p = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, (uint)test.Length); if (p == IntPtr.Zero) { throw new OutOfMemoryException(); } else { Marshal.Copy(test, 0, p, test.Length); }