钩子的使用

来源:互联网 发布:wifi跑包软件 编辑:程序博客网 时间:2024/05/17 01:35

在编写程序的过程中,我遇到了这样的需求:在基于Windows® 9x 或Windows NT4.0
  的程序中,要求确定键盘、鼠标处于空闲状态的时间。
  查询了有关资料文档以后,发现Windows 9x和Windows NT4.0没有提供API或系统调用来实现这样的功能。但是,在Windows 2000中提供了一个新的函数:GetLastInputInfo(),这个函数使用结构 LASTINPUTINFO 作为参数:
  LASTINPUTINFO lpi;
  lpi.cbSize = sizeof(lpi);
  GetLastInputInfo(&lpi);
  调用函数GetLastInputInfo()以后, 结构成员lpi.dwTime 中的值便是自上次输入事件发生以后的毫秒数。这个值也就是键盘、鼠标处于空闲状态的时间。
  可惜的是这个函数只能在Windows 2000中使用,Windows 9x 或Windows NT4.0不提供此API函数。
  那么,如何在Windows 9x 或Windows NT4.0中实现GetLastInputInfo()的功能呢?
  笔者的方法是利用系统钩子对键盘、鼠标进行监控。
  Windows中的钩子实际上是一个回调函数,当用户有输入动作的时候,Windows要调用这个函数。比较典型的系统钩子应用就是键盘钩子和鼠标钩子,
  HHOOK g_hHookKbd = NULL;
  HHOOK g_hHookMouse = NULL;
  在Windows中,一个系统(相对于一个特定进程而言)钩子必须用一个动态链接库(DLL)来实现。不妨将这个动态链接库命名为IdleUI.dll。 这个动态链接库在Windows 9x和Windows NT4.0中实现了GetLastInputInfo()的功能。IdleUI.dll中有三个函数:
  BOOL IdleUIInit()
  void IdleUITerm();
  DWORD IdleUIGetLastInputTime();
  IdleUIInit()是环境初始化函数,IdleUITerm()是环境清理函数,分别在MFC应用程序的InitInstance() 和 ExitInstance()中调用它们。当用IdleUIInit()做完初始化后,就可以调用第三个函数IdleUIGetLastInputTime()来获取最后一次输入事件后的时钟。从而实现与GetLastInputInfo()一样的功能。
  程序TestIdleUI.exe是用来测试IdleUI动态库的,程序中调用了IdleUIInit 和 IdleUITerm,同时在程序的客户区中间显示键盘、鼠标空闲的秒数。
  void CMainFrame::OnPaint()
  {
  CPaintDC dc(this);
  CString s;
  DWORD nsec = (GetTickCount() - IdleUIGetLastInputTime())/1000;
  s.Format( "鼠标或键盘空闲 %d 秒。",nsec);
  CRect rc;
  GetClientRect(&rc);
  dc.DrawText(s, &rc, DT_CENTER|DT_VCENTER|DT_SINGLELINE);
  }
  图一显示了TestIdleUI运行时的情形。
  图一
  为了连续的显示,TestIdleUI设置刷新定时器间隔为一秒。
  void CMainFrame::OnTimer(UINT)
  {
  Invalidate();
  UpdateWindow();
  }
  运行TestIdleUI,当键盘和鼠标什么也不做时,可以看到计时器跳动,当移动鼠标或按键时,计时器又恢复到零,这样就实现了对输入设备空闲状态的监控。实现细节请看下面对IdleUI.dll工作原理的描述:
  首先调用IdleUIInit ()进行初始化,安装两个钩子:一个用于监控鼠标输入,一个用于监控键盘输入。
  HHOOK g_hHookKbd;
  HHOOK g_hHookMouse;
  g_hHookKbd = SetWindowsHookEx(WH_KEYBOARD,
  MyKbdHook,
  hInst, 0);
  g_hHookMouse = SetWindowsHookEx(WH_MOUSE,
  MyMouseHook,
  hInst, 0);
  当用户移动鼠标或按下键盘键时,Windows调用其中的一个钩子并且钩子函数开始记录时间:
  LRESULT CALLBACK MyMouseHook(int code,
  WPARAM wp,
  LPARAM lp)
  {
  if (code==HC_ACTION) {
  // note the tick count
  g_dwLastInputTick = GetTickCount();
  }
  return ::CallNextHookEx(g_hHookMouse,
  code, wp, lp);
  }
  如法炮制MyKbdHook。IdleUIGetLastInputTime 返回结果g_dwLastInputTick, 并且IdleUITerm 卸载两个钩子。
  这个程序中有一个细节使用了一些技巧:通常,建立一个动态链接库时,链接器将静态数据标记为非共享,也就是说,每一个调用DLL的进程都获得自己的数据拷贝------在本程序中是g_hHookKbd、g_hHookMouse和g_dwLastInputTick。当在整个进程空间中需要且只需要一个这些数据的实例时,这样的静态数据标记就不适合了,为了解决这个问题,必须实现数据共享。为此得把数据放入一个特定的段地址中,然后将它们标记为共享。实现代码如下:
  #pragma data_seg (".IdleUI") // 可以取任何别的名字
  HHOOK g_hHookKbd = NULL;
  HHOOK g_hHookMouse = NULL;
  DWORD g_dwLastInputTick = 0;
  #pragma data_seg ()
  这段代码告诉链接器将三个变量放到叫“.IdleUI”的数据段中。然后在模块定义文件.DEF中加入下面的代码来共享这个数据段:
  SECTIONS .IdleUI READ WRITE SHARED // in IdleUI.def

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 药流第一天才吃了一粒米非司怎么办 药流半月同房第二天大出血怎么办 口臭想让它变得不臭怎么办 来单位干了几天不满意想离职怎么办 药流吃药期间吃什么吐什么怎么办 养狗家里味道大怎么办养花有用吗 第一天上班站的脚疼怎么办 入职没有人事所需要的证书怎么办 入职第一天后不想去了怎么办 警察在执法过程中殴打群众怎么办 肾结石打了3天针痛得厉害怎么办 征兵体检过了到部队退兵怎么办 圆通快递要求退回结果被签收怎么办 新生儿蛋蛋淹了破皮了怎么办 要是和同学玩的时候打到睾丸怎么办 睾丸撞了一下里面碎了怎么办 睾丸被蚊子咬了挠坏流水疼怎么办 沐浴乳大量的灌注到尿道里怎么办 当电脑显示有文件损害时怎么办? 电脑上的压缩包手机上打不开怎么办 第五人格多酷账号退出了怎么办 更新显卡驱动时屏幕关闭了怎么办 不知道杯孕做了两次C丁怎么办 小说签约后更不到要求的字数怎么办 电脑中了感染病毒杀不干净怎么办 电脑下载的软件有病毒了怎么办 电脑强制关机后开不了机怎么办 受刺激后出现精神异常该怎么办 当屏幕出现暂时无法移动时怎么办 英雄联盟欧服连接不上服务器怎么办 试客联盟认证手机号成空号了怎么办 汽车脚垫不贴合翘起来了怎么办 版权保护迅雷下载不了的资源怎么办 30岁在外地城市找不到工作怎么办 新买的苹果爱拍充不进去电是怎么办 绝地求生东南亚服匹配不到人怎么办 电脑卡住了怎么办鼠标也点不动 幽灵行动荒野没有主线任务了怎么办 拼多多购买的东西下架了怎么办 电脑版的荒野行动玩的时候卡怎么办 欧洲卡车模拟2气压过低怎么办