听香水榭

半壁草房待明月,一盏清茗酬知音
  首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

Microsoft .NET CF 与非托管代码交互注意事项

Posted on 2005-08-19 10:34  清雷  阅读(1093)  评论(0编辑  收藏  举报
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);
}