2013-02-21 70 views
2

我有一个程序,有一些线环,可以发布任务。其中一个线程循环是UI线程循环。它必须处理窗口消息以及发布的任务,所以我发送WM_USER消息来唤醒调度循环中的线程。PostMessage的与WM_USER似乎没有到达时MsgWaitForMultipleObjectsEx来检查它

问题是,有时(特别是当有很多其他窗口消息,如WM_PAINTWM_RESIZE)我的WM_USER消息不会唤醒线程。看起来PostMessage函数不会从MsgWaitForMultipleObjectsEx调用唤醒线程,但我找不出原因。

这是什么样子的(有些意译为简单起见):

#define HaveWorkMessage (WM_USER + 100) 

class ThreadLoopUI { 
public: 
    ThreadLoopUI() 
     : myHaveWork(0) {} 

    void PostTask(Task& aTask) { 
     { 
      ScopedLock lock(myMutex); 
      myTaskQueue.push_back(aTask); 
     } 

     ScheduleWork(); 
    } 

    void ScheduleWork() { 
     if (InterlockedExchange(&myHaveWork, 1)) { 
      // No need to spam the message queue 
      return; 
     } 

     if (!PostMessage(myHWnd, HaveWorkMessage, reinterpret_cast<WPARAM>(this), 0)) { 
      std::cerr << "Oh noes! Could not post!" << std::endl; 
     } 
    } 

    void Run() { 
     for (;;) { 
      // SIMPLIFICATION, SEE EDIT BELOW 
      DWORD waitResult = MsgWaitForMultipleObjectsEx(0, NULL, (DWORD)INFINITE, QS_ALLINPUT, MWMO_INPUTAVAILABLE); 

      if (waitResult == WAIT_FAILED) { 
        std::cerr << "Well, that was unexpected..." << std::endl; 
        continue; 
      } 

      bool doWork = false; 

      MSG message; 
      if (PeekMessage(&message, NULL, 0, 0, PM_REMOVE)) { 

        if (message == HaveWorkMessage) { 
         doWork = true; 
         InterlockedExchange(&myHaveWork, 0); 
        } 

        // Send the message on to the window procedure 
        TranslateMessage(&message); 
        DispatchMessage(&message); 
      } 

      if (doWork) { 
       // Process all tasks in work queue 
      } 
     } 
    } 
private: 
    HWND     myHwnd; 
    Mutex    myMutex; 
    std::vector<Task> myTaskQueue; 
    LONG volatile  myHaveWork; 
} 

编辑:以上MsgWaitForMultipleObjectsEx直接调用是一个简化。其实,我调用看起来像这样的功能:

void WaitForMessages() { 
    DWORD waitResult = MsgWaitForMultipleObjectsEx(0, NULL, (DWORD)INFINITE, QS_ALLINPUT, MWMO_INPUTAVAILABLE); 

    if (waitResult == WAIT_OBJECT_O) { 
     // Comment from the Chromium source: 
     // A WM_* message is available. 
     // If a parent child relationship exists between windows across threads 
     // then their thread inputs are implicitly attached. 
     // This causes the MsgWaitForMultipleObjectsEx API to return indicating 
     // that messages are ready for processing (Specifically, mouse messages 
     // intended for the child window may appear if the child window has 
     // capture). 
     // The subsequent PeekMessages call may fail to return any messages thus 
     // causing us to enter a tight loop at times. 
     // The WaitMessage call below is a workaround to give the child window 
     // some time to process its input messages. 
     MSG message = {0}; 
     DWORD queueStatus = GetQueueStatus(QS_MOUSE); 
     if (HIWORD(queueStatus) & QS_MOUSE && 
      !PeekMessage(&message, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_NOREMOVE)) 
     { 
      WaitMessage(); 
     }    
    } 
} 
+0

什么是“不可见的到来”为题??? – sashoalm 2013-02-21 10:05:10

+0

@satuon:固定:) – 2013-02-21 10:51:43

+1

您是否尝试过'QS_ALLINPUT | QS_ALLPOSTMESSAGE'? – 2013-02-21 10:56:27

回答

0

我现在已经找到了罪魁祸首,并且似乎在某些情况下,消息在窗口外由消息循环从队列中调度(即它们自动发送到WindowProcedure)。为了解决这个我改变了我的WindowProcedure是这样的:

LRESULT CALLBACK 
ThreadLoopUI::WindowProcedure( 
    HWND aWindowHandle, 
    UINT aMessage, 
    WPARAM aWParam, 
    LPARAM aLParam) 
{ 
    switch (aMessage) 
    { 
    case HaveWorkMessage: 
     // This might happen if windows decides to start dispatch messages from our queue 
     ThreadLoopUI* threadLoop = reinterpret_cast<ThreadLoopUI*>(aWParam); 

     InterlockedExchange(&threadLoop->myHaveWork, 0); 

     // Read the next WM_ message from the queue and dispatch it 
     threadLoop->PrivProcessNextWindowMessage(); 

     if (threadLoop->DoWork()) 
     { 
      threadLoop->ScheduleWork(); 
     } 

     break; 
    } 

    return DefWindowProc(aWindowHandle, aMessage, aWParam, aLParam); 

谢谢大家对您的帮助和建议!

+0

如果您发送进入模式循环的消息,则会发生这种情况,因为该模式循环不会有您自定义的HaveWorkMessage处理程序。 – 2013-03-01 13:38:32

+0

是的,我在我的代码的其他地方发布了一个'WM_ENTERSIZEMOVE'消息,这使得窗口进入一个模态循环。 – 2013-03-04 08:26:57

0

不知道这是你的情况的罪魁祸首,但你要组织这样的PostMessage()保证使用的代码后,目标线程已经有了消息循环。

新主题最初没有任何消息队列中,它不仅是第一次调用尝试从它那里得到消息后创建。我不知道如果在这里MsgWaitForMultipleObjectsEx()计数,所以我会建议开始与调用线程PeekMessage(),只是为了创建队列。

您的应用程序应该保证它永远不会职位/发送消息到线程的PeekMessage()返回之前,或者可以简单地迷路的消息。

+0

你说的是真的,但我不认为这是我的问题的根源。大多数情况下,这个消息传递是有效的,但是现在每次都失败了。它与消息是否是第一个(即在创建队列之前)无关。 – 2013-02-21 10:53:36

3

MsgWaitForMultipleObjects[Ex]表示由于一条或多条消息而返回时,您必须go into a loop processing all of them。您的代码只处理一条消息,这意味着第二条消息仍未处理。这就是为什么你永远不会得到你的消息:在你有机会看到它之前,你放弃了。

+0

'MsgWaitForMultipleObjectsEx'变种允许'MWMO_INPUTAVAILABLE'这将使它也检查现有的未读输入。 – 2013-03-01 09:10:32