程序用一个系统范围的远程钩子来实现监视所有键盘输入的功能,且程序中的文本框属性为只读,这样可更好的观察键盘钩子(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时,传的是地址。其他都好说。注意程序的调试与断点的设置。