關於鍵盤輸入

關於鍵盤輸入


  應用程序應該像接收鼠標輸入一樣可以接收鍵盤輸入,Windows中的應用程序是以窗體消息的形式來獲取鍵盤輸入。

  本節包括以下內容:

鍵盤輸入模型

  系統通過安裝當前鍵盤的設備驅動來實現與應用程序的設備無關性,也可以通過用戶或應用程序的鍵盤佈局設置來實現語言無關性。鍵盤設備驅動接收鍵盤的“掃描碼”,然後把“掃描碼”發送給鍵盤佈局,通過鍵盤佈局被轉換爲消息併發送到應用程序的相應窗口。

  鍵盤上每一個鍵都有一個唯一值,這個唯一值就稱爲“掃描碼”(scan code),對於鍵盤上每個鍵來說,“掃描碼”是設備相關的。當用戶按鍵時會產生兩次掃描碼,一次是按下鍵時,一次是放開時。

  然後,鍵盤驅動把掃描碼解釋並轉換(映射)爲“虛鍵碼”(virtual-key code),這個碼是設備無關的,其值被系統所定義並用來標識每一個鍵。轉換掃描碼後,鍵盤佈局會創建一個包含掃描碼、虛鍵碼以及其他按鍵信息的消息,並把這個消息放入系統消息隊列。接着,系統從系統消息隊列中刪除該消息,再投遞到相應線程的消息隊列中。最後,線程的消息循環移除該消息並傳遞到相應窗口過程以進行處理。下圖即鍵盤輸入模型:

Keyboard input processing model

鍵盤聚焦及激活

  系統投遞鍵盤消息到前臺線程的消息隊列中,這個前臺線程應該是創建當前獲得焦點的窗口的線程。鍵盤聚焦(Keyboard focus)是一個窗體的臨時屬性。系統通過鍵盤聚焦來向所有的顯示窗體共享鍵盤,從用戶的角度講,鍵盤聚焦也就意味着,從一個窗口轉到另外一個。獲取焦點的窗口接收(從創建它的線程的消息隊列中)接收所有的鍵盤消息,直到焦點轉移到另外的窗體上。

  線程可以通過調用GetFocus函數來確定那個窗口爲當前窗口(已經鍵盤聚焦),也可以通過SetFocus來使哪個窗口獲取焦點。當鍵盤聚焦從一個窗口換到另外一個時,系統會發送WM_KILLFOCUS到失去焦點的窗口,然後發送WM_SETFOCUS消息到獲得焦點的窗口。

  鍵盤聚焦與活動窗口有一定關係,活動窗口(active window)是最上層用戶正在操作的窗口。鍵盤聚焦的窗體或者是活動窗體,或者是活動窗體的子窗體。爲了幫助用戶辨別活動窗體,系統把它置於Z-order的最上層,並高亮它的標題欄及邊框。

  用戶可以通過點擊來激活一個頂級窗體,也可以用ALT+TAB(ALT+ESC)組合鍵或者通過任務列表來選擇一個窗體。線程可以通過SetActiveWindow函數來激活一個頂級窗體,也可以通過GetActiveWindow函數來確定它創建的頂級窗體是否已被激活。

  當一個窗體的活動狀態變更時,系統會發送WM_ACTIVATE消息。wParam參數的低字(譯者注:wParam在Win32中是一個32位的整數,從高到低依次編碼的話應該是31、30、29、……、2、1、0,低字指的是15到0)部分 如果爲0表示窗體未激活,否則表示激活。默認的窗口處理過程收到WM_ACTIVATE消息時,會設置鍵盤聚焦到活動窗口。

  要阻止應用程序接收鍵盤及鼠標事件的話,可以使用BlockInput。需要注意的是,BlockInput函數不會影響異步的鍵盤輸入狀態表,也就是說,當輸入被阻塞時,調用SendInput函數會改變異步鍵盤輸入狀態表。(譯者注:原文直譯可能讓人更加摸不着北,應該是說,BlockInput函數不會影響異步的鍵盤輸入,這個時候,調用SendInput的話,還是會改變異步的鍵盤輸入狀態信息)。

