[转载] 全局键盘钩子(WH_KEYBOARD)

为了显示效果,在钩子的DLL中我们会获取挂钩函数的窗体句柄,这里的主程序窗体名为"TestMain",通过FindWindow查找。

KeyBoardHook.dll代码

 1 library KeyBoardHook;
 2 
 3 { Important note about DLL memory management: ShareMem must be the
 4   first unit in your library's USES clause AND your project's (select
 5   Project-View Source) USES clause if your DLL exports any procedures or
 6   functions that pass strings as parameters or function results. This
 7   applies to all strings passed to and from your DLL--even those that
 8   are nested in records and classes. ShareMem is the interface unit to
 9   the BORLNDMM.DLL shared memory manager, which must be deployed along
10   with your DLL. To avoid using BORLNDMM.DLL, pass string information
11   using PChar or ShortString parameters. }
12 
13 uses
14   SysUtils,Windows,Messages,
15   Classes;
16 
17 var
18   fHook:HHOOK;
19   //执行挂钩程序的窗体句柄
20   CallHandle:HWND;
21 
22 {$R *.res}
23 
24 //回调过程
25 function HookProc(code:Integer;wParam:WPARAM;lParam:LPARAM):LRESULT;stdcall;
26 var
27   processid:Cardinal;
28 begin
29   //如果有键盘动作
30   if code = HC_Action then
31   begin
32     //获取注入进程的进程id
33     processid := GetCurrentProcessId;
34     //如果CallHandle,则查找TestMain窗体句柄
35     if CallHandle = 0 then
36       CallHandle := FindWindow(nil,'TestMain');
37     //获取按键状态 小于0表示按下,如果不做判断,按键按下或抬起都会执行SendMessage
38     //下面发送WM_USER+101消息,此消息可以用自定义的消息标识发送
39     if GetKeyState(wParam) < 0 then
40       SendMessage(CallHandle,WM_USER+101,wParam,processid);
41   end
42   else
43     //下一个钩子
44     Result := CallNextHookEx(fHook,code,wParam,lParam);
45 end;
46 
47 procedure SetHook;stdcall;
48 begin
49   //挂钩,这里没有做挂钩失败的提示
50   fHook := SetWindowsHookEx(WH_KEYBOARD,@HookProc,HInstance,0);
51 end;
52 
53 procedure StopHook;stdcall;
54 begin
55   //摘钩
56   if fHook <> 0 then
57     UnhookWindowsHookEx(fHook);
58 end;
59 
60 exports
61   SetHook name 'SetHook',
62   StopHook name 'StopHook';
63 
64 begin
65   //初始CallHandle为0
66   CallHandle := 0;
67 end.

TestKeyBoardHook主程序代码

{
此窗体用来执行挂钩,为了方便起见,我们把系统的按键返回到该窗体的Memo组件中进行
显示,所以在dll中,做了获取主窗体的句柄的工作,以便发消息给主窗体,告诉它是哪个
按键被按下
}
unit TestMain;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls,TlHelp32;

type
  TfrmTestMain = class(TForm)
    Memo1: TMemo;
    btn_SetHook: TButton;
    btn_StopHook: TButton;
    procedure btn_SetHookClick(Sender: TObject);
    procedure btn_StopHookClick(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
    procedure WndProc(var Message: TMessage);override;
  public
    { Public declarations }
  end;

var
  frmTestMain: TfrmTestMain;

implementation

procedure SetHook;stdcall;external 'KeyBoardHook';
procedure StopHook;stdcall;external 'KeyBoardHook';

{$R *.dfm}

procedure TfrmTestMain.btn_SetHookClick(Sender: TObject);
begin
  SetHook;
end;

procedure TfrmTestMain.btn_StopHookClick(Sender: TObject);
begin
  StopHook;
end;

procedure TfrmTestMain.WndProc(var Message: TMessage);
var
  hSnapShot:THandle;
  pEntry:TProcessEntry32;
  find:Boolean;
  proName:string;
begin
  if Message.Msg = WM_USER+101 then
  begin
    //创建进程快照
    hSnapShot := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    pEntry.dwSize := SizeOf(pEntry);
    find := Process32First(hSnapShot,pEntry);                  
    while find do
    begin
      //取进程名字
      proName := pEntry.szExeFile;
      if pEntry.th32ProcessID = Message.LParam then Break;
      find := Process32Next(hSnapShot,pEntry);
    end;
    Memo1.Lines.Add('进程:' + proName + ',ID:' +IntToStr(Message.LParam)+'按下按键:'+Chr(Message.WParam));
    CloseHandle(hSnapShot);
  end;
  inherited;
end;

procedure TfrmTestMain.FormClose(Sender: TObject;
  var Action: TCloseAction);
begin
  StopHook;
end;

end.

原文地址:https://www.cnblogs.com/caohenry999/p/6611912.html