2013-02-21 53 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