Windows API 消息和回調函數理解

MSG結構

typedef struct tagMSG {

HWND           hwnd;     //這個消息所在的窗口句柄

UINT         message;     //消息標識符,如WM_SIZE、WM_COMMAND、WM_QUIT等等

WPARAM     wParam;     //32位消息的特定附加信息

LPARAM        lParam;     // 32位消息的特定附加信息

DWORD            time;    // /消息創建時的時間

POINT                   pt;   //消息創建時的鼠標位置

} MSG, *PMSG;

 

GetMessage()

 

BOOL GetMessage

//從消息隊列中“摘取”一個消息信息放到lpMsg所指的變量裏;如果所取窗口的消息隊列中沒有消息,則程序會暫停在GetMessage 函數裏,不會返回。

LPMSG lpMsg,  //傳出參數、函數執行成功,從消息隊列中“摘”取的一個消息信息會放入lpMsg所指的MSG結構變量中。

HWND hWnd ,  //傳入參數。你要獲取你程序中哪個窗口的消息,那就把相應的窗口句柄代入其中。

UINT wMsgFilterMin,//

UINT wMsgFilterMax);

//返回值:如果取的是WM_QUIT消息,則返回值爲0,如果取的是其它消息,返回值爲非0值。

//WM_QUIT消息是退出程序的消息。當我們想讓程序退出時,我們就可以發一個WM_QUIT消息,讓GetMessage返回0值。


 

TranslateMessage()

 

BOOL TranslateMessage( CONST MSG *lpMsg);

//對GetMessage取得的MSG消息結構中的信息進行必要的預處理。

//GetMessage函數取得的消息,要經過TranslateMessage處理一下,纔可以傳給DispatchMessage函數 。

//因此,TranslateMessage必放在GetMessage與DispatchMessage之間。


DispatchMessage()

 

LONG DispatchMessage( CONST MSG *lpMsg);

//用來完成調用WinPro回調函數並把由GetMessage取得的消息結構MSG變量中的信息傳遞給WinPro回調函數。

 

 

鼠標消息

隨着 Windows 操作系統的流行,鼠標因爲其精確定位和操作方便的優點而成爲計算機不可缺少的輸入設備。

鼠標的基礎知識

 1 .鼠標操作和鼠標消息用戶在使用鼠標操作的過程中,經常會使用的主要方式有五種 ,如表所示。

操作名稱 描述
單擊(Click) 按下並迅速釋放鼠標按鈕
雙擊(Double Click) 連續快速完成兩次單擊操作
移動(Move) 鼠標光標移動
拖動(Drag) 按下鼠標一鍵不放,同時執行鼠標移動操作
與鍵盤的特殊鍵組合 在按下Ctrl鍵或Shift鍵的同時執行鼠標單擊操作

 

其中,前三種操作是最爲基本的操作,可以產生Windows內部定義的消息,並通過這些消息來判斷用戶具體執行了哪種操作。


Windows定義的鼠標消息共有20條,其中非編輯區的鼠標消息一般交由系統處理,程序只處理編輯區內的鼠標消息。編輯區內的鼠標消息共有10條,如表所示。

消息常量 操作描述 消息常量 操作描述 消息常量 操作描述
WM_MOUSEMOVE 移動鼠標
WM_LVBUTTONDOWN 按下鼠標左鍵 WM_RVBUTTONDBLCLK 按下鼠標右鍵 WM_MVBUTTONDOWM 按下鼠標中鍵
WM_LBUTTONUP 釋放鼠標左鍵 WM_RBUTTONUP 釋放鼠標右鍵 WM_MBUTTONUP 釋放鼠標中鍵
WM_LBUTTONDBLCLK 雙擊鼠標左鍵 WM_RBUTTONDBLCLK 雙擊鼠標右鍵 WM_MBUTTONDBLCLK 雙擊鼠標中鍵

 

對於前表所列的鼠標操作中的最後兩種,不能直接使用Windows定義的消息來判斷,只能通過編程,將多種消息和數據組合之後判斷。

例如,判斷用戶是否按下鼠標左鍵之後進行拖動操作可以通過case語句來實現:

case WM_MOUSEMOVE:

if (wParam&MK_LBUTTON) //只處理鼠標拖動的消息
{ …… // 處理程序
}

wParam參數中保存了在消息產生時其他操作進行的狀態;用戶可以通過位屏蔽操作來判斷在該消息產生的同時,其餘操作是否正在進行。這正是在程序中判斷複雜鼠標操作的基本方法。例如,上面判斷拖動操作的程序段就用了位操作 wParam& MK_LBUTTON, 判斷在鼠標移動(WM_MOUSEMOVE)的同時鼠標左鍵是否同時被接下。如果,鼠標左鍵同時按下,則位操作的結果爲TRUE,說明當前操作爲拖動操作,程序可以繼續進行下一步處理。又如需要判斷單擊鼠標左鍵時是否同時按下了Ctrl鍵或Shift鍵,可以用以下程序段來處理:

