基于钩子的改键

玩dota时候,大多的人会使用改建精灵,在网上找了找资料,发现这个其实不难编,于是自己实现了一个。

首先,要了解什么是钩子(HOOK,彼得潘里的虎克船长就是这个)。我们都知道在windows程序是靠消息驱动的,比如说我们在某程序中点击了鼠标,那么,系统会向这个程序发送一个鼠标消息,在通过其绑定的回调函数来处理这个鼠标消息。钩子就是在系统传递消息时把这个消息截获,然后按照我们的意愿对其处理。

在改键中,我们希望截获某个键消息,然后用一个其他的键的消息来替代它,这个过程正好是钩子的能力范围之内。

我们来看看如何使用钩子,这里我要介绍一个钩子函数 “HHOOK SetWindowsHookEx(          int idHook,    HOOKPROC lpfn,    HINSTANCE hMod,    DWORD dwThreadId);”

这个在MSDN里可已找到。

我们先看一下他的参数:

idHook这个参数是设置我们要绑定什么样的钩子,windows系统的消息是多种的,那么对于消息操作的钩子也应该是多种的。比如说,鼠标钩子和键盘钩子就是不同类型的。

lpfn这个参数是设定回调函数的,对于截获的消息,我们要如何处理它,就在这个函数里面写相应的代码。(注意,这是一个函数指针也就是函数名)。

hMod这个参数有一点小复杂,因为它与后面的dwThreadId有关,如果这里是NULL那么dwThreadId就是当前调用这个函数的线程id。这个参数的意义在于如果我们是用的是全局的钩子(也就是对所有桌面的上的线程的消息均截获),那么就需要从dll(动态链接库)中加载钩子,而此参数就是指向那个动态链接库句柄。

dwThreadId如果不是全局钩子,那么这个就是调用函数的线程的id,一般使用GetCurrentThreadId()函数。如果是全局钩子,这里置为0。

我们在总结一下后两个参数,如果你的SetWindowsHookEX函数写在了你要监视的线程中,那么就不需要dll,那么hMod置为NULL,dwThreadId设置为当前线程id。如果你要监视其他线程的消息,那么就需要使用到dll,那么就需要指定dll的句柄,dwThreadId置为0。

在改键的程序中,我们使用的钩子类型是WH_KEYBOARD_LL,这是个底层的键盘钩子,他对应的回调函数我们叫LowLevelKeyboardProc(int code ,WPARAM wParam,LPARAM lParam);

我们来解释一个回调函数的参数:

code参数如果是非0值,那么不能在更进一步的处理,而且必须调用CallNextHookEx(HHOOk hMod)函数,并返回此函数的返回值。

wParam参数是指定我们键盘是什么状态,有WM_KEYDOW、WM_KEYUP、WM_SYSKEYDOWN、WM_SYSKEYUP这四种状态。

了Param是一个结构体指针,这里面有我们具体按下了哪个键的键值。一下是具体格式,我们只用第一个vkCode这里存的是键值。

typedef struct {
    DWORD vkCode;
    DWORD scanCode;
    DWORD flags;
    DWORD time;
    ULONG_PTR dwExtraInfo;
} KBDLLHOOKSTRUCT, *PKBDLLHOOKSTRUCT;

以下是代码

1.dll部分的代码

keyHookDLL.h

#ifndef _KEYHOOKDLL_H_
#define _KEYHOOKDLL_H_



#ifdef DLL_EXPORTS
#define DLLAPI __declspec(dllexport)
#else
#define DLLAPI __declspec(dllimport)
#endif

extern HMODULE g_hInist;

HHOOK hHook = NULL;
BYTE* changeKeys = NULL;
UINT count_keys = 0;

void DLLAPI setHook(BYTE* data,UINT count);//设置键盘钩子的键值映射
void DLLAPI freeHook();//释放钩子

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



#endif

keyHookDLL.cpp

#include "stdafx.h"
#define DLL_EXPORTS

#include "KeyHookDLL.h"

