使用SetWindowsHookEx(WH_KEYBOARD,...)制作全局键盘钩子

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/booksyhay/article/details/10146103

学习《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")





展开阅读全文

没有更多推荐了,返回首页