按鍵消息

  按下鍵會產生WM_KEYDOWN或WM_SYSKEYDOWN消息,然後會被放置在當前鍵盤聚焦的窗口所在線程的消息隊列中。同樣釋放按鍵也會產生消息,這個消息將會是WM_KEYUP或者WM_SYSKEYUP。

  Key-up與Key-down消息通常應該成對出現,但如果用戶按下鍵後呆足夠長時間的話,鍵盤會自動重複描述這一情況,系統會對應產生一系列的WM_KEYDOWN或WM_SYSKEYDOWN事件,但不管怎樣,用戶釋放按鍵時,只會產生一個WM_KEYUP或WM_SYSKEYUP消息。

本節包括以下內容:

系統及非系統按鍵

  系統中系統按鍵與非系統按鍵是截然不同的,系統按鍵產生系統按鍵消息:WM_SYSKEYDOWN、WM_SYSKEYUP,而非系統按鍵產生非系統按鍵消息:WM_KEYDOWN與WM_KEYUP。

  如果你的窗口處理過程確實有必要處理系統按鍵消息的話,一定要確認在處理完畢後,該過程把消息傳遞給了DefWindowProc函數。否則,所有的系統操作,包括ALT鍵都會失效,即便窗口的確獲得了焦點。也就是說,用戶將不能訪問窗口菜單或者系統菜單,又或者使用ALT+ESC(ALT+TAB)組合鍵激活其他窗口了。

  系統按鍵消息主要是系統使用的,系統用這些消息提供菜單的內置鍵盤接口,以及允許用戶控制激活不同的窗口。系統按鍵消息通常是用戶按下ALT及某個鍵的組合鍵時產生的,又或者在用戶按下但沒有窗體擁有鍵盤焦點(比如,激活的應用程序最小化)時產生。如果消息產生的話,就會發送到激活窗體的消息隊列中。

  非系統按鍵消息是需要應用程序窗體處理的,DefWindowProc函數不會對這些消息作任何處理,窗體的處理過程可以忽略任意的不需要的非系統按鍵消息。

虛鍵碼描述

  按鍵消息的wParam參數包含了按鍵的虛鍵碼,窗口處理過程根據這個虛鍵碼來處理或者忽略一個按鍵消息。

  典型的窗口處理過程中僅會處理一小部分按鍵消息,其餘的部分只是簡單的接收並忽略。例如,窗口處理過程可能僅處理WM_KEYDOWN消息,以及光標移動鍵、換檔鍵(也可以說控制鍵),還有功能鍵的虛鍵碼。窗口處理過程中一般不會處理字符鍵的按鍵消息,相反,應該使用TranslateMessage函數把它們轉換成字符消息。關於TranslateMessage與字符消息的更多信息,請參見字符消息(Character Message)。

按鍵消息標誌

  按鍵消息的lParam消息中包含了按鍵的額外信息,其中包括:重複次數、掃描碼、擴充鍵標誌、上下文標誌、前鍵狀態標誌,以及轉換狀態標誌。下圖指示了這些標誌及值在lParam中的位置:

  按鍵標誌中可以存儲以下值:

KF_ALTDOWN ALT鍵標誌,標識ALT鍵是否按下。
KF_DLGMODE 對話框標誌,標識對話框是否激活。
KF_EXTENDED 擴充鍵標誌
KF_MENUMODE 菜單模式標誌,標識菜單是否激活。
KF_REPEAT 重複次數
KF_UP 轉換狀態標誌