case WM_ LBUTTONDOWN:
if(wParam& MK_CTROL)
{//Ctrl鍵同時按下
  if (wParam&MK_ SHIFT)
  {// Ctrl 鍵和Shift鍵都同時按下
    …… // 處理程序
  }
  else { // Ctrl健同時按下,但 Shift鍵沒有被按下
    ……. // 處理程序
  }
}
else if(wParam&MK_ SHIFT)
{ // Shift鍵同時按下,但 Ctrl鍵沒有被接下
  …… // 處理程序
}
else
{// Shift 鍵和Ctrl鍵都未按下
  …… // 處理程序
}

 

lParam參數保存了消息產生時鼠標所在點的座標,其中低16位爲X座標,高16位爲Y座標。

在處理鼠標消息的時候,如果需要處理鼠標雙擊消息,則在註冊窗口類時,窗口的風格必須包括CS_DBCLCKS。否則即使執行了雙擊操作,窗口也只能收到兩條WM_ BUTTONUP和 WM_BUTTONDOWN消息。區分雙擊操作和兩次單擊操作是以兩次擊鍵的時間間隔爲標準的。當兩次擊鍵的時間間隔小於 500毫秒時, Windows將其視爲雙擊操作:如果兩次擊鍵的時間間隔大於500毫秒,Windows將其視爲兩次單擊操作。500毫秒爲默認的時間間隔,用戶可以通過調用SetDoubleClickTime函數來修改這一時間間隔。

