程序用一个系统范围的远程钩子来实现监视所有键盘输入的功能,且程序中的文本框属性为只读,这样可更好的观察键盘钩子(WH_KEYBOARD)是否正确的截获了WM_KEYUP或WM_KEYDOWN消息。
现在把键盘钩子当作远程钩子使用,即需要将钩子函数写入到DLL中去,且其实含有共享的数据段。因为系统中只有DLL程序是可以插入到其他进程的地址空间中去。命名所写的钩子函数Dll文件名为HookDll;
在HoolDll.h文件中声明需要导出的函数:extern "C" _declspec(dllexport) BOOL InstallHook(HWND,UINT);extern "C" _declspec(dllexport) VOID UninstallHook();
//其中__declspec (dllexport)是Windows扩展关键字的组合,表示DLL里的对象的存储类型关键字;extern "C"用于C++程序使用该函数时的函数声明的链接属性
HookDll.cpp文件:
//设置共享的数据段,并命名为"shared"
#pragma data_seg("shared")extern "C" _declspec(dllexport) HWND hWnd=NULL;extern "C" _declspec(dllexport) HHOOK hHook=NULL;extern "C" _declspec(dllexport) UINT dwMessage=NULL;extern "C" _declspec(dllexport) unsigned short szAscii[256]={0};#pragma data_seg,
//告诉链接器"shared"数据段具有RWS属性(Read、Write和Shared),为共享属性
#pragma comment(linker,"/SECTION:shared,RWS")
HINSTANCE hInstance;
int WINAPI DllMain(HINSTANCE _hInstance,DWORD dwReason,PVOID pvReserved){ hInstance=_hInstance; return TRUE;}
//键盘钩子回调函数LRESULT CALLBACK HookProc(int _dwCode,WPARAM _wParam,LPARAM _lParam){ unsigned char lpKeyState[256];//申请256个字节的内存空间
CallNextHookEx(hHook,_dwCode,_wParam,_lParam);;
GetKeyboardState(lpKeyState);//可按照VK_xx虚拟码的顺序排列填写按键状态;
//但区分VK_LSHIFT和VK_RSHIFT;而ToAscii函数检测的是VK_SHIFT,所以要对SHIFT按键进行处理
short ax=GetKeyState(VK_SHIFT);//获取VK_SHIFT的状态
lpKeyState[VK_SHIFT]=ax;
//ToAscii用法:(将虚拟码或扫描码转换为ASCII码)
int ToAscii(UINT dwVirtKey,UINT uScanCode,PBYTE lpKeyState,LPWORD lpBuffer,UINT uFlags) //其中uScanCode去高16位的LPARAM的值,来标志一次的记录HIWORD(LPARAM)
int t=ToAscii(_wParam,HIWORD(_lParam),lpKeyState,szAscii,0); //对于每个击键动作,钩子回调函数会在按下和释放的时候被调用两次;但只需根据lParam的最高位
//标志来记录一次
szAscii[t]='/0'; PostMessage(hWnd,dwMessage,(WPARAM)szAscii,NULL); //向主程序发送包含已转换为ASCII码的按键消息(WM_HOOK)
//此处有一点要注意:消息中包含的WPARAM类型的变量(szAscii)是一个地址;今后的操作要是对应的地址操作
//否则容易引起地址内存的未知错误
}
return 0;}
//安装钩子BOOL InstallHook(HWND _hWnd,UINT _dwMessage){ hWnd=_hWnd; dwMessage=_dwMessage; //自定义的钩子消息
HHOOK hHook=SetWindowsHookEx(WH_KEYBOARD,(HOOKPROC)HookProc,hInstance,NULL);//设置键盘钩子(WH_KEYBOARD) if(hHook) return TRUE; else return FALSE;}
//卸载钩子VOID UninstallHook(){ UnhookWindowsHookEx(hHook);}
总结:
安装好了键盘钩子后,在写被监视的程序KeyHook.cpp:
#define WM_HOOK WM_USER+100 //在自定义的消息WM_USER后定义自己使用的消息WM_HOOK;
#pragma comment(lib,"HOOKDLL.lib")
LRESULT CALLBACK DialogProc(HWND hDlg,UINT message,WPARAM wParam,LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInst,LPSTR lpCmdLine,int nShowCmd){ DialogBoxParam(hInstance,(LPCSTR)IDD_DLG,NULL,(DLGPROC)DialogProc,NULL); ExitProcess(NULL); return TRUE;}
LRESULT CALLBACK DialogProc(HWND hDlg,UINT message,WPARAM wParam,LPARAM lParam){ switch(message) { case WM_CLOSE: UninstallHook();//卸载钩子 EndDialog(hDlg,NULL); break;
case WM_INITDIALOG: if(!InstallHook(hDlg,WM_HOOK)){ MessageBox(hDlg,"InstallHook-Failed",NULL,MB_OK); EndDialog(hDlg,NULL); } break;
case WM_HOOK: //钩子消息发生时 unsigned int *dwTemp;
dwTemp=(unsigned int *)wParam; //地址类型的强制性转换,把握两边的数据性质相同就行了 if(*dwTemp==0x000d){ //若是ASCII码为回车符 *dwTemp=0x0a0d; //则转换为换行符 } else dwTemp=(unsigned int *)wParam;
SendDlgItemMessage(hDlg,IDC_EDIT,EM_REPLACESEL,0,(LPARAM)dwTemp); break; } return 0;}
在这个钩子程序中最开始写了后引起了内存泄漏,经过纠结一段时间后才发现在钩子回调函数中PostMessage时,传的是地址。其他都好说。注意程序的调试与断点的设置。