键盘钩子的实现

    技术2022-05-20  31

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


    最新回复(0)