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

参考

相关文章
|
29天前
|
安全 Windows
永久关闭 Windows 11 系统更新
永久关闭 Windows 11 系统更新
114 0
|
13天前
|
存储 负载均衡 Java
如何配置Windows主机MPIO多路径访问存储系统
Windows主机多路径(MPIO)是一种技术,用于在客户端计算机上配置多个路径到存储设备,以提高数据访问的可靠性和性能。本文以Windows2012 R2版本为例介绍如何在客户端主机和存储系统配置多路径访问。
55 13
如何配置Windows主机MPIO多路径访问存储系统
|
3天前
|
Windows Python
如何反向读取Windows系统日志EVTX文件?
以下是如何反向读取Windows系统日志EVTX文件
12 2
|
28天前
|
Windows
.NET 隐藏/自定义windows系统光标
【10月更文挑战第20天】在.NET中,可以使用`Cursor`类来控制光标。要隐藏光标,可将光标设置为`Cursors.None`。此外,还可以通过从文件或资源加载自定义光标来更改光标的样式。例如,在表单加载时设置`this.Cursor = Cursors.None`隐藏光标,或使用`Cursor.FromFile`方法加载自定义光标文件,也可以将光标文件添加到项目资源中并通过资源管理器加载。这些方法适用于整个表单或特定控件。
|
28天前
|
Apache 数据中心 Windows
将网站迁移到阿里云Windows系统云服务器,访问该站点提示连接被拒绝,如何处理?
将网站迁移到阿里云Windows系统云服务器,访问该站点提示连接被拒绝,如何处理?
|
28天前
|
域名解析 缓存 网络协议
Windows系统云服务器自定义域名解析导致网站无法访问怎么解决?
Windows系统云服务器自定义域名解析导致网站无法访问怎么解决?
|
29天前
|
Windows
安装Windows XP系统
安装Windows XP系统
|
2天前
|
监控 安全 网络安全
Windows Server管理:配置与管理技巧
Windows Server管理:配置与管理技巧
15 3
|
5天前
|
存储 安全 网络安全
Windows Server 本地安全策略
由于广泛使用及历史上存在的漏洞,Windows服务器成为黑客和恶意行为者的主要攻击目标。这些系统通常存储敏感数据并支持关键服务,因此组织需优先缓解风险,保障业务的完整性和连续性。常见的威胁包括勒索软件、拒绝服务攻击、内部威胁、恶意软件感染等。本地安全策略是Windows操作系统中用于管理计算机本地安全性设置的工具,主要包括用户账户策略、安全选项、安全设置等。实施强大的安全措施,如定期补丁更新、网络分段、入侵检测系统、数据加密等,对于加固Windows服务器至关重要。
|
1月前
|
边缘计算 安全 网络安全