C# 调用 Win32 API的实现示例
Win32 API(Windows 32-bit Application Programming Interface)是微软为 Windows 操作系统提供的底层编程接口,包含了操作系统的核心功能(如窗口管理、文件操作、进程控制、内存管理、系统信息获取等),本质上是一组用 C/C++ 编写的原生函数。
一、核心概念解析
1. 什么是 Win32 API?
Win32 API(Windows 32-bit Application Programming Interface)是微软为 Windows 操作系统提供的底层编程接口,包含了操作系统的核心功能(如窗口管理、文件操作、进程控制、内存管理、系统信息获取等),本质上是一组用 C/C++ 编写的原生函数。
2. C# 为什么能调用 Win32 API?
C# 运行在 .NET 运行时(CLR)中,属于托管代码;而 Win32 API 是非托管代码(直接运行在操作系统层面)。.NET 提供了 P/Invoke(Platform Invocation Services,平台调用服务) 机制,这是 CLR 提供的核心功能,允许托管代码调用非托管的函数(如 Win32 API)。
3. P/Invoke 核心要素
要在 C# 中调用 Win32 API,必须满足以下条件:
- 函数签名匹配:C# 中声明的函数必须和 Win32 API 的原生签名(返回值、参数类型、调用约定)一致;
- DLL 导入:指定 Win32 API 所在的系统 DLL(如
kernel32.dll、user32.dll、advapi32.dll等); - 数据类型映射:C/C++ 的原生类型(如
DWORD、HANDLE、LPCSTR)需要映射到 C# 对应的类型(如uint、IntPtr、string)。
二、Win32 API 调用的语法规则
1. 基础声明格式
在 C# 中,通过 DllImport 特性(位于 System.Runtime.InteropServices 命名空间)声明 Win32 API 函数,核心语法如下:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
|
2. 关键参数说明
| 特性参数 | 作用 |
| DllName | Win32 API 所在的系统 DLL 名称(如 kernel32.dll、user32.dll) |
| CharSet | 字符编码:CharSet.Ansi(ANSI)、CharSet.Unicode(UTF-16)、CharSet.Auto(自动) |
| SetLastError | 设为 true 时,可通过 Marshal.GetLastWin32Error() 获取系统错误码 |
| CallingConvention | 调用约定:Win32 API 默认为 StdCall(C# 默认为 Winapi,等价于 StdCall) |
3. 常见类型映射(Win32 → C#)
Win32 API 的原生类型和 C# 类型必须严格映射,否则会导致调用失败甚至程序崩溃:
|
C# 等效类型 | 说明 | ||
| DWORD | uint | 32 位无符号整数 | ||
| HANDLE | IntPtr | 句柄(指针类型,用 IntPtr 兼容 32/64 位) | ||
| LPCSTR | string | ANSI 字符串(常量指针) | ||
| LPWSTR | string | Unicode 字符串(可变指针) | ||
| BOOL | bool/int | Win32 的 BOOL 是 int(0 / 非 0),C# 可用 bool 兼容 | ||
|
int/long | 直接映射 | ||
| VOID | void | 无返回值 |
三、控制台实战案例(多个场景)
环境准备
- 开发工具:Visual Studio(任意版本)或 VS Code + .NET SDK
- 创建项目:控制台应用(.NET Framework/.NET Core/.NET 5+ 均可,示例用 .NET 8)
- 核心命名空间:
System.Runtime.InteropServices(必须)
案例 1:获取系统目录(简单无参数 / 返回值)
需求:调用 kernel32.dll 中的 GetSystemDirectory 函数,获取 Windows 系统目录(如 C:\Windows\System32)。
步骤 1:查看 Win32 API 原生签名
|
1 2 3 4 5 |
|
- 返回值:实际复制到缓冲区的字符数(不含终止符);
- 字符集:
A后缀表示 ANSI,W后缀表示 Unicode(推荐用 Unicode)。
步骤 2:C# 声明并调用
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
|
运行结果
Windows 系统目录:C:\Windows\System32
案例 2:弹出系统消息框(调用 user32.dll)
需求:调用 user32.dll 中的 MessageBox 函数,弹出 Windows 原生消息框(控制台程序也能调用 GUI 相关 API)。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
|
关键说明
MessageBoxW的返回值:1 = 确定,2 = 取消,3 = 终止,4 = 重试,5 = 忽略等;- 消息框类型可以通过
|组合(如MB_OK | MB_ICONWARNING表示 OK 按钮 + 警告图标); - 控制台程序调用 GUI API 时,
hWnd传IntPtr.Zero即可。
案例 3:获取进程 ID(调用 GetCurrentProcessId)
需求:调用 kernel32.dll 的 GetCurrentProcessId 获取当前控制台程序的进程 ID。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
运行结果
当前控制台程序的进程 ID:12345
按任意键退出...
案例 4:读写 INI 文件
需求:调用 kernel32.dll 的 WritePrivateProfileString 和 GetPrivateProfileString 读写 INI 配置文件(Win32 原生 INI 操作)。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
|
运行结果
成功写入 INI 文件:D:\Win32ApiDemo\bin\Debug\net8.0\demo.ini
读取到的值:张三
生成的 INI 文件内容
|
1 2 |
|
四、常见问题与避坑指南
1. 调用失败的常见原因
- 类型不匹配:如 Win32 的
DWORD用了 C# 的int(虽然有时能运行,但 64 位系统会出问题); - 字符集错误:调用
A后缀的 API 却用CharSet.Unicode,或反之; - 调用约定错误:Win32 API 几乎都是
StdCall,若设为Cdecl会导致栈溢出; - 缓冲区大小不足:如获取系统目录时缓冲区太小,返回值为 0;
- 权限问题:部分 Win32 API 需要管理员权限(如修改系统设置),需右键以管理员运行程序。
2. 如何调试 Win32 API 调用?
- 启用
SetLastError = true,调用后通过Marshal.GetLastWin32Error()获取错误码,对照 Windows 错误码表 排查; - 检查 API 名称是否正确(如是否漏写
W/A后缀); - 用
IntPtr替代所有句柄类型(避免 32/64 位兼容性问题); - 在 try-catch 中捕获
EntryPointNotFoundException(API 名称错误)、AccessViolationException(内存访问错误)等异常。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)