2011-11-19 45 views
0

我一直在嘗試理解事件循環(一般不太好),並且我已經讀過Windows消息循環是單線程的。如果是這樣,Application.DoEvents如何工作?事件循環沒有一次處理一條消息,並且在處理每個消息/事件時阻塞?消息事件循環是否需要存在於與處理Application.DoEvents消息的消息不同的線程上?如果有獨立的線程,我們稱之爲「主」線程的是哪一個?我相信我錯過了一些非常簡單的東西,我只是不知道它是什麼。是Application.DoEvents發送消息到一個單獨的線程?

回答

0

我花了一整天的時間搞清楚這一點(如果我說的是錯的,請評論,讓我知道,所以我可以糾正它)。我實際上必須構建一箇舊的Win32應用程序並自己創建消息循環(我是一個非常持久的SOB)。因此,有一個叫WinMain函數是啓動消息循環,看起來像這樣:

while (GetMessage(&msg, NULL, 0, 0)) 
{ 
    if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) 
    { 
     TranslateMessage(&msg); 
     DispatchMessage(&msg); 
    } 
} 

有關的GetMessage事情()是它阻止,直到一個消息是在消息隊列中可用。如果你運行一個Windowed應用程序,並且坐在那裏查看窗口(不要導致任何將消息發送到隊列的動作),主線程(創建窗口的線程)在GetMessage() 。現在,當一條消息發佈時,我們輸入while循環(即如果消息不是0,則退出)。 DispatchMessage()是這裏有趣的功能。這個函數最終會導致(在.NET中)EventHandler的控件和執行引發事件。我感到困惑的是,如果調用堆棧是GetMessage()/ DispatchMessage()/.../ EventHandler,那麼Application.DoEvents()如何處理消息呢?那很簡單。在Win32中的DoEvents是這樣的:

void DoEvents() 
{ 
    MSG msg; 
    HACCEL hAccelTable; 

    hAccelTable = LoadAccelerators(hInst, MAKEINTRESOURCE(IDC_TESTWIN32)); 

    // Main message loop: 
    while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE) != 0) 
    { 
     if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) 
     { 
      TranslateMessage(&msg); 
      DispatchMessage(&msg); 
     } 
    } 
} 

所以的DoEvents()實際上啓動另一個循環來處理事件,而在最初的消息循環的DispatchMessage函數()內! 關鍵的區別在於,不使用GetMessage(),直到隊列中有消息時才阻塞,我們使用PeekMessage()返回0,並在隊列中不再有任何消息時存在循環。

那麼如果我們點擊一​​個按鈕兩次,並且在該按鈕的EventHandler中我們有一個DoEvents()調用呢?初始事件循環將處理第一次點擊並觸發事件。當EventHandler正在執行時,在DoEvents()調用中,事件將被再次觸發,EventHandler將再次進入(有點像遞歸調用)。太可怕了!

所以最後,一切都發生在一個單獨的線程中,並且DoEvents()實際上會阻塞,直到所有的消息都被處理並返回。現在我要睡上幾天了。