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

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.dllkernel32.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函数:

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

  2. 直接查找“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函数的要点

  1. public static extern后边的函数名字要与Win32 API的完全一样(DllImport中没有EntryPoint时。如果有EntryPoint,名字可以由用户自定义。详见示例1)。
  2. 函数除了要有相应的DllImport类修饰外,还要声明成public static extern类型的。
  3. 函数的返回值和参数类型要与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 < 10000; i++)
            {
                Beep(random.Next(10000), 100);
            }

            Console.WriteLine("End");
        }
    }
}

参考:

https://blog.csdn.net/bcbobo21cn/article/details/50930221
C#调用Win32常见API函数汇总: https://blog.csdn.net/zhengzhe1937/article/details/8528363

C#学习汇总 - 总目录

  • 10
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ElecNoon

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值