Windows 編程 第九回 關於鍵盤的那點兒事

鍵盤對於大家來說可能再也熟悉不過了,它和鼠標是現在最常用的電腦輸入設備。雖然在現在的圖形界面操作系統下使用鼠標比使用鍵盤更方便、更廣泛,但是鼠標還是一時半會兒取代不了它的老前輩——鍵盤的地位,尤其是在打字方面。這一回我們就從編程的角度重新認識一下鍵盤吧。

鍵盤基礎

我們用前面的知識分析個例子吧。比如我們在打字時按下了鍵盤上的一個按鍵,即用戶觸發了一個事件,有事件產生,系統自然要將其包裝成相應的消息並交由相關程序來處理。簡而言之,Windows程序獲得鍵盤輸入的方式:鍵盤輸入以消息的形式傳遞給程序的窗口過程。

如果要說的再詳細一點,可以這麼敘述:

當用戶按下某個鍵時,

1.鍵盤會檢測到這個動作,並通過鍵盤控制器把掃描碼(scan code)傳送到計算機;

  鍵盤掃描碼跟具體的硬件有關的,不同廠商對同一個鍵的掃描碼有可能不同。

2.計算機接收到掃描碼後,將其交給鍵盤驅動程序;

3.鍵盤驅動程序把這個掃描碼轉換爲鍵盤虛擬碼;

  虛擬碼與具體硬件無關,不同廠商的鍵盤,同一個鍵的虛擬碼總是相同的。

3.然後,鍵盤驅動程序把該鍵盤操作的掃描碼和虛擬碼以及其它信息傳遞給操作系統;

4.操作系統將獲得的信息封裝在一個鍵盤消息中,並把該鍵盤消息插入到消息列隊。

5.通過Windows的消息系統,該鍵盤消息被送到某個窗口中;

6.窗口所在的應用程序接收到消息後,可以瞭解到有關鍵盤操作的信息,然後決定作出一定的響應。

注意,4中產生的這些消息並不保存在應用程序的消息隊列中。實際上,Windows在系統消息隊列中保存這些消息。系統消息隊列是獨立的消息隊列, 它由Windows維護,用於初步保存用戶從鍵盤和鼠標輸入的信息。只有當Windows應用程序處理完前一個用戶輸入消息時,Windows纔會從系統 消息隊列中取出下一個消息,並將其放入響應應用程序的消息隊列中。概括的來講,此過程分爲兩步:首先在系統消息隊列中保存消息,然後將它們放入應用程序的 消息隊列。

與所有的個人計算機硬件一樣,鍵盤必須由在Windows下運行的所有應用程序共享。有些應用程序可能有多個窗口,鍵盤又必須由該應用程序內的所有 窗口共享。當按下鍵盤上的鍵時,只會有一個窗口的窗口過程接收鍵盤消息,並且此消息結構體中hwnd字段就指出了接收此消息窗口的句柄。我們可以說接收特 定鍵盤事件的窗口具有輸入焦點。

應用程序從Windows接受的關於鍵盤事件的消息可以分爲擊鍵消息和字符消息。我想大家從名字就應該對這兩種消息有了一種感官上的認識了:前者應 該與按下(或鬆開)鍵盤上的按鍵這一動作有關,後者應該與按鍵上印的字符有關。先有個模糊的印象,接下來我們就要詳細瞭解這兩種消息了。

擊鍵消息

先認識一下它們的名字吧:WM_KEYDOWN、WM_KEYUP、WM_SYSKEYDOWN和WM_SYSKEYUP。一般我喜歡先從名字中揣測新知識的含義,不妨你也先猜一下它們的具體含義。

或許正如你猜的,當你按下一個鍵時,Windows把WM_KEYDOWN或者WM_SYSKEYDOWN消息放入消息隊列;當你釋放一個鍵 時,Windows把WM_KEYUP或者WM_SYSKEYUP消息放入消息隊列。通常“down(按下)”和“up(放開)”消息是成對出現的。不 過,如果你按住一個鍵使得自動重複功能生效,那麼當該鍵最後被釋放時,Windows會給窗口過程發送一系列WM_KEYDOWN(或者 WM_SYSKEYDOWN)消息和一個WM_KEYUP(或者WM_SYSKEYUP)消息。

 

