我有点困惑。我试图做的是在另一个线程中创建窗口消息循环。我的代码如下所示:
//...
#include <thread>
//...
void MyClass::runMainLoop() {
new thread(mainLoop, this); //I know this will cause a memory leak - just for testing
}
void MyClass::mainLoop(MyClass* _this) { // <- static method
cout << "start thread" << endl; //loop function started
MSG msg;
while (true) {
while (PeekMessage(&msg, _this->_hWnd, 0, 0, PM_REMOVE)) {
cout << msg.message << " "; //we've got a message! (don't get here inside a thread)
if (msg.message == WM_QUIT) {
cout << "exiting" << endl; //closing window
break;
}
DispatchMessage(&msg);
}
Sleep(2);
}
}
消息&#34;启动线程&#34;出现了,但我没有看到任何处理过的消息。另一方面,当我在没有创建线程的情况下调用mainLoop()方法时,一切似乎都正常工作:
void MyClass::runMainLoop() {
mainLoop(this);
}
我试着深入研究MSDN,但没有发现我的问题。看来,我的知识存在一些差距,无法在合理的时间内填补。
我的想法是这个帖子不知何故不知道&#34;关于我在主程序线程中创建的窗口。
所以,问题是 - 我做错了什么?为什么消息循环不在线程中工作?
修改
创建窗口的代码。它运行在程序的主线程中。
WNDCLASSEX wcx;
PIXELFORMATDESCRIPTOR pfd;
RECT rect;
HGLRC hRCTemp;
DWORD style, exStyle;
int x, y, format;
_hInstance = (HINSTANCE)GetModuleHandle(NULL);
//Register window class
memset(&wcx, 0, sizeof(wcx));
wcx.cbSize = sizeof(wcx);
wcx.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wcx.lpfnWndProc = (WNDPROC)windowProc;
wcx.hInstance = _hInstance;
wcx.lpszClassName = L"windowClassName";
wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
RegisterClassEx(&wcx)
//Window styles
style = WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
exStyle = WS_EX_APPWINDOW;
//place window at the cetner of the screen
x = (GetSystemMetrics(SM_CXSCREEN) - width) / 2;
y = (GetSystemMetrics(SM_CYSCREEN) - height) / 2;
rect.left = x;
rect.right = x + width;
rect.top = y;
rect.bottom = y + height;
//Adjust window size to styles
AdjustWindowRectEx(&rect, style, FALSE, exStyle);
//Create a window
_hWnd = CreateWindowEx(exStyle, wcx.lpszClassName, L"Window caption", style, rect.left, rect.top,
rect.right - rect.left, rect.bottom - rect.top, NULL, NULL, _hInstance, NULL);
编辑2:
感谢所有评论我的问题的人,我现在明白问题出在哪里:创建窗口的线程会收到消息。
所以,我将重新解释这个问题:我可以将窗口消息重定向到另一个线程吗?我试过了AttachThreadInput,但没有成功。
答案 0 :(得分:1)
HWND与创建它的线程上下文相关联。只有创建HWND的线程上下文才能接收该HWND的消息。当您在mainLoop()
中直接调用runMainLoop()
时,mainLoop()
正在创建HWND的同一线程上下文中运行,这就是它工作的原因。将mainLoop()
移动到其他线程后,它将无法再接收HWND的消息。
HWND的消息泵必须与创建HWND的线程相同。没有绕过那个限制。因此,如果要在不同的线程中为HWND提供服务,则必须在该线程中创建HWND。
答案 1 :(得分:1)
最好的办法是制作人/消费者模式。
创建窗口时,添加将添加到队列的处理程序:
// Global scope...
std::queue<MSG*> g_messages;
std::mutex g_mutex;
std::condition_variable g_cond;
然后你的主循环看起来像(对于创建窗口的线程):
void MyClass::mainLoop(MyClass* _this) { // <- static method
while (true) {
MSG *msg = new MSG;
while (PeekMessage(msg, _this->_hWnd, 0, 0, PM_REMOVE)) {
std::unique_lock<std::mutex> lock(g_mutex);
g_messages.push(msg);
g_cond.notify_all();
}
DispatchMessage(msg);
}
}
并创建这样的线程:
std::thread t([]() {
MSG *msg;
while (true) {
// Wait for a message
{
std::unique_lock<std::mutex> lock(g_mutex);
while (g_messages.empty()) g_cond.wait(lock);
msg = g_messages.front();
g_messages.pop();
}
// Got a message, process it
if (msg->message == WM_QUIT) {
cout << "exiting" << endl; //closing window
break;
}
}
});
答案 2 :(得分:0)
对您的编辑2的回复
您可以通过在线程中创建窗口,将窗口的消息重定向到另一个线程。线程不仅要处理消息,还要通过事件驱动模型和MsgWaitForMultipleObjects来创建。或者通过私人消息通过PostThreadMessage从主线程发送到创建的线程。
后一种模式:
来自主线程:
创建主题
调用PostThreadMessage(tid,MYPRIVATEMESSAGE,wparam,lparam)
在创建的主题中:
loop GetMessage()
{
if (message == MYPRIVATEMESSAGE)
{
create the window, use wParam/lParam to pass optional parameters
}
else
{
translate message
dispatch message
}
}