暴强贴:从.NET平台调用Win32 API

C#调用Win32API
本文介绍如何使用C#语言通过DllImport特性调用Win32API,实现跨平台功能,如弹出消息框等。
2005年12月20日 17:10:00

作者:刘铁猛
日期:2005-12-20
关键字:C# .NET Win32 API

版权声明:本文章受知识产权法保护,如果阁下想转载,在转载的时候烦劳阁下连同在下的姓名一起转载,并向bladey@tom.com发一个Mail,我很想知道我的文章都去哪里了.谢谢.

小序
Win32 API可以直接控制Microsoft Windows的核心,因为API(Application Programming Interface)本来就是微软留给我们直接控制Windows的接口。想玩儿吗?呵呵,太难了。
C#使用非常简单,写程序就像打拱猪,Sorry -_-! ,搭积木一样简单。想玩儿吗?呵呵,没办法直接控制Windows的核心。
难道就没有两全其美的办法吗?当然不是!要不微软的产品早就没人买了。其实从C#(或者说.NET平台)调用Win32 API还是非常简单滴~~~~今天偶们大家就一起来研究研究。

一. 基础知识
Win32 API是C语言(注意,不是C++语言,尽管C语言是C++语言的子集)函数集。C#语言与C语言是完全不同的(除了语法上比较像),所以,要想用C#语言调用C语言的Win32 API,要费上一番周折。首先我们就要准备一些基础知识。
1. Win32 API函数放在哪里?
Win32 API函数是Windows的核心,比如我们看到的窗体、按钮、对话框什么的,都是依靠Win32函数"画"在屏幕上的,由于这些控件(有时也称组件)都用于用户与Windows进行交互,所以控制这些控件的Win32 API函数称为"用户界面"函数(User Interface Win32 API),简称UI函数;还有一些函数,并不用于交互,比如管理当前系统正在运行的进程、硬件系统状态的监视等等..这些函数只有一套,但是可以被所有的Windows程序调用(只要这个程序的权限足够高),简而言之,API是为程序所共享的。为了达到所有程序能共享一套API的目的,Windows采用了"动态链接库"的办法。之所以叫"动态链接库",是因为这样的函数库的调用方式是"随用随取"而不是像静态链接库那样"用不用都要带上"。
这里不太好理解,不要紧,我们举个小例子。我们把Windows比做一个游乐场,而把在游乐场里玩儿的小孩比做一个一个程序。小孩在玩的过程中可能要喝水。我们有两个办法让小家伙们想喝水的时候就有水喝:1.给每个小家伙配一个水壶,小家伙们喝了的话就喝自己带的水;2.给游乐场配一个饮水机,谁渴了谁来喝。显然,第二个方法要好得多,这体现在三个地方。第一,带着水壶,小家伙身体不灵活、玩不爽(影响程序的速度),况且这只是带了一个水壶,要是再带上饭盒呢?还有轮滑、头盔、创可贴、纱布..AK-47 My God,如果带全了就赶上美国大兵了。所以游乐园里还是有个公用"仓库"要来的方便,让大家随用随取(动态链接)。第二,小家伙们带了那么多东西,占了游乐场很多地方,让游乐场拥挤不堪,别的小朋友就进不来了(程序体积大,影响程序和系统的性能)。第三,如果某件物品升级了,比如水壶从一升的改为二升的,那么每个小家伙就必须go home去换新的(重新编译程序,由编译器把新的静态库链接进程序主体里),而第二种情况里,只要游乐场把自己仓库里的水壶换个型号,那么所有小家伙就都在同一时间拥有了大容量的水壶。(悟空!我就一会儿不在,你怎么就乱丢东西?!打到小朋友多不好~~~~~)
悟空已经急了,我就不再叽叽歪歪了..呃..Win32 API函数是放在Windows系统的核心库文件中的,这些库在硬盘里的存储形式是.dll文件。我们常用到的dll文件是user32.dllkernel32.dll两个文件,还有其它一些dll文件也非常重要,大家要在实践中多积累经验。
我们知道Win32 API函数是放在dll文件中了,但新问题又来了--我们怎么调用它们呢?这些dll文件是用C语言写的,源代码经C语言编译器编译之后,会以二进制可执行代码形式存放在这些dll文件中,就好像苹果被打碎机打成果酱后装在罐子里一样--你再也分不清哪个是你GF给你的,哪个是你老妈给你的一样。为了能让程序使用这些函数,微软在发布每个新的操作系统的时候,也会放出这个系统的SDK,目前最新的是Win2003 SP1 SDK,据说Vista的马上就要放出来,而且已经把UI的API从核心库中分离出去以提高系统的稳定性了。SDK里有一些C语言的头文件(.h文件),这些文件里描述了核心dll文件里都有哪些Win32 API函数,在写程序的时候,把这些.h文件用#include"....."指令包含进你的程序里,你就可以使用这些Win32 API了。至于程序是怎样链接的,超出了本文的范围--也超出了本人的知识范围:D
至此,如果你是C语言高手,已经可以使用Windows SDK去调教Windows了!不过,今天我们讨论的是C#语言调用Win32 API的问题。我们现在已经知道API函数放在dll动态链接库文件里,也知道C语言怎么调用它们了,那么C#语言怎么办呢?C#语言是不能使用C语言的.h文件的。C#语言也使用dll动态链接库,不过这些dll都是.NET版本的,具有"自描述性",也就是自己肚子里都有哪些函数都已经写在自己的metadata里了,不用再附加一个.h文件来说明。现在,我们已经找到了问题的关键点:如何用.NET平台上的C#语言来调用Win32平台上的dll文件。答案非常简单:使用DllImport特性
二. 小试牛刀
下面,就让我们写一个小程序,试一试如何用C#语言和DllImport特性来调用Win32 API。