WM_SYSKEYDOWN和WM_SYSKEYUP中的“SYS”代表“系統”,它表示該擊鍵對Windows系統比對Windows應用程序更 加重要。WM_SYSKEYDOWN和WM_SYSKEYUP消息經常由與Alt相組合的擊鍵產生,這些擊鍵激活程序菜單或者系統菜單上的選項,或者用於 切換活動窗口等系統功能(Alt-Tab或者Alt-Esc),也可以用作系統菜單加速鍵(Alt鍵與一個功能鍵相結合,例如Alt-F4用於關閉應用程 序)。程序通常忽略WM_SYSKEYUP和WM_SYSKEYDOWN消息,而是將它們傳送到DefWindowProc來處理。由於Windows要 處理所有Alt鍵的功能,所以你無需攔截這些消息。當然您想在自己的窗口過程中加上攔截系統按鍵的程序代碼,也不是不行。

但是,請再考慮一下,幾乎所有會影響用戶程序窗口的消息都會先通過用戶窗口過程。只有用戶把消息傳送到DefWindowProc,Windows系統纔會對消息進行默認處理。例如,如果你將下面幾行語句:

case WM_SYSKEYDOWN:

case WM_SYSKEYUP:

case WM_SYSCHAR:

       return 0;

       

加入到一個窗口過程中,那麼當你的程序主窗口擁有輸入焦點時,就可以有效地阻止所有Alt鍵操作,其中包括Alt- Tab、Alt-Esc以及菜單操作。看到了吧,這些消息無法送達DefWindowProc,Windows系統就無法進行相關處理。雖然我懷疑你會這 麼做,但是,我相信你會感到窗口過程的強大功能。

 

   WM_KEYDOWN和WM_KEYUP消息通常是在按下或者釋放不帶Alt鍵的鍵時產生的,你的程序可以使用或者忽略這些消息,Windows本身並不處理這些消息。

 

對以上四個擊鍵消息中,wParam是虛擬鍵代碼,表示按下或釋放的鍵,而lParam則包含屬於按鍵的其它數據(我們暫且不用理會它)。wParam、lParam還記得嗎,如果記不得的話,再回去複習一下第四回中消息結構體的講解。

 

下面我們就來詳細的認識一下什麼是虛擬鍵碼。

虛擬鍵碼(虛擬碼與具體硬件無關,不同廠商的鍵盤,同一個鍵的虛擬碼總是相同的)保存在WM_KEYDOWN、WM_KEYUP、WM_SYSKEYDOWN和WM_SYSKEYUP消息wParam參數中。簡單地講,此代碼標識按下或釋放的鍵。

我們使用的大多數虛擬鍵碼的名稱都以VK_開頭。下表列出了這些名稱和數值(十進制和十六進制),以及與虛擬鍵相對應的IBM兼容機種鍵盤上的鍵。下表也標出了Windows執行時是否需要這些鍵。下表還按數字順序列出了虛擬鍵碼。

十進制

十六進制

WINUSER.H標識符

必需?

IBM兼容鍵盤

1

01

VK_LBUTTON

 

鼠標左鍵

2

02

VK_RBUTTON

 

鼠標右鍵

3

03

VK_CANCEL

ˇ

Ctrl-Break

4

04

VK_MBUTTON

 

鼠標中鍵

前四個虛擬鍵碼中有三個指的是鼠標鍵。你永遠都不會從鍵盤消息中獲得這些鼠標鍵代碼。在下一回可以看到,我們能夠從鼠標消息中獲得它們。VK_CANCEL代碼是一個虛擬鍵碼,它包括同時按下兩個鍵(Ctrl-Break)。Windows應用程序通常不使用此鍵。

十進制

十六進制

WINUSER.H標識符

必需?

IBM兼容鍵盤

8

08

VK_BACK

ˇ

Backspace

9

09

VK_TAB

ˇ

Tab

12

0C

VK_CLEAR

 

Num Lock關閉時的數字鍵盤5

13

0D

VK_RETURN

ˇ

Enter (或者另一個)

16

10

VK_SHIFT

ˇ

Shift (或者另一個)

17

11

VK_CONTROL

ˇ

Ctrl (或者另一個)

18

12

VK_MENU

ˇ

Alt (或者另一個)

19

13

VK_PAUSE

 

Pause

20

14

VK_CAPITAL

ˇ

Caps Lock

27

1B

VK_ESCAPE

ˇ

Esc

32

20

VK_SPACE

ˇ

Spacebar

