用低级键盘钩子检测键盘某键粘死故障

    最近工作用的Thinkpad左ALT键可能使用太多损坏,表现为是不是粘死,在工作中用快捷达不到目标时才发现这一状况,且该状态是不可见的,发生时总要排除其它原因后才通过重复敲击ALT键来释放这一BT的状态,尤其在处理关键任务时发生令人心烦意乱,于是捡起丢了很多年的VC写个键盘钩子监视这一情况,以便及时发行并整改。

    解决的原理为采用低级键盘钩子检测ALT键的状态,若发现ALT键长期被按着不释放,说明已经发生了故障,通过任务栏的气球提示故障信息,可及时敲打ALT键进行恢复,虽然办法比较笨,但在更换键盘前可以凑合着使用(Thinkpad的监盘更换要400大洋,不知道这个价位是不是真的合理)!

    要使用全局钩子,因此要把钩子函数放在动态库中,首先编写动态库,下面是动态库的主要代码内容:

// dllmain.cpp : Defines the entry point for the DLL application.
#include "stdafx.h"
__declspec(dllexport) HMODULE glhModule;
__declspec(dllexport) HHOOK glhHook;
__declspec(dllexport) DWORD dwALTPressTime; 

__declspec(dllexport)
	LRESULT CALLBACK LowLevelKeyboardProc(
	int code,
	WPARAM wParam,
	LPARAM lParam
	){
		if (code < 0)
		{
			return CallNextHookEx(glhHook, code, wParam, lParam);
		}
		LPKBDLLHOOKSTRUCT lpKBDLLHook;
		lpKBDLLHook = (LPKBDLLHOOKSTRUCT)lParam;
		if (wParam == WM_SYSKEYDOWN && (lpKBDLLHook->vkCode == VK_LMENU || lpKBDLLHook->vkCode == VK_RMENU))
		{
			if (dwALTPressTime == 0)
			{
				dwALTPressTime = lpKBDLLHook->time;
			}
		}
		if (wParam == WM_KEYUP && (lpKBDLLHook->vkCode == VK_LMENU || lpKBDLLHook->vkCode == VK_RMENU))
		{
				dwALTPressTime = 0;
		}
		return CallNextHookEx(glhHook, code, wParam, lParam);    
}

BOOL APIENTRY DllMain( HMODULE hModule,
	DWORD  ul_reason_for_call,
	LPVOID lpReserved
	)
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
		glhModule = hModule;
		dwALTPressTime = 0;
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}
	return TRUE;
}


 

    然后创建一个MFC对话框应用来安装和监视钩子的运行结果,将对话框配置什么的最简化处理,对话框类的头文件内容如下:

// keymonitorDlg.h : header file
//

#pragma once


// CkeymonitorDlg dialog
class CkeymonitorDlg : public CDialogEx
{
	// Construction
public:
	CkeymonitorDlg(CWnd* pParent = NULL);	// standard constructor

	// Dialog Data
	enum { IDD = IDD_KEYMONITOR_DIALOG };

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


	// Implementation
protected:
	HICON m_hIcon;

	// Generated message map functions
	virtual BOOL OnInitDialog();
	afx_msg void OnPaint();
	afx_msg HCURSOR OnQueryDragIcon();
	DECLARE_MESSAGE_MAP()

	afx_msg LRESULT OnShellNotifyIconCallBackMessage(WPARAM wParam, LPARAM lParam);
	afx_msg void OnPopupmenuExit();
public:
	afx_msg void OnClose();
	afx_msg void OnTimer(UINT_PTR nIDEvent);
private:
	DWORD m_dv;
	DWORD m_qv;
	DWORD m_tv;
public:
	afx_msg void OnBnClickedApply();
	afx_msg void OnPopupmenuShow();
};


 

    对话框的实现代码内容如下:

// keymonitorDlg.cpp : implementation file
//

#include "stdafx.h"
#include "keymonitor.h"
#include "keymonitorDlg.h"
#include "afxdialogex.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

