C#【高级篇】.NET平台调用Win32 API

您所在的位置:网站首页 win32是什么语言写的 C#【高级篇】.NET平台调用Win32 API

C#【高级篇】.NET平台调用Win32 API

2023-09-29 06:35| 来源: 网络整理| 查看: 265

C#学习汇总 - 总目录

C#【高级篇】 .NET平台调用Win32 API 前言一、基础知识1、Win32 API函数是什么?2、Win32 API放在哪?3、C#如何调用Win32 API函数?4、.NET框架为何不包括所有的Win32 API?5、如何在MSDN中查询Win32 API函数?(1)MSDN在线查看(2)MSDN离线查看 二、C#调用Win32 API函数的要点三、常用 Win32数据类型与.NET平台数据类型的对应表附加:Win32类型和.net类型的对应表 四、几个示例1、弹出一个MessageBox对话框(1)MessageBox函数的Win32原型:(2)C#源代码: 2、调用 Beep() API 来发出声音(1)函数原型:(2)分析:(3)编写C#原型:(4)C#源代码【生成的随机声音在二十世纪六十年代的科幻电影中很常见】: 参考:

前言

Win32 API可以直接控制Microsoft Windows的核心,因为API(Application Programming Interface)本来就是微软留给我们直接控制Windows的接口。

一、基础知识

Win32 API是C语言函数集(注意,不是C++语言,尽管C语言是C++语言的子集)。

1、Win32 API函数是什么?

Win32 API函数是Windows的核心,比如我们看到的窗体、按钮、对话框什么的,都是依靠Win32函数“画”在屏幕上的,由于这些控件(有时也称组件)都用于用户与Windows进行交互,所以控制这些控件的Win32 API函数称为**“用户界面”函数**(User Interface Win32 API),简称UI函数。

还有一些函数,并不用于交互,比如管理当前系统正在运行的进程、硬件系统状态的监视等等……这些函数只有一套,但是可以被所有的Windows程序调用(只要这个程序的权限足够高),简而言之,API是为程序所共享的。

为了达到所有程序能共享一套API的目的,Windows采用了“动态链接库”的办法。之所以叫“动态链接库”,是因为这样的函数库的调用方式是“随用随取”而不是像静态链接库那样“用不用都要带上”。

2、Win32 API放在哪?

Win32 API函数是放在Windows系统的核心库文件中的,这些库在硬盘里的存储形式是.dll文件。我们常用到的dll文件User32.dll和kernel32.dll两个文件。

这些dll文件是用C语言写的,源代码经C语言编译器编译之后,会以二进制可执行代码形式存放在这些dll文件中。为了能让程序使用这些函数,微软在发布每个新的操作系统的时候,也会放出这个系统的SDK。 SDK里有一些C语言的头文件(.h文件),这些文件里描述了核心dll文件里都有哪些Win32 API函数,在写程序的时候,把这些.h文件用#include"…"指令包含进你的程序(C/C++程序)里,你就可以使用这些Win32 API了。

3、C#如何调用Win32 API函数?

C#语言也使用dll动态链接库,不过这些dll都是.NET版本的,具有“自描述性”,也就是自己肚子里都有哪些函数都已经写在自己的metadata里了,不用再附加一个.h文件来说明。

现在,我们已经找到了问题的关键点:如何用.NET平台上的C#语言来调用Win32平台上的dll文件。答案非常简单:使用DllImport特性。

【注意:】 1.对类库的了解,直接决定了你编程的效率和质量——用类库里的组件比我们“从轮子造起”要快得多、安全得多。 2.不到万不得已,不要去直接调Win32 API函数——那是不安全的。

4、.NET框架为何不包括所有的Win32 API?

.NET Framework是对Win32 API的良好封装,大部分Win32 API函数都已经封装在了.NET Framework类库的各个类里了。

C# 用户经常提出两个问题:“我为什么要另外编写代码来使用内置于 Windows 中的功能?在框架中为什么没有相应的内容可以为我完成这一任务?”