表中的鍵--Backspace、Tab、Enter、Escape和Spacebar-通常用於Windows程序。不過,Windows一般用字符消息(而不是鍵盤消息)來處理這些鍵。另外,Windows程序通常不需要監視Shift、Ctrl或Alt鍵的狀態。

十進制

十六進制

WINUSER.H標識符

必需?

IBM兼容鍵盤

33

21

VK_PRIOR

ˇ

Page Up

34

22

VK_NEXT

ˇ

Page Down

35

23

VK_END

ˇ

End

36

24

VK_HOME

ˇ

Home

37

25

VK_LEFT

ˇ

左箭頭

38

26

VK_UP

ˇ

上箭頭

39

27

VK_RIGHT

ˇ

右箭頭

40

28

VK_DOWN

ˇ

下箭頭

41

29

VK_SELECT

   

42

2A

VK_PRINT

   

43

2B

VK_EXECUTE

   

44

2C

VK_SNAPSHOT

 

Print Screen

45

2D

VK_INSERT

ˇ

Insert

46

2E

VK_DELETE

ˇ

Delete

47

2F

VK_HELP

   

上表列出的前八個碼可能是與VK_INSERT和VK_DELETE一起最常用的虛擬鍵碼。

Print Screen鍵在平時都被Windows應用程序所忽略。VK_SELECT、VK_PRINT、VK_EXECUTE和VK_HELP只在我們很少見的鍵盤上出現,也不用去理會。

十進制

十六進制

WINUSER.H標識符

必需?

IBM兼容鍵盤

48-57

30-39

ˇ

主鍵盤上的0到9

65-90

41-5A

ˇ

A到Z

Windows也包括在主鍵盤上的字母和數字鍵的虛擬鍵碼(數字鍵盤將單獨處理)。注意,數字和字母的虛擬鍵碼是ASCII碼。Windows程序幾乎從不使用這些虛擬鍵碼;實際上,程序使用的是ASCII碼字符的字符消息。

十進制

十六進制

WINUSER.H標識符

必需?

IBM兼容鍵盤

91

5B

VK_LWIN

 

左Windows鍵

92

5C

VK_RWIN

 

右Windows鍵

93

5D

VK_APPS

 

Applications鍵

上表所示的代碼是由Microsoft Natural Keyboard及其兼容鍵盤產生的:Windows用VK_LWIN和VK_RWIN鍵打開“開始”菜單。應用程序能夠通過顯示幫助信息或者當成快捷方式鍵看待來處理application鍵。

十進制

十六進制

WINUSER.H標識符

必需?

IBM兼容鍵盤

96-105

60-69

VK_NUMPAD0到VK_ NUMPAD9

 

NumLock打開時數字鍵盤上的0到9

106

6A

VK_MULTIPLY

 

數字鍵盤上的*

107

6B

VK_ADD

 

數字鍵盤上的+

108

6C

VK_SEPARATOR

   

109

6D

VK_SUBTRACT

 

數字鍵盤上的-

110

6E

VK_DECIMAL

 

數字鍵盤上的.

111

6F

VK_DIVIDE

 

數字鍵盤上的/

上表所示的代碼用於數字鍵盤上的鍵(如果有的話)。

十進制

十六進制

WINUSER.H標識符

必需?

IBM兼容鍵盤

112-121

70-79

VK_F1到VK_F10

ˇ

功能鍵F1到F10

122-135

7A-87

VK_F11到VK_F24

 

功能鍵F11到F24

144

90

VK_NUMLOCK

 

Num Lock

145

91

VK_SCROLL

 

Scroll Lock

最後,雖然多數的鍵盤都有12個功能鍵,但Windows只需要10個,而數字標識卻有24個。另外,程序通常用功能鍵作爲鍵盤加速鍵,這樣,它們 通常不處理上表所示的按鍵。另外,還定義了一些其它虛擬鍵碼,但它們只用於非標準鍵盤上的鍵,或者通常在大型主機終端機上使用的鍵。這裏就不在列表介紹 了。

 

如果程序能夠獲得每個按鍵的信息,這當然很理想,但是大多數Windows程序忽略了幾乎所有的按鍵,而只處理部分的按鍵消息。 WM_SYSKEYDOWN和WM_SYSKEYUP消息是由Windows系統函數使用的,你不必爲此費心,就算你要處理WM_KEYDOWN消息,通 常也可以忽略WM_KEYUP消息。

