当前位置: 首页 > news >正文

Windows C++ 任意线程通过hwnd将操作发送到UI线程执行


前言

做Windows界面开发时,经常需要在多线程环境中将操作抛到主线程执行,通常做法是定义一个WM_USER消息,将函数指针通过SendMessage发送给窗口,窗口过程中接收消息后执行函数。本文提供的方法可以在任意地方使用,不需要重新定义消息以及接收消息。


一、基本实现

只是基本的实现方法,也包含了基本原理。

1、自定义WM消息

#define  WM_INVOKE WM_USER+3328

2、发送消息

需要UI线程执行的函数

 void action(void* arg){
 //ui线程执行的操作
 }

发送到ui线程

SendMessage(hwnd, WM_INVOKE, action, arg);

3、窗口过程中执行函数

static LRESULT  wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparma)
{
	switch (msg) {
	case WM_INVOKE:
	{	
		void(*action)(void* s);
		action = wparam;
		action(lparma);
	}
	break;
	}
	return 0;
}

二、优化实现

上述实现,需要在每个窗口或每个项目的窗口过程写代码,移植起来很麻烦。我们通过钩子的方式做成,实现一次到处使用
定义消息略,与基本实现一致。

1、钩子过程中执行函数

定义一个钩子过程,并在钩子过程中执行函数。

LRESULT CALLBACK hook_proc(int code, WPARAM wParam, LPARAM lParam) {
	CWPSTRUCT* msg = lParam;
	if (msg->message == WM_INVOKE) { 
		((void(*)(void* s)) msg->wParam)(msg->lParam);
	}
	return 0;
}

2、设置钩子

HHOOK hook = SetWindowsHookEx(WH_CALLWNDPROC, hook_proc, GetModuleHandle(NULL), GetWindowThreadProcessId(hwnd, NULL));

3、发送消息

将函数通过消息发送出去

SendMessage(hwnd, WM_INVOKE, action, arg);

4、卸载钩子

UnhookWindowsHookEx(hook);

三、完整代码

1、C

#include<Windows.h>
#define  WM_INVOKE WM_USER+3328
static LRESULT CALLBACK hook_proc(int code, WPARAM wParam, LPARAM lParam) {
	CWPSTRUCT* msg = lParam;
	if (msg->message == WM_INVOKE) { 
		((void(*)(void* s)) msg->wParam)(msg->lParam);
	}
	return 0;
}
/// <summary>
/// 将操作切换到窗口线程执行,同步。
/// </summary>
/// <param name="hwnd">窗口句柄</param>
/// <param name="action">执行的操作</param>
/// <param name="arg">透传参数</param>
void invoke(HWND hwnd, void(*action)(void* arg), void* arg) {
	if (GetCurrentThreadId() != GetWindowThreadProcessId(hwnd, NULL)) {
		HHOOK hook = SetWindowsHookEx(WH_CALLWNDPROC, hook_proc, GetModuleHandle(NULL), GetWindowThreadProcessId(hwnd, NULL));
		SendMessage(hwnd, WM_INVOKE, action, arg);
		UnhookWindowsHookEx(hook);
	}
	else action(arg);
}

1、C++

与c的区别是,能使用std::function,可捕获变量。

#include<Windows.h>
#include<functional>
#define  WM_INVOKE WM_USER+3328
static LRESULT CALLBACK hook_proc(int code, WPARAM wParam, LPARAM lParam) {
	CWPSTRUCT* msg = (CWPSTRUCT*)lParam;
	if (msg->message == WM_INVOKE) {
		(*((std::function<void()>*) msg->wParam))();
	}
	return 0;
}
/// <summary>
/// 将操作切换到窗口线程执行,同步。
/// </summary>
/// <param name="hwnd">窗口句柄</param>
/// <param name="func">执行的操作</param>
void invoke(HWND hwnd, const std::function<void()>& func) {
	if (GetCurrentThreadId() != GetWindowThreadProcessId(hwnd, NULL)) {
		HHOOK hook = SetWindowsHookEx(WH_CALLWNDPROC, hook_proc, GetModuleHandle(NULL), GetWindowThreadProcessId(hwnd, NULL));
		SendMessage(hwnd, WM_INVOKE, (WPARAM)&func, 0);
		UnhookWindowsHookEx(hook);
	}
	else func();
}

四、使用示例

1、C

void action(void* arg)
{
	printf("invoked %d\n",(int)arg);
}
invoke(hwnd,action,123)

2、C++

int a=123;
invoke(hwnd, [&]() {
	printf("invoked %d\n", a);
	});

总结

以上就是今天要讲的内容,本文仅仅简单的实现了通用的线程invoke,且只支持同步,通用的异步invoke实现稍微复杂些(基本实现的方式则比较简单),以后有空再做。总的来说,有了本文的代码很大程度的方便了使用,尤其是一个新的项目突然需要invoke功能,按照基本实现的方式在窗口中写一遍是很麻烦的,而优化的实现则可以直接复用,调用invoke即可。

相关文章:

  • window mysql 安装出现的问题
  • 防火墙中的SNAT 与DNAT
  • 网络流量监控软件AnaTraf:优化性能、排除故障的最佳选择
  • 每日leetcode--接雨水
  • PyTorch之完整的神经网络模型训练
  • 算法模版总结
  • 蓝桥杯[OJ 3412]-最小化战斗力差距-CPP-贪心
  • 大语言模型(LLM) RAG概念
  • 面试题:限流的算法有哪些?
  • php8连接mysql
  • pytest生成allure的报告
  • Rust 安装与版本更新
  • 探索Linux世界:基本指令(文件查看、时间相关、grep、打包压缩及相关知识)
  • Dynamic Wallpaper v17.4 mac版 动态视频壁纸 兼容 M1/M2
  • 20240312-2-贪心算法
  • .net6Api后台+uniapp导出Excel
  • MySQL--索引优化实战篇(4)
  • 警院复试C程序设计学习笔记 第九章——用户建立自己的数据类型
  • 比特币普通地址、隔离见证(兼容)、隔离见证(原生)、Taproot 地址傻傻分不清楚
  • JVM和JVM内存管理
  • 山东鄄城发生一起交通事故,造成4人死亡、2人受伤
  • 第二期人工智能能力建设研讨班在京开班,近40国和区域组织代表参加
  • 老人将房产遗赠给外孙,三个女儿却认为遗嘱应无效,法院判了
  • 白玉兰奖征片综述丨综艺市场破局焕新,多元赛道重塑价值坐标
  • 从采购到销售!市场监管总局指导行业协会防控肉品风险
  • 湛江霞山通报渔船火灾:起火船舶共8艘,无人员伤亡或被困