学习《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
#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
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;
}
在网上查了一下,可能是共享数据段的问题。加入下面最后一行之后,问题解决。
// 共享数据段
#pragma data_seg("YCIShared")
HWND g_hWndCaller = NULL; // 保存主窗口句柄
HHOOK g_hHook = NULL; // 保存钩子句柄
#pragma data_seg()
#pragma comment(linker, "/SECTION:YCIShared,rws")