Windows程序通常爲不產生字符的擊鍵使用WM_KEYDOWN消息。對於光標移動鍵、功能鍵、Insert和Delete 鍵,WM_KEYDOWN消息是最有用的。不過, Insert、Delete和功能鍵經常作爲菜單加速鍵。因爲Windows能把菜單加速鍵翻譯爲菜單命令消息,所以你就不必自己來處理按鍵。可以歸納如 下:多數情況下,你將只爲光標移動鍵(有時也爲Insert和Delete鍵)處理WM_KEYDOWN消息。在使用這些鍵的時候,你可以通過 GetKeyState來檢查Shift鍵和Ctrl鍵的狀態。例如,Windows程序經常使用Shift與光標鍵的組合鍵來擴大文字處理文檔裏選中的 範圍。Ctrl鍵常用於修改光標鍵的意義。例如,Ctrl與右箭頭鍵相組合可以表示光標右移一個詞。

字符消息

while (GetMessage (&msg, NULL, 0, 0))
{
    TranslateMessage (&msg) ;
    DispatchMessage (&msg) ;
}

大家對這個還有印象吧,這是WinMain中典型的消息循環。GetMessage函數用隊列中的下一個消息填入msg結構的字段。DispatchMessage以此消息爲參數調用適當的窗口過程。

在這兩個函數之間是TranslateMessage函數,它將擊鍵消息轉換爲字符消息。如果消息爲WM_KEYDOWN或者 WM_SYSKEYDOWN,並且擊鍵與換擋狀態①相組合產生一個字符,則TranslateMessage把字符消息放入消息隊列中。此字符消息將是 GetMessage從消息隊列中得到的擊鍵消息之後的下一個消息。

 

字符消息可以分爲四類:WM_CHAR和WM_DEADCHAR消息是從WM_KEYDOWN得到的;而WM_SYSCHAR和 WM_SYSDEADCHAR消息是從WM_SYSKEYDOWN消息得到的。在大多數情況下,Windows程序會忽略除WM_CHAR之外的任何消 息。所以我們後面涉及的僅僅是WM_CHAR消息。

伴隨四個字符消息的lParam參數與產生字符代碼消息的按鍵消息之lParam參數相同。不過,參數wParam不是虛擬鍵碼。實際上,它是ANSI或Unicode字符代碼。

因爲TranslateMessage函數從WM_KEYDOWN和WM_SYSKEYDOWN消息產生了字符消息,所以字符消息是夾在擊鍵消息之 間傳遞給窗口過程的。例如,如果Caps Lock未打開,而使用者按下再釋放A鍵,則窗口消息處理程序將接收到如下表所示的三個消息:

消息

按鍵或者代碼

WM_KEYDOWN

“A”的虛擬鍵碼(0x41)

WM_CHAR

“a”的字符代碼(0x61)

WM_KEYUP

“A”的虛擬鍵碼(0x41)

如果你按下Shift鍵,再按下A鍵,然後釋放A鍵,再釋放Shift鍵,就會輸入大寫的A,而窗口過程會接收到五個消息,如下表所示:

 

消息

按鍵或者代碼

WM_KEYDOWN

虛擬鍵碼VK_SHIFT (0x10)

WM_KEYDOWN

“A”的虛擬鍵碼(0x41)

WM_CHAR

“A”的字符代碼(0x41)

WM_KEYUP

“A”的虛擬鍵碼(0x41)

WM_KEYUP

虛擬鍵碼VK_SHIFT(0x10)

如果用戶按住A鍵,以使自動重複產生一系列的按鍵,那麼對每條WM_KEYDOWN消息,都會得到一條字符消息,如下表所示:

 

消息

按鍵或者代碼

WM_KEYDOWN

“A”的虛擬鍵碼(0x41)

WM_CHAR

“a”的字符代碼(0x61)

WM_KEYDOWN

“A”的虛擬鍵碼(0x41)

WM_CHAR

“a”的字符代碼(0x61)

WM_KEYDOWN

“A”的虛擬鍵碼(0x41)

WM_CHAR

“a”的字符代碼(0x61)

WM_KEYDOWN

“A”的虛擬鍵碼(0x41)

WM_CHAR

“a”的字符代碼(0x61)

WM_KEYUP

“A”的虛擬鍵碼(0x41)

組合使用Ctrl鍵與字母鍵會產生從0x01(Ctrl-A)到0x1A(Ctrl-Z)的ASCII控制代碼,其中的某些控制代碼也可以由下表列出的鍵產生:

 

 

