天天看點

VC中的定時器與消息機制

     用VC程式設計,最重要的是要熟悉消息機制。但這一點好像并不是那麼容易搞懂,至少現在我還不是完全明白,隻有遇到問題時才想辦法把它查清楚。

     案例:通過SetTimer設定一個定時器,在OnTimer中,設定彈出一個對話框,如MessageBox("Hello")。你會發現對話框會不斷的彈出,而不是阻塞在那裡?如果你對這個問題很清楚,并且不會認為這是多線程機制,那恭喜你,你一定對Windows消息機制比較熟悉,也比我知道的要多,那你就多給我挑挑毛病吧。否則,希望我的這篇文章能對你有所幫助。

     首先說明,這個結果絕對不是多線程引起的。有同學開始認為是多線程,每次OnTimer調用單獨開一個線程。但我打開任務管理器跟蹤發現線程并沒有增加。于是我上csdn請教高手,經過一個下午的讨論,才算弄明白,其核心問題還在于消息機制。

     如果用Windows API寫過程式,你應該明白在WinMain中有一個消息循環,不斷的取消息,轉換,重新發送消息,代碼如下:

// Main message loop:

while (GetMessage(&msg, NULL, 0, 0))

{

   if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))

   {

    TranslateMessage(&msg);

    DispatchMessage(&msg);

   }

}

     通過GetMessage得到一個消息,如果沒有消息,則會阻塞到這裡。這個消息從哪裡來?作業系統。作業系統為每個線程建立一個消息隊列(在收到第一個消息時真正建立),而每個線程可以包括多個視窗。也就是說,這個消息隊列包括了該線程中所有的視窗消息。是以,通過GetMessage擷取消息時,當第二個參數為NULL時,會将同線程中其他視窗的消息也得到。然後進行消息轉換,首先轉換快捷鍵為相應的消息,然後轉換你的鍵盤操作,通過消息轉換将其解析為到底是輸入字元,還是控制鍵。轉換完成後,再通過DispatchMessage來分發消息。分發給誰?作業系統。作業系統收到消息後,調用消息所屬視窗的WndProc函數,根據消息的類型進行處理。處理完成後,作業系統将控制權再轉交給程式,完成Dispatch,繼續進行消息循環。

     這裡需要注意的就是,每個視窗的消息循環不隻是處理本視窗的消息,也将同線程下其他視窗的消息也處理掉了。這就是為什麼當OnTimer彈出一個對話框後還可以繼續彈出對話框,是因為彈出的MessageBox本身的消息循環接替了上一個視窗循環的工作。如此遞歸調用,這也是為什麼消息機制支援嵌套調用。這裡有一個問題,DispatchMessage為什麼不由程式本身來調用,而非要經過作業系統?這就是出于便捷考慮,如果直接處理,那麼一個視窗必須有權限通路其他視窗的處理函數,不然怎麼實作處理其他視窗的調用呢?這就是回調函數的優點。

     另外一個問題,程式在執行消息時,會不會在一條消息處理沒有完畢時,就去處理下一條?比如不斷的調用OnTimer?答案是不會,除非在處理函數中有一個消息循環,類似于本文的案例。否則,隻可能在一條消息處理完畢後,才執行下一條。

     OnTimer的定時是不精确的,并且WM_TIMER消息的優先級非常低,總是添加到消息隊列的尾部。并且同一個定時器消息隻會在隊列中出現一次,類似于WM_PAINT消息。你不用擔心在OnTimer處理函數中執行太久後收到一堆的WM_TIMER消息,但你也要注意,你的WM_TIMER消息已經丢失了一些。

     OnTimer本身的使用這裡就不說了,網上的例子很多,希望看到這篇文章能有所收獲!

原文位址:http://hi.baidu.com/sangwf/blog/item/55ae90ee6c8a1c2b2cf534f6.html