using System;
using System.Runtime.InteropServices;
class Program
{
[DllImport("User32.dll")]
public static extern int MessageBox(int h, string m, string c, int type);

static int Main()
{
MessageBox(0, "Hello Win32 API", "
水之真谛", 4);
Console.ReadLine();
return 0;
}
}


新建一个C#的控制台程序,把VS自动生成的代码清空,把上面的代码Copy过去就可以编译执行了。让我们剖析一下这个程序:
1. 要使用DllImport这个特性(特性也是一种类),必须使用这一句using System.Runtime.InteropServices;
,导入"运行时- <交互服务"。喔~~~~运行时的交互服务不就是"动态链接"吗?感谢microsoft!>
2. 然后我们就可以制造一个DllImport类的实例,并把这个实例绑定在我们要使用的函数上了。"特性类"这种类非常怪--制造类实例的时候不使用MyClass mc = new MyClass();这种形式,而是使用 [特性类(参数列表)]这种形式;特性类不能独立存在,一定要用作修饰其它目标上(本例是修饰后面的一个函数),不同的特性可以用来修饰类、函数、变量等等;特性类实例在被编译的时候也不产生可执行代码,而是被放进metadata里以备检索。总之,你记住特性类很怪就是了,想了解更多就查查MSDN,懒得查就先这么记--不懂惯性定律不影响你学骑自行车。 [DllImport("User32.dll")]是说我们要使用的Win32 API函数在User32.dll这个文件里。问题又来了:我怎么知道那么多API函数都在哪个dll文件里呢?这个你可以在MSDN里查到,位置是 Root- 。打开这页,你会看到有很多API的分类,API全在这里了。打开一个分类,比如Dialog Box,在Functions段,你会看到很多具体的函数,其中就有上面用到的MessageBox函数,点击进入。你将打开MessageBox的详细解释和具体用法。它的名字、返回值、参数类型尽收眼底、一览无余!而且很练英文哦~~~~在这一页的底部,你可以看到一个小表格,里面有一项"Minimum DLL Version user32.dll"就是说这个函数在user32.dll里。
3. 接下来就是我们的函数了。在C#里调用Win32函数有这么几个要点。第一:名字要与Win32 API的完全一样。第二:函数除了要有相应的DllImport类修饰外,还要声明成public static extern类型的。第三:也是最变态的一点,函数的返回值和参数类型要与Win32 API完全一致!这可难煞我们这群初学者--Win32的数据类型比较搞怪,比如什么LPSTR、什么HINSTANCE都是些虾米东东呢?给大家一个小参考,我的Blog里有《Windows数据类型探幽--千回百转你是谁?》系列拙文,可以查一下。另外在此,我从MSDN里摘出一张表来,是常用Win32数据类型与.NET平台数据类型的对应表:
Figure 2 Non-Pointer Data Types

Win32 TypesSpecificationCLR Type
char, INT8, SBYTE, CHAR8-bit signed integerSystem.SByte
short, short int, INT16, SHORT16-bit signed integerSystem.Int16
int, long, long int, INT32, LONG32, BOOL, INT 32-bit signed integerSystem.Int32
__int64, INT64, LONGLONG64-bit signed integerSystem.Int64
unsigned char, UINT8, UCHAR, BYTE8-bit unsigned integerSystem.Byte
unsigned short, UINT16, USHORT, WORD, ATOM, WCHAR, __wchar_t16-bit unsigned integerSystem.UInt16
unsigned, unsigned int, UINT32, ULONG32, DWORD32, ULONG, DWORD, UINT32-bit unsigned integerSystem.UInt32
unsigned __int64, UINT64, DWORDLONG, ULONGLONG64-bit unsigned integerSystem.UInt64
float, FLOATSingle-precision floating pointSystem.Single
double, long double, DOUBLEDouble-precision floating pointSystem.Double
In Win32 this type is an integer with a specially assigned meaning; in contrast, the CLR provides a specific type devoted to this meaning.

有了这些东西,我们就能把一个Win32 API函数转成C#函数了。还拿MessageBox函数为例(看刚才给出的函数表),它的Win32原形如下:

int MessageBox( HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType );

函数名:MessageBox将保持不变。
返回值:int 将保持不变(无论是Win32还是C#,int都是32位整数)
参数表:H开头意味着是Handle,一般情况下Handld都是指针类型,Win32平台的指针类型是用32位来存储的,所以在C#里正好对应一个int整型。不过,既然是指针,就没有什么正负之分,32位都应该用来保存数值--这样一来,用uint(无符号32位整型)来对应Win32的H类型更合理。不过提醒大家一点,int是受C#和.NET CLR双重支持的,而uint只受C#支持而不受.NET CLR支持,所以,本例还是老老实实地使用了int型。(肚子饿了..再坚持坚持..)
至于LPCTSTR是Long Pointer to Constant String的缩写,说白了就是--字符串。所以,用C#里的string类型就对了。
修饰符:要求有相应的DllImport和public static extern

经过上面一番折腾,Win32的MessageBox函数就包装成C#可以调用的函数了:

[DllImport("User32.dll")]
public static extern int MessageBox(int h, string m, string c, int type);


好人做到底,我把四个参数的用处也说一下:
第一个:弹出的MessageBox的父窗口是谁。本例中没有,所以是0,也就是"空指针"。
第二个:MessageBox的内容。本例中是"Hello Win32 API"。
第三个:MessageBox的标题。本例中用的是本人Blog的名字--水之真谛--请大家不要忘记。
第四个:MessageBox上的按钮是什么,如果是0,那就只有一个OK,MessageBox太短了,你将看不全"水之真谛"四个字,于是偶改成了4,这样就有两个按钮了。这些在MSDN的函数用法里都有。不过,我还是非常推荐您阅读一下本人的另一篇拙作《一个Win32程序的进化》
至此,一个麻雀虽小、五毒俱全~~~Sorry -_-! 五脏俱全的C#调用Win32 API的程序就分析完了。原理并不难吧!应届生拿去蒙HR足够了!真正见功底的地方是你使用MSDN、SDK、.NET Framework类库VC/VC#的熟练程度。相信我--MSDN+SDK+VC/C#绝对足够把Windows收拾得服服帖帖了:D
三. 真的有必要吗?
嘿嘿嘿嘿..看我的表情,我在坏坏地笑哦!你们都上当啦!操作Windows的底层不一定都要调用Win32 API滴~~~~(哪儿来的砖头!!!)
我想说的是:.NET Framework是对Win32 API的良好封装,大部分Win32 API函数都已经封装在了.NET Framework类库的各个类里了。如果说Win32 API函数是散落在地上的珍珠的话,那么.NET Framework就是把这些珍珠按种类分放到了各个抽屉里--让我想起我妈来了--我的书放得满地满床的时候我总是能找到,她一收拾我就再也找不到了,郁闷。唉..没办法,我们还是仔细把.NET Framework类库好好翻翻吧,会有很多惊喜哦!
最后,用一个例子结束我们的文章吧!
例子是这样滴~~~~~
那是在很久很久以前,我给一个公司写程序用来控制用户登录,在登录之前,用户不能把鼠标移出登录窗体,因为要控制鼠标,所以我首先想起了调用Win32 API中与Cursor相关的函数来--于是不管三七二十一、花了九牛二虎之力调用了Win32 API中的ClipCursor()这个函数,效果还不错。
结果前两天翻.NET Framework类库的时候,发现System.Windows.Forms.Cursor类的Clip属性就是专门做这个用的!差点没把鼻子气歪了..请大家自己动手创建一个C#的Windows程序,把下面的核心代码贴到主窗体的双击事件里,试一试。做这个例子的目的就是要告诉大家:1.对类库的了解程序直接决定了你编程的效率和质量--用类库里的组件比我们"从轮子造起"要快得多、安全得多。2.不到万不得已,不要去直接调Win32 API函数--那是不安全的。

private void Form1_DoubleClick(object sender, EventArgs e)
{
Rectangle r = new Rectangle(this.Left, this.Top, this.Width, this.Height);
System.Windows.Forms.Cursor.Clip = r;
}


最后,大家一定非常想知道,.NET Framework都为我们封装好了哪些Win32 API,OK,MSDN里有一篇文章,专门列出了这些。文章的名字是《Microsoft Win32 to Microsoft .NET Framework API Map》请感兴趣的朋友自己阅读。
四. 感恩
新年快到了,这篇文章也做为一份小小的礼物,一是博大家一乐,二是让我们永远铭记这幸福的时刻。
送给对我有着知遇之恩的陆经理;
送给刘莹Lead感谢在工作中给予的指导和支持;
送给我的Team伙伴乐莲、王勇(Worksoft),没有你们的帮助,我是不可能开始工作的!
送给我宿舍的兄弟张雄,没有你,我可能要睡在城铁站了。
送给段玮和陈宁,感谢你们组织的活动和培训。
送给陈曦、陈建、王勇、常勇、舜贤、挺挺、对面的女孩李芳,还有本组的JJMM,还有小朱..与你们共事是我最大的快乐!

文章写完鸟~~~~倾城MM大概也把饭做好鸟~~~~回家鸟~~~~


版权声明:本文章受知识产权法保护,如果阁下想转载,在转载的时候烦劳阁下连同在下的姓名一起转载,并向bladey@tom.com发一个Mail,我很想知道我的文章都去哪里了.谢谢.


看贴要回贴,不回贴,小心我用弹弓打你家玻璃!!!



Trackback: http://tb.blog.youkuaiyun.com/TrackBack.aspx?PostId=557351


作为Microsoft 32位平台的应用程序编程接口, Win32 API是从事Windows应用程序开发所必备的。 首先对Win32 API函数做完整的概述;然后收录五大类函数: 窗口管理、图形设备接口、系统服务、国际特性以及网络服务; 在附录部分,讲解如何在Visual Basic和Delphi中对其调用。 本书是从事Windows应用程序开发的软件工程师的必备参考手册。 控件与消息函数 共91个函数 硬件与系统函数 共98个函数 设备场景函数 共73个函数 绘图函数 共105个函数 位图、图标和光栅运算函数 共39个函数 菜单函数 共37个函数 文本和字体函数 共41个函数 打印函数 共66个函数 文件处理函数 共118个函数 进程和线程函数 共40个函数 Windows消息函数 共11个函数 网络函数 共14个函数 目 录 第一章 Win32 API概论…………………………………………………………………………1 1.1 为什么使用Win32 API …………………………………………………………………1 1.2 Win32 API简介 …………………………………………………………………………1 1.3 综述………………………………………………………………………………………11 第二章 窗口管理函数(Windows Control Function) ……………………………………13 2.1 易用特性函数(Accessibility Features)…………………………………………13 2.2 按钮函数(Button)……………………………………………………………………20 2.3 插入标记(^)函数(Caret)…………………………………………………………21 2.4 组合框函数(Combo box) ……………………………………………………………24 2.5 通用对话框函数(Common Dialog Box) ……………………………………………25 2.6 标函数(Cursor)………………………………………………………………………36 2.7 对话框函数(Dialog Box)……………………………………………………………40 2.8 编辑控制函数(Edit Control)………………………………………………………54 2.9 图标函数(Icon)………………………………………………………………………54 2.10 键盘加速器函数(Keyboard Accelerator)……………………………………… 61 2.11 键盘输入函数(Keyboard InPut) …………………………………………………63 2.12 列表框函数(List box) ……………………………………………………………75 2.13 菜单函数(Menu) ……………………………………………………………………76 2.14 消息和消息队列函数(Message and Message Queue)……………………………90 2.15 鼠标输入函数(Mouse Input) ……………………………………………………100 2.16 多文档接口函数(Multiple Document Interface) ……………………………103 2.17 资源函数(Resource)………………………………………………………………105 2.18 滚动条函数(Scroll Bar)…………………………………………………………113 2.19 窗口函数(Window)…………………………………………………………………119 2.20 窗口类函数(Window Class)………………………………………………………144 2.21 窗口过程函数(Window Procedure)………………………………………………150 2.22 窗口属性函数(Window Property) ………………………………………………152 第三章 图形设备接口函数(Graphic Device Interface Function) …………………155 3.1 位图函数(Bitmap) …………………………………………………………………155 3.2 笔刷函数(Brush)……………………………………………………………………171 3.3 剪切函数(Clipping) ………………………………………………………………176 3.4 颜色函数(Color)……………………………………………………………………179 3.5 坐标空间与变换函数(Coordinate Space Transformation)……………………186 3.6 设备环境函数(Device Context) …………………………………………………195 3.7 填充形态函数(Filled shape) ……………………………………………………211 3.8 字体和正文函数(Font and Text)…………………………………………………215 3.9 ICM 2.0函数 …………………………………………………………………………238 3.10 线段和曲线函数(Line and Curve)………………………………………………295 3.11 图元文件函数(Metafile)…………………………………………………………300 3.12 多显示器函数(Multiple Display Monitors) …………………………………311 3.13 绘图函数和画图函数(Painting and Drawing)…………………………………313 3.14 路径函数(Path)……………………………………………………………………328 3.15 画笔函数(Pen) ……………………………………………………………………332 3.16 打印及打印假脱机程序函数(Printing and Print Spooler)…………………334 3.17 矩形函数(Rectangle) ……………………………………………………………371 3.18 区域函数(Region)…………………………………………………………………374 第四章 系统服务函数(System Service Function) ……………………………………383 4.1 访问控制函数(Access Control) …………………………………………………383 4.2 原子函数(Atom) ……………………………………………………………………406 4.3 客户/服务器访问控制函数(Client/Server Access Control) ………………409 4.4 剪板函数(Clipboard)……………………………………………………………431 4.5 通信函数(Communication)…………………………………………………………436 4.6 控制台函数(Console)………………………………………………………………444 4.7 数据解压库函数(Data Decompression Library) ………………………………463 4.8 调试函数(Debugging)………………………………………………………………466 4.9 设备输入输出函数(Device Input and Output)…………………………………472 4.10 动态数据交换函数(Dynamic Data Exchange) …………………………………474 4.11 动态数据交换管理函数(Dynamic Data Exchange Management)………………476 4.12 动态链接库函数(Dynamic-Link Library)………………………………………489 4.13 错误函数(Error) …………………………………………………………………496 4.14 事件日志函数(Event Logging) …………………………………………………499 4.15 文件函数(File)……………………………………………………………………503 4.16 文件安装库函数(File Installation Library) ………………………………542 4.17 文件映射函数(File Mapping)……………………………………………………546 4.18 文件系统函数 File System)………………………………………………………551 4.19 句柄和对象函数(Handle and Object)………………………………………………556 4.20 挂钩函数(Hook)………………………………………………………………………560 4.21 ImageHlp函数…………………………………………………………………………572 4.22 大整数操作函数(Iarge Integer Operations)……………………………………594 4.23 低层访问控制函数(Low-Level Access Control)………………………………596 4.24 LSAPI函数 …………………………………………………………………………617 4.25 邮槽函数(Mailslot)………………………………………………………………622 4.26 内存管理函数(Memory Management) ……………………………………………623 4.27 管道函数(Pipe) …………………………………………………………………655 4.28 电源管理函数(Power Management) …………………………………………… 663 4.29 进程和线程函数(Process and Thread)…………………………………………666 4.30 注册表函数(Registry)……………………………………………………………700 4.31 字符串操作函数(String Manipulation)……………………………………… 724 4.32 结构化异常处理函数(Structured Exception Handling) ……………………742 4.33 同步函数(Synchronization) ……………………………………………………745 4.34 系统信息函数(System Information)……………………………………………766 4.35 系统消息函数(System Message)…………………………………………………780 4.36 系统关机函数(System Shutdown) ………………………………………………781 4.37 磁带备份函数(Tape Backup) ……………………………………………………783 4.38 时间函数(Time)……………………………………………………………………789 4.39 计时器函数(Timer) ………………………………………………………………795 4.40 工具帮助函数(Tool Help) ………………………………………………………796 4.41 窗口站和桌面函数(Window Station and Desktop)……………………………799 4.42 Windows NT 4.0访问控制函数(Window NT 4.0 Access-Control)……………808 4.43 WinTrust函数(WinTrust)…………………………………………………………814 第五章 国际特性函数(International Peatures Punction)时性…………………………815 5.1 输入方法编辑函数(Input Method Editor)…………………………………………815 5.2 国家语言支持函数(National Language Support)………………………………… 828 5.3 Unicode和字符集函数(Unicode and Character Set)……………………………… 843 第六章 网络服务函数(Networding Service Function)……………………………………849 6.1 数据链路控制函数(DLC)………………………………………………………………849 6.2 网络函数(Net)…………………………………………………………………………849 6.3 NetBIOS函数……………………………………………………………………………896 6.4 网络DDE函数(Networking DDE)……………………………………………………897 6.5 RAS服务器管理函数(RAS Server Administration)………………………………901 6.6 远程访问服务函数(Remote Access Administration)………………………………910 6.7 服务函数(Service)……………………………………………………………………929 6.8 Windows网络函数(Windows Networking)……………………………………………930 附录1 如何在VB中调用DLL API ……………………………………………………………945 1 DLL API的声明……………………………………………………………………………945 2 DLL API调用……………………………………………………………………………947 附录2 在Delphi中直接调用Windows API…………………………………………………953
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值