C#实现操作Windows窗口句柄:SendMessage/PostMessage发送系统消息、事件和数据【窗口句柄总结之二】

简介: SendMessage/PostMessage API 可以实现发送系统消息,这些消息可以定义为常见的鼠标或键盘事件、数据的发送等各种系统操作......

SendMessage向窗口或控件发送文本、按键、鼠标事件等消息

SendMessage的定义如下,用于向窗口句柄发送系统定义的消息,以及额外的数据。

LRESULT SendMessage(
  [in] HWND   hWnd,
  [in] UINT   Msg,
  [in] WPARAM wParam,
  [in] LPARAM lParam
);

SendMessage向窗口发送消息事件

向窗口发送文字(输入文字)

WM_SETTEXT = 0x000C表示发送文字消息,第4个参数表示文字内容。

/// <summary>
/// 发送文本到窗口句柄(或控件)
/// </summary>
/// <param name="hWnd"></param>
/// <param name="text"></param>
public static void SendText(IntPtr hWnd, string text)
{
    SendMessage(hWnd, WM_SETTEXT, IntPtr.Zero, text);
}

const int WM_SETTEXT = 0x000C;
[DllImport("User32.dll", EntryPoint = "SendMessage")]
private static extern int SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, string lParam);

我们通过帮助类项Winform的TextBox控件发送文本测试:

var wndHandle = WndHelper.FindWindow(null, "Form测试窗体的标题栏");

if (wndHandle != IntPtr.Zero)
{
    IntPtr txtHandle = WndHelper.FindWindowEx(wndHandle, IntPtr.Zero, null, "");
    if (txtHandle != IntPtr.Zero)
    {
        WndHelper.SendText(txtHandle,"我是文字");
    }
}

这个时候,如果有两个TextBox控件,则永远只会获取某一个(最后一个添加到窗体的TextBox),因此,只会向此控件发送消息文本。

这个时候,就需要通过遍历的方式获取到其他控件了。

向窗口发送键盘按键、发送回车键

/// <summary>
/// 发送回车键(Enter)到窗口(或控件)
/// </summary>
/// <param name="hWnd"></param>
public static void SendEnter(IntPtr hWnd)
{
    SendMessage(hWnd, WM_CHAR, VK_ENTER, 0);
}
/// <summary>
/// 发送键盘按键到窗口(或控件)
/// </summary>
/// <param name="hWnd"></param>
/// <param name="keyValue">按键的数字值KeyValue</param>
public static void SendKey(IntPtr hWnd, uint keyValue)
{
    SendMessage(hWnd, WM_CHAR, keyValue, 0);
}
const int WM_CHAR = 0x0102;
const uint VK_ENTER = 0x0D; // 13 Enter键
[DllImport("User32.dll", EntryPoint = "SendMessage")]
private static extern int SendMessage(IntPtr hWnd, uint Msg, uint wParam, uint lParam);
结合第四个参数可以发送组合按键。

比如,发送字母A的按键:

WndHelper.SendKey(txtHandle, 65); // 发送字母A

发送回车

WndHelper.SendEnter(txtHandle);
关于键盘输入更多的内容参见 Using Keyboard Input

向窗口发送鼠标事件

前面的代码中已经有使用SendClick发送鼠标事件的代码,下面对此进行下介绍。

  • 发送Click点击事件
/// <summary>
/// 发送点击事件
/// </summary>
/// <param name="hwnd">控件句柄</param>
public static void SendClick(IntPtr hwnd)
{
    SendMessage(hwnd, WM_CLICK, 0, 0);
}
/// <summary>
/// 发送点击事件
/// </summary>
/// <param name="hwnd">控件句柄</param>
/// <param name="x">鼠标位置x</param>
/// <param name="y">鼠标位置y</param>
public static void SendClick(IntPtr hwnd, int X, int Y)
{
    int lparm = (Y << 16) + X;
    SendMessage(hwnd, WM_CLICK, 0, lparm);
}

[DllImport("user32.dll", EntryPoint = "SendMessage")]
private static extern int SendMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);
/// <summary>
/// 点击消息
/// </summary>
const uint WM_CLICK = 0xF5;

直接调用SendClick方法即可。

  • 发送鼠标按下、抬起事件,模拟点击
/// <summary>
/// 发送鼠标点击事件,包含MouseDown和MouseUp
/// </summary>
/// <param name="hwnd">控件句柄</param>
/// <param name="x">鼠标位置x</param>
/// <param name="y">鼠标位置y</param>
public static void SendMouseClick(IntPtr hwnd, int X, int Y)
{
    int lparm = (Y << 16) + X;
    int lngResult = SendMessage(hwnd, WM_LBUTTONDOWN, 0, lparm);
    int lngResult2 = SendMessage(hwnd, WM_LBUTTONUP, 0, lparm);
}
/// <summary>
/// 鼠标按下
/// </summary>
const uint WM_LBUTTONDOWN = 0x0201;
/// <summary>
/// 鼠标抬起
/// </summary>
const uint WM_LBUTTONUP = 0x0202;

向窗口发送关闭指令

public static void CloseWindow(IntPtr hwnd)
{
    SendMessage(hwnd, WM_CLOSE, 0, 0);
}

const int WM_CLOSE = 0x0010;

SendMessage实现在进程间传送数据