// CkeymonitorDlg dialog

#pragma comment(lib,"E:\\Source\\keymon\\Release\\keymon.lib")

__declspec(dllimport) HMODULE glhModule;
__declspec(dllimport) HHOOK glhHook;
__declspec(dllimport) DWORD dwALTPressTime; 
__declspec(dllimport)
	LRESULT CALLBACK LowLevelKeyboardProc(
	int code,
	WPARAM wParam,
	LPARAM lParam
	);


#define nTimerID1 1
#define nTimerID2 2

UINT const WMAPP_NOTIFYCALLBACK = WM_APP + 1;
class __declspec(uuid("705C8E3B-86E6-48B6-8DD0-A6E1C1F84880")) KeyMonitorIcon;

void ShowErrorMessage(DWORD dwErrorCode)   
{   
	LPVOID lpMsgBuf;  

	FormatMessage(  
		FORMAT_MESSAGE_ALLOCATE_BUFFER |   
		FORMAT_MESSAGE_FROM_SYSTEM |  
		FORMAT_MESSAGE_IGNORE_INSERTS,  
		NULL,  
		dwErrorCode,  
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),  
		(LPTSTR) &lpMsgBuf,  
		0, NULL );  

	MessageBox(NULL, (LPCTSTR)lpMsgBuf, TEXT("Error Message"), MB_OK);   

	LocalFree(lpMsgBuf);  
}  


CkeymonitorDlg::CkeymonitorDlg(CWnd* pParent /*=NULL*/)
	: CDialogEx(CkeymonitorDlg::IDD, pParent)
	, m_dv(0)
	, m_qv(0)
	, m_tv(0)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CkeymonitorDlg::DoDataExchange(CDataExchange* pDX)
{
	DDX_Text(pDX,IDC_EDIT1,m_dv);
	DDX_Text(pDX,IDC_EDIT2,m_qv);
	DDX_Text(pDX,IDC_EDIT3,m_tv);
	CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CkeymonitorDlg, CDialogEx)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_MESSAGE(WMAPP_NOTIFYCALLBACK,OnShellNotifyIconCallBackMessage)
	ON_COMMAND(ID_POPUPMENU_EXIT, &CkeymonitorDlg::OnPopupmenuExit)
	ON_WM_CLOSE()
	ON_WM_TIMER()
	ON_BN_CLICKED(IDC_BUTTON1, &CkeymonitorDlg::OnBnClickedApply)
	ON_COMMAND(ID_POPUPMENU_SHOW, &CkeymonitorDlg::OnPopupmenuShow)
END_MESSAGE_MAP()

// CkeymonitorDlg message handlers

BOOL CkeymonitorDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon

	// TODO: Add extra initialization here
	NOTIFYICONDATA nid = {sizeof(nid)};
	nid.hWnd = this->m_hWnd;
	// add the icon, setting the icon, tooltip, and callback message.
	// the icon will be identified with the GUID
	nid.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE | NIF_SHOWTIP | NIF_GUID;
	nid.guidItem = __uuidof(KeyMonitorIcon);
	nid.uCallbackMessage = WMAPP_NOTIFYCALLBACK;
	LoadIconMetric(theApp.m_hInstance, MAKEINTRESOURCE(IDR_MAINFRAME), LIM_SMALL, &nid.hIcon);
	wcsncpy_s(nid.szTip,L"ALT Key Monitor",sizeof(nid.szTip));
	Shell_NotifyIcon(NIM_ADD, &nid);

	// NOTIFYICON_VERSION_4 is prefered
	nid.uVersion = NOTIFYICON_VERSION_4;
	Shell_NotifyIcon(NIM_SETVERSION, &nid);

	glhHook = ::SetWindowsHookEx(WH_KEYBOARD_LL,(HOOKPROC)LowLevelKeyboardProc,glhModule,NULL);
	if (glhHook == NULL)
	{
		::ShowErrorMessage(::GetLastError());
	}
	m_dv = 30000;
	m_qv = 1000;
	m_tv = 15000;
	UpdateData(FALSE);

	SetTimer(nTimerID1,3000,NULL);

	return TRUE;  // return TRUE  unless you set the focus to a control
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CkeymonitorDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

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

		// Center icon in client rectangle
		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;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialogEx::OnPaint();
	}
}