重複次數

  你可以通過檢查重複次數,來確定一次按鍵是否產生了多個按鍵消息。如果鍵盤產生WM_KEYDOWN或WM_SYSKEYDOWN消息後,超過一定時間應用程序還未處理這些消息的話,系統就會增加重複計數。通常,是因爲用戶保持按鍵狀態較長時間,而啓動了鍵盤的自動重複技術機制。系統不會因此產生多個鍵盤消息,相反,系統會組合這些消息,並增加這個消息的重複次數。釋放一個按鍵時不會啓動自動重複機制,所以WM_KEYUP與WM_SYSKEYUP消息的重複次數總會是1。

掃描碼

  掃描碼是用戶按鍵時由鍵盤硬件產生的,這個值是設備相關的,用來標識不同的鍵,對於字符也是通過按鍵來表示的。應用程序通常會忽略掃描碼,實際上,它使用設備無關的虛鍵碼來說明按鍵消息。

擴充鍵標誌

  擴充鍵標誌用來標識按鍵消息中是否包含了增強型鍵盤的附加鍵,這些擴充鍵包括:鍵盤右手邊的ALT、CTRL鍵,INS、DEL、HOME、END、PAGE UP、PAGE DOWN,小鍵盤左邊的方向鍵,NUM LOCK、BREAK(CTRL+PAUSE)、PRINT SCRNT以及小鍵盤上的除號(/)鍵及ENTER鍵。如果鍵爲以上鍵的話,擴充鍵標誌即會設置。

上下文標誌

  上下文標誌是爲了說明按鍵消息產生時,ALT鍵是否已經按下,如果爲1,表示ALT鍵已經按下,否則沒有按下。

前鍵狀態標誌

  前鍵狀態標誌用來說明產生按鍵消息的鍵原來是擡起的還是按下的。如果爲1,表示原來是按下的,0原來是擡起的。你可以通過該標誌來辨別該消息是否是由鍵盤自動重複機制產生的。如果爲1,表示WM_KEYDOWN與WM_SYSKEYDOWN消息是自動產生的,對於WM_KEYUP與WM_SYSKEYUP消息來說,該標誌總會爲0。

轉換狀態標誌

  轉換狀態標誌用來說明該消息是按下鍵時還是釋放鍵時產生的,對於WM_KEYDOWN、WM_SYSKEYDOWN來說該標誌總會爲0,對於WM_KEYUP、WM_SYSKEYUP總會是1。

字符消息

  按鍵消息可以提供許多按鍵的基本信息,但卻不提供字符鍵的字符碼,要想得到字符碼,應用程序必須在自己的線程循環中包含TranslateMessage函數,TranslateMessage傳遞WM_KEYDOWN或WM_SYSKEYDOWN消息到鍵盤佈局,通過檢查消息的虛鍵碼,如果發現它是一個字符鍵的話,鍵盤佈局就會提供一個字符碼的等價物(會考慮SHIFT及CAPS LOCK鍵的狀態),然後產生一個包括字符碼的字符消息,並放到消息隊列的頭部。消息循環的下一次處理就會把字符消息從隊列中刪除,並分發給相應的窗口處理過程。

  本節包含以下內容:

非系統字符消息

  在窗口的處理過程中可以處理如下的字符消息:<?XML:NAMESPACE PREFIX = MSHELP />WM_CHAR、WM_DEADCHAR、WM_SYSCHAR、WM_SYSDEADCHAR以及WM_UNICHAR。TranslateMessage處理WM_KEYDOWN消息時會產生WM_CHAR或WM_DEADCHAR消息,與之類似,當處理WM_SYSKEYDOWN消息時會產生WM_SYSCHAR或WM_SYSDEADCHAR消息。

  應用程序處理鍵盤輸入時通常會忽略除WM_CHAR與WM_UNICHAR外的所有消息,只是把它們傳遞給DefWindowProc函數。注意:WM_CHAR使用了16位Unicode轉換格式(UTF),WM_UNICHAR使用了UTF-32格式。系統使用WM_SYSCHAR及WM_SYSDEADCHAR消息實現了菜單助記符的工程。

  所有字符消息中的wParam參數包含了字符鍵的字符碼,其值取決於接收消息的窗口的窗口類,如果是用的RegisterClass函數的Unicode版本註冊的窗口類,系統就會向所有那個類的窗口實例提供Unicode字符,否則就是ASCII字符碼,更多信息,請參照Unicode及字符集。

  字符消息中lParam參數值與key-down消息中lParam參數值相同。更多信息,參照按鍵消息標誌。