按鍵

字符代碼

產生方法

ANSI C控制字符

Backspace

0x08

Ctrl-H

\b

Tab

0x09

Ctrl-I

\t

Ctrl-Enter

0x0A

Ctrl-J

\n

Enter

0x0D

Ctrl-M

\r

Esc

0x1B

Ctrl-[

 

最右列給出了在ANSI C中定義的控制字符,它們用於描述這些鍵的字符代碼。有時Windows程序將Ctrl與字母鍵的組合用作菜單快捷鍵,此時,不會將字母鍵轉換成字符消息。

處理擊鍵和字符消息的基本規則是:如果需要讀取輸入到窗口的鍵盤字符,那麼您可以處理WM_CHAR消息。如果需要讀取光標鍵、功能鍵、Delete、Insert、Shift、Ctrl以及Alt鍵,那麼您可以處理WM_KEYDOWN消息。

但是Tab鍵怎麼辦?Enter、Backspace和Escape鍵又怎麼辦?傳統上,這些鍵都產生表6-13列出的ASCII控制字符。但是在Windows中,它們也產生虛擬鍵碼。這些鍵應該在處理WM_CHAR或者在處理WM_KEYDOWN期間處理嗎?

Petzold先生將Tab、Enter、Backspace和Escape鍵處理成控制字符,而不是虛擬鍵。通常這樣處理WM_CHAR:

case WM_CHAR:
        [other program lines]
    switch (wParam)
    {
    case '\b':            // backspace
           [other program lines]
            break ;
    case '\t':            // tab
            [other program lines]
            break ;
    case '\n':            // linefeed
           [other program lines]
            break ;
    case '\r':            // carriage return
           [other program lines]
            break ;
    default:                      // character codes
            [other program lines]
            break ;
    }
return 0 ;
 
WM_DEADCHAR和WM_SYSDEADCHAR是“死鍵”消息,Windows程序基本忽略它們,我們就不用瞭解了。

趣味閱讀——鍵盤小史

鍵盤的歷史非常悠久,早在1714年,就開始相繼有英、美、法、意、瑞士等國家的人發明了各種形式的打字機,最早的鍵盤就是那個時候用在那些技術還不成熟的打字機上的。直到1868年,打字機之父”——美國人克里斯托夫·拉森·肖爾斯(Christopher Latham Sholes)獲打字機模型專利並取得經營權經營,又於幾年後設計出現代打字機的實用形式和首次規範了鍵盤,即現在電腦上仍廣泛沿用的“QWERTY”鍵盤。

鍵盤字母排列順理論上應該按照字母使用頻率的高低來排序的。有心的讀者也許會感到奇怪:爲什麼要把26個字母作這種無規則的排列呢?既難記憶又難熟練。據說其原因是這樣的:

  在19世紀70年代,肖爾斯公司是當時最大的專門生產打字機的廠家。由於當時機械工藝不夠完善,使得字鍵在擊打之後的彈回速度較慢,一旦打字員擊鍵速度太快,就容易發生兩個字鍵絞在一起的現象,必須用手很小心地把它們分開,從而嚴重影響了打字速度。爲此,公司時常收到客戶的投訴。

  爲了解決這個問題,設計師和工程師傷透了腦筋。後來,有一位聰明的工程師提議:打字機絞鍵的原因,一方面是字鍵彈回速度慢,另一方面也是打字員速度太快了。既然我們無法提高彈回速度,爲什麼不想辦法降低打字速度呢?

  這無疑是一條新思路。降低打字員的速度有許多方法,最簡單的方法就是打亂26個字母的排列順序,把較常用的字母擺在笨拙的手指下,比如,字母"O""S""A"是使用頻率很高的,卻放在最笨拙的右手無名指、左手無名指和左手小指來擊打。使用頻率較低的"V""J""U"等字母卻由最靈活的食指負責。

  結果,這種"QWERTY"式組合的鍵盤誕生了,並且逐漸定型。後來,由於材料工藝的發展,字鍵彈回速度遠大於打字員擊鍵速度,但鍵盤字母順序卻無法改動。至今出現過許多種更合理的字母順序設計方案,但都無法推廣,可知社會的習慣勢力是多麼強大。

(摘自百度百科

 

①當你擊某鍵時,換擋鍵(Shift,Ctrl和Alt)或開關鍵(Cap Lock,Num Lock和Scroll Lock)是否也被同時按下的狀態。

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