当框架小组构建他们的 .NET 部分时,他们评估了为使 .NET 程序员可以使用 Win32 而需要完成的工作,结果发现 Win32 API 集非常庞大。他们没有足够的资源为所有 Win32 API 编写托管接口、加以测试并编写文档,因此只能优先处理最重要的部分。许多常用操作都有托管接口,但是还有许多的 Win32 部分没有托管接口。

补充: .NET Framework都为我们封装好了哪些Win32 API? MSDN里有一篇文章,专门列出了这些。文章的名字是《Microsoft Win32 to Microsoft .NET Framework API Map》

5、如何在MSDN中查询Win32 API函数?

MSDN官网:https://msdn.microsoft.com/zh-cn/

(1)MSDN在线查看

例如我们查找MessageBox函数:

搜索“Windows API Index”,一步步查找 在这里插入图片描述 在这里插入图片描述在这里插入图片描述在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

直接查找“MessageBox” 在这里插入图片描述

(2)MSDN离线查看

输入的关键字是MSDN Library。以下是下载MSDN Library的链接: 微软官网下载:https://www.microsoft.com/en-us/download/details.aspx?id=20955 其他链接:http://download.microsoft.com/download/1/f/0/1f07c259-7ff2-4902-9205-ad1dfb87ccab/VS2008SP1MSDNENUX1506188.iso

下载之后解压iso文件,找到setup文件夹下的setup.exe双击安装,也可以使用虚拟光驱安装。

安装后,在点击 开始->所有应用—>找到MSDN Library for Visual Studio 2008 SP1 点击就可以使用了。

二、C#调用Win32 API函数的要点 public static extern后边的函数名字要与Win32 API的完全一样(DllImport中没有EntryPoint时。如果有EntryPoint,名字可以由用户自定义。详见示例1)。函数除了要有相应的DllImport类修饰外,还要声明成public static extern类型的。函数的返回值和参数类型要与Win32 API完全一致! 三、常用 Win32数据类型与.NET平台数据类型的对应表

在这里插入图片描述

附加:Win32类型和.net类型的对应表 BOOL=System.Int32 BOOLEAN=System.Int32 BYTE=System.UInt16 CHAR=System.Int16 COLORREF=System.UInt32 DWORD=System.UInt32 DWORD32=System.UInt32 DWORD64=System.UInt64 FLOAT=System.Float HACCEL=System.IntPtr HANDLE=System.IntPtr HBITMAP=System.IntPtr HBRUSH=System.IntPtr HCONV=System.IntPtr HCONVLIST=System.IntPtr HCURSOR=System.IntPtr HDC=System.IntPtr HDDEDATA=System.IntPtr HDESK=System.IntPtr HDROP=System.IntPtr HDWP=System.IntPtr HENHMETAFILE=System.IntPtr HFILE=System.IntPtr HFONT=System.IntPtr HGDIOBJ=System.IntPtr HGLOBAL=System.IntPtr HHOOK=System.IntPtr HICON=System.IntPtr HIMAGELIST=System.IntPtr HIMC=System.IntPtr HINSTANCE=System.IntPtr HKEY=System.IntPtr HLOCAL=System.IntPtr HMENU=System.IntPtr HMETAFILE=System.IntPtr HMODULE=System.IntPtr HMONITOR=System.IntPtr HPALETTE=System.IntPtr HPEN=System.IntPtr HRGN=System.IntPtr HRSRC=System.IntPtr HSZ=System.IntPtr HWINSTA=System.IntPtr HWND=System.IntPtr INT=System.Int32 INT32=System.Int32 INT64=System.Int64 LONG=System.Int32 LONG32=System.Int32 LONG64=System.Int64 LONGLONG=System.Int64 LPARAM=System.IntPtr LPBOOL=System.Int16[] LPBYTE=System.UInt16[] LPCOLORREF=System.UInt32[] LPCSTR=System.String LPCTSTR=System.String LPCVOID=System.UInt32 LPCWSTR=System.String LPDWORD=System.UInt32[] LPHANDLE=System.UInt32 LPINT=System.Int32[] LPLONG=System.Int32[] LPSTR=System.String LPTSTR=System.String LPVOID=System.UInt32 LPWORD=System.Int32[] LPWSTR=System.String LRESULT=System.IntPtr PBOOL=System.Int16[] PBOOLEAN=System.Int16[] PBYTE=System.UInt16[] PCHAR=System.Char[] PCSTR=System.String PCTSTR=System.String PCWCH=System.UInt32 PCWSTR=System.UInt32 PDWORD=System.Int32[] PFLOAT=System.Float[] PHANDLE=System.UInt32 PHKEY=System.UInt32 PINT=System.Int32[] PLCID=System.UInt32 PLONG=System.Int32[] PLUID=System.UInt32 PSHORT=System.Int16[] PSTR=System.String PTBYTE=System.Char[] PTCHAR=System.Char[] PTSTR=System.String PUCHAR=System.Char[] PUINT=System.UInt32[] PULONG=System.UInt32[] PUSHORT=System.UInt16[] PVOID=System.UInt32 PWCHAR=System.Char[] PWORD=System.Int16[] PWSTR=System.String REGSAM=System.UInt32 SC_HANDLE=System.IntPtr SC_LOCK=System.IntPtr SHORT=System.Int16 SIZE_T=System.UInt32 SSIZE_=System.UInt32 TBYTE=System.Char TCHAR=System.Char UCHAR=System.Byte UINT=System.UInt32 UINT32=System.UInt32 UINT64=System.UInt64 ULONG=System.UInt32 ULONG32=System.UInt32 ULONG64=System.UInt64 ULONGLONG=System.UInt64 USHORT=System.UInt16 WORD=System.UInt16 WPARAM=System.IntPtr 四、几个示例 1、弹出一个MessageBox对话框 (1)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的内容。 第三个:MessageBox的标题。 第四个:MessageBox上的按钮是什么,如果是0,那就只有一个OK;改成了4,这样就有两个按钮了。

