明輝手游網(wǎng)中心:是一個(gè)免費(fèi)提供流行視頻軟件教程、在線(xiàn)學(xué)習(xí)分享的學(xué)習(xí)平臺(tái)!

重定義鍵盤(pán)的2種完成方法

[摘要]在windows操作系統(tǒng)中,如果我們想對(duì)鍵盤(pán)進(jìn)行重定義,比如說(shuō)按某鍵就可發(fā)直接上網(wǎng),按某鍵可以直接關(guān)閉窗口等等,如何實(shí)現(xiàn)呢!在Visual C++中用常規(guī)class wizard方法是不可以實(shí)現(xiàn)的,...
在windows操作系統(tǒng)中,如果我們想對(duì)鍵盤(pán)進(jìn)行重定義,比如說(shuō)按某鍵就可發(fā)直接上網(wǎng),按某鍵可以直接關(guān)閉窗口等等,如何實(shí)現(xiàn)呢!在Visual C++中用常規(guī)class wizard方法是不可以實(shí)現(xiàn)的,這里我們用兩種方法去實(shí)現(xiàn)它。

方法1:利用RegisterHotKey函獲數(shù)實(shí)現(xiàn)

程序?qū)崿F(xiàn)原理:首先用戶(hù)預(yù)定一個(gè)熱鍵,無(wú)論該程序是前臺(tái)程序還是后臺(tái)程序,只
要用戶(hù)按了這個(gè)鍵,就執(zhí)行我們定義好的函數(shù)。程序中要對(duì)熱鍵消息WM_HOTKEY進(jìn)行
捕獲,并通過(guò)消息參數(shù)了解哪一個(gè)鍵被按下。
    因?yàn)閂C中的CLASSWIZARD中沒(méi)有對(duì)消息WM_HOTKEY進(jìn)行封裝,我們只有通過(guò)手工加入代碼實(shí)現(xiàn)。該消息的映射及處理。

  具體實(shí)現(xiàn)步驟如下:

l         1 用MFC AppWizard建立一個(gè)工程名為:HotKey基于Dialog base的對(duì)話(huà)框程序,點(diǎn)擊finish。



l         2 聲明熱鍵消息處理函數(shù)原型
  在HotKeyDlg.h中消息映射聲明處(AFX_mSG字樣之后)加入如下語(yǔ)句(其中畫(huà)線(xiàn)部分是加入的代碼):

// Generated message map functions

       //{{AFX_MSG(CHotKeyDlg)

       virtual BOOL OnInitDialog();

       afx_msg void OnSysCommand(UINT nID, LPARAM lParam);

       afx_msg void OnPaint();

       afx_msg HCURSOR OnQueryDragIcon();

       LRESULT OnHotKey(WPARAM wParam,LPARAM lParam);

       //}}AFX_MSG

           DECLARE_MESSAGE_MAP()

l         3 消息與相應(yīng)處理函數(shù)相關(guān)聯(lián) 在HotKeyDlg.Cpp中加入消息映射宏,使消息與相應(yīng)處理函數(shù)發(fā)生關(guān)系,加入如下語(yǔ)句(其中畫(huà)線(xiàn)部分是加入的代碼):

BEGIN_MESSAGE_MAP(CHotKeyDlg, CDialog)

       //{{AFX_MSG_MAP(CHotKeyDlg)

       ON_WM_SYSCOMMAND()

       ON_WM_PAINT()

       ON_WM_QUERYDRAGICON()

       ON_MESSAGE(WM_HOTKEY,OnHotKey)

       //}}AFX_MSG_MAP

END_MESSAGE_MAP()

l         4 為方便以后的操作預(yù)先在CHotKeyDlg類(lèi)中利用CLASSWIZARD實(shí)現(xiàn)一個(gè)響應(yīng)WM_CREATE和WM_DESTROY消息的函數(shù)OnCreate( )與OnDestroy( )的框架,(利用CLASSWIZARD很容易實(shí)現(xiàn),請(qǐng)參考有關(guān)VC的書(shū)籍,在此不再贅述)。

l         5 向系統(tǒng)登記熱鍵

  在OnCreate()函數(shù)中加入如下代碼以向系統(tǒng)登記熱鍵,本例子的熱鍵設(shè)為ESC.     

