轉載一下經典的the Forgers偽造者win32教程
http://winprog.org/tutorial/zh/message_loop_cn.html
好像很多人都不知道這個經典的教程,轉載其中一章打打廣告。
theForger's Win32 API教程第二版(簡體中文)
基礎建立一個簡單應用圖形裝置介面工具與文件附表 |
理解訊息迴圈在寫任何有實際價值的程式之前,都要理解整個訊息迴圈和windows程式傳遞訊息的結構.當前為止我們已經試了一些訊息的處理,我還應該更進一步的看一下這個過程,因為你如果不理解它們的話,後面的內容將會對你很難. 什麼是訊息?一個訊息就是一個整數.如果你到標頭檔案中看一下(瞭解API的好地方)你就會發現這樣的內容: #define WM_INITDIALOG 0x0110 #define WM_COMMAND 0x0111 #define WM_LBUTTONDOWN 0x0201 ... 等等..訊息用來進行windows系統中的所有通訊,至少在基本方面是這樣的.如果你想要你的視窗或是控制元件(其實就一種特別的視窗)做點什麼你就給它發個訊息.如果另外一個視窗要你做點什麼它也發給你一個訊息..如果發生了使用者按動了鍵盤,移動了滑鼠,點選了一個按鈕之類的事件,系統就向相關的視窗傳送訊息.你要是就是那個被傳至訊息的視窗,你就要處理這些訊息. 每個windows訊息可能擁有至多兩個引數,wParam和lParam.最初wParam是16bit,lParam是32bit,但在Win32平臺下兩者都是32bit.不是每個訊息使用了這些引數,而且每個訊息以不同的方式來使用.比如,WM_CLOSE不使用它們任一個,所以你應該忽略它們.WM_COMMAND訊息兩個都使用,wParam有兩個部分,HIWORD(wParam)中含有提示訊息(如果有的話),LOWORD(wParam)含有傳送訊息的控制元件或選單的標識號.lParam含有傳送訊息的控制元件的HWND(視窗的控制代碼)或者為NULL,當訊息不是由控制元件傳送. HIWORD()和LOWORD()是windows定義的兩個巨集,用來從一個32bit值中分離出高字(0xffff0000)和低字(0x0000ffff)的兩位元組.在Win32中,一個WORD是16bit,所以一個DWORD為32bit. 可以用PostMessage()或SendMessage()來發送訊息.PostMessage()把訊息放入訊息佇列再立即返回.就是說你呼叫了PostMessage()後訊息可能被處理了,也可能還沒有被處理. SendMessage()則真接把訊息送往視窗並且在視窗沒有結束處理訊息之前不返回.如果我們想關閉一個視窗我們可以傳送一個WM_CLOSE訊息:PostMessage(hwnd,WM_CLOSE,0,0);這跟我們點選視窗頂部的按鈕一樣的效果.注意wParam和lParam都為0.這是因為我們剛才說了WM_CLOSE並不用它們. 對話方塊一旦你開始使用對話方塊,你將需要向此控制元件傳送訊息以與它們通訊.你可以先使用GetDlgItem()來用ID得到控制元件的控制代碼然後用SendMessage(),也可以直接用SendDlgItemMessage()這個一步到位的函式.你給它一個視窗的控制代碼和一個子視窗的ID,它就會得到字視窗的控制代碼並向它傳送訊息.SendDlgItemMessage()和類似的API,如GetDlgItemText()可以用在所有的視窗上面,而不是僅僅在對話方塊上. 什麼是訊息佇列打個比方,你正在處理WM_PAINT訊息,此時突然使用者在鍵盤上敲了一大堆的東西.這時候會怎樣?你應該中止你的工作去響應鍵盤的輸入,還是簡單地把這些輸入給忽略掉? 錯!顯然兩種方式都不能接受,所以我們引入了訊息佇列的概念,訊息被髮送時就被放入佇列,被處理後就從佇列中刪除.這就保證了你不會丟掉訊息,你在處理某個的時候,另外的就在佇列中等待你來取走它們. 什麼是訊息迴圈while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
這是windows程式中一個非常重要的概念..你的視窗過程並不是由系統來神奇地呼叫的,實際上你在DispatchMessage()中自己間接地呼叫了它.如果你願意,你可以對訊息呼叫GetWindowLong()得到它的視窗過程再直接呼叫它! while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
WNDPROC fWndProc = (WNDPROC)GetWindowLong(Msg.hwnd, GWL_WNDPROC);
fWndProc(Msg.hwnd, Msg.message, Msg.wParam, Msg.lParam);
}
我對前面的例子用了這種方法,可以工作,但是有很多的方面這種方法沒有注意到,比如Unicode/ANSI轉換,時鐘回撥函式等等,它很可能在我們的簡單試驗中可以工作.所以試下就可以了,不要在實際的程式碼中用它:) 注意我們用GetWindowLong()來從視窗來查詢它的視窗過程.為什麼我們不直接呼叫WndProc()?因為我們的訊息迴圈為我們程式中的所有視窗的訊息服務,包括按鈕,列表框這樣擁有自己的視窗過程的視窗,所以我們要我們呼叫了正確的視窗過程.因為多個視窗可能使用一個視窗過程,第一個引數(視窗的控制代碼)告訴視窗過程某個訊息是為那個視窗的. 你可以看到,你的應用程式的主要時間就是在訊息迴圈這裡打轉,你很高興地向那些快樂的視窗發訊息讓他們處理.如果你要退出程式你怎麼辦?因為我們使用了一個while迴圈,如果GetMessage()返回了FLASE(就是0),迴圈就結束我們就可以到WinMain()的結束點.這就是PostQuitMessage()做的工作.它向佇列傳送一個WM_QUIT訊息,GetMessage()就向Msg結構體填充資料並返回0,而不返回正的值. 這個地方,Msg的wParam成員含有你傳向PostQuitMessage()的資料,你可以忽略它,也可以從WinMain()返回當這個程序的結束碼. 要點:GetMessage()遇到錯誤後返回-1.你要記住這點,說不定哪次你在這裡會犯錯...即使GetMessage()被定義為返回一個BOOL值,它還是會返回TRUE和FLASE之外的值,因為BOOL被定義為UINT(unsigned int).下面的程式碼可能看起來可工作,但是不能正確處理某些情況: while(GetMessage(&Msg, NULL, 0, 0))
while(GetMessage(&Msg, NULL, 0, 0) != 0)
while(GetMessage(&Msg, NULL, 0, 0) == TRUE)
上面的寫法都是錯誤的!可能你注意到我在這個教程中使用了第一個,剛剛提到了,如果GetMessage()不失敗,並不會出錯,你程式碼要是正確的話是不會出錯.但是你要是在讀本教程的話我並不能以此做為前提,你的程式碼可能有很多錯誤,GetMessage()會在某些點出錯:)這種寫法我已經更正了,如果我漏了某些地方請原諒. while(GetMessage(&Msg, NULL, 0, 0) > 0)
應該始終使用這段擁有相同的效果的程式碼. 我希望你對windows訊息迴圈有了進一步的瞭解,如果沒有,不用怕,你使用它們一些時間後就會更清楚了. |
Copyright © 1998-2008, Brook Miles (forgey). All rights reserved.