PeekMessage里面的线程

时间:2014-04-15 17:17:54

标签: c++ winapi visual-studio-2012 c++11

我有点困惑。我试图做的是在另一个线程中创建窗口消息循环。我的代码如下所示:

//...
#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,但没有成功。

3 个答案:

答案 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
  }
}