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,如下 ...
随机推荐
- HDU1106 排序
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1106 Problem Description 输入一行数字,如果我们把这行数字中的‘5’都看成空格 ...
- MyBatis用嵌套ResultMap实现一对多映射
我的技术博客经常被流氓网站恶意爬取转载.请移步原文:http://www.cnblogs.com/hamhog/p/3959451.html,享受整齐的排版.有效的链接.正确的代码缩进.更好的阅读体验 ...
- WPF中XAML转义字符
字符 转义字符 备注 & (ampersand) & 这个没什么特别的,几乎所有的地方都需要使用转义字符 > (greater-than character) > 在属性( ...
- Android emulator warning----Emulator window was out of view and was recentred
最近在打开Android emulator时,总会提示“Emulator window was out of view and was recentred ”,然后无法打开模拟器,但是可以使用Win7 ...
- ASP.NET MVC4学习笔记之总体概述
断断续续使用ASP.NET MVC框架也有一年多了,也算积累了一些经验,唉,一直想写一些笔记好好总结一下,人太懒不想动笔,今天终于决定开始.希望自己能坚持下去. 这篇文章大体介绍ASP.NET MVC ...
- ApacheBench 使用教程
ab压力测试 ab是Apache超文本传输协议(HTTP)的性能测试工具.其设计意图是描绘当前所安装的Apache的执行性能. 主要是显示你安装的Apache每秒可以处理多少个请求. 格式: ./ab ...
- Eclipse中查看JDK类库的源代码
在Eclipse中查看JDK类库的源代码!!! 设置: 1.点 “window”-> "Preferences" -> "Java" -> & ...
- 如何设置让SFTP的用户限制在某个目录下
通常SFTP的任何用户登录之后能看到整个系统的文件目录,这样很不安全. 通过chroot我们可以将某个用户登录SFTP后只能在某个限定的目录下操作,这样可以更安全.我们来看看怎么设置. 1.创建一个用 ...
- Entity Framework学习笔记(五)----Linq查询(2)---贪婪加载
请注明转载地址:http://www.cnblogs.com/arhat 在上一章中,我们使用了Linq对Entity Framework进行了一个查询,但是通过学习我们却发现了懒加载给我来的性能上的 ...
- Python实战(1)
此次实战完全按照Python教程 - 廖雪峰的官方网站进行 首先下载windows版本的Python2.7,附上下载链接http://www.python.org/ftp/python/2.7.6/p ...