使用SetWindowsHookEx(WH_KEYBOARD,...)製作全域性鍵盤鉤子
阿新 • • 發佈:2019-02-15
學習《Windows程式設計》時,照著例子作了一個全域性的鍵盤鉤子,可以截獲到使用者的按鍵。
先是動態庫部分:
/////////////////////////////////////////// // KeyHookLib.h檔案 // 定義函式修飾巨集,方便引用本DLL工程的匯出函式 #ifdef KEYHOOKLIB_EXPORTS #define KEYHOOKLIB_API __declspec(dllexport) #else #define KEYHOOKLIB_API __declspec(dllimport) #endif // 自定義與主程式通訊的訊息 #define HM_KEY WM_USER + 101 // 宣告要匯出的函式 BOOL KEYHOOKLIB_API WINAPI SetKeyHook(BOOL bInstall, DWORD dwThreadId = 0, HWND hWndCaller = NULL);
//////////////////////////////////////////////// // KeyHookLib.cpp檔案 #include <windows.h> #define KEYHOOKLIB_EXPORTS #include "KeyHookLib.h" // 共享資料段 #pragma data_seg("YCIShared") HWND g_hWndCaller = NULL; // 儲存主視窗控制代碼 HHOOK g_hHook = NULL; // 儲存鉤子控制代碼 #pragma data_seg() // 一個通過記憶體地址取得模組控制代碼的幫助函式 HMODULE WINAPI ModuleFromAddress(PVOID pv) { MEMORY_BASIC_INFORMATION mbi; if(::VirtualQuery(pv, &mbi, sizeof(mbi)) != 0) { return (HMODULE)mbi.AllocationBase; } else { return NULL; } } // 鍵盤鉤子函式 LRESULT CALLBACK KeyHookProc(int nCode, WPARAM wParam, LPARAM lParam) { if(nCode < 0 || nCode == HC_NOREMOVE) return ::CallNextHookEx(g_hHook, nCode, wParam, lParam); if(lParam & 0x40000000) // 訊息重複就交給下一個hook鏈 { return ::CallNextHookEx(g_hHook, nCode, wParam, lParam); } // 通知主視窗。wParam引數為虛擬鍵碼, lParam引數包含了此鍵的資訊 ::PostMessage(g_hWndCaller, HM_KEY, wParam, lParam); return ::CallNextHookEx(g_hHook, nCode, wParam, lParam); } // 安裝、解除安裝鉤子的函式 BOOL WINAPI SetKeyHook(BOOL bInstall, DWORD dwThreadId, HWND hWndCaller) { BOOL bOk; g_hWndCaller = hWndCaller; if(bInstall) { g_hHook = ::SetWindowsHookEx(WH_KEYBOARD, KeyHookProc, ModuleFromAddress(KeyHookProc), dwThreadId); bOk = (g_hHook != NULL); } else { bOk = ::UnhookWindowsHookEx(g_hHook); g_hHook = NULL; } return bOk; }
然後是應用程式部分:
// win32_DllApp.h #include <afxwin.h> class CMyApp : public CWinApp { public: BOOL InitInstance(); }; class CMainDialog : public CDialog { public: CMainDialog(CWnd* pParentWnd = NULL); protected: virtual BOOL OnInitDialog(); virtual void OnCancel(); afx_msg long OnHookKey(WPARAM wParam, LPARAM lParam); DECLARE_MESSAGE_MAP() };
// win32_DllApp.cpp
#include "win32_DllApp.h"
#include "resource.h"
#include "KeyHookLib.h"
#pragma comment(lib, "Win32DllLib.lib")
CMyApp theApp;
BOOL CMyApp::InitInstance()
{
CMainDialog dlg;
m_pMainWnd = &dlg;
dlg.DoModal();
return FALSE;
}
CMainDialog::CMainDialog(CWnd* pParentWnd) : CDialog(IDD_DIALOG1, pParentWnd)
{
}
BOOL CMainDialog::OnInitDialog()
{
CDialog::OnInitDialog();
//SetIcon(theApp.LoadIcon(IDI_MAIN), FALSE);
::SetWindowPos(m_hWnd, HWND_TOPMOST, 0, 0,
0, 0, SWP_NOSIZE|SWP_NOREDRAW|SWP_NOMOVE);
// 安裝鉤子
if(!SetKeyHook(TRUE, 0, m_hWnd))
MessageBox("安裝鉤子失敗!");
return TRUE;
}
void CMainDialog::OnCancel()
{
SetKeyHook(FALSE);
CDialog::OnCancel();
}
BEGIN_MESSAGE_MAP(CMainDialog, CDialog)
ON_MESSAGE(HM_KEY, OnHookKey)
END_MESSAGE_MAP()
long CMainDialog::OnHookKey(WPARAM wParam, LPARAM lParam)
{
// 此時引數wParam為使用者按鍵的虛擬鍵碼,
// lParam引數包含按鍵的重複次數、掃描碼、前一個按鍵狀態等資訊
char szKey[80];
::GetKeyNameText(lParam, szKey, 80);
CString strItem;
strItem.Format(" 使用者按鍵:%s \r\n", szKey);
// 新增到編輯框中
CString strEdit;
GetDlgItem(IDC_KEYMSG)->GetWindowText(strEdit);
GetDlgItem(IDC_KEYMSG)->SetWindowText(strItem + strEdit);
::MessageBeep(MB_OK);
return 0;
}
結果:當本視窗為活動視窗時,能收到HM_KEY訊息;如果不是,則不能收到。
在網上查了一下,可能是共享資料段的問題。加入下面最後一行之後,問題解決。
// 共享資料段
#pragma data_seg("YCIShared")
HWND g_hWndCaller = NULL; // 儲存主視窗控制代碼
HHOOK g_hHook = NULL; // 儲存鉤子控制代碼
#pragma data_seg()
#pragma comment(linker, "/SECTION:YCIShared,rws")