SendMessage的另一个巧妙的作用是实现跨窗体,或跨进程间的数据传递,当然,在传递时需要在对应的窗口处理函数WndProc中进行数据的获取。

对于Winform,即需要重写WndProc函数接受数据传递。

对于同一进程内的不同窗体间的数据传递,理论上,SendMessage可以使用任何消息传递额外数据(应该是不会产生额外操作的消息);

但是对于跨进程间的数据传递,则需要使用WM_COPYDATA = 0x004A消息类型实现,它用于传递只读数据。

更具体的介绍参见后续的文章。

SendMessage和PostMessage函数【更推荐PostMessage】

SendMessage用于向Windows或窗口发送特定的消息,SendMessage会调用窗口处理程序,并且直到窗口处理程序处理完消息才会返回。

所以,SendMessage发送消息会阻塞当前线程的执行,直到消息处理完成,这一点需要特别注意。

PostMessageSendMessage的作用一样,但是PostMessage是将消息发送到创建窗口的线程关联的消息队列中,并立即返回,不会等到消息处理完成。

PostMessage相当于异步发送消息,不会阻塞等待。

在多线程应用中推荐使用PostMessage,可以在各自创建的窗口间进行安全的通信,非常有用。

另外的想要发送消息,并立即返回,可以参考SendMessageCallbackSendNotifyMessage

PostMessagePostThreadMessage 函数则是发送到线程消息队列,并立即返回。

关于SendMessage和PostMessage的返回

SendMessage的返回表示消息处理的结果、消息处理后的返回值,由于无法控制另一个窗体内的消息处理,实际获取的结果作用并不大。可以作为消息是否正确处理完成的参考。

PostMessage返回表示是否发送成功,是一个bool值。但并不能决定何时执行、是否执行,只是成功发送到了消息队列中。

扩展 The difference between SendMessage and PostMessage

SendMessage发送的消息和系统定义的消息类型(消息列表)

参见官方文档Windows消息和消息队列

参考

相关文章
|
7月前
|
运维 安全 Linux
【清爽加速】Windows 11 Pro 24H2-Emmy精简系统
“清爽加速”Windows 11 Pro 24H2 针对老旧或低配设备,通过精简系统、优化服务与简化装机流程,降低资源占用,提升运行流畅度,兼顾安全性与稳定性,让老设备也能轻松应对日常办公与轻度娱乐需求。
469 1
【清爽加速】Windows 11 Pro 24H2-Emmy精简系统
|
7月前
|
安全 搜索推荐 开发者
【适度精简】Windows 7 旗舰版-emmy精简系统
Windows 7旗舰版因硬件占用高、冗余组件多、兼容性差及缺乏安全更新等问题,逐渐难以满足用户需求。适度精简版通过去除无用组件、优化性能与安全性,提升老旧设备运行效率,增强兼容性与稳定性,同时保留用户熟悉的操作界面,降低学习成本,满足个性化需求,延续Windows 7的实用价值。
361 2
|
7月前
|
安全 数据安全/隐私保护 Windows
ZyperWin++使用教程!让Windows更丝滑!c盘飘红一键搞定!ZyperWin++解决系统优化、Office安装和系统激活
ZyperWin++是一款仅5MB的开源免费Windows优化工具,支持快速优化、自定义设置与垃圾清理,兼具系统加速、隐私保护、Office安装等功能,轻便无广告,小白也能轻松上手,是提升电脑性能的全能管家。
2143 0
|
8月前
|
Ubuntu Linux Windows
windows11系统安装ubuntu系统详细步骤
安装后,您可以直接从商店启动应用程序来源
1547 0
|
9月前
|
持续交付 Windows
如何使用Sysprep准备Windows系统并使用自动应答
通过Sysprep准备Windows系统,可实现SID重置与系统定制。进入Sysprep后,可安装软件、设置默认桌面文件,并使用Windows SIM创建应答文件以实现自动化部署。适用于系统克隆与批量部署场景。
|
分布式数据库 Windows
|
6月前
|
安全 数据安全/隐私保护 虚拟化
Windows Server 2022 中文版、英文版下载 (2025 年 10 月更新)
Windows Server 2022 中文版、英文版下载 (2025 年 10 月更新)
1668 2
Windows Server 2022 中文版、英文版下载 (2025 年 10 月更新)
|
6月前
|
安全 Unix 物联网
Windows 7 & Windows Server 2008 R2 简体中文版下载 (2025 年 10 月更新)
Windows 7 & Windows Server 2008 R2 简体中文版下载 (2025 年 10 月更新)
706 0
Windows 7 & Windows Server 2008 R2 简体中文版下载 (2025 年 10 月更新)
|
6月前
|
存储 SQL 人工智能
Windows Server 2025 中文版、英文版下载 (2025 年 10 月更新)
Windows Server 2025 中文版、英文版下载 (2025 年 10 月更新)
954 0
|
7月前
|
运维 安全 网络安全
Windows Server 2019拨号“找不到设备”?Error 1058解决指南
Windows Server 2019拨号报错1058?别急!这不是硬件故障,而是关键服务被禁用。通过“服务依存关系”排查,依次启动“安全套接字隧道协议”“远程接入连接管理”和“路由与远程访问”服务,仅需4步即可恢复PPPoE或VPN拨号功能,轻松解决网络中断问题。
569 1