不使用字符的消息

  有些非English鍵盤中,包含一些本身不產生字符的字符鍵,它們只是用來爲後續的按鍵提供一個區分(譯者注:或者應該說是,爲了區分後續的按鍵吧)。這些鍵就被稱爲無用鍵(dead keys),These keys are called dead keys. 德文鍵盤中的揚聲符就是一個無用鍵的例子,爲了輸入由“o”及揚聲符組成的符號(譯者注:應該是類似“ó”的符號),德文的用戶需要按下揚聲符鍵,然後是“o”鍵。獲得焦點的窗口就會收到以下消息序列:

  1. WM_KEYDOWN
  2. WM_DEADCHAR
  3. WM_KEYUP
  4. WM_KEYDOWN
  5. WM_CHAR
  6. WM_KEYUP

  當TranslateMessage處理無用鍵的WM_KEYDOWN消息時,就會產生WM_DEADCHAR消息,儘管WM_DEADCHAR消息的wParam參數中包含了揚聲符這個無用鍵的字符碼,但應用程序通常會忽略這個消息,而會接着處理後續按鍵的WM_CHAR消息。WM_CHAR消息的WM_CHAR參數中包含了那個字母與揚聲符的字符碼。如果後續的按鍵產生的字符不能與揚聲符組合,系統就會產生兩個WM_CHAR消息,第一個消息的wParam參數中包含了揚聲符的字符碼,第二個消息的wParam參數中包含了後續字符鍵的字符碼。

  當TranslateMessage處理一個系統無用鍵(與ALT的組合鍵)的WM_SYSKEYDOWN消息時,就會產生WM_SYSDEADCHAR消息。應用程序通常忽略WM_SYSDEADCHAR消息。

鍵狀態

  處理鍵盤消息時,應用程序除了需要處理當前按鍵消息的那個按鍵外,還可能需要確定另外一個鍵的狀態。比如,一個字處理軟件,可能允許用戶使用SHIFT+END來選擇一個文本塊,那這個應用程序就必須在任意收到END鍵的按鍵消息時,檢驗SHIFT鍵是否已按下。應用程序可以處理當前的按鍵消息時使用GetKeyState函數來確定一個虛鍵的狀態,也可以通過GetAsyncKeyState函數來獲得當前某個虛鍵的狀態。

  鍵盤佈局維護着一個按鍵名稱列表,僅產生一個字符的按鍵的名稱與按鍵的名稱相同,非字符鍵如TAB、ENTER的名稱以字符串的形式存儲。應用程序可以通過調用GetKeyNameText函數來從設備驅動中得到任意鍵的名稱。

按鍵及字符轉換

  系統包含若干特殊用途的函數來轉換掃描碼、字符碼、以及虛鍵碼,這些函數包括:MapVirtualKey,ToAscii,ToUnicode及VkKeyScan。

  另外,Microsoft® Rich Edit 3.0支持HexToUnicode IME,它可以讓用戶通過熱鍵在十六進制及Unicode字符之間轉換,這也意味着Rich Edit 3.0可以合併到一個應用程序中,使得這個應用程序可以繼承HexToUnicode IME的功能。