SetDoubleClickTime函數的原型定義如下:
BOOL SetDoubleClickTime(UINT uInterval // 新的擊鍵時間間隔)

2.鼠標捕捉

在通常情況下,只有當鼠標位於窗體內時,窗體才能接收到鼠標的消息。如果需要接收所有的鼠標消息而不論鼠標是否在窗口內,這時可以調用SetCapture函數來實現。
SetCapture函數的原型定義如下:

HWND SetCapture (
  HWND hwnd // 窗口句柄
);

調用SetCapture函數後,所有鼠標操作所產生的消息都直接發送到指定窗口。因爲此時鼠標可能位於窗口之外,所以鼠標的座標可能爲負值。由於調用該函數會使其他窗口不能接收到鍵盤和鼠標的消息,因此在完成操作後應及時調用ReleaseCapture 函數釋放鼠標捕獲。

ReleaseCapture函數的原型定義如下:

BOOL ReleaseCapture(VOID);

 

 

鍵盤基礎

Windows程序獲得鍵盤輸入的方式:鍵盤輸入以消息的形式傳遞給程序的窗口過程。實際上,第一次學習消息時,鍵盤就是一個明顯的例子:消息應該傳遞給應用程序的信息類型。
Windows用8種不同的消息來傳遞不同的鍵盤事件。程序可以忽略其中至少一半的消息而不會有任何問題。在大多數情況下,這些消息中包含的鍵盤信息會多於程序所需要的。處理鍵盤的部分工作就是識別出哪些消息是重要的,哪些是不重要的。

鍵盤基礎知識:

用鍵盤當作輸入設備,每當用戶按下或釋放某一個鍵時,會產生一箇中斷,該中斷激活鍵盤驅動程序KEYBOARD.DRV來對鍵盤中斷進行處理。 KEYBOARD.DRV程序會根據用戶的不同操作進行編碼,然後調用Windows用戶模塊USER.EXE生成鍵盤消息,並將該消息發送到消息隊列中等候處理。

1.掃描碼和虛擬碼

掃描碼對應着鍵盤上的不同鍵,每一個鍵被按下或釋放時,都會產生一個唯一的掃描碼作爲本身的標識。掃描碼依賴於具體的硬件設備,即當相同的鍵被按下或釋放時,在不同的機器上可能產生不同的掃描碼。在程序中通常使用由Windows系統定義的與具體設備無關的虛擬碼。在擊鍵產生掃描碼的同時,鍵盤驅動程序KEYBOARD.DRV截取鍵的掃描碼,然後將其翻譯成對應的虛擬碼,再將掃描碼和虛擬碼一齊編碼形成鍵盤消息。所以,最後發送到消息隊列的鍵盤消息中,既包含了掃描碼又包含了虛擬碼。

2.輸入焦點

同一時刻,Windows中可能有多個不同的程序在運行,也就是說有多個窗口同時存在。這時,鍵盤由多個窗口共享,但只有一個窗口能夠接收到鍵盤消息,這個能夠接收鍵盤消息的窗口被稱爲擁有輸入焦點的窗口。擁有輸入焦點的窗口應該是當前的活動窗口,或者是活動窗口的子窗口,其標題和邊框會以高亮度顯示,以區別於其他窗口。擁有輸入焦點的也可以是圖標而不是窗口,此時,Windows也將消息發送給圖標,只是消息的格式略有不同。

窗口過程可以通過發送WM_SETFOCUS和 WM_KILLFOCUS消息使窗體獲得或失去輸入焦點。程序也可以通過捕獲WM_SETFOCUS和WM_KILLFOCUS消息來判斷窗體何時獲得或失去輸入焦點。其中WM_SETFOCUS消息表示窗口正獲得輸入焦點,WM_ KILLFOCUS消息表示窗口正失去輸入焦點。

3.鍵盤消息

鍵盤消息分爲系統鍵消息和非系統鍵消息。系統鍵消息是指由Aft鍵和其他鍵組合而產生的按鍵消息。當系統鍵被按下時產生WM_ SYSKEYDOWN消息,當系統鍵被釋放時產生WM_SYSKEYUP消息。 Aft鍵與其他鍵形成的組合鍵通常用於對程序菜單和系統菜單進行選擇,或用於在不同的程序之間進行切換。因此,系統鍵消息應該交由Windows進行處理,用戶所編制的程序一般不處理系統鍵消息,而是將這些消息交由DefWindowProc函數進行處理。如果用戶想對系統鍵消息進行處理,應該在處理完這些消息後,再將其發送給DefWindowProc函數,使得Windows系統能夠正常工作。

某些擊鍵消息可以被轉換成字符消息,例如字母鍵、數字鍵等。而有些鍵只能產生按鍵消息而沒有字符消息,例如 Shift鍵、Insert鍵等。消息循環中的 TranslateMessage函數可以實現從擊鍵消息向字符消息的轉化。當GetMessage函數捕獲一個WM_SYSKEYDOWN消息或 WM_KEYDOWN消息後,TranslateMessage函數判斷產生該消息的鍵是否能夠被轉換成字符消息,如果能,就將該消息轉換成字符消息,再通過DispatchMessape函數將轉換後的字符消息發送到消息隊列中去。字符消息共有以下四種,如表所示。

 

字符 系統字符 非系統字符
普通字符 WM_SYSCHAR WM_CHAR
死字符 WM_SYSDEADCHAR WM_DEADCHAR

 

其中死字符是由某些特殊鍵盤上的按鍵所造成的,Windows一般忽略死字符所產生的消息。

Windows的消息一般是通過一個MSG結構體變量傳送給消息處理函數的。對於鍵盤消息, MSG結構體變量的各個域中較重要的是lParam域和 wParam域。

wParam域:對於非字符消息,wParam域保存按鍵的虛擬健代碼;對於字符消息, wParam域保存字符的ASCII碼。

lParam域:32位的lParam變量被分爲六部分,記錄了以下相關信息:重複次數、OEM掃描碼、擴展鍵標誌、關聯鍵標誌、前一擊鍵狀態和轉換狀態。

lParam域各位的含義如表所示。

 

位數 含義
0-15 擊鍵重複次數累加
16-23 OEM掃描碼
24 是否爲擴展鍵
25-28 未定義
29 是否便用關聯鍵,及Alt鍵是否同時按下
30 前一次擊鍵狀態,0表示該鍵前一次狀態爲擡起,1表示前一次狀態爲按下
31 轉換狀態

 

按鍵的次序不同,產生的消息也不相同。例如,按下並釋放1鍵,讀過程依次產生如表所示三條消息。按下1鍵所產生的消息和wParam的取值

消息??????? wParam變量取值
WM_KEYDOWN??虛擬碼1
WM_CHAR????ASCII碼“1”
WM_KEYUP ????虛擬碼1

如果按下Shift鍵後再按下1鍵並釋放,則依次產生如表所示的消息。按下 Shift鍵後按 1健所產生的消息和 wParam的取值

消息??????? wParam變量取值
WM_KEYDOWN ??虛擬碼 VK_SHIFT
WM_KEYDOWN ??虛擬碼 VK_1
WM_CHAR ASCII??碼 “1”
WM_KEYUP ????虛擬碼 VK_1
WM_KEYUP???? 虛擬碼 VK_SHIFT

 


WM_SIZE消息

wParam域:保存了窗體新尺寸的左上角座標,變量的32位分爲兩個部分,低16位保存X座標,高16位保存Y座標。

lParam域:保存了窗體新尺寸的右下角座標,變量的32位分爲兩個部分,低16位保存X座標,高16位保存Y座標。

在編程過程中,通常通過LOWORD宏定義來獲得32位變量的低16位數值,通過HIWORD宏定義來獲得32位變量的高歷位數值。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章