学用钩子函数

学用钩子函数

    钩子函数并不是什么高深的技术,在Microsoft的Win32 SDK手册上就有记述。不过很可惜秉承M$的一贯风格,要看懂可是不容易的事!而且它的例子是使用SDK写的,又不完整!这就让我们这些用只会Delphi的程序员更看不懂了。不过用钩子函数是很有用的,例如鼠标钩子可以拦截下所有的鼠标消息的。
    前段时间我写我的第一个软件《聊天快贴》的时候,学习了一下鼠标钩子函数,现在我把我几周的学习成果写出来希望对大家有帮助。
    钩子函数一共有12种(这里就不列举出来了,不过说实话我多数钩子也没有用过^_^),分为全局子和线程钩子两种。线程钩子就只监视某个线程,全局钩子可以监视Windows的所有线程。具体的你可以看看Delphi 带的Win32 SDK,就是是全英文的,可惜了。全局钩子是必须用DLL加载,也就是说钩子函数这部分必须包装为一个DLL文件,然后再在主程序中调用钩子DLL中函数才可以!而且有些钩子是必须以全局钩子的方式存在,也就是一定要用DLL包装它才可以。
再解释一下设置钩子的Api函数:
function SetWindowsHookEx(idHook: Integer; lpfn: TFNHookProc; hmod: HINST; dwThreadId: DWORD): HHOOK; stdcall;这是在Delphi下的说明,其中第一个参数是钩子的类型;第二个参数是钩子函数的地址;第个参数是包含钩子函数的模块句柄;第四个参数指定监视的线程;返回钩子句柄。如果指定了某个确定的线程就只监视那个线程,即是线程钩子;如果为空,即是监视所有线程的全局钩子。其它几个相关函数就没有什么讲的了,只要照着用就可以了。具体的看我的源程序吧! 
    另外如果你只想使用进程钩子的话,有一个现成的控件可以用,就是Rx的RxWindowHook控件。拖到你窗体上,设置Active为True就可以了。 然后他只有BeforeMessage(消息从消息队列取走前)和AfterMessage(消息从消息队列取走后)两个事件,响应他就可以了,怎么用就看你的了。
    下面我说说比较实用的全局钩子的使用!我写了一个最简单的鼠标全局钩子的例子,我还假定你懂如何写DLL。好了,来看源程序:(BTW:我学习编程技巧的时候总是想看一些最简单的例子,可是有些作者习惯用复杂的应用作为例子。学起来真是痛苦!!!)

一、DLL的工程文件。

library hookprj;

uses
  SysUtils,
  Classes,
  hkprocunit in 'hkprocunit.pas';

{$R *.RES}
exports
  EnableMouseHook, //只要把这两个函数输出就可以了,
  DisableMouseHook;//不会不懂函数的意思吧^_^。
begin

end.

二、DLL输出函数的实现单元。

unit hkprocunit;

interface

uses
  Windows,Messages;

var
  hHk: HHOOK;//钩子的句柄值。
  function MouseHookProc(nCode: Integer;WParam: WPARAM;LParam: LPARAM):   LRESULT;stdcall;
  //鼠标钩子的回调函数,即是用它来处理得到消息后要干什么。这里我只是发送  一个//WM_PASTE消息。
  //nCode参数是Hook的标志,一般只关心小于0时。看下面的详细说明
  //WParam参数表示鼠标消息的类型
  //LParam参数是一个指向 TMOUSEHOOKSTRUCT 结构的指针。结构包含了鼠标消息的状态,我只用了hwnd一个
  //即鼠标消息要传递给的窗口句柄。
  //返回值如果不是0的话windows就把这个消息丢掉,其它的程序就不会再收到这个消息了。

  function EnableMouseHook:Boolean; stdcall; export;
  function DisableMouseHook:Boolean; stdcall; export;//两个函数都是Boolean类型,成功都是返回True

implementation