熱鍵支持

  一個熱鍵是一個可以產生WM_HOTKEY消息的鍵組合,系統會把這個消息放置到消息隊列的頂部,而繞開任何隊列中已有的消息。應用程序使用熱鍵可以向用戶提供更高優先級的鍵盤輸入,例如,通過定義CTRL+C組合鍵,應用可以使用戶避免冗長的操作。

  要定義熱鍵的話,應用程序需要調用RegisterHotKey函數來指定一個可以產生WM_HOTKEY的組合鍵,再傳入接收消息的窗體的handle以及熱鍵的唯一標識就可以了。用戶按下熱鍵時,創建那個窗體的線程的消息隊列中就會收到WM_HOTKEY。消息中的wParam參數包含熱鍵的標識。應用程序可以在一個線程中定義多個熱鍵,但每個熱鍵必須有一個唯一標識。應用程序終止前,應該調用UnregisterHotKey函數來撤銷熱鍵。

  應用程序可以使用一個熱鍵控件,使得用戶能夠方便自定義熱鍵,熱鍵控件通常用來定義一個熱鍵,使得能夠激活一個窗口,他們不使用RegisterHotKey及UnregisterHotKey函數,相反,使用熱鍵控件的應用程序通常發送WM_SETHOTKEY消息來設置熱鍵,無論何時,用戶按下熱鍵,系統會發送一個指定SC_HOTKEY的WM_SYSCOMMAND消息。詳細信息,可參照“應用熱鍵控件”。

瀏覽及其他功能鍵

  Microsoft Windows®可以支持某些特殊鍵:瀏覽器功能鍵、媒體功能鍵、應用程序載入鍵以及電源管理鍵。WM_APPCOMMAND可以提供這些特殊鍵的支持。另外,ShellProc也被修改爲可以支持額外鍵的函數了。

  在一個組件應用程序中的一個子窗體直接執行這些額外鍵的命令是不太可能的,因此,一旦有這樣的鍵按下的話,DefWindowProc將發送一個WM_APPCOMMAND消息到一個窗體,DefWindowProc也會(冒泡式的)引發(bubble)父窗體處理WM_APPCOMMAND消息。 這同點擊鼠標右鍵彈出上下文菜單的模式類似,DefWindowProc在鼠標右擊時發送一個WM_CONTEXTMENU消息,冒泡式的到它的父親。需要額外說明的是,如果DefWindowProc收到一個給頂級窗體的WM_APPCOMMAND消息的話,就會以HSHELL_APPCOMMAND代碼調用一個外殼鉤子(shell hook)。

  Windows也支持Microsoft IntelliMouse® Explorer,它是一個有五個按鍵的鼠標。兩個額外鍵支持瀏覽器的前進後退。更多信息,請參照XBUTTON。

模擬輸入

  要模擬一個連續的一系列的用戶輸入,就可以使用SendInput函數。這個函數需要三個參數:第一個參數cInputs,表示將要模擬的輸入事件的個數(INPUT數組的大小);第二個參數rgInputs,是INPUT結構的數組,每個元素都描述了輸入事件類型及事件的附加信息;最後一個是cbSize,是按字節計的INPUT結構的大小。

  SendInput函數通過向設備輸入流中注入一系列的模擬事件來實現模擬輸入,效果類似於重複調用keybd_event或mouse_event函數,除了系統需要確認模擬事件件沒有插入其他輸入事件。一旦調用結束,其返回值就會指出有幾個輸入事件成功運行了,如果爲0,則說明輸入被阻塞了。

  SendInput函數不會重設當前的鍵盤狀態,因此,如果調用此函數時,用戶已經按下了什麼鍵的話,就可能被函數產生的事件所幹擾,如果你擔心潛在的衝突的話,可以用GetAsyncKeyState函數檢查鍵盤狀態,並按需要糾正。

