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消息和消息队列

参考

相关文章
|
17天前
|
存储 监控 算法
基于 C# 的局域网计算机监控系统文件变更实时监测算法设计与实现研究
本文介绍了一种基于C#语言的局域网文件变更监控算法,通过事件驱动与批处理机制结合,实现高效、低负载的文件系统实时监控。核心内容涵盖监控机制选择(如事件触发机制)、数据结构设计(如监控文件列表、事件队列)及批处理优化策略。文章详细解析了C#实现的核心代码,并提出性能优化与可靠性保障措施,包括批量处理、事件过滤和异步处理等技术。最后,探讨了该算法在企业数据安全监控、文件同步备份等场景的应用潜力,以及未来向智能化扩展的方向,如文件内容分析、智能告警机制和分布式监控架构。
41 3
|
2月前
|
SQL 数据库 数据安全/隐私保护
C#wpf学习卡后台管理系统
C#wpf学习卡后台管理系统
86 32
|
4月前
|
存储 安全 UED
Cyber Triage 3.13 for Windows - 数字取证和事件响应
Cyber Triage 3.13 for Windows - 数字取证和事件响应
148 71
Cyber Triage 3.13 for Windows - 数字取证和事件响应
|
4月前
|
物联网 数据处理 C#
C#实现上位机开发,串口通信,读写串口数据并处理16进制数据
C#实现上位机开发,串口通信,读写串口数据并处理16进制数据。在自动化、物联网以及工业控制行业中,上位机开发是一项重要的技能。本教程主要介绍使用C#进行上位机开发,重点在于串口通信和数据处理。
712 82
|
2月前
|
SQL 小程序 API
如何运用C#.NET技术快速开发一套掌上医院系统?
本方案基于C#.NET技术快速构建掌上医院系统,结合模块化开发理念与医院信息化需求。核心功能涵盖用户端的预约挂号、在线问诊、报告查询等,以及管理端的排班管理和数据统计。采用.NET Core Web API与uni-app实现前后端分离,支持跨平台小程序开发。数据库选用SQL Server 2012,并通过读写分离与索引优化提升性能。部署方案包括Windows Server与负载均衡设计,确保高可用性。同时针对API差异、数据库老化及高并发等问题制定应对措施,保障系统稳定运行。推荐使用Postman、Redgate等工具辅助开发,提升效率与质量。
|
6月前
|
开发框架 .NET Java
C#集合数据去重的5种方式及其性能对比测试分析
C#集合数据去重的5种方式及其性能对比测试分析
75 11
|
6月前
|
开发框架 .NET Java
C#集合数据去重的5种方式及其性能对比测试分析
C#集合数据去重的5种方式及其性能对比测试分析
99 10
|
6月前
|
存储 监控 算法
企业内网监控系统中基于哈希表的 C# 算法解析
在企业内网监控系统中,哈希表作为一种高效的数据结构,能够快速处理大量网络连接和用户操作记录,确保网络安全与效率。通过C#代码示例展示了如何使用哈希表存储和管理用户的登录时间、访问IP及操作行为等信息,实现快速的查找、插入和删除操作。哈希表的应用显著提升了系统的实时性和准确性,尽管存在哈希冲突等问题,但通过合理设计哈希函数和冲突解决策略,可以确保系统稳定运行,为企业提供有力的安全保障。
|
6月前
|
缓存 安全 网络协议
使用事件日志识别常见 Windows 错误
事件查看器是Windows操作系统中的标准诊断工具,用于记录系统事件,包括硬件问题、软件中断和系统行为等详细信息。通过分析这些日志,管理员能够追踪和解决系统错误。访问方法包括使用快捷键Win + R输入eventvwr.msc,或通过控制面板进入。事件查看器中的每条记录包含事件ID、来源和描述,帮助识别和解决问题。常见错误如蓝屏死机、DLL错误、驱动程序错误等,可通过更新驱动程序、运行系统诊断、使用恢复功能等方式解决。
387 4
|
8月前
|
存储 开发框架 .NET
C#语言如何搭建分布式文件存储系统
C#语言如何搭建分布式文件存储系统
163 2