function MouseHookProc(nCode: Integer;WParam: WPARAM;LParam: LPARAM): LRESULT;stdcall;
var
  MouseHookStruct: ^TMOUSEHOOKSTRUCT;//这个结构Delphi在Windows单元有定义,直接用就可以了。
  nState: SHORT;//得到键盘状态的GetKeyState函数的返回值。这是一个16位的数。 
begin
  Result := 0; //最好首先给他一个返回值,不然会有警告的!记住这可不是C语言。
  //当nCode小于0时表示还有其它的Hook,必须把参数传给他。
  //此时就要用Api函数CallNextHookEx让他调用下一个Hook!!!当然不用好像也可以。 
  if nCode < 0 then 
    Result := CallNextHookEx(hHk,nCode,WParam,LParam)//参数是现成的,直接用就可以了,详细的说明可以参考Win32 SDK
  else if wParam = WM_LBUTTONDBLCLK then //判断是不是鼠标左键双击事件
  begin
    nState := GetKeyState(VK_CONTROL);//这个函数只有一个参数,就是要得到的键的键值,这里用windows的虚拟键值表示ctrl键。
    if (nState and $8000) = $8000 then//如果按下了,那么返回值的最高位为1
    begin //即是16进制的8000,如果没有按下就返回0
      MouseHookStruct := Pointer(LParam);//转换指针并付值给MouseHookStruct变量。
      SendMessage(MouseHookStruct.hwnd,WM_PASTE,0,0);//如果条件都满足了就发送WM_PASTE(粘贴)消息
    end;
  end;

end;

function EnableMouseHook:Boolean; stdcall; export;
begin
  if hHk = 0 then //为了安全,必须判断一下再设置钩子。
  begin 
    // 第三个参数的Hinstance 在Delphi中有定义,用就可以了。第四个参数必须为0
    hHk := SetWindowsHookEx(WH_MOUSE,@MouseHookProc,Hinstance,0);
    Result := True;
  end
  else
    Result := False;
end;

function DisableMouseHook:Boolean; stdcall; export;
begin
  if hHk <> 0 then //如果有钩子就卸掉他。
  begin
    UnHookWindowsHookEx(hHk);
    hHk := 0;
    Result := True;
  end
  else
    Result := False;
end;

end.

三、使用钩子的应用程序的工程文件。

program Project1;

uses
  Forms,
  Unit1 in 'Unit1.pas' {Form1};

{$R *.RES}

begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

四、使用钩子的应用程序代码。

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Button1: TButton;//放上两个Button和一个Edit控键用来试用我们的钩子函数。
    Button2: TButton;
    Edit1: TEdit;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { private declarations }

  public
    { public declarations }
end;

var
  Form1: TForm1;
  //下面是引用hookprj.dll中的函数。
    function EnableMouseHook:Boolean; stdcall; external 'Hookprj.dll' name 'EnableMouseHook';
    function DisableMouseHook:Boolean; stdcall; external 'Hookprj.dll' name 'DisableMouseHook';
implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
begin
  if EnableMouseHook then
    ShowMessage('启动钩子成功');
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  if DisableMouseHook then
    ShowMessage('停止钩子成功');
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  //这里调用是必须的,否则有可能没有卸载钩子就退出了,那就不好了。 
  DisableMouseHook;