語言、場所及鍵盤佈局

  語言指的是自然語言,如English、French及Japanese,子語言是某個特定地理區域的自然語言的變種,如英語的子語言就包括England語及美國英語。而應用程序所指的是語言標識,用來唯一的辨別語言及子語言。

  應用程序通常使用場所(locales)來設置指定輸入輸出的語言,例如設置鍵盤的場所會影響鍵盤產生的字符值;設置顯示器或打印機的場所會影響字形顯示或打印。應用程序通過調入使用鍵盤佈局來設置場所,通過選擇指定場所所支持的字體來設置顯示器或打印機的場所。

  鍵盤佈局不僅是用來自定按鍵的物理位置,而且也是用來確定那些按鍵所決定的字符值的。每個佈局表示當前的輸入語言,也用來確定哪個或哪些鍵組合可以產生哪些字符值。

  每種鍵盤佈局都有相應的標識佈局及語言的句柄(handle),句柄的低字部分是語言標識符,高字部分是設備句柄(描述了物理佈局),或者爲0,表示使用默認的物理佈局。用戶可以用任意的輸入語言聯合到一個物理佈局上。如,說English的用戶,可能不時地要去French工作,他就可以設置輸入語言爲French,而不用變更鍵盤物理佈局,這也意味着用戶可以用自己熟悉的English佈局輸入French文字。

  應用程序不要想直接操作輸入語言,相反,用戶可以設置語言與佈局的組合,然後在他們之間交替變更。當用戶點擊進入一個不同語言的文本中時,應用程序調用ActivateKeyboardLayout函數來激活用戶的默認佈局;如果用戶需要編輯一段文本,但它的語言不再現在的列表中,應用程序將調用LoadKeyboardLayout函數得到一個基於那個語言的佈局。

  ActiveKeyboardLayout函數爲當前任務設置輸入語言,hkl參數可以是鍵盤佈局的句柄或者0擴充的語言標識,鍵盤佈局句柄可以通過LoadKeyboardLayout或GetKeyboardLayoutList函數獲得,HKL_NEXT及HKL_PREV也可以用來選擇下一個或者上一個鍵盤。

  GetKeyboardLayoutName函數可以獲取調用線程的當前鍵盤佈局的名稱。如果應用程序用LoadKeyboardLayout函數創建了當前佈局,GetKeyboardLayoutName將得到創建佈局時使用的相同的字符串,否則,將得到當前佈局的相應現場的主要語言標識符,這也意味着該函數可能不會區分相同主要語言的不同佈局,因此不能返回輸入語言的特定信息。然而GetKeyboardLayout函數可以確定使用的輸入語言。

  LoadKeyboardLayout函數調入一個鍵盤佈局並使它對用戶可用。應用程序可以通過使用KLF_ACTIVATE使得佈局立即對當前線程可用,如果沒有指定KLF_ACTIVATE也可以使用KLF_REORDER重新設置佈局。應用程序應該在調入鍵盤佈局時使用KLF_SUBSTITUTE_OK,以便確保用戶的優先設置,如果有,就會被選擇。

  要多語言支持的話,LoadKeyboardLayout提供KLF_REPLACELANG與KLF_NOTELLSHELL標誌。KLF_REPLACELANG標誌可以不用改變語言而直接替換爲一個存在的鍵盤佈局。嘗試替換爲一個相同語言標識的已存在的佈局,不指定KLF_REPLACELANG是錯誤的。KLF_NOTELLSHELL標誌會組織函數在佈局添加或替換時通知shell。 在連續的一系列調用中,這很有用,除了最後一次調用外,其他調用都應該使用該標誌。

  UnloadKeyboardLayout函數是受限的,它不能調出系統的默認輸入語言,這可以保證用戶總是同shell及溫文件系統一樣輸入相同字符集的文字。



原文:ms-help://MS.MSDNQTR.2003FEB.2052/winui/winui/windowsuserinterface/userinput/keyboardinput/aboutkeyboardinput.htm

© 2003 Microsoft Corporation. All rights reserved.

翻譯:[email protected],2004-11-22止

發佈了40 篇原創文章 · 獲贊 3 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章