DllImport的具体用法
如果您无法肯定哪个 DLL 定义了您要使用的 Windows API 函数,Platform SDK 文档将为您提供最好的帮助资源。在
Windows API 函数主题文字临近结尾的位置,SDK 文档指定了 C 应用程序要使用该函数必须链接的 .lib
文件。在几乎所有的情况下,该 .lib 文件具有与定义该函数的系统 DLL 文件相同的名称。例如,如果该函数需要 C 应用程序链接到
Kernel32.lib,则该函数就定义在 Kernel32.dll 中。您可以在 MessageBeep 中找到有关
MessageBeep 的 Platform SDK 文档主题。在该主题结尾处,您会注意到它指出库文件是 User32.lib;这表明
MessageBeep 是从 User32.dll 中导出的。
可选的 DllImportAttribute 属性
除了指出宿主 DLL 外,DllImportAttribute
还包含了一些可选属性,其中四个特别有趣:EntryPoint、CharSet、SetLastError 和
CallingConvention。
EntryPoint 在不希望外部托管方法具有与 DLL 导出相同的名称的情况下,可以设置该属性来指示导出的 DLL
函数的入口点名称。当您定义两个调用相同非托管函数的外部方法时,这特别有用。另外,在 Windows
中还可以通过它们的序号值绑定到导出的 DLL 函数。如果您需要这样做,则诸如“#1”或“#129”的 EntryPoint 值指示
DLL 中非托管函数的序号值而不是函数名。
CharSet 对于字符集,并非所有版本的 Windows 都是同样创建的。Windows 9x 系列产品缺少重要的 Unicode
支持,而 Windows NT 和 Windows CE 系列则一开始就使用 Unicode。在这些操作系统上运行的 CLR
将Unicode 用于 String 和 Char 数据的内部表示。但也不必担心 — 当调用 Windows 9x API
函数时,CLR 会自动进行必要的转换,将其从 Unicode转换为 ANSI。
如果 DLL 函数不以任何方式处理文本,则可以忽略 DllImportAttribute 的 CharSet 属性。然而,当 Char
或 String 数据是等式的一部分时,应该将 CharSet 属性设置为 CharSet.Auto。这样可以使 CLR 根据宿主
OS 使用适当的字符集。如果没有显式地设置 CharSet 属性,则其默认值为
CharSet.Ansi。这个默认值是有缺点的,因为对于在 Windows 2000、Windows XP 和 Windows NT®
上进行的 interop 调用,它会消极地影响文本参数封送处理的性能。
应该显式地选择 CharSet.Ansi 或 CharSet.Unicode 的 CharSet 值而不是使用
CharSet.Auto 的唯一情况是:您显式地指定了一个导出函数,而该函数特定于这两种 Win32 OS
中的某一种。ReadDirectoryChangesW API 函数就是这样的一个例子,它只存在于基于 Windows NT
的操作系统中,并且只支持 Unicode;在这种情况下,您应该显式地使用 CharSet.Unicode。
有时,Windows API 是否有字符集关系并不明显。一种决不会有错的确认方法是在 Platform SDK 中检查该函数的 C
语言头文件。(如果您无法肯定要看哪个头文件,则可以查看 Platform SDK 文档中列出的每个 API
函数的头文件。)如果您发现该 API 函数确实定义为一个映射到以 A 或 W
结尾的函数名的宏,则字符集与您尝试调用的函数有关系。Windows API 函数的一个例子是在 WinUser.h 中声明的
GetMessage API,您也许会惊讶地发现它有 A 和 W 两种版本。
SetLastError 错误处理非常重要,但在编程时经常被遗忘。当您进行 P/Invoke 调用时,也会面临其他的挑战 —
处理托管代码中 Windows API 错误处理和异常之间的区别。我可以给您一点建议。
如果您正在使用 P/Invoke 调用 Windows API 函数,而对于该函数,您使用 GetLastError
来查找扩展的错误信息,则应该在外部方法的 DllImportAttribute 中将 SetLastError 属性设置为
true。这适用于大多数外部方法。
这会导致 CLR 在每次调用外部方法之后缓存由 API 函数设置的错误。然后,在包装方法中,可以通过调用类库的
System.Runtime.InteropServices.Marshal 类型中定义的
Marshal.GetLastWin32Error 方法来获取缓存的错误值。我的建议是检查这些期望来自 API
函数的错误值,并为这些值引发一个可感知的异常。对于其他所有失败情况(包括根本就没意料到的失败情况),则引发在
System.ComponentModel 命名空间中定义的 Win32Exception,并将
Marshal.GetLastWin32Error 返回的值传递给它。如果您回头看一下图 1 中的代码,您会看到我在 extern
MessageBeep 方法的公共包装中就采用了这种方法。
CallingConvention 我将在此介绍的最后也可能是最不重要的一个 DllImportAttribute 属性是
CallingConvention。通过此属性,可以给 CLR
指示应该将哪种函数调用约定用于堆栈中的参数。CallingConvention.Winapi
的默认值是最好的选择,它在大多数情况下都可行。然而,如果该调用不起作用,则可以检查 Platform SDK
中的声明头文件,看看您调用的 API 函数是否是一个不符合调用约定标准的异常 API。
通常,本机函数(例如 Windows API 函数或 C- 运行时 DLL
函数)的调用约定描述了如何将参数推入线程堆栈或从线程堆栈中清除。大多数 Windows API
函数都是首先将函数的最后一个参数推入堆栈,然后由被调用的函数负责清理该堆栈。相反,许多 C-运行时 DLL
函数都被定义为按照方法参数在方法签名中出现的顺序将其推入堆栈,将堆栈清理工作交给调用者。
幸运的是,要让 P/Invoke 调用工作只需要让外围设备理解调用约定即可。通常,从默认值
CallingConvention.Winapi 开始是最好的选择。然后,在 C 运行时 DLL
函数和少数函数中,可能需要将约定更改为 CallingConvention.Cdecl。
DllImport的具体用法的更多相关文章
- System.ServiceProcess与System.Configuration.Install命名空间的介绍
System.ServiceProcess 命名空间提供用于实现.安装和控制 Windows 服务应用程序的类.服务是长期运行的可执行文件,其运行没有用户界面 System.ServiceProces ...
- C#6.0语言规范(十) 类
类是可以包含数据成员(常量和字段),函数成员(方法,属性,事件,索引器,运算符,实例构造函数,析构函数和静态构造函数)和嵌套类型的数据结构.类类型支持继承,这是一种派生类可以扩展和专门化基类的机制. ...
- C# DllImport的用法
大家在实际工作学习C#的时候,可能会问:为什么我们要为一些已经存在的功能(比如Windows中的一些功能,C++中已经编写好的一些方法)要重新编写代码,C#有没有方法可以直接都用这些原本已经存在的功能 ...
- .Net中C#的DllImport的用法
大家在实际工作学习C#的时候,可能会问:为什么我们要为一些已经存在的功能(比如 Windows中的一些功能,C++中已经编写好的一些方法)要重新编写代码,C#有没有方法可以直接都用这些原本已经存在的功 ...
- 【转帖】.Net中C#的DllImport的用法
在 C# 中通过 P/Invoke 调用Win32 DLL http://msdn.microsoft.com/zh-cn/library/aa686045.aspx 大家在实际工作学习C#的时候 ...
- DLLImport的用法C#
它来调用WIN32的API或者调用一下C或C++编写的DLL.使用实例:将编译好的C++ DLL拷贝到BIN目录(DLLImport会从程序启动目录BIN开始查找相应名称的DLL,未找到则转至syst ...
- C#中DllImport用法汇总
最近使用DllImport,从网上google后发现,大部分内容都是相同,又从MSDN中搜集下,现将内容汇总,与大家分享. 大家在实际工作学习C#的时候,可能会问:为什么我们要为一些已经存在的功能(比 ...
- C#中DllImport用法
http://blog.csdn.net/u011981242/article/details/52622923 http://www.jb51.net/article/46384.htm 读取身份证 ...
- C# 关键字extern用法
修饰符用于声明在外部实现的方法.extern 修饰符的常见用法是在使用 Interop 服务调入非 托管代码时与 DllImport 属性一起使用:在这种情况下,该方法还必须声明为 static,如下 ...
随机推荐
- 4月12日学习笔记——jQuery事件
下面是在 jQuery 中最常使用的 bind()方法举例:$("#testDiv4").bind("click", showMsg); 我们为 id 是 te ...
- HashMap 读后感
HashMap是一个哈希表,内部通过链地址法解决哈希冲突.内部用Entry数组保存数据,每个Entry是一个单向链表. HashMap不保证插入的顺序,线程不同步,允许null 下面是几个重要点: 保 ...
- LLVM language 参考手册 翻译停止相关
再翻译LLVM language 参考手册的时候,个人感觉很多东西都不是很懂,因此打算学习完编译原理后再去继续研究翻译,多有不便望见谅
- linux 安装sysstat使用iostat、mpstat、sar、sa(转载)
使用yum安装 #yum install sysstat sysstat的安装包是:sysstat-5.0.5-1.i386.rpm,装完了sysstat-5.0.5-1.i386.rpm后 就会有i ...
- Win7开始菜单之【附件】不全解决方案
Win7开始菜单之[附件]不全解决方案 1:打开你的[开始]菜单,转到附件,如果你发现你的附件里的“附件”不是那么全的话,如下图:来吧,我告诉你如何恢复它到最初的模样……哦,或许如果你不急于恢复的话, ...
- pyQuery的安装
1. 直接通过pip安装 你会发现lxml怎么搞都报错,后来单独先安装libxml2和libxslt pip是找不到这个包的,只好百度.发现有很多的例子的解决方案.后来发现了个实用的. 2. 先安装l ...
- 移植Oracle procedure 到 postgresql
1.登录postgresql psql -h 192.168.137.131 -p 5432 postgres satusc@6789#JKL 2.创建用户 CREATE USER name thun ...
- 一个简单WPF登陆界面,包含记住密码,自动登录等功能,简洁美观
简介:这是一个自己以前用WPF设计的登陆界面,属于一个实验性的界面窗体,如果用于产品还很有不足.但也是有一点学习价值.后台代码略有复杂,但基本上都有注释 分类,略有代码经验的一般都能看懂. 登陆界面外 ...
- 开发流程习惯的养成—TFS简单使用
才开始用,所以是个很基础的介绍,欢迎大家一起交流学习 一.追本溯源 讲到开发流程,还要从敏捷开始,因为敏捷才有了开发流程的重视,整个流程也是按照敏捷的思想进行的,这里不再叙述敏捷的定义 敏捷的流程(个 ...
- WPF-控件-编辑圆角TextBox
使用模板 代码如下: <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xm ...