end;
end.
Windows2000 + Delphi5.0sp1 测试通过

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
三:程序的设计: I:设置钩子 设置钩子是通过SetWindowsHookEx ()的API函数. 原形: HHOOK SetWindowsHookEx(int idHook,HOOKPROC lpfn,HINSTANCE hMod,DWORD dwThreadId) idhook:装入钩子的类型. lpfn: 钩子进程的入口地址 hMod: 应用程序的事件句柄 dwThreadId: 装入钩子的线程标示 参数: idHook: 这个参数可以是以下值: WH_CALLWNDPROC、WH_CALLWNDPROCRET、WH_CBT、WH_DEBUG、WH_FOREGROUNDIDLE、WH_GETMESSAGE、WH_JOURNALPLAYBACK、WH_JOURNALRECORD、WH_KEYBOARD、 WH_KEYBOARD_LL、WH_MOUSE、WH_MOUSE_LL、WH_MSGFILTER、WH_SHELL、WH_SYSMSGFILTER。 对于这些参数,我不想一一加以解释,因为MSDN中有关于他们的详细注解。我只挑选其中的几个加以中文说明。 WH_KEYBOARD:一旦有键盘敲打消息(键盘的按下、键盘的弹起),在这个消息被放在应用程序的消息队列前,WINDOWS将会调用你的钩子函数钩子函数可以 改变和丢弃键盘敲打消息。 WH_MOUSE:每个鼠标消息在被放在应用程序的消息队列前,WINDOWS将会调用你的钩子函数钩子函数可以改变和丢弃鼠标消息。 WH_GETMESSAGE:每次当你的应用程序调用一个GetMessage()或者一个PeekMessage()为了去从应用程序的消息队列中要求一个消息WINDOWS都会调用你的钩子函数。 而钩子函数可以改变和丢弃这个消息。 II:释放钩子 钩子的释放使用的是UnhookWindowsHookEx()函数 原形:BOOL UnhookWindowsHookEx( HHOOK hhk ) UnhookWindowsHookEx()函数将释放的是钩子链中函数SetWindowsHookEx所装入的钩子进程。 hhk: 将要释放的钩子进程的句柄。 III:钩子进程 钩子进程使用函数HookProc;其实HookProc仅仅只是应用程序定义的符号。比如你可以写成KeyBoardHook.但是参数是不变的。Win32 API提供了诸如:CallWndProc、 GetMsgProc、DebugProc、CBTProc、MouseProc、KeyboardProc、MessageProc等函数,对于他们的详细讲解,可以看MSDN我在此只讲解一下KeyBoardHook的含义。 原形:LRESULT CALLBACK KeyBoardHook (int nCode, WPARAM wParam, LPARAM lParam) 说明:钩子进程是一些依附在一个钩子上的一些函数,因此钩子进程只被WINDOWS调用而不被应用程序调用,他们有就需要作为一个回调函数(CALLBACK)。 参数说明: nCode:钩子代码,钩子进程使用钩子代码去决定是否执行。而钩子代码的值是依靠钩子的种类来定的。每种钩子种类都有他们自己一系列特性的代码。比如对于WH_KEYBOARD, 钩子代码的参数有:HC_ACTION,HC_NOREMOVE。HC_ACTION的意义:参数wParam 和lParam 包含了键盘敲打消息的信息,HC_NOREMOVE的意义:参数wParam 和lParam包含了 键盘敲打消息的信息,并且,键盘敲打消息一直没有从消息队列中删除。(应用程序调用PeekMessage函数,并且设置PM_NOREMOVE标志)。也就是说当nCode等于HC_ACTION, 钩子进程必须处理消息。而为HC_NOREMOVE,钩子进程必须传递消息给CallNextHookEx函数,而不能做进一步的处理,而且必须有CallNextHookEx函数的返回值。 wParam:键盘敲打所产生的键盘消息,键盘按键的虚拟代码。 lParam:包含了消息细节。 注意:如果钩子进程中nCode小于零,钩子进程必须返回(return) CallNextHookEx(nCode,wParam,lParam);而钩子进程中的nCode大于零,但是钩子进程并不处理消息, 作者推荐你调用CallNextHookEx并且返回该函数的返回值。否则,如果另一个应用程序也装入WH_KEYBOARD 钩子,那么该钩子将不接受钩子通知并且返回一个不正确的值。 如果钩子进程处理了消息,它可能返回一个非零值去阻止系统传递该信息到其它剩下的钩子或者windows进程。所以最好在钩子进程的最后都返回CallNextHookEx的返回

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值