void DLLAPI setHook(BYTE* data,UINT count)
{
	hHook = SetWindowsHookEx(WH_KEYBOARD_LL,LowLevelKeyboardProc,g_hInist,0);
	changeKeys = new BYTE[count];
	for (UINT i = 0;i<count;i++)
	{
		changeKeys[i] = data[i];
	}
	count_keys = count;
}
void DLLAPI freeHook()
{
	UnhookWindowsHookEx(hHook);
	delete[] changeKeys;
}
BOOL iskeydown = FALSE;
LRESULT CALLBACK LowLevelKeyboardProc(int code,WPARAM wParam,LPARAM lParam)
{
	
	KBDLLHOOKSTRUCT* hookInfo = (KBDLLHOOKSTRUCT*)lParam;
	if (code == HC_ACTION)
	{
		if (WM_KEYDOWN == wParam && !iskeydown)
		{
			if (changeKeys[0] == hookInfo->vkCode)
			{
				iskeydown = TRUE;
				keybd_event(VK_NUMPAD7,0,0,0);
				keybd_event(VK_NUMPAD7,0,KEYEVENTF_KEYUP,0);
				iskeydown = FALSE;
				return 1;
			}
			if (changeKeys[1] == hookInfo->vkCode)
			{
				iskeydown = TRUE;
				keybd_event(VK_NUMPAD8,0,0,0);
				keybd_event(VK_NUMPAD8,0,KEYEVENTF_KEYUP,0);
				iskeydown = FALSE;
				return 1;
			}
			if (changeKeys[2] == hookInfo->vkCode)
			{
				iskeydown = TRUE;
				keybd_event(VK_NUMPAD4,0,0,0);
				keybd_event(VK_NUMPAD4,0,KEYEVENTF_KEYUP,0);
				iskeydown = FALSE;
				return 1;
			}
			if (changeKeys[3] == hookInfo->vkCode)
			{
				iskeydown = TRUE;
				keybd_event(VK_NUMPAD5,0,0,0);
				keybd_event(VK_NUMPAD5,0,KEYEVENTF_KEYUP,0);
				iskeydown = FALSE;
				return 1;
			}
			if (changeKeys[4] == hookInfo->vkCode)
			{
				iskeydown = TRUE;
				keybd_event(VK_NUMPAD1,0,0,0);
				keybd_event(VK_NUMPAD1,0,KEYEVENTF_KEYUP,0);
				iskeydown = FALSE;
				return 1;
			}
			if (changeKeys[5] == hookInfo->vkCode)
			{
				iskeydown = TRUE;
				keybd_event(VK_NUMPAD2,0,0,0);
				keybd_event(VK_NUMPAD2,0,KEYEVENTF_KEYUP,0);
				iskeydown = FALSE;
				return 1;
			}

		}
	}
	
	
	return CallNextHookEx(hHook,code,wParam,lParam);
	//return 1;
}


MainDLL.cpp

// HookDLL.cpp : 定义 DLL 应用程序的入口点。
//

#include "stdafx.h"


#ifdef _MANAGED
#pragma managed(push, off)
#endif

HMODULE g_hInist = NULL;// 调用dll的句柄
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 )
{
	switch (ul_reason_for_call)
	{
		case DLL_PROCESS_ATTACH:
			g_hInist = hModule;
			break;
		case DLL_THREAD_ATTACH:
			break;
		case DLL_PROCESS_DETACH:
			break;
		case DLL_THREAD_DETACH:
			break;
	}
    return TRUE;
}

#ifdef _MANAGED
#pragma managed(pop)
#endif


建立一个机遇对话框的MFC工程。添加如下的对话框资源

keyHookDlg.h

// KeyHookDlg.h : 头文件
//

#pragma once


// CKeyHookDlg 对话框
class CKeyHookDlg : public CDialog
{
// 构造
public:
	CKeyHookDlg(CWnd* pParent = NULL);	// 标准构造函数

// 对话框数据
	enum { IDD = IDD_KEYHOOK_DIALOG };

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);	// DDX/DDV 支持


// 实现
protected:
	HICON m_hIcon;

	// 生成的消息映射函数
	virtual BOOL OnInitDialog();
	afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
	afx_msg void OnPaint();
	afx_msg HCURSOR OnQueryDragIcon();
	DECLARE_MESSAGE_MAP()
public:
	BOOL isHooked;
	BOOL isUnHooked;
public:
	// 小键盘数字键映射到其他键上
	CString m_num7;
	CString m_num8;
	CString m_num4;
	CString m_num5;
	CString m_num1;
	CString m_num2;
	BYTE m_changeKey[6];
	afx_msg void OnBnClickedBtnHook();
	afx_msg void OnBnClickedBtnUnhook();
};

keyHookDlg.cpp

// KeyHookDlg.cpp : 实现文件
//

#include "stdafx.h"
#include "KeyHook.h"
#include "KeyHookDlg.h"
#include "KeyHookDLL.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif

#pragma comment(lib,"HookDLL.lib")

// 用于应用程序“关于”菜单项的 CAboutDlg 对话框

class CAboutDlg : public CDialog
{
public:
	CAboutDlg();

// 对话框数据
	enum { IDD = IDD_ABOUTBOX };

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

// 实现
protected:
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
END_MESSAGE_MAP()


// CKeyHookDlg 对话框




CKeyHookDlg::CKeyHookDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CKeyHookDlg::IDD, pParent)
	,isHooked(FALSE)
	,isUnHooked(FALSE)
	, m_num7("")
	, m_num8("")
	, m_num4("")
	, m_num5("")
	, m_num1("")
	, m_num2("")
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
	
	m_changeKey[0] = VK_NUMPAD7;
	m_changeKey[1] = VK_NUMPAD8;
	m_changeKey[2] = VK_NUMPAD4;
	m_changeKey[3] = VK_NUMPAD5;
	m_changeKey[4] = VK_NUMPAD1;
	m_changeKey[5] = VK_NUMPAD2;
	

}

void CKeyHookDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Text(pDX, IDC_EDIT_NUM7, m_num7);
	DDX_Text(pDX, IDC_EDIT_NUM8, m_num8);
	DDX_Text(pDX, IDC_EDIT_NUM4, m_num4);
	DDX_Text(pDX, IDC_EDIT_NUM5, m_num5);
	DDX_Text(pDX, IDC_EDIT_NUM1, m_num1);
	DDX_Text(pDX, IDC_EDIT_NUM2, m_num2);
}

BEGIN_MESSAGE_MAP(CKeyHookDlg, CDialog)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	//}}AFX_MSG_MAP
	ON_BN_CLICKED(IDC_BTN_HOOK, &CKeyHookDlg::OnBnClickedBtnHook)
	ON_BN_CLICKED(IDC_BTN_UNHOOK, &CKeyHookDlg::OnBnClickedBtnUnhook)
END_MESSAGE_MAP()


// CKeyHookDlg 消息处理程序

BOOL CKeyHookDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// 将“关于...”菜单项添加到系统菜单中。

	// IDM_ABOUTBOX 必须在系统命令范围内。
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		CString strAboutMenu;
		strAboutMenu.LoadString(IDS_ABOUTBOX);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
	//  执行此操作
	SetIcon(m_hIcon, TRUE);			// 设置大图标
	SetIcon(m_hIcon, FALSE);		// 设置小图标

	// TODO: 在此添加额外的初始化代码

	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}

void CKeyHookDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialog::OnSysCommand(nID, lParam);
	}
}

// 如果向对话框添加最小化按钮,则需要下面的代码
//  来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,
//  这将由框架自动完成。

void CKeyHookDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // 用于绘制的设备上下文

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// 使图标在工作矩形中居中
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// 绘制图标
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

//当用户拖动最小化窗口时系统调用此函数取得光标显示。
//
HCURSOR CKeyHookDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}


void CKeyHookDlg::OnBnClickedBtnHook()
{
	// TODO: 在此添加控件通知处理程序代码
	UpdateData();
	if (m_num7.GetLength()>1)
	{
		MessageBox(_T("can't be string !"),_T("ERROR"),IDOK);
		return;
	}
	if (m_num8.GetLength()>1)
	{
		MessageBox(_T("can't be string !"),_T("ERROR"),IDOK);
		return;
	}
	if (m_num4.GetLength()>1)
	{
		MessageBox(_T("can't be string !"),_T("ERROR"),IDOK);
		return;
	}
	if (m_num5.GetLength()>1)
	{
		MessageBox(_T("can't be string !"),_T("ERROR"),IDOK);
		return;
	}
	if (m_num1.GetLength()>1)
	{
		MessageBox(_T("can't be string !"),_T("ERROR"),IDOK);
		return;
	}
	if (m_num2.GetLength()>1)
	{
		MessageBox(_T("can't be string !"),_T("ERROR"),IDOK);
		return;
	}
	
	if (!isHooked && !isUnHooked)
	{
		
		BYTE* _keys = new BYTE[6];
		_keys[0] = *((BYTE*)(m_num7.GetBuffer()));
		_keys[1] = *((BYTE*)(m_num8.GetBuffer()));
		_keys[2] = *((BYTE*)(m_num4.GetBuffer()));
		_keys[3] = *((BYTE*)(m_num5.GetBuffer()));
		_keys[4] = *((BYTE*)(m_num1.GetBuffer()));
		_keys[5] = *((BYTE*)(m_num2.GetBuffer()));
			
		setHook(_keys,6);
		isHooked = TRUE;
		isUnHooked = TRUE;
		delete[] _keys;
	}
	else
	{
		MessageBox(_T("you have already Hooked"),_T("Notice"),IDOK);
		return;
	}
}

void CKeyHookDlg::OnBnClickedBtnUnhook()
{
	// TODO: 在此添加控件通知处理程序代码
	if (isHooked && isUnHooked)
	{
		freeHook();
		isUnHooked = FALSE;
		isHooked = FALSE;
	}
	else
	{
		MessageBox(_T("you have already Hooked"),_T("Notice"),IDOK);
		return;
	}
}


实验结果,按下数字键“1”。

这里的MFC工程文件,我知列举了我有代码修改的部分,也就是对话框部分。

由于本人的水平有限,希望大家有更好的方法或者技术一起分享一下。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值