以文本方式查看主题 - 计算机科学论坛 (http://bbs.xml.org.cn/index.asp) -- 『 Dot NET,C#,ASP,VB 』 (http://bbs.xml.org.cn/list.asp?boardid=43) ---- 从C#程序中调用非受管DLLs (http://bbs.xml.org.cn/dispbbs.asp?boardid=43&rootid=&id=76557) |
-- 作者:卷积内核 -- 发布时间:8/31/2009 3:24:00 PM -- 从C#程序中调用非受管DLLs 从所周知,.NET已经渐渐成为一种技术时尚,那么C#很自然也成为一种编程时尚。如何利用浩如烟海的Win32 API以及以前所编写的 Win32 代码已经成为越来越多的C#程序员所关注的问题。本文将介绍如何从C#代码中调用非受管DLLs。如果某个函数是一个带有串类型(char*)输出参数的Win32 API 或者是DLL输出函数,那么从C#中如何调用它呢?对于输入参数的情形问题到不大,但如何获取从参数中返回的串呢?此外,如何调用有结构(struct)和回调(callback)作为参数的函数,如GetWindowsRect 和EnumWindows?那我们又如何将参数从C++和MFC中转换成C# 所要的类型呢?下面就让我们来一一解决这些问题。 微软.NET的一个最主要的优势是它提供一个语言无关的开发系统。我们可以用Visual Basic、C++、C#等等语言来编写类,然后在其它语言中使用,我们甚至可以用不同的语言来派生类。但是如何调用以前开发的非受管DLL呢?方法是必须将.NET对象转化成结构、char*以及C语言的指针。用行话说就是参数必须被列集(marshal)。说到列集,用一两句话也说不清楚。所幸的是实现列集并不要我们知道太多的东西。 为了从C# 中调用DLL函数,首先必须要有一个声明,就象长期以来使用Visual Basic的程序员所做的那样,只不过在C#中使用的是DllImport关键字: using System.Runtime.InteropServices; // DllImport所在的名字空间 // Win32API: 此为名字空间,打包所选的Win32 API 函数 ///////////////////////////////////////////////////////////////// [StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)] public class Win32 { [DllImport("user32.dll")] [DllImport("user32.dll")] [DllImport("user32.dll")] [DllImport("user32.dll")] ...... |
-- 作者:卷积内核 -- 发布时间:8/31/2009 3:25:00 PM -- 在C#中如何调用呢?如何传递RECT呢?方法是将它作为一个C#结构,用另一个属性:它就是StructLayout: [StructLayout(LayoutKind.Sequential)] public struct RECT { public int left; public int top; public int right; public int bottom; } 一旦有了结构定义,便可以象下面这样来打包实现: [DllImport("user32.dll")] public static extern int GetWindowRect(int hwnd, ref RECT rc); 注意这里用到了ref,这一点很重要,CLR会将RECT作为引用传递,以便函数可以修改我们的对象,而不是无名字的堆栈拷贝。定义了GetWindowRect之后,我们可以象下面这样调用: RECT rc = new RECT(); int hwnd = // get it ... Win32.GetWindowRect(hwnd, ref rc); 注意这里必须声明并使用ref——罗嗦!C# 结构的缺省列集类型还能是什么?——LPStruct,所以就不必再用MarshalAs了。但如果RECT是个类,而非结构的话,那就必须象下面这样实现打包: // 如果RECT 是个类,而不是结构 [DllImport("user32.dll")] public static extern int GetWindowRect(int hwnd, [MarshalAs(UnmanagedType.LPStruct)] RECT rc); C#与C++类似,许多事情都可以殊途同归,System.Drawing中已经有了一个Rectangle结构用来处理矩形,所以为什么要重新发明轮子呢? [DllImport("user32.dll")] public static extern int GetWindowRect(int hwnd, ref Rectangle rc); 运行时既然已经知道如何将Rectangle作为Win32 RECT进行列集。请注意,在实际的代码中就没有必要再调用GetWindowRect(Get/SetWindowText亦然),因为Windows.Forms.Control类已具有这样的属性:用Control.DisplayRectangle获取窗口矩形,用Control.Text设置/获取控件文本 Rectangle r = mywnd.DisplayRectangle; mywnd.Text = "I''''m so cute"; 如果出于某种原因已知的是某个HWND,而不是一个控件派生对象,那么只需要象示范的那样来打包API。以上我们已经搞掂了串、结构以及矩形……还有什么呢?对了,还有回调(callbacks)。如何将回调从C#传递到非受管代码呢?记住只要用委托(delegate)即可: delegate bool EnumWindowsCB(int hwnd, int lparam); 一旦声明了委托/回调类型,就可以象下面这样打包: [DllImport("user32")] public static extern int EnumWindows(EnumWindowsCB cb, int lparam); 上面的delegate仅仅是声明了一个委托类型,我们还必须在类中提供一个实际的委托实现: // 在类中 public static bool MyEWP(int hwnd, int lparam) { // do something return true; } 然后对它进行打包处理: EnumWindowsCB cb = new EnumWindowsCB(MyEWP); Win32.EnumWindows(cb, 0); 聪明的读者回注意到我们这里掩饰了lparam的问题,在C中,如果你给EnumWindows一个LPARAM,则Windows会用它通知回调函数。一般典型的lparam是一个结构或类指针,其中包含着我们需要的上下文信息。但是记住,在.NET中绝对不能提到"指针"!那么如何做呢?这是可以将lparam声明为IntPtr并用GCHandle对它进行打包: // 现在lparam 是 IntPtr delegate bool EnumWindowsCB(int hwnd, IntPtr lparam); // 在GCHandle中打包对象 // WinArray: 用EnumWindows 产生顶层窗口的清单ArrayList namespace WinArray { public class WindowArray : ArrayList { // 这里声明的是private类型的委托,因为只有我使用它,其实没必要这样做。 private static bool MyEnumWindowsCB(int hwnd, IntPtr param) { // 这是唯一的public 类型方法,你需要调用的唯一方法 using System; class MyApp { [STAThread]
|
W 3 C h i n a ( since 2003 ) 旗 下 站 点 苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》 |
78.125ms |