我在学习研究WorldWind的源代码时,发现它的底层代码大量调用外部的dll,包括不同平台的DLL。也就是托管语言C#中调用非托管的dll函数。以前也遇到了这样的情况,自己只是“依猫画虎”并没深入学习和了解。现在我分几部分学习,算是弥补这一不足!
Interop的意思就是托管/非托管代码之间的互操作。
extern 修饰符用于声明在外部实现的方法。extern 修饰符的常见用法是在使用 Interop 服务调入非托管代码时与 DllImport 属性一起使用;在这种情况下,该方法还必须声明为 static,如下面的示例所示:
[DllImport("avifil32.dll")]
private static extern void AVIFileInit();
将 abstract和 extern 修饰符一起使用来修改同一成员是错误的。使用 extern 修饰符意味着方法在 C# 代码的外部实现,而使用 abstract 修饰符意味着在类中未提供方法实现。
示例:
在该示例中,程序接收来自用户的字符串并将该字符串显示在消息框中。程序使用从 User32.dll 库导入的 MessageBox 方法。
using System.Runtime.InteropServices;
class MainClass
{
[DllImport("User32.dll")]
public static extern int MessageBox(int h, string m, string c, int type);
static int Main()
{
string myString;
Console.Write("Enter your message: ");
myString = Console.ReadLine();
return MessageBox(0, myString, "My Message Box", 0);
}
}
指示该属性化方法由非托管动态链接库 (DLL) 作为静态入口点公开。用 DllImport 属性修饰的方法必须具有static extern 修饰符。
命名空间:System.Runtime.InteropServices
DllImportAttribute 属性提供对从非托管 DLL 导出的函数进行调用所必需的信息。作为最低要求,必须提供包含入口点的 DLL 的名称。可直接将此属性应用于 C# 或 C++ 方法定义;
注意 JScript 不支持此属性。可以使用 C# 或 Visual Basic 包装类从 JScript 程序访问非托管 API 方法。
示例
下面的代码示例演示如何使用 DllImportAttribute 属性导入 Win32 MessageBox 函数。然后,代码示例将调用导入的方法。
using System; using System.Runtime.InteropServices; class Example { // Use DllImport to import the Win32 MessageBox function. [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern int MessageBox(IntPtr hWnd, String text, String caption, uint type); static void Main() { // Call the MessageBox function using platform invoke. MessageBox(new IntPtr(0), "Hello World!", "Hello Dialog", 0); } }
利用平台调用这种服务,托管代码可以调用在动态链接库 (DLL)(如 Win32 API 中的 DLL)中实现的非托管函数。此服务将查找并调用导出的函数,然后根据需要跨越互用边界封送其参数(整数、字符串、数组、结构等)。
使用导出的 DLL 函数
-
最低限度上,必须指定函数的名称和包含该函数的 DLL 的名称。
-
可以使用现有类,为每一非托管函数创建单独的类,或者创建包含一组相关的非托管函数的一个类。
-
[Visual Basic] 使用带 Function 和 Lib 关键字的 Declare 语句。在某些少见的情况下,可以使用带 Shared Function 关键字的 DllImportAttribute。这些情况在本节后面部分进行说明。
[C#] 使用 DllImportAttribute 标识 DLL 和函数。用 static 和 extern 修饰符标记方法。
[C++] 使用 DllImportAttribute 标识 DLL 和函数。用 extern "C" 标记包装方法或函数。
利用平台调用,可以通过调用 Win32 API 和其他 DLL 中的函数来控制操作系统中相当大的一部分。除了 Win32 API 之外,还有许多其他的 API 和 DLL 可通过平台调用来调用。
下表将说明 Win32 API 中几个常用的 DLL。
DLL | 内容说明 |
---|---|
GDI32.dll |
用于设备输出的图形设备接口 (GDI) 函数,例如用于绘图和字体管理的函数。 |
Kernel32.dll |
用于内存管理和资源处理的低级别操作系统函数。 |
User32.dll |
用于消息处理、计时器、菜单和通信的 Windows 管理函数。 |
下面的示例将说明如何定义和调用 User32.dll 中的 MessageBox 函数,并将简单字符串当作参数进行传递。在这些示例中,DllImportAttribute.CharSet Field 字段设置为 Auto,以便让目标平台确定字符宽度和字符串封送处理。
C#
using System.Runtime.InteropServices; public class Win32 { [DllImport("user32.dll", CharSet=CharSet.Auto)] public static extern IntPtr MessageBox(int hWnd, String text, String caption, uint type); } public class HelloWorld { public static void Main() { Win32.MessageBox(0, "Hello World", "Platform Invoke Sample", 0); } }
using namespace System::Runtime::InteropServices; typedef void* HWND; [DllImport("user32", CharSet=CharSet::Auto)] extern "C" IntPtr MessageBox(HWND hWnd, String* pText, String* pCaption, unsigned int uType); void main(void) { String* pText = L"Hello World!"; String* pCaption = L"Platform Invoke Sample"; MessageBox(0, pText, pCaption, 0); }