自己看核心編程,對訊息分流器不太理解,這篇寫的不錯,所以轉載。 文章作者:欣欣 原文連結:http://blog.csdn.net/hopkins9961629/archive/2006/01/25/588184.aspx很好理解,windows作業系統使用訊息處理機制,那麼,我們所設計的程式如何才能分辨和處理系統中的各種訊息呢?這就是訊息分流器的作用. 簡單來說,訊息分流器就是一段代碼,在我的講述中,將分7重來循序漸進的介紹它.從最初的第1重到最成熟的第7重,它的樣子會有很大的變化.但,實現的功能都是一樣的,所不同的,僅僅是變得更加簡練罷了. 程式開始時候,會是main函數,然後會產生初始的視窗,同時會調用WndProc函數.這是一個自訂的函數,名字也會有變化,但其功能是一樣的,就是運行訊息分流器.WndProc函數如下: LRESULT CALLBACK WndProc (HWND hwnd, UINT msg,WPARAM wParam, LPARAM lParam) { //...... return DefWindowProc(hwnd, msg, wParam, lParam); } 這其中,hwnd是視窗的控制代碼,msg是系統發送來的訊息的名字.wParam和lParam則是隨訊息一起發送來的訊息參數. WndProc函數使用了訊息分流器,下面把訊息分流器的內容解釋一下: 一重,當不同的訊息出現時,在其中寫入相應的程式語句即可。 LRESULT CALLBACK WndProc (HWND hwnd, UINT msg,WPARAM wParam, LPARAM lParam) { switch(msg) { case WM_CREATE: // ... return 0; case WM_PAINT: // ... return 0; case WM_DESTROY: //... return 0; } return DefWindowProc(hwnd, msg, wParam, lParam); } 二重,運用三個訊息分流器進行處理。 LRESULT CALLBACK WndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { case WM_CREATE: return HANDLE_WM_CREATE(hwnd, wParam, lParam, Cls_OnCreate); case WM_PAINT: return HANDLE_WM_PAINT(hwnd, wParam, lParam, Cls_OnPaint); case WM_DESTROY: return HANDLE_WM_DESTROY(hwnd, wParam, lParam, Cls_OnDestroy); } return DefWindowProc(hwnd, msg, wParam, lParam); } 這裡的HANDLE_WM_CREATE,HANDLE_WM_PAINT,HANDLE_WM_DESTROY就是訊息分流器。 與訊息不同之處就是在前面增加了“HANDLE_”字元,windows的訊息分流器就是這樣的模樣。 它的本質就是宏定義。 其中的四個參數有三個都是從本函數的入口參數中直接得到的,即為hwnd, wParam, lParam。 只有第四的參數是表明調用的函數。 訊息分流器是在winowsx.h檔案中定義的。由此,可以看出第四個參數是調用的函數,其定義如下: #define HANDLE_WM_CREATE(hwnd, wParam, lParam, fn) ((fn)((hwnd), (LPCREATESTRUCT)(lParam)) ? 0L : (LRESULT)-1L) #define HANDLE_WM_PAINT(hwnd, wParam, lParam, fn) ((fn)(hwnd), 0L) #define HANDLE_WM_DESTROYCLIPBOARD(hwnd, wParam, lParam, fn) ((fn)(hwnd), 0L) 0L是表示int類型的變數,其數值為0。 int類型時,可在後面加l或者L(小寫和大寫形式) 表明無符號數時,可在後面加u或者U(小寫和大寫形式) float類型時,可在後面加f或者F(小寫和大寫形式) 例如: 128u 1024UL 1L 8Lu 3.14159F 0.1f LRESULT是一個系統的資料類型,其定義如下: typedef LONG_PTR LRESULT; LONG_PTR也是一個系統的資料類型,其定義如下: #if defined(_WIN64) typedef __int64 LONG_PTR; #else typedef long LONG_PTR; #endif 由此可見,LRESULT的實質就是64的long類型的變數 那麼(LRESULT)-1L的實質並不是減法,而是((LRESULT)(-1L)),即強制類型轉換 三重,把訊息分流器的宏定義代換回去,就成了下面的樣子 LRESULT CALLBACK WndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { case WM_CREATE: return Cls_OnCreate(hwnd, (LPCREATESTRUCT)(lParam)) ? 0L : (LRESULT)-1L; // 如果處理了訊息,則Cls_OnCreate應返回TRUE,導致WndProc返回0,否則Cls_OnCreate返回FALSE,導致WndProc返回-1; case WM_PAINT: return Cls_OnPaint(hwnd), 0L; // 逗號運算式;Cls_OnPaint是void類型,這裡返回0; case WM_DESTROY: return Cls_OnDestroy(hwnd), 0L; // 同Cls_OnPaint } return DefWindowProc(hwnd, msg, wParam, lParam); } 在逗號運算式,C++會計算每個運算式,但完整的逗號運算式的結果是最右邊運算式的值。 所以,會return 0。 然後,就可以手動的編寫各個處理函數了:Cls_OnCreate,Cls_OnPaint,WM_DESTROY。 四重, LRESULT CALLBACK WndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { HANDLE_MSG(hwnd, WM_CREATE, Cls_OnCreate); HANDLE_MSG(hwnd, WM_PAINT, Cls_OnPaint); HANDLE_MSG(hwnd, WM_DESTROY, Cls_OnDestroy); } return DefWindowProc(hwnd, msg, wParam, lParam); } HANDLE_MSG也是一個宏,它在windowsx.h中定義,如下: #define HANDLE_MSG(hwnd, message, fn) case (message): return HANDLE_##message((hwnd), (wParam), (lParam), (fn)) 這個宏要做的就是根據不同的message(##用來串連前後的字串),把自己“變成”相應的HANDLE_XXXXMESSAGE形式的宏,再通過相應的宏來執行訊息處理代碼。 說白了,就是把message的訊息做為替換,##就是一個替換的標誌。 如果沒有##,就成了HANDLE_message了,這樣,宏是不會被代換的。 如果就單獨一個,則會代換,如hwnd和fn。 比如實際代碼中寫入: HANDLE_MSG(hwnd, WM_CREATE, Cls_OnCreate) 則經過轉換就變成: case (WM_CREATE): return HANDLE_WM_CREATE((hwnd), (wParam), (lParam), (Cls_OnCreate)) 這與二重一模一樣。 以上四重,是訊息分離器的基本使用,但,這不完整,訊息分離器主要應用在對話方塊訊息處理中。 這裡,視窗子類化是我們經常使用的手段,這也可以通過訊息分流器實現, 第五重 LRESULT CALLBACK Dlg_Proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { HANDLE_MSG(hwnd, WM_INITDIALO , Cls_OnInitDialog); // 不能直接使用HANDLE_MSG宏 HANDLE_MSG(hwnd, WM_COMMAND, Cls_OnCommand); // 不能直接使用HANDLE_MSG宏 } return false; } 由於是視窗子類化,所以,最後,返回的是false,以表明,如果沒有約定響應的訊息, 則返回父親視窗false,如果有,則返回ture,這是與前四重不同的地方。 一般情況下,對話方塊過程函數應該在處理了訊息的情況下返回TRUE,如果沒有處理,則返回FALSE。 如果對話方塊過程返回了FALSE,那麼對話方塊管理器為這條訊息準備預設的對話操作。 但是,這其中有錯誤,因為有的訊息,需要單獨處理。單獨處理的訊息列表見SetDlgMsgResult宏。 第六重 這點小問題,這就需要用到SetDlgMsgResult(hwnd, msg, result)宏。 LRESULT CALLBACK Dlg_Proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { case WM_INITDIALO: return (SetDlgMsgResult(hwnd, Msg, HANDLE_WM_INITDIALO((hwnd), (wParam), (lParam), (fn))); case WM_COMMAND: return (SetDlgMsgResult(hwnd, Msg, HANDLE_WM_COMMAND((hwnd), (wParam), (lParam), (fn))); } return false; } 這裡,就用直接用到了第二重的訊息分流器,而拋棄了其他。 這個宏定義如下: #define SetDlgMsgResult(hwnd, msg, result) ( ( (msg) == WM_CTLCOLORMSGBOX || (msg) == WM_CTLCOLOREDIT || (msg) == WM_CTLCOLORLISTBOX || (msg) == WM_CTLCOLORBTN || (msg) == WM_CTLCOLORDLG || (msg) == WM_CTLCOLORSCROLLBAR || (msg) == WM_CTLCOLORSTATIC || (msg) == WM_COMPAREITEM || (msg) == WM_VKEYTOITEM || (msg) == WM_CHARTOITEM || (msg) == WM_QUERYDRAGICON || (msg) == WM_INITDIALOG ) ? (BOOL)(result) : (SetWindowLongPtr((hwnd), DWLP_MSGRESULT, (LPARAM)(LRESULT)(result)), TRUE) ) 為了表述清楚,所以用了此格式,這是一個三項運算式,首先對訊息類型進行考察。 如果對話方塊過程處理的訊息恰巧為返回特定值中的一個,則如實返回result; 不要被前面的BOOL蒙蔽,BOOL在標頭檔中的定義實際上是一個int型, 一旦需要返回非TRUE或FALSE的其他值,照樣可以; 這樣,我們的Cls_OnInitDialog就能夠正確的返回它的BOOL值了, 而Cls_OnCommand在處理之後,也可以由後面的逗號運算式正確的返回一個TRUE表示訊息已處理。 第七重 我們還可以把case也包含進來,就成了如下的樣子。 LRESULT CALLBACK Dlg_Proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { chHANDLE_DLGMSG(hwnd, WM_INITDIALOG, Cls_OnInitDialog); chHANDLE_DLGMSG(hwnd, WM_COMMAND, Cls_OnCommand); } return false; } chHANDLE_DLGMSG是牛人定義的一個宏,它把case也包含進來了。 #define chHANDLE_DLGMSG(hwnd, message, fn) case (message): return (SetDlgMsgResult(hwnd, uMsg, HANDLE_##message((hwnd), (wParam), (lParam), (fn)))) 這樣,程式中的語句 switch (uMsg) { chHANDLE_DLGMSG(hwnd, WM_INITDIALOG, Dlg_OnInitDialog); chHANDLE_DLGMSG(hwnd, WM_SIZE, Dlg_OnSize); chHANDLE_DLGMSG(hwnd, WM_COMMAND, Dlg_OnCommand); } 就被翻譯成: switch (uMsg) { case (WM_INITDIALOG): return (SetDlgMsgResult(hwnd, uMsg, HANDLE_WM_INITDIALOG((hwnd), (wParam), (lParam), (Dlg_OnInitDialog)))); case (WM_SIZE) return (SetDlgMsgResult(hwnd, uMsg, HANDLE_WM_SIZE((hwnd), (wParam), (lParam), (Dlg_OnSize)))); case (WM_COMMAND) return (SetDlgMsgResult(hwnd, uMsg, HANDLE_WM_COMMAND((hwnd), (wParam), (lParam), (Dlg_OnCommand)))); } 這樣,訊息分流器,就介紹完畢. |