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

22 篇文章 1 订阅
8 篇文章 1 订阅

前言

做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即可。

  • 7
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
了解了您的问题,您是想通过获取窗口的句柄来实现截屏功能。在 Windows 平台上,可以使用以下方式来实现: ```c++ #include <Windows.h> bool CaptureScreen(HWND hWnd, const char* filename) { HDC hDc = GetDC(hWnd); HDC hMemDc = CreateCompatibleDC(hDc); RECT rcClient; GetClientRect(hWnd, &rcClient); int nWidth = rcClient.right - rcClient.left; int nHeight = rcClient.bottom - rcClient.top; HBITMAP hBitmap = CreateCompatibleBitmap(hDc, nWidth, nHeight); HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemDc, hBitmap); BitBlt(hMemDc, 0, 0, nWidth, nHeight, hDc, 0, 0, SRCCOPY); SelectObject(hMemDc, hOldBitmap); DeleteDC(hMemDc); ReleaseDC(hWnd, hDc); PBITMAPINFO pbi; pbi = CreateBitmapInfoStruct(hWnd, hBitmap); LPBYTE lpBits; lpBits = (LPBYTE)GlobalAlloc(GMEM_FIXED, pbi->bmiHeader.biSizeImage); GetDIBits(hMemDc, hBitmap, 0, (UINT)pbi->bmiHeader.biHeight, lpBits, pbi, DIB_RGB_COLORS); HANDLE hf; BITMAPFILEHEADER hdr; PBITMAPINFOHEADER pbih; LPBYTE lpData; DWORD dwTotal; DWORD cb; BYTE* hp; DWORD dwTmp; pbih = (PBITMAPINFOHEADER)pbi; lpData = (LPBYTE)(lpBits + pbih->biSize + pbi->bmiHeader.biClrUsed * sizeof(RGBQUAD)); dwTotal = ((DWORD)(pbih->biSizeImage + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER))); hdr.bfType = 0x4d42; hdr.bfSize = dwTotal; hdr.bfReserved1 = 0; hdr.bfReserved2 = 0; hdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER) + pbi->bmiHeader.biClrUsed * sizeof(RGBQUAD); hf = CreateFile(filename, GENERIC_READ | GENERIC_WRITE, (DWORD)0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL); if (hf == INVALID_HANDLE_VALUE) return false; WriteFile(hf, (LPVOID)&hdr, sizeof(BITMAPFILEHEADER), (LPDWORD)&dwTmp, NULL); WriteFile(hf, (LPVOID)pbih, sizeof(BITMAPINFOHEADER) + pbi->bmiHeader.biClrUsed * sizeof(RGBQUAD), (LPDWORD)&dwTmp, (NULL)); WriteFile(hf, (LPVOID)lpData, (DWORD)pbih->biSizeImage, (LPDWORD)&dwTmp, NULL); CloseHandle(hf); GlobalFree((HGLOBAL)lpBits); DeleteObject(hBitmap); return true; } ``` 上面的代码中,`CaptureScreen` 函数接收两个参数:`hWnd` 表示窗口句柄,`filename` 表示截图保存的文件名。该函数实现了从指定窗口截图,并将截图保存到指定文件中的功能。 注:上述代码中使用了一些辅助函数,如 `CreateBitmapInfoStruct`,请根据实际情况进行实现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CodeOfCC

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值