欢迎来到.net学习网

欢迎联系站长一起更新本网站!QQ:879621940

您当前所在位置:首页 »  .NET本质论第一卷:公共语言运行库教程 » 正文

本教程章节列表

非托管模块-P/Invoke和参数副本

创建时间:2013年06月04日 23:41  阅读次数:(3354)
分享到:
P/Invoke引擎有特殊的、用于处理外部DLL错误的工具。由于P/Invoke引擎本身就进行系统调用,所以,由GetLastError返回的错误代码未必就是精确的。为了保存这个错误代码,映射到调用SetLastError函数的P/Invoke方法必须标记为SetLastError=true。在P/Invoke调用之后,托管代码可以使用  System.Runtime.InteropServices.Marshal.GetLastWin32Error方法再现错误代码。考察下面的c#程序,它通过P/Invoke调用closeHandle函数:
using System;
using System.Runtime.InteropServices;

class App{
[DLLImport(“kernel32.dll”,SetLastError=true)]
static extern bool CloseHandle(IntPtr handle);

static void Main()
{
if(!CloseHandle(IntPtr.Zero)){
Console.WriteLine(“Error:{0}”,Marshal.GetLastWin32Error());
}
}
}

注意,在DllImport特性中,SetLastError=true。在本例中,程序将打印出下面的消息:
Error:6
这个消息对应于Win32错误代码ERROR_INV ALID_HANDLE。如果没有设置SetLastError=true,程序将打印出下面的消息:
Error:126

这个消息对应于Win32错误代码ERROR_MOD_NOT_FOUND。除非将方法标记为SetLastError-true,否则, P/Invoke引擎将不会保存CloseHandle函数所设置的值(ERROR_INVALID_HANDLE)。

另一个常用错误报告技术就是使用数字的HRESULT,P/Invoke支持两种选项,用于处理返回HRESULT的函数。默认情况下,P/Invoke将HRESULT当作由函数返回的32位整型数,需要程序员手工进行测试失败。调用这类函数的更为便捷的方式是传递PreserveBig=false参数给DllImport特性。这便告诉P/Invoke层,将那个32位整型数当作COM HRESULT,并且在失败时抛出COMException异常。

为了便于理解PreserveSig选项,考察一个传统的C DLL,其中,公开下面的函数(伪IDL)。
HERSULT__STDCALL CoSomeAPI([in] log a1,[out,retval] short *pa2);
无论有没有PreserveSig选项,你都可以导入这个函数。下面的导入使用了PerserveSig=true,这也是P/Invoke默认的情形:
//将HRESULT作为函数结果返回
[DllImport(“ole32.dll”,EntryPoint=”CoSomeAPI”)]
public extern static int CoSomeAPI1(int al,out short a2);

由于声明的缘故,调用者必须手工检查方法失败与否的结果。相反,下面的导入则消除了PreserveSig选项:
//在失败的HRESULT上抛出COMException异常
[DllImport(“ole32.dll”,EntryPoint=”CoSomeAPI”,PerserveSig=false)]
public extern static short CoSomeAPI2(int a1);

这个代码将通知p/Invoke引擎自动检查HRESULT,并将失败的调用映射到COMException类型上。注意,在OLE32Wrapper.CoSomeAPI32的情形下,方法返回short值,它对应于底层函数的最终[out,retval]参数。如果P/Invoke方法被声明返回voia,P/Invoke层则可能假定指定的参数列表与底层的本机定义精确地匹配,这个映射只有当PreserveSig选项设为false时才会发生。

如前面所谈到的,每个PreaerveSig选项方法有两个签名。一个托管的签名:另一个则是外部DLL所期望的签名。这可以从图10.3看出。由于依赖参数的类型.P/Invoke引擎可能(也可能不)需要执行内存中的转换。在拷贝时不需要转换的类型称为blittable类型,需要转换的类型则称为nonblittable类型。显而易见,当只使用blittable参数时,P/Invoke调用的性能将会很高。这是因为建立第二个堆栈帧只用一条IA-32指令就够了.但使用nonblittable参数就不是这样了。
 
表10.2是基本blittable类型和nonblittable类型的列表,以及它们在C/IDL的默认映射。你可以使用System.Runtime.InteropServices.MarshalAs特性。
P/Invoke和参数副本

基于参数到参数(或者字段到字段)的方式,覆盖这些默认映射。当使用P/Invoke封送(marshal)堆栈帧时,这个特性标明使用哪个非托管类型。如表10.3所示,MarshalAs特性需要UnmanagedType类型的参数。UnmanagedType类型是一个枚举类型,其值与P/Invoke封道器(marshaler)知道如何处理的类型相对应。通过将MarshaAs特性应用到字段的参数上,可以指定P/Invoke应该使用哪种外部类型:通过增加其他的参数到MarshalAs特性上,你可以编写数组的处理.包括使用SizeParamIndex参数用于COM风格[size_is]的支持;此外,通过使用MarshalType参数指定自定义的封送器,可以扩展P/Invoke封送器。自定义的封送器必须实现ICustomMarshaler接口,它允许封送器存托管类犁的实例与原始内存之间进行底层转换。,
来源:.net学习网
说明:所有来源为 .net学习网的文章均为原创,如有转载,请在转载处标注本页地址,谢谢!
【编辑:Wyf】

打赏

取消

感谢您的支持,我会做的更好!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

最新评论

共有评论0条
  • 暂无任何评论,请留下您对本文章的看法,共同参入讨论!
发表评论:
留言人:
内  容:
请输入问题 43+58=? 的结果(结果是:101)
结  果: