1. 程式人生 > 實用技巧 >轉載一下經典的the Forgers偽造者win32教程

轉載一下經典的the Forgers偽造者win32教程

http://winprog.org/tutorial/zh/message_loop_cn.html

好像很多人都不知道這個經典的教程,轉載其中一章打打廣告。

theForger's Win32 API教程第二版(簡體中文)

主頁

基礎
  1. 開始學習
  2. 一個簡單的視窗
  3. 處理訊息
  4. 理解訊息迴圈
  5. 使用資源
  6. 選單和圖示
  7. 對話方塊
  8. 非模態對話方塊
  9. 標準控制元件
  10. 對話方塊常見問題
建立一個簡單應用
  1. 在執行時建立控制元件
  2. 檔案與常用對話方塊
  3. 工具欄與狀態列
  4. 多文件介面
圖形裝置介面
  1. 點陣圖,裝置上下文
  2. 透明點陣圖
  3. 定時器與動畫
  4. 文字,字型與顏色
工具與文件
  1. 參考
  2. 免費的Visual C++(最新更新)
附表
  1. 常見錯誤的解決方法
  2. API vs. MFC
  3. 關於資原始檔的說明

理解訊息迴圈

  在寫任何有實際價值的程式之前,都要理解整個訊息迴圈和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);
}
  1. 訊息迴圈呼叫了GetMessage(),它到你的訊息佇列中去看.如果佇列是空的你的程式就在那裡等(阻塞在那裡).

  2. 當一個事件發生導致一個訊息加到佇列中去(比如系統註冊了一次滑鼠點選)GetMessage()就返回一個正值表示有一個訊息待處理,並且它將我們傳遞的MSG結構體填充.如果遇到了WM_QUIT它就返回0,如果有錯誤發生就返回負值

  3. 我們拿到訊號(在Msg變數中)並傳給TranslateMessage(),它進行一些額外的處理,將虛鍵值轉為字元資訊.這一步其實是可有可無的,但是有些地方會依賴這個步驟

  4. 一旦上面的工作完成了我們把訊息傳給DispatchMessage().DispatchMessage()先看看訊號是給哪個視窗的,再找到那個視窗的視窗過程. 再呼叫那個過程,引數為視窗的控制代碼,訊息,wParam和lParam

  5. 在你的視窗過程中,你得到了那些引數,就可以為所欲為了.如果你沒有處理某些特定的訊息,那你就呼叫DefWindowProc()來為你做一些預設的操作(一般就是什麼都不做).

  6. 一旦你完成了對訊息的處理,你的視窗過程就返回了,DispatchMessage()返回了,我們就再次迴圈

這是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.