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

2024-03-10 01:44

本文主要是介绍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即可。

这篇关于Windows C++ 任意线程通过hwnd将操作发送到UI线程执行的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/792612

相关文章

Linux线程之线程的创建、属性、回收、退出、取消方式

《Linux线程之线程的创建、属性、回收、退出、取消方式》文章总结了线程管理核心知识:线程号唯一、创建方式、属性设置(如分离状态与栈大小)、回收机制(join/detach)、退出方法(返回/pthr... 目录1. 线程号2. 线程的创建3. 线程属性4. 线程的回收5. 线程的退出6. 线程的取消7.

Linux下进程的CPU配置与线程绑定过程

《Linux下进程的CPU配置与线程绑定过程》本文介绍Linux系统中基于进程和线程的CPU配置方法,通过taskset命令和pthread库调整亲和力,将进程/线程绑定到特定CPU核心以优化资源分配... 目录1 基于进程的CPU配置1.1 对CPU亲和力的配置1.2 绑定进程到指定CPU核上运行2 基于

mybatis执行insert返回id实现详解

《mybatis执行insert返回id实现详解》MyBatis插入操作默认返回受影响行数,需通过useGeneratedKeys+keyProperty或selectKey获取主键ID,确保主键为自... 目录 两种方式获取自增 ID:1. ​​useGeneratedKeys+keyProperty(推

c++ 类成员变量默认初始值的实现

《c++类成员变量默认初始值的实现》本文主要介绍了c++类成员变量默认初始值,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录C++类成员变量初始化c++类的变量的初始化在C++中,如果使用类成员变量时未给定其初始值,那么它将被

Javaee多线程之进程和线程之间的区别和联系(最新整理)

《Javaee多线程之进程和线程之间的区别和联系(最新整理)》进程是资源分配单位,线程是调度执行单位,共享资源更高效,创建线程五种方式:继承Thread、Runnable接口、匿名类、lambda,r... 目录进程和线程进程线程进程和线程的区别创建线程的五种写法继承Thread,重写run实现Runnab

C++中NULL与nullptr的区别小结

《C++中NULL与nullptr的区别小结》本文介绍了C++编程中NULL与nullptr的区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编... 目录C++98空值——NULLC++11空值——nullptr区别对比示例 C++98空值——NUL

SpringBoot线程池配置使用示例详解

《SpringBoot线程池配置使用示例详解》SpringBoot集成@Async注解,支持线程池参数配置(核心数、队列容量、拒绝策略等)及生命周期管理,结合监控与任务装饰器,提升异步处理效率与系统... 目录一、核心特性二、添加依赖三、参数详解四、配置线程池五、应用实践代码说明拒绝策略(Rejected

C++ Log4cpp跨平台日志库的使用小结

《C++Log4cpp跨平台日志库的使用小结》Log4cpp是c++类库,本文详细介绍了C++日志库log4cpp的使用方法,及设置日志输出格式和优先级,具有一定的参考价值,感兴趣的可以了解一下... 目录一、介绍1. log4cpp的日志方式2.设置日志输出的格式3. 设置日志的输出优先级二、Window

Java操作Word文档的全面指南

《Java操作Word文档的全面指南》在Java开发中,操作Word文档是常见的业务需求,广泛应用于合同生成、报表输出、通知发布、法律文书生成、病历模板填写等场景,本文将全面介绍Java操作Word文... 目录简介段落页头与页脚页码表格图片批注文本框目录图表简介Word编程最重要的类是org.apach

Python实现对阿里云OSS对象存储的操作详解

《Python实现对阿里云OSS对象存储的操作详解》这篇文章主要为大家详细介绍了Python实现对阿里云OSS对象存储的操作相关知识,包括连接,上传,下载,列举等功能,感兴趣的小伙伴可以了解下... 目录一、直接使用代码二、详细使用1. 环境准备2. 初始化配置3. bucket配置创建4. 文件上传到os