RegisterHotKey(m_hWnd,1001,NULL, VK_ESCAPE);

//函數(shù)參數(shù)請(qǐng)參考有關(guān)VC的書(shū)籍,在此不再贅述

l         6 處理熱鍵

在消息處理函數(shù)OnHotKey()中對(duì)熱鍵進(jìn)行處理,并可加入用戶(hù)希望運(yùn)行的程序代碼等:
     (注下面代碼是在HotKeyDlg.cpp中完全用手工加入的代碼)

     LRESULT CHotKeyDlg::OnHotKey(WPARAM wParam,LPARAM lParam)

{

       HWND hwnd;

       hwnd=::GetForegroundWindow();

       ::PostMessage(hwnd,WM_CLOSE,0,0);

       return 0;

}

l         7 程序運(yùn)行完畢后解除熱鍵

在OnDestroy()中通過(guò)UnRegisterHotKey()解除熱鍵登記,釋放系統(tǒng)資源.

  UnregisterHotKey( m_hWnd, 1001);

l         8 編譯并運(yùn)行程序
  運(yùn)行程序后,無(wú)論何時(shí)只要按下熱鍵Esc后本程序便立關(guān)閉當(dāng)前的窗口。

方法2:利用鍵盤(pán)鉤子函獲數(shù)實(shí)現(xiàn)

說(shuō)到鉤子函數(shù),可能對(duì)許多初學(xué)編程的人很陌生,我這里就多說(shuō)幾句:

WINDOW的消息處理機(jī)制為了能在應(yīng)用程序中監(jiān)控系統(tǒng)的各種事件消息,提供了掛接

各種反調(diào)函數(shù)(HOOK)的功能。這種掛鉤函數(shù)(HOOK)類(lèi)似擴(kuò)充中斷驅(qū)動(dòng)程序,掛鉤

可以?huà)旖佣鄠(gè)反調(diào)函數(shù)構(gòu)成一個(gè)掛接函數(shù)鏈。系統(tǒng)產(chǎn)生的各種消息首先被送到各種

掛接函數(shù),掛接函數(shù)根據(jù)各自的功能對(duì)消息進(jìn)行監(jiān)視、修改和控制等,然后交還控

制權(quán)或?qū)⑾鬟f給下一個(gè)掛接函數(shù)以致最終達(dá)到窗口函數(shù)。WINDOW系統(tǒng)的這種反

調(diào)函數(shù)掛接方法雖然會(huì)略加影響到系統(tǒng)的運(yùn)行效率,但在很多場(chǎng)合下是非常有用

的,通過(guò)合理有效地利用鍵盤(pán)事件的掛鉤函數(shù)監(jiān)控機(jī)制可以達(dá)到預(yù)想不到的良好效果。

l         1 在WINDOW下可進(jìn)行掛接的過(guò)濾函數(shù)包括11種:

WH_CALLWNDPROC 窗口函數(shù)的過(guò)濾函數(shù)

WH_CBT 計(jì)算機(jī)培訓(xùn)過(guò)濾函數(shù)

WH_DEBUG 調(diào)試過(guò)濾函數(shù)

WH_GETMESSAGE 獲取消息過(guò)濾函數(shù)

WH_HARDWARE 硬件消息過(guò)濾函數(shù)

WH_JOURNALPLAYBACK 消息重放過(guò)濾函數(shù)

WH_JOURNALRECORD 消息記錄過(guò)濾函數(shù)

WH_MOUSE 鼠標(biāo)過(guò)濾函數(shù)

WH_MSGFILTER 消息過(guò)濾函數(shù)

WH_SYSMSGFILTER 系統(tǒng)消息過(guò)濾函數(shù)

WH_KEYBOARD 鍵盤(pán)過(guò)濾函數(shù)

WH_KEYBOARD_LL在WindowsNt4.0以上可用的鍵盤(pán)過(guò)濾函數(shù)

WH_MOUSE_LL 在WindowsNt4.0以上可用的鼠標(biāo)過(guò)濾函數(shù)

l           2 其中鍵盤(pán)過(guò)濾函數(shù)是最常用最有用的過(guò)濾函數(shù)類(lèi)型,不管是哪一種類(lèi)型的過(guò)濾函