(2)C#源代码: using System; using System.Runtime.InteropServices;//必须引入的【运行时.交互服务】(运行时的交互服务不就是“动态链接”吗?感谢Microsoft!) class Program { [DllImport("User32.dll")]//制造一个DllImport类的实例,并把这个实例绑定在我们要使用的函数上! public static extern int MessageBox(int h, string m, string c, int type); static int Main() { MessageBox(0, "内容:Hello Win32 API", "标题:风格0", 0); MessageBox(0, "内容:Hello Win32 API", "标题:风格1", 1); MessageBox(0, "内容:Hello Win32 API", "标题:风格2", 2); MessageBox(0, "内容:Hello Win32 API", "标题:风格3", 3); MessageBox(0, "内容:Hello Win32 API", "标题:风格4", 4); Console.ReadLine(); return 0; } }

运行结果【依次弹出如下对话框】: 在这里插入图片描述

在这里插入图片描述

进一步测试: 在这里插入图片描述加上入口函数名称后,进一步测试: 在这里插入图片描述

2、调用 Beep() API 来发出声音 (1)函数原型: BOOL Beep( DWORD dwFreq,   // 声音频率 DWORD dwDuration  // 声音持续时间 );

MSDN中的查询结果: 在这里插入图片描述

(2)分析:

由于 DWORD 是 4 字节的整数,因此我们可以使用 int 或 uint 作为 C# 对应类型。由于 int 是 CLS 兼容类型(可以用于所有 .NET 语言),以此比 uint 更常用,并且在多数情况下,它们之间的区别并不重要。bool 类型与 BOOL 对应。

(3)编写C#原型: [DllImport("kernel32.dll")] public static extern bool Beep(int frequency, int duration); (4)C#源代码【生成的随机声音在二十世纪六十年代的科幻电影中很常见】: using System; using System.Runtime.InteropServices; namespace Beep { class Class1 { [DllImport("kernel32.dll")] public static extern bool Beep(int frequency, int duration); static void Main(string[] args) { Random random = new Random(); for (int i = 0; i


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3