多线程与WINDOWS窗口创建、消息循环

在多线程中,GetMessage()必须和创建窗口在同一个线程。否则:

1、GetMessage的调用会一直堵塞。消息处理函数不会被调用。

2、SendMessage也无法发消息,发了对方也接收不到不能及时处理。

下面梳理多线程、创建窗口、消息循环的方式和结果:

一、以往大多在WIN32界面程序创建窗口,即:

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {

 // 创建窗口界面。

}

二、下面介绍在console程序创建窗口,应用里。并介绍多线程消息。即:

// 不一定是:int main(int argc, char* argv[])。 意不意外~

int main() {

   // 创建窗口界面。

}

下面给一个完整的例子,解释多线程对windows窗口消息循环的影响。

多线程采用C++标准库的<thread>,更多说明见注释:

#include <windows.h>    //包含 Windows 相关的 API 函数
#include <thread> // 多线程

// 消息处理函数
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
// 注册窗口
bool RegisterWindow();
// 窗口创建
bool Create();
// 消息循环
void MsgLoop();

// 测试在一个线程的场景。
void Test();
// 测试在多线程场景。
void TestWithThread();
void TestAllInThread();

/*************************
* 入口函数,测试窗口消息
***************************/
int main()
{
	// 可以分别注释对应的接口,验证
	
	// 结果:正常。测试不启用线程的场景。
	Test();
	
	// 结果:异常。测试窗口注册、创建和消息循环不在同一个线程里。
	//TestWithThread();

	// 结果:正常。测试窗口注册、创建、消息循环都在同一个线程里。
	//TestAllInThread();

	return 0;
}

void TestNoThread() {
	RegisterWindow(); // 窗口注册
	Create();                 // 窗口创建
	 //  消息循环,不启用线程。
	//这里调用GetMessage会等待其它线程、进程发到当前窗口的消息。
	MsgLoop();           
}

void TestWithThread() {
	RegisterWindow(); // 窗口注册
	Create();                 // 窗口创建

	 // 消息循环, 放到不同窗口注册和创建的另一个线程里。 
	// 线程里面调用GetMessage会一直堵塞。
	// 导致不能正常收到其它线程、进程发到当前窗口的消息。
	std::thread t(MsgLoop);
	t.join(); // 等待消息线程结束,避免程序直接退出。
}

void WindThread() {
	RegisterWindow(); // 窗口注册
	Create();                 // 窗口创建
	 // 消息循环, 和窗口注册和创建同一个线程里。 
	//这里调用GetMessage会等待其它线程、进程发到当前窗口的消息。
	MsgLoop();
}

void TestAllInThread() {
	std::thread t(WindThread);
	t.join();
}

// 消息处理函数
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	bool handle = true;
	switch (uMsg)    //消息选择
	{
	case WM_LBUTTONDOWN:
		break;
	default:
		handle = false;
		break;
	}
	if (handle) {
		return S_OK;
	}
	return DefWindowProcA(hwnd, uMsg, wParam, lParam);
}

// 注册窗口
bool RegisterWindow() {
	HINSTANCE hInstance = GetModuleHandleA(NULL);

	//当然, 如果不创建窗口, 不使用窗口 也可以使用 main.
	WNDCLASSEXA wc = { 0 };            //窗口类结构, 为注册窗口类作准备
	wc.cbClsExtra = 0;                //附加的类信息, 没有, 设为0
	wc.cbSize = sizeof(wc);            //WNDCLASSEX结构的大小
	wc.cbWndExtra = 0;                //窗口额外内存, 没有, 设为0
	wc.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);            //窗口背景, 这里使用灰色背景
	wc.hCursor = LoadCursorA(NULL, MAKEINTRESOURCEA(IDC_ARROW));        //应用程序使用的鼠标类型
	wc.hIcon = LoadIconA(NULL, MAKEINTRESOURCEA(IDI_APPLICATION));    //光标类型
	wc.hIconSm = NULL;                    //应用程序程序的小光标, 不管, 设为 NULL
	wc.hInstance = hInstance;            //应用程序程序实例句柄, 由 WinMain 函数传递过来

	//当前窗口的消息处理函数, 传递 WndProc 的地址
	wc.lpfnWndProc = WndProc;

	wc.lpszClassName = "vbgk_class";    //创建类时使用的类名, 可以自定义
	wc.lpszMenuName = NULL;                //菜单, 没有, 就使用 NULL
	wc.style = CS_HREDRAW | CS_VREDRAW;    //类的风格, 垂直重绘, 水平重绘

	if (!RegisterClassExA(&wc)) { //注册窗口类
		MessageBoxA(NULL, "Register Class Failed!", NULL, MB_OK);
		return false;
	}
	return true;
}

// 窗口创建
bool Create() {
	HINSTANCE hInstance = GetModuleHandleA(NULL);
	//创建窗口咯, 介绍一下 CreateWindowEx 函数的参数
	HWND hWnd = CreateWindowExA(
		0,                    //扩展窗口风格
		"vbgk_class",        //这里就是我们刚才创建的窗口类名
		"test title",
		WS_OVERLAPPEDWINDOW,//窗口风格
		CW_USEDEFAULT,        //初始化时的 X 坐标
		CW_USEDEFAULT,        //Y坐标
		320,        //窗口宽度, 我们这里设为 320
		240,        //窗口高度
		NULL,        //父窗口句柄, 没有
		NULL,        //菜单, 没有
		hInstance,    //实例句柄, 来自WinMain
		NULL        //发送 WM_CREATE 消息时的附加参数, 一般为零
	);

	if (!hWnd || INVALID_HANDLE_VALUE == hWnd) {
		return false;
	}

	UpdateWindow(hWnd);                //更新窗口
	ShowWindow(hWnd, SW_HIDE);        //隐藏窗口

	return true;
}

// 消息循环
void MsgLoop() {
	MSG msg;
	BOOL bRet = TRUE;

	//进入消息循环,
	while ((bRet = GetMessageA(&msg, NULL, 0, 0)) != -1) {
		if (bRet == 0) {
			break;
		}
		TranslateMessage(&msg);    //翻译消息
		//分发消息- 即会调用WndProc();
		DispatchMessageA(&msg);
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值