鍵盤和鼠標在
windows
中的重要性不必多說,地球人都知道!鍵盤上每一個有意義的鍵都對應(yīng)著一個唯一的標識值,稱之為掃描碼。但是這種掃描碼是硬件相關(guān)的,為了實現(xiàn)設(shè)備無關(guān)性的要求,在
windows
應(yīng)用程序中,使用的往往是與設(shè)備無關(guān)的虛擬碼。
對鍵盤操作的響應(yīng)過程基本如下:
用戶按下一個鍵時,與鍵盤驅(qū)動程序(
KEYBOARD.DRV
)進行中斷處理并調(diào)用
windows
用戶模塊(
USER.EXE
)中的有關(guān)程序來生成鍵盤消息,然后消息被發(fā)送到系統(tǒng)的消息隊列中由相應(yīng)的應(yīng)用應(yīng)用程序進行處理。鼠標的處理過程與鍵盤類似。但是注意無論是鼠標還是鍵盤的所產(chǎn)生的消息經(jīng)過操作系統(tǒng)處理后都只會被發(fā)送給特定的窗口,即具有“輸入焦點”的窗口來進行處理。
?
???????????
鍵盤消息
鍵盤消息通常可分為按鍵消息和字符消息兩類,用戶按下或松開一個鍵時,就產(chǎn)生一個按鍵消息,當一個按鍵組合產(chǎn)生了一個可以顯示的字條時,就產(chǎn)生了一個字符消息。
按鍵消息一般又可以分為系統(tǒng)按鍵和非系統(tǒng)按鍵。
系統(tǒng)按鍵:是指使用了
Alt
等與相關(guān)輸入鍵組成產(chǎn)生的消息,一般這些消息都由操作系統(tǒng)內(nèi)部直接處理。如果應(yīng)用程序處理了這些系統(tǒng)鍵消息,就要調(diào)用
DefWindowProc
函數(shù),以便不影響
windows
對它們的處理。
非系統(tǒng)按鍵:對應(yīng)于那些不使用組合鍵的按鍵消息。
?
再對按鍵消息的兩個變量
wParam
和
lParam
做一些解釋:(名堂還真不少
T_T
)
1.????????
wParam
包含了識別按下鍵的虛鍵碼,這些碼是由系統(tǒng)定義的設(shè)備無關(guān)的。可以在
windows.h
中找到找到相關(guān)定義。
2.????????
lParam
32
位的變量
lParam
所表示的含義可以分為以下
7
個部分
(1)?????
重復(fù)計數(shù)位(
0~15
位)
?????
表示當前消息的重復(fù)次數(shù)。
(2)?????
OEM
掃描碼(
16~23
位)
OEM
掃描碼是鍵盤發(fā)送的碼值,因為是設(shè)備相關(guān)的幫一般被忽略掉。
(3)?????
擴展鍵標志(
24
位)
在有
Alt
或
Ctrl
鍵按下時為
1,
否則為
0
。
(4)?????
保留位(
25~28
位)
??????????
系統(tǒng)保留,一般不用。
(5)?????
關(guān)聯(lián)碼(
29
位)
主要用來記錄某鍵與
Alt
等鍵的組合狀態(tài),若按下
Alt
鍵,當
WM_SYSKEYDOWN
消息發(fā)送到某個激活窗口時,其值為
1,
否則為
0
。
(6)?????
鍵的先前狀態(tài)(
30
位)
用于記錄先前某鍵的狀態(tài)。
(7)?????
轉(zhuǎn)換狀態(tài)(
31
位)
用于記錄被始終按下的某鍵所產(chǎn)生的消息。
?
??????
在
WinMain
函數(shù)里的消息循環(huán)中包含了
TranslateMessage
函數(shù),它的主要功能是把按鍵消息轉(zhuǎn)化為字符消息,即把按鍵所產(chǎn)生的原始的KEYDOWN/KEYUP消息轉(zhuǎn)化成WM_CHAR消息。
同樣,字符消息也可以分為系統(tǒng)和非系統(tǒng)消息兩類。
?
Windows
系統(tǒng)支持兩類字符集:
OEM
和
ANSI
。
OEM
是
IBM
的字符集,在
windows
中使用不多,目前大多使用的是
ANSI
字符集。
?
???????????
鼠標消息
1.????????
鼠標操作
簡單的單擊操作包含了按下和松開這一全過程;而雙擊操作實際上是指用戶在知時間內(nèi)(默認為
0.5
秒)的再次單擊操作。
在鼠標消息中,參數(shù)
lParam
包含了鼠標的位置,低字節(jié)是
X
坐標,高字節(jié)是
Y
坐標。參數(shù)
wParam
則包含了一個指示各種虛鍵狀態(tài)的值。
通過用戶區(qū)消息的
wParam
和
lParam
參數(shù),程序員就可以確定鼠標的位置和狀態(tài)。
對于鼠標的消息處理,一般分為兩種,一種要對
Ctrl
等鍵進行監(jiān)視,另一種則不需要。下面是一個示例
case WM_LbUTTONDWON:?????? //
鼠標按下時
ctrl
和
shift
都被按下
?????? If(( wParam&MK_CONTROL) && ( wParam&MK_SHIFT) )
?????? …
?????? break;
case WM_LBUTTONDOWN:?????? //
不監(jiān)視組合按鍵
?????? …
?????? break;
此外,要使窗口能監(jiān)視雙擊消息,必須在注冊窗口類的時候使該類具有
CS_DBLCLKS
屬性才行,否則只能收到兩條單擊消息。
2.????????
光標
可以使用系統(tǒng)光標或者調(diào)用
LoadCursor
加載自定義光標資源
???????????
示例程序
在下面的一個例子中,顯示鼠標和鍵盤的消息響應(yīng)。程序的用戶區(qū)被分為四個區(qū)域,每個區(qū)域里光標設(shè)置成不同的樣式。通過監(jiān)視鍵盤按鍵,可對一個
10
個字符長的緩沖區(qū)里輸入字符,并最后顯示在鼠標上方,鼠標移動時同時修改字符輸出的位置。可以按下
BACK
鍵刪掉已經(jīng)輸入的字符,緩沖區(qū)滿時再輸入和空的時候刪字符的操作都被拒絕,并用消息框進行提示。代碼如下:
//
定義的靜態(tài)變量
#define
?BufSize?10
static
?
char
?lpszBuffer[BufSize];
static
?
int
?nNumChar?
=
?
0
;
static
?
int
?i?
=
?
0
;
static
?POINT?pt;
????……
LRESULT?CALLBACK?WndProc(HWND?hWnd,?UINT?iMsg,?
?????????????????????????UINT?wParam,?
?????????????????????????LONG?lParam)