// The system calls this function to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CkeymonitorDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}

LRESULT CkeymonitorDlg::OnShellNotifyIconCallBackMessage(WPARAM wParam, LPARAM lParam)
{
	if (lParam==WM_RBUTTONDOWN)
	{
		CMenu menu;
		menu.LoadMenu(IDR_RIGHT_MENU);
		CMenu* pMenu=menu.GetSubMenu(0);
		CPoint pos;
		GetCursorPos(&pos);
		pMenu->TrackPopupMenu(TPM_LEFTALIGN|TPM_RIGHTBUTTON,pos.x,pos.y,AfxGetMainWnd());
	}
	return LRESULT();
}

void CkeymonitorDlg::OnPopupmenuExit()
{
	// TODO: Add your command handler code here
	NOTIFYICONDATA nid = {sizeof(nid)};
	nid.uFlags = NIF_GUID;
	nid.guidItem = __uuidof(KeyMonitorIcon);
	Shell_NotifyIcon(NIM_DELETE, &nid);

	KillTimer(nTimerID1);
	KillTimer(nTimerID2);

	::UnhookWindowsHookEx(glhHook);

	OnOK();
}


void CkeymonitorDlg::OnClose()
{
	ShowWindow(SW_HIDE);
}


void CkeymonitorDlg::OnTimer(UINT_PTR nIDEvent)
{
	// TODO: Add your message handler code here and/or call default
	switch(nIDEvent){
	case nTimerID1:	
		if (dwALTPressTime != 0)
		{
			DWORD dwTime = GetMessageTime();			
			if (dwTime - dwALTPressTime > m_dv)
			{
				// Display a low ink balloon message. This is a warning, so show the appropriate system icon.
				NOTIFYICONDATA nid = {sizeof(nid)};
				nid.uFlags = NIF_INFO | NIF_GUID;
				nid.guidItem = __uuidof(KeyMonitorIcon);
				// respect quiet time since this balloon did not come from a direct user action.
				nid.dwInfoFlags = NIIF_WARNING | NIIF_RESPECT_QUIET_TIME;
				wcsncpy_s(nid.szInfoTitle,L"Oops!!!",sizeof(nid.szInfoTitle));
				wcsncpy_s(nid.szInfo,L"Please check your ALT key,this is a fucking problem!",sizeof(nid.szInfo));
				Shell_NotifyIcon(NIM_MODIFY, &nid);
				KillTimer(nTimerID1);
				SetTimer(nTimerID2,m_tv,NULL);
			}
		} 		
		break;
	case nTimerID2:
		KillTimer(nTimerID2);
		SetTimer(nTimerID1,m_qv,NULL);
		break;
	default:break;
	}
	CDialogEx::OnTimer(nIDEvent);
}


void CkeymonitorDlg::OnBnClickedApply()
{
	// TODO: Add your control notification handler code here
	UpdateData(TRUE);
	KillTimer(nTimerID1);
	KillTimer(nTimerID2);
	SetTimer(nTimerID1,m_qv,NULL);
}

void CkeymonitorDlg::OnPopupmenuShow()
{
	// TODO: Add your command handler code here
	ShowWindow(SW_NORMAL);
}


 

    其它代码保持向导生成的现状,通过m_dv定义ALT键念住的允许时间,没m_qv定义对话框应用轮询钩子执行结果的时间间隔,m_tv定义气球提示故障信息的间隔时间,这个三个参数设置的缺省值,也可以在对话框中进行设置。运行后关闭对话框将隐藏主窗口,通过任务栏的图标可以退出或显示主窗口,至此需要的功能全部完成,简陋地开始服役了!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值