我们来详细学习如何使用 user32.dll
,它是 Windows 系统中负责用户界面交互的核心 DLL,包含窗口管理、消息处理、键盘鼠标输入等功能。下面从基础到进阶,一步一步教你调用其中的常用函数。
在 C# 中调用 user32.dll
需要使用 DllImport
特性,这需要引入 System.Runtime.InteropServices
命名空间。所有示例都基于这个前提,先创建一个控制台应用程序(或 Windows 应用程序),并在代码开头添加:
cs
using System;
using System.Runtime.InteropServices; // 必须引入,用于DllImport
第一步:调用最基础的函数 ------ 显示消息框(MessageBox)
MessageBox
是 user32.dll
中最常用的函数之一,用于显示系统风格的弹窗。
步骤 1:声明函数
首先需要在 C# 中声明 user32.dll
中的 MessageBox
函数,函数定义要和 DLL 中的原生接口一致:
cs
// 声明 user32.dll 中的 MessageBox 函数
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern int MessageBox(
IntPtr hWnd, // 父窗口句柄(null表示无父窗口)
string lpText, // 弹窗内容
string lpCaption, // 弹窗标题
uint uType // 弹窗样式(按钮、图标组合)
);
CharSet = CharSet.Unicode:确保中文等字符正常显示。
返回值 int:表示用户点击的按钮(如 1 = 确定,2 = 取消)。
步骤 2:调用函数
在 Main
方法中调用声明好的函数,测试不同样式的弹窗:
cs
static void Main(string[] args)
{
// 示例1:基础弹窗(只有"确定"按钮)
int result1 = MessageBox(
IntPtr.Zero, // 无父窗口
"这是一个简单的消息框", // 内容
"基础示例", // 标题
0x00000000 // 样式:只有"确定"按钮(0)
);
Console.WriteLine($"用户点击了按钮:{result1}"); // 点击"确定"会输出 1
// 示例2:带"确定"和"取消"按钮 + 警告图标
int result2 = MessageBox(
IntPtr.Zero,
"是否继续操作?",
"确认提示",
0x00000003 | 0x00000030 // 0x3=确定+取消;0x30=警告图标(组合用 |)
);
Console.WriteLine($"用户点击了按钮:{result2}"); // 确定=1,取消=2
}
运行效果
第一个弹窗只有 "确定" 按钮,点击后控制台输出 1。
第二个弹窗有 "确定""取消" 按钮和警告图标,点击对应按钮会输出 1 或 2。
第二步:获取屏幕分辨率(GetSystemMetrics)
GetSystemMetrics
函数可以获取系统相关的尺寸信息,比如屏幕宽度、高度。
步骤 1:声明函数
cs
// 声明获取系统尺寸的函数
[DllImport("user32.dll")]
public static extern int GetSystemMetrics(int nIndex);
参数 nIndex:指定要获取的信息(比如 0 = 屏幕宽度,1 = 屏幕高度)。
步骤 2:调用函数
cs
static void Main(string[] args)
{
// 获取屏幕宽度(参数 0 表示 SM_CXSCREEN)
int screenWidth = GetSystemMetrics(0);
// 获取屏幕高度(参数 1 表示 SM_CYSCREEN)
int screenHeight = GetSystemMetrics(1);
Console.WriteLine($"屏幕分辨率:{screenWidth} × {screenHeight}");
// 额外示例:获取任务栏高度(参数 29 表示 SM_CYCAPTION)
int taskbarHeight = GetSystemMetrics(29);
Console.WriteLine($"任务栏高度:{taskbarHeight} 像素");
}
运行效果
控制台会输出你的屏幕分辨率(如 1920 × 1080
)和任务栏高度。
第三步:查找窗口并操作(FindWindow + SetWindowText)
FindWindow:根据窗口标题或类名查找窗口句柄(类似窗口的 "身份证")。
SetWindowText:修改窗口的标题。
cs
// 查找窗口(根据类名和窗口名)
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
// 修改窗口标题
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern bool SetWindowText(IntPtr hWnd, string lpString);
步骤 2:调用函数(实战:修改记事本窗口标题)
先手动打开一个记事本(Notepad),标题默认为 "无标题 - 记事本"。
运行以下代码:
cs
static void Main(string[] args)
{
// 查找记事本窗口(类名"Notepad",标题"无标题 - 记事本")
IntPtr notepadWnd = FindWindow("Notepad", "无标题 - 记事本");
if (notepadWnd != IntPtr.Zero)
{
// 找到窗口后,修改标题
bool success = SetWindowText(notepadWnd, "被C#修改过的标题 - 记事本");
if (success)
{
Console.WriteLine("记事本标题修改成功!");
}
else
{
Console.WriteLine("标题修改失败");
}
}
else
{
Console.WriteLine("未找到记事本窗口,请先打开记事本");
}
}
运行效果
如果记事本已打开且标题正确,它的标题会被改为 "被 C# 修改过的标题 - 记事本"。
四、移动鼠标:SetCursorPos
函数
SetCursorPos
可以直接设置鼠标指针在屏幕上的位置,非常直观。
步骤 1:声明函数
cs
using System;
using System.Runtime.InteropServices;
class User32Demo
{
// 声明 SetCursorPos 函数:设置鼠标位置
[DllImport("user32.dll")]
public static extern extern bool SetCursorPos(int X, int Y);
// 参数:X(水平坐标)、Y(垂直坐标),返回值:是否成功
}
步骤 2:使用函数移动鼠标
cs
static void Main(string[] args)
{
// 1. 移动鼠标到屏幕左上角(0, 0)
bool success1 = SetCursorPos(0, 0);
Console.WriteLine($"移动到左上角:{ (success1 ? "成功" : "失败") }");
// 等待1秒,让你看清效果
System.Threading.Thread.Sleep(1000);
// 2. 移动鼠标到屏幕中心(假设屏幕分辨率是1920×1080)
int screenWidth = 1920; // 可通过前面学的 GetSystemMetrics(0) 获取实际宽度
int screenHeight = 1080; // 可通过 GetSystemMetrics(1) 获取实际高度
bool success2 = SetCursorPos(screenWidth / 2, screenHeight / 2);
Console.WriteLine($"移动到中心:{ (success2 ? "成功" : "失败") }");
// 等待1秒
System.Threading.Thread.Sleep(1000);
// 3. 移动鼠标到右下角
bool success3 = SetCursorPos(screenWidth - 10, screenHeight - 10);
Console.WriteLine($"移动到右下角:{ (success3 ? "成功" : "失败") }");
}
运行效果
鼠标会依次移动到屏幕左上角 → 中心 → 右下角,每次移动后会在控制台显示结果。
五、发送窗口消息:SendMessage
函数
SendMessage
是 user32.dll
中非常强大的函数,它可以向指定窗口发送各种消息(如点击按钮、输入文本、关闭窗口等),实现对窗口的精细控制。
先理解:什么是 "窗口消息"?
Windows 系统中,所有窗口交互都是通过 "消息" 机制实现的。例如:
点击按钮 → 窗口收到 WM_LBUTTONDOWN(左键按下)消息
键盘输入 → 窗口收到 WM_CHAR(字符输入)消息
窗口关闭 → 窗口收到 WM_CLOSE 消息
SendMessage 就是手动给窗口发送这些消息,模拟用户操作。
步骤 1:声明函数和常用消息常量
cs
class User32Demo
{
// 声明 SendMessage 函数:发送消息到窗口
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr SendMessage(
IntPtr hWnd, // 目标窗口句柄(通过 FindWindow 获取)
uint Msg, // 消息类型(如 WM_CLOSE 表示关闭窗口)
IntPtr wParam, // 消息参数1(根据消息类型变化)
string lParam // 消息参数2(字符串类型,如输入的文本)
);
// 常用消息常量(可在微软文档中查询更多)
public const uint WM_CLOSE = 0x0002; // 关闭窗口消息
public const uint WM_SETTEXT = 0x000C; // 设置窗口文本(如输入框内容)
public const uint WM_LBUTTONDOWN = 0x0201; // 鼠标左键按下
public const uint WM_LBUTTONUP = 0x0202; // 鼠标左键释放(模拟点击)
}
步骤 2:实战 1:关闭指定窗口(发送 WM_CLOSE
消息)
以关闭记事本为例:
cs
static void Main(string[] args)
{
// 1. 先找到记事本窗口(确保已打开,标题为"无标题 - 记事本")
IntPtr notepadWnd = FindWindow("Notepad", "无标题 - 记事本");
if (notepadWnd == IntPtr.Zero)
{
Console.WriteLine("未找到记事本窗口");
return;
}
// 2. 发送关闭窗口消息(WM_CLOSE)
SendMessage(notepadWnd, User32Demo.WM_CLOSE, IntPtr.Zero, null);
Console.WriteLine("已发送关闭消息,记事本应该会关闭");
}
// 注意:需要先声明前面学过的 FindWindow 函数
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
运行效果
如果记事本已打开,会收到关闭消息并弹出保存提示(和手动点击关闭按钮效果一致)。
步骤 3:实战 2:向记事本输入文本(发送 WM_SETTEXT
消息)
记事本的编辑区域是一个子窗口(类名 "Edit"
),需要先找到它的句柄,再发送输入消息:
cs
static void Main(string[] args)
{
// 1. 找到记事本主窗口
IntPtr notepadWnd = FindWindow("Notepad", null); // 忽略标题,找所有记事本
if (notepadWnd == IntPtr.Zero)
{
Console.WriteLine("请先打开一个记事本");
return;
}
// 2. 找到记事本的编辑区域子窗口(类名"Edit",无标题)
IntPtr editWnd = FindWindowEx(notepadWnd, IntPtr.Zero, "Edit", null);
if (editWnd == IntPtr.Zero)
{
Console.WriteLine("未找到编辑区域");
return;
}
// 3. 发送输入文本消息(WM_SETTEXT)
SendMessage(editWnd, User32Demo.WM_SETTEXT, IntPtr.Zero, "这是通过 SendMessage 输入的文本!");
Console.WriteLine("已向记事本输入文本");
}
// 额外声明 FindWindowEx 函数(用于查找子窗口)
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindowEx(
IntPtr hWndParent, // 父窗口句柄
IntPtr hWndChildAfter, // 起始子窗口(null表示从第一个开始)
string lpClassName, // 子窗口类名
string lpWindowName // 子窗口标题
);
运行效果
记事本的编辑区域会自动填入文本 "这是通过 SendMessage 输入的文本!"。