{
????HDC?hdc;
????PAINTSTRUCT?ps;
????HCURSOR?hCur;
????
switch
(iMsg)

????
{
????
case
?WM_CHAR:
//
處理非系統(tǒng)鍵的消息
????????
if
(wParam?
==
?VK_BACK)
//
按下退格鍵
????????
{
????????????
if
(nNumChar?
==
?
0
)

????????????
{
????????????????MessageBox(hWnd,?
"
沒有字符可以刪除!
"
,?NULL,?MB_OK);
????????????}
????????????
else
????????????
{
????????????????
--
nNumChar;
????????????????
//
此函數(shù)刷新用戶區(qū),會產(chǎn)生PAINT消息
????????????????InvalidateRect(hWnd,?NULL,?TRUE);
????????????}
????????????
break
;
????????}
????????
if
(nNumChar?
>=
?BufSize)
//
字符超過緩沖區(qū)大小
????????
{
????????????MessageBox(hWnd,?
"
緩沖區(qū)已滿!刪除字符請用退格鍵
"
,?NULL,?MB_OK);
????????????
break
;
????????}
????????lpszBuffer[nNumChar
++
]?
=
?(unsigned?
char
)wParam;
????????InvalidateRect(hWnd,?NULL,?TRUE);
????????
break
;
????????
????
case
?WM_PAINT:
//
將處理過的字符輸出
????????hdc?
=
?BeginPaint(hWnd,?
&
ps);
????????
//
調(diào)整坐標使字出現(xiàn)在鼠標上方
?????????TextOut(hdc,?pt.x
-
15
,?pt.y
-
15
,?lpszBuffer,?nNumChar);
????????EndPaint(hWnd,?
&
ps);
????????
break
;

????
case
?WM_MOUSEMOVE:
//
移動鼠標后改變文本輸出坐標并刷新
????????pt.x?
=
?LOWORD(lParam);
????????pt.y?
=
?HIWORD(lParam);
//
鼠標的坐標
????????
//
在不同的區(qū)域顯示不同的光標
????????
if
(pt.x?
<
?
400
?
&&
?pt.y?
<
?
400
)
????????????hCur?
=
?LoadCursor(NULL,?IDC_NO);
????????
else
?
if
(pt.x?
>
?
400
?
&&
?pt.y?
<
?
400
)
????????????hCur?
=
?LoadCursor(NULL,?IDC_HELP);
????????
else
?
if
(pt.x?
<
?
400
?
&
?pt.y?
>
?
400
)
????????????hCur?
=
?LoadCursor(NULL,?IDC_SIZEALL);
????????
else
?
if
(pt.x?
>
400
?
&&
?pt.y?
>
?
400
)
????????????hCur?
=
?LoadCursor(NULL,?IDC_CROSS);
????????SetCursor(hCur);
????????InvalidateRect(hWnd,?NULL,?TRUE);
????????
break
;
????
????
case
?WM_DESTROY:
????????PostQuitMessage(
0
);
????????
return
?
0
;
????
default
:
????????
return
?(DefWindowProc(hWnd,?iMsg,?wParam,?lParam));
????}
????
return
?
0
;
}