數(shù),其掛接的基本方法都是相同的。

WINDOW調(diào)用掛接的反調(diào)函數(shù)時(shí)總是先調(diào)用掛接鏈?zhǔn)椎哪莻(gè)函數(shù),因此必須將鍵盤(pán)掛

鉤子函數(shù)利用函數(shù)SetWindowsHookEx()將其掛接在函數(shù)鏈?zhǔn)。至于消息是否傳遞給函

數(shù)鏈的下一個(gè)函數(shù)是由每個(gè)具體函數(shù)功能確定的,如果消息需要傳統(tǒng)給下一個(gè)函

數(shù),可調(diào)用API函數(shù)的CallNextHookEx()來(lái)實(shí)現(xiàn),如果不傳遞直接返回即可。

l         在程序中可以利用函數(shù)SetWindowsHookEx()來(lái)掛接過(guò)濾函數(shù),在掛接函數(shù)時(shí)必須指

出該掛接函數(shù)的類(lèi)型、函數(shù)的入口地址以及函數(shù)是全局性的還是局部性的,掛接函

數(shù)的具體調(diào)用格式如下:

HHOOK SetWindowsHookEx(int idHook,HOOKPROC lpfn,HINSTANCE hMod,DWORD dwThreadId);

其中,第一個(gè)參數(shù)是鉤子的類(lèi)型;第二個(gè)參數(shù)是鉤子函數(shù)的地址;第三個(gè)參數(shù)是包

含鉤子函數(shù)的模塊句柄;第四個(gè)參數(shù)指定監(jiān)視的線(xiàn)程。如果指定確定的線(xiàn)程,即為

線(xiàn)程專(zhuān)用鉤子;如果指定為空,即為全局鉤子。其中,全局鉤子函數(shù)必須包含在DLL

(動(dòng)態(tài)鏈接庫(kù))中,而線(xiàn)程專(zhuān)用鉤子還可以包含在可執(zhí)行文件中。得到控制權(quán)的鉤子

函數(shù)在完成對(duì)消息的處理后,如果想要該消息繼續(xù)傳遞,那么它必須調(diào)用另外一個(gè)

SDK中的API函數(shù)CallNextHookEx來(lái)傳遞它。鉤子函數(shù)也可以通過(guò)直接返回TRUE來(lái)丟

棄該消息,并阻止該消息的傳遞。

的確如果函數(shù)是全局性的,那么它必須放在一個(gè)DLL(動(dòng)態(tài)鏈接庫(kù))中,但是我發(fā)現(xiàn)在

window 2000以上的版本中,不用寫(xiě) DLL(動(dòng)態(tài)鏈接庫(kù))就可以作出全局性的鍵盤(pán)函數(shù)。用它可以做很多事情。

UnhookWindowsHookEx(int idHook)函數(shù)即可實(shí)現(xiàn)對(duì)掛接鉤子的卸載。

l         我們開(kāi)始寫(xiě)程序吧!用MFC AppWizard建立一個(gè)工程名為: KBoardHook基于Dialog base的對(duì)話(huà)框程序,點(diǎn)擊finish。

l         在KBoardHookDlg.cpp的最上邊加上

HHOOK hhkLowLevelKybd2000;

hhkLowLevelKybd2000為全局變量。

l         為方便以后的操作預(yù)先在CKBoardHookDlg類(lèi)中利用CLASSWIZARD實(shí)現(xiàn)一個(gè)響應(yīng)WM_CREATE和WM_DESTROY消息的函數(shù)OnCreate( )與OnDestroy( )的框架,

l         在OnCreate()函數(shù)中通過(guò)SetWindowsHookEx與系統(tǒng)掛起鉤子代碼如下

hhkLowLevelKybd2000  = SetWindowsHookEx(WH_KEYBOARD_LL,

       LowLevelKeyboardProc,      AfxGetApp()->m_hInstance, 0);

l         在OnDestroy()中通過(guò)UnhookWindowsHookEx ()解除已經(jīng)掛起鉤子,釋放系統(tǒng)資源, 代碼如下:        UnhookWindowsHookEx(hhkLowLevelKybd2000);

l         這時(shí)在KBoardHookDlg.h中聲明LowLevelKeyboardProc ,在class CKBoardHookDlg : public Cdialog的上面加入如下代碼

LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam);

// 當(dāng)nCode為0(在winuser.h中有如下定義:#define HC_ACTION  0)時(shí)wParam, lParam才包含所應(yīng)有的鍵盤(pán)信息,wParam的值代表了鍵盤(pán)的消息可以為WM_KEYDOWN

WM_KEYUP, lParam為指向KBDLLHOOKSTRUCT的指針。



l         再在KBoardHookDlg.cpp的最后加入如下代碼(這此代碼都是手工加入的,不能用ClassWzrd)













LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)

{

              PKBDLLHOOKSTRUCT p = (PKBDLLHOOKSTRUCT) lParam;

              if (nCode == HC_ACTION) {

                     int vKey=LOBYTE(p->vkCode);

                     switch (wParam)

                     {

                     case WM_KEYDOWN:

                            {

                                   if(vKey==27)

                                   {

                                          HWND hwnd;

                                          hwnd=::GetForegroundWindow();

                                          ::PostMessage(hwnd,WM_CLOSE,0,0);

                                   }

                            }

                     }

                     

              }

              return CallNextHookEx(NULL, nCode, wParam, lParam);

}

這時(shí)編譯程序,一共出了6個(gè)

其中前兩個(gè)錯(cuò)是

error C2065: 'WH_KEYBOARD_LL' : undeclared identifier

error C2065: 'PKBDLLHOOKSTRUCT' : undeclared identifier

看看msdn 明明說(shuō)可以有而且在winuser.h中定義了,我試著在KBoardHookDlg.cpp 的前面加入#include "winuser.h" ,但是結(jié)果還是一樣的,我們?cè)僮犯降自倏纯磜inuser.h,發(fā)現(xiàn)里面明明定義了WH_KEYBOARD_LL與PKBDLLHOOKSTRUCT,但是就是不好用,怎么辦呢?把它們找到,復(fù)制到KBoardHookDlg.h中原先手工加入的定義LowLevelKeyboardProc函數(shù)上面(下面就是我們要復(fù)制的代碼)

#define WH_KEYBOARD_LL     13

typedef struct tagKBDLLHOOKSTRUCT {

    DWORD   vkCode;

    DWORD   scanCode;

    DWORD   flags;

    DWORD   time;

    DWORD   dwExtraInfo;

} KBDLLHOOKSTRUCT, FAR *LPKBDLLHOOKSTRUCT, *PKBDLLHOOKSTRUCT;

這時(shí)再編譯程序,程序就可以運(yùn)行了。這樣我們就同樣實(shí)現(xiàn)了改變鍵盤(pán)的目的。



小結(jié),上述兩種方法是不同的原理,其加載方法也不盡相同,對(duì)于第一種方法其實(shí)現(xiàn)可以在win98/win2000/winXP中都能通用,但對(duì)于第兩種方法,其只能在win2000/winXP中應(yīng)用,如果相在win98中應(yīng)用就要寫(xiě)成dll進(jìn)行調(diào)用,較為繁瑣,但是其功能是強(qiáng)大的,因?yàn)樗桥c系統(tǒng)進(jìn)行了掛鉤,所以用戶(hù)在任何窗口下按鍵盤(pán)都會(huì)觸發(fā)它,例如我們想屏蔽鍵盤(pán),只要把LowLevelKeyboardProc函數(shù)中的

return CallNextHookEx(NULL, nCode, wParam, lParam);

改為 return true;

就可以屏蔽除Ctrl+Alt+Del以外的所有按鍵。

至于為什么么在winuser.h中定義了那兩個(gè)量編譯時(shí)卻說(shuō)沒(méi)定義,我也是百思不得其解,希望我寫(xiě)這篇文章能拋磚引玉,激發(fā)大家學(xué)習(xí)vc的興趣。


注(作者同意作品進(jìn)刊盤(pán)和網(wǎng)絡(luò)版)

聯(lián)系方式:大連理工大學(xué)電子系995班孫宇哲
Email:sunyuzhe@263.net
主頁(yè):http://sunyuzhe.363.net
Tel:0411-4702214