DELPHI盒子
!实时搜索: 盒子论坛 | 注册用户 | 修改信息 | 退出
检举帖 | 全文检索 | 关闭广告 | 捐赠
技术论坛
 用户名
 密  码
自动登陆(30天有效)
忘了密码
≡技术区≡
DELPHI技术
lazarus/fpc/Free Pascal
移动应用开发
Web应用开发
数据库专区
报表专区
网络通讯
开源项目
论坛精华贴
≡发布区≡
发布代码
发布控件
文档资料
经典工具
≡事务区≡
网站意见
盒子之家
招聘应聘
信息交换
论坛信息
最新加入: jsuguo
今日帖子: 31
在线用户: 18
导航: 论坛 -> 论坛精华贴 斑竹:liumazi,iamdream  
作者:
男 hbqckzj (醉里寻欢) ★☆☆☆☆ -
盒子活跃会员
2003/12/11 15:30:29
标题:
哪里有关于钩子函数(HOOK)完整的教程下载啊?期待! 浏览:22010
加入我的收藏
楼主: 本人看了一些有关钩子的文章,可是还是不太了解用法,要找相关的教程研究,请求指点!
----------------------------------------------
秋风清 秋月明,几上闲琴黯生尘。
夜来忽忆少年事,陌上柳绿草青青。
相逢一笑言未语,携手相看自倾心。
二十二载云烟里,云含风韵烟含情。
风尘渺落风霜浸,但闻长空雁长鸣。
姿容如花颜如玉,只在平生梦里寻。
秋风清 秋月明,秋灯秋雨愁煞人。
天涯路远人何在,午夜梦回空扰惊。
作者:
男 bios (阿贡) ★☆☆☆☆ -
盒子中级会员
2003/12/11 15:48:22
1楼: 偶也要 这个很难理解!用途 很多啊!
----------------------------------------------
按此在新窗口浏览图片
按此在新窗口浏览图片
作者:
男 loubingtt (我的生活与你无关) ★☆☆☆☆ -
盒子活跃会员
2003/12/11 16:47:53
2楼: 恩。我这方面的资料到有不少,什么鼠标,键盘钩子,api钩子。。等等。可。。恩我不知道怎么把它弄上来啊:(
 至于教程我好象有一个。好象是汇编形似的。。如果要的话。我帮你找找看

----------------------------------------------
按此在新窗口浏览图片
作者:
男 hbqckzj (醉里寻欢) ★☆☆☆☆ -
盒子活跃会员
2003/12/11 21:42:45
3楼: 谢谢楼上的兄弟啊,帮忙发到俺邮箱里来吧,感激不尽!
kangzhijian@sohu.com

或者加俺QQ传给俺也行啊,37314306!拜托了!
----------------------------------------------
秋风清 秋月明,几上闲琴黯生尘。
夜来忽忆少年事,陌上柳绿草青青。
相逢一笑言未语,携手相看自倾心。
二十二载云烟里,云含风韵烟含情。
风尘渺落风霜浸,但闻长空雁长鸣。
姿容如花颜如玉,只在平生梦里寻。
秋风清 秋月明,秋灯秋雨愁煞人。
天涯路远人何在,午夜梦回空扰惊。
作者:
男 bios (阿贡) ★☆☆☆☆ -
盒子中级会员
2003/12/11 21:52:49
4楼: 偶: jeamicle@etang.com
----------------------------------------------
按此在新窗口浏览图片
按此在新窗口浏览图片
作者:
男 crazy (疯子) ★☆☆☆☆ -
盒子活跃会员
2003/12/12 0:01:10
5楼: 偶要:xiaozejia@163.net
----------------------------------------------
春花秋月何時了

往事知多少

小樓昨夜又東風
作者:
男 cnxxx (我本高手) ★☆☆☆☆ -
盒子活跃会员
2003/12/12 0:13:04
6楼: FTP到ftp://uploads@2ccc.com或者发到efile.com.cn
----------------------------------------------
黑夜中,一眼望去,我就是高手。
作者:
男 loubingtt (我的生活与你无关) ★☆☆☆☆ -
盒子活跃会员
2003/12/12 1:29:54
7楼: 基本概念 
钩子(Hook),是Windows消息处理机制的一个平台,应用程序可以在上面设置子程以监视指定窗口的某种消息,而且所监视的窗口可以是其他进程所创建的。当消息到达后,在目标窗口处理函数之前处理它。钩子机制允许应用程序截获处理window消息或特定事件。

钩子实际上是一个处理消息的程序段,通过系统调用,把它挂入系统。每当特定的消息发出,在没有到达目的窗口前,钩子程序就先捕获该消息,亦即钩子函数先得到控

制权。这时钩子函数即可以加工处理(改变)该消息,也可以不作处理而继续传递该消息,还可以强制结束消息的传递。


--------------------------------------------------------------------------------

运行机制

1、钩子链表和钩子子程:

每一个Hook都有一个与之相关联的指针列表,称之为钩子链表,由系统来维护。这个列表的指针指向指定的,应用程序定义的,被Hook子程调用的回调函数,也就是该钩子的各个处理子程。当与指定的Hook类型关联的消息发生时,系统就把这个消息传递到Hook子程。一些Hook子程可以只监视消息,或者修改消息,或者停止消息的前进,避免这些消息传递到下一个Hook子程或者目的窗口。最近安装的钩子放在链的开始,而最早安装的钩子放在最后,也就是后加入的先获得控制权。

Windows 并不要求钩子子程的卸载顺序一定得和安装顺序相反。每当有一个钩子被卸载,Windows 便释放其占用的内存,并更新整个Hook链表。如果程序安装了钩子,但是在尚未卸载钩子之前就结束了,那么系统会自动为它做卸载钩子的操作。

钩子子程是一个应用程序定义的回调函数(CALLBACK Function),不能定义成某个类的成员函数,只能定义为普通的C函数。用以监视系统或某一特定类型的事件,这些事件可以是与某一特定线程关联的,也可以是系统中所有线程的事件。

钩子子程必须按照以下的语法:

LRESULT CALLBACK HookProc
(
  int nCode, 
      WPARAM wParam, 
      LPARAM lParam
);

HookProc是应用程序定义的名字。

nCode参数是Hook代码,Hook子程使用这个参数来确定任务。这个参数的值依赖于Hook类型,每一种Hook都有自己的Hook代码特征字符集。

wParam和lParam参数的值依赖于Hook代码,但是它们的典型值是包含了关于发送或者接收消息的信息。

2、钩子的安装与释放:

使用API函数SetWindowsHookEx()把一个应用程序定义的钩子子程安装到钩子链表中。SetWindowsHookEx函数总是在Hook链的开头安装Hook子程。当指定类型的Hook监视的事件发生时,系统就调用与这个Hook关联的Hook链的开头的Hook子程。每一个Hook链中的Hook子程都决定是否把这个事件传递到下一个Hook子程。Hook子程传递事件到下一个Hook子程需要调用CallNextHookEx函数。

HHOOK SetWindowsHookEx( 
     int idHook,      // 钩子的类型,即它处理的消息类型
     HOOKPROC lpfn,   // 钩子子程的地址指针。如果dwThreadId参数为0
      // 或是一个由别的进程创建的线程的标识,
      // lpfn必须指向DLL中的钩子子程。
      // 除此以外,lpfn可以指向当前进程的一段钩子子程代码。
      // 钩子函数的入口地址,当钩子钩到任何消息后便调用这个函数。
     HINSTANCE hMod,  // 应用程序实例的句柄。标识包含lpfn所指的子程的
DLL。
      // 如果dwThreadId 标识当前进程创建的一个线程,
      // 而且子程代码位于当前进程,hMod必须为NULL。
      // 可以很简单的设定其为本应用程序的实例句柄。
     DWORD dwThreadId // 与安装的钩子子程相关联的线程的标识符。
      // 如果为0,钩子子程与所有的线程关联,即为全局钩子。
                 ); 

  函数成功则返回钩子子程的句柄,失败返回NULL。

  以上所说的钩子子程与线程相关联是指在一钩子链表中发给该线程的消息同时发送给钩子子程,且被钩子子程先处理。

在钩子子程中调用得到控制权的钩子函数在完成对消息的处理后,如果想要该消息继续传递,那么它必须调用另外一个SDK中的API函数CallNextHookEx来传递它,以执行钩子链表所指的下一个钩子子程。这个函数成功时返回钩子链中下一个钩子过程的返回值,返回值的类型依赖于钩子的类型。这个函数的原型如下:

LRESULT CallNextHookEx
   (
    HHOOK hhk;
    int nCode;
    WPARAM wParam;
    LPARAM lParam;
    );

hhk为当前钩子的句柄,由SetWindowsHookEx()函数返回。

NCode为传给钩子过程的事件代码。

wParam和lParam 分别是传给钩子子程的wParam值,其具体含义与钩子类型有关。


钩子函数也可以通过直接返回TRUE来丢弃该消息,并阻止该消息的传递。否则的话,其他安装了钩子的应用程序将不会接收到钩子的通知而且还有可能产生不正确的结果。

钩子在使用完之后需要用UnHookWindowsHookEx()卸载,否则会造成麻烦。释放钩子比较简单,UnHookWindowsHookEx()只有一个参数。函数原型如下:

UnHookWindowsHookEx
(
 HHOOK hhk;
);

函数成功返回TRUE,否则返回FALSE。

3、一些运行机制:

在Win16环境中,DLL的全局数据对每个载入它的进程来说都是相同的;而在Win32环境中,情况却发生了变化,DLL函数中的代码所创建的任何对象(包括变量)都归调用它的线程或进程所有。当进程在载入DLL时,操作系统自动把DLL地址映射到该进程的私有空间,也就是进程的虚拟地址空间,而且也复制该DLL的全局数据的一份拷贝到该进程空间。也就是说每个进程所拥有的相同的DLL的全局数据,它们的名称相同,但其值却并不一定是相同的,而且是互不干涉的。


因此,在Win32环境下要想在多个进程中共享数据,就必须进行必要的设置。在访问同一个Dll的各进程之间共享存储器是通过存储器映射文件技术实现的。也可以把这些需要共享的数据分离出来,放置在一个独立的数据段里,并把该段的属性设置为共享。必须给这些变量赋初值,否则编译器会把没有赋初始值的变量放在一个叫未被初始化的数据段中。

#pragma data_seg预处理指令用于设置共享数据段。例如:

#pragma data_seg("SharedDataName")
HHOOK hHook=NULL;
#pragma data_seg()

在#pragma data_seg("SharedDataName")和#pragma data_seg()之间的所有变量将被访问该Dll的所有进程看到和共享。再加上一条指令#pragma comment(linker,"/section:.SharedDataName,rws"),那么这个数据节中的数据可以在所有DLL的实例之间共享。所有对这些数据的操作都针对同一个实例的,而不是在每个进程的地址空间中都有一份。

当进程隐式或显式调用一个动态库里的函数时,系统都要把这个动态库映射到这个进程的虚拟地址空间里(以下简称"地址空间")。这使得DLL成为进程的一部分,以这个进程的身份执行,使用这个进程的堆栈。

4、系统钩子与线程钩子:

SetWindowsHookEx()函数的最后一个参数决定了此钩子是系统钩子还是线程钩子。


线程勾子用于监视指定线程的事件消息。线程勾子一般在当前线程或者当前线程派生的线程内。


系统勾子监视系统中的所有线程的事件消息。因为系统勾子会影响系统中所有的应用程序,所以勾子函数必须放在独立的动态链接库(DLL) 中。系统自动将包含"钩子回调函数"的DLL映射到受钩子函数影响的所有进程的地址空间中,即将这个DLL注入了那些进程。

几点说明:

(1)如果对于同一事件(如鼠标消息)既安装了线程勾子又安装了系统勾子,那么系统会自动先调用线程勾子,然后调用系统勾子。 

(2)对同一事件消息可安装多个勾子处理过程,这些勾子处理过程形成了勾子链。当前勾子处理结束后应把勾子信息传递给下一个勾子函数。 

(3)勾子特别是系统勾子会消耗消息处理时间,降低系统性能。只有在必要的时候才安装勾子,在使用完毕后要及时卸载。


--------------------------------------------------------------------------------

钩子类型

每一种类型的Hook可以使应用程序能够监视不同类型的系统消息处理机制。下面描述所有可以利用的Hook类型。

1、WH_CALLWNDPROC和WH_CALLWNDPROCRET Hooks

WH_CALLWNDPROC和WH_CALLWNDPROCRET Hooks使你可以监视发送到窗口过程的消息。系统在消息发送到接收窗口过程之前调用WH_CALLWNDPROC Hook子程,并且在窗口过程处理完消息之后调用WH_CALLWNDPROCRET Hook子程。

WH_CALLWNDPROCRET Hook传递指针到CWPRETSTRUCT结构,再传递到Hook子程。

CWPRETSTRUCT结构包含了来自处理消息的窗口过程的返回值,同样也包括了与这个消息关联的消息参数。

2、WH_CBT Hook

在以下事件之前,系统都会调用WH_CBT Hook子程,这些事件包括:

1. 激活,建立,销毁,最小化,最大化,移动,改变尺寸等窗口事件;

2. 完成系统指令;

3. 来自系统消息队列中的移动鼠标,键盘事件;

4. 设置输入焦点事件;

5. 同步系统消息队列事件。


Hook子程的返回值确定系统是否允许或者防止这些操作中的一个。

3、WH_DEBUG Hook

在系统调用系统中与其他Hook关联的Hook子程之前,系统会调用WH_DEBUG Hook子程。你可以使用这个Hook来决定是否允许系统调用与其他Hook关联的Hook子程。

4、WH_FOREGROUNDIDLE Hook

当应用程序的前台线程处于空闲状态时,可以使用WH_FOREGROUNDIDLE Hook执行低优先级的任务。当应用程序的前台线程大概要变成空闲状态时,系统就会调用WH_FOREGROUNDIDLE Hook子程。

5、WH_GETMESSAGE Hook

应用程序使用WH_GETMESSAGE Hook来监视从GetMessage or PeekMessage函数返回的消息。你可以使用WH_GETMESSAGE Hook去监视鼠标和键盘输入,以及其他发送到消息队列中的消息。

6、WH_JOURNALPLAYBACK Hook

WH_JOURNALPLAYBACK Hook使应用程序可以插入消息到系统消息队列。可以使用这个Hook回放通过使用WH_JOURNALRECORD Hook记录下来的连续的鼠标和键盘事件。只要WH_JOURNALPLAYBACK Hook已经安装,正常的鼠标和键盘事件就是无效的。

WH_JOURNALPLAYBACK Hook是全局Hook,它不能象线程特定Hook一样使用。

WH_JOURNALPLAYBACK Hook返回超时值,这个值告诉系统在处理来自回放Hook当前消息之前需要等待多长时间(毫秒)。这就使Hook可以控制实时事件的回放。

WH_JOURNALPLAYBACK是system-wide local hooks,它們不會被注射到任何行程位址空間。

7、WH_JOURNALRECORD Hook

WH_JOURNALRECORD Hook用来监视和记录输入事件。典型的,可以使用这个Hook记录连续的鼠标和键盘事件,然后通过使用WH_JOURNALPLAYBACK Hook来回放。

WH_JOURNALRECORD Hook是全局Hook,它不能象线程特定Hook一样使用。

WH_JOURNALRECORD是system-wide local hooks,它們不會被注射到任何行程位址空間。

8、WH_KEYBOARD Hook

在应用程序中,WH_KEYBOARD Hook用来监视WM_KEYDOWN and WM_KEYUP消息,这些消息通过GetMessage or PeekMessage function返回。可以使用这个Hook来监视输入到消息队列中的键盘消息。

9、WH_KEYBOARD_LL Hook

WH_KEYBOARD_LL Hook监视输入到线程消息队列中的键盘消息。

10、WH_MOUSE Hook

WH_MOUSE Hook监视从GetMessage 或者 PeekMessage 函数返回的鼠标消息。使用这个Hook监视输入到消息队列中的鼠标消息。

11、WH_MOUSE_LL Hook

WH_MOUSE_LL Hook监视输入到线程消息队列中的鼠标消息。

12、WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks

WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以监视菜单,滚动条,消息框,对话框消息并且发现用户使用ALT+TAB or ALT+ESC 组合键切换窗口。WH_MSGFILTER Hook只能监视传递到菜单,滚动条,消息框的消息,以及传递到通过安装了Hook子程的应用程序建立的对话框的消息。WH_SYSMSGFILTER Hook监视所有应用程序消息。


WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以在模式循环期间过滤消息,这等价于在主消息循环中过滤消息。


通过调用CallMsgFilter function可以直接的调用WH_MSGFILTER Hook。通过使用这个函数,应用程序能够在模式循环期间使用相同的代码去过滤消息,如同在主消息循环里一样。

13、WH_SHELL Hook

外壳应用程序可以使用WH_SHELL Hook去接收重要的通知。当外壳应用程序是激活的并且当顶层窗口建立或者销毁时,系统调用WH_SHELL Hook子程。

WH_SHELL 共有5钟情況:

1. 只要有个top-level、unowned 窗口被产生、起作用、或是被摧毁;

2. 当Taskbar需要重画某个按钮;

3. 当系统需要显示关于Taskbar的一个程序的最小化形式;

4. 当目前的键盘布局状态改变;

5. 当使用者按Ctrl+Esc去执行Task Manager(或相同级别的程序)。

按照惯例,外壳应用程序都不接收WH_SHELL消息。所以,在应用程序能够接收WH_SHELL消息之前,应用程序必须调用SystemParametersInfo function注册它自己。

----------------------------------------------
按此在新窗口浏览图片
作者:
男 hbqckzj (醉里寻欢) ★☆☆☆☆ -
盒子活跃会员
2003/12/12 10:12:32
8楼: BEGIN
CTRL + C ;
CTRL + V ;
CTRL + S ;
END;

意犹未尽,我还要啊!还没满足哦,我欲望很强烈....

(晕,这位老兄,你又想歪了!)^_^


----------------------------------------------
秋风清 秋月明,几上闲琴黯生尘。
夜来忽忆少年事,陌上柳绿草青青。
相逢一笑言未语,携手相看自倾心。
二十二载云烟里,云含风韵烟含情。
风尘渺落风霜浸,但闻长空雁长鸣。
姿容如花颜如玉,只在平生梦里寻。
秋风清 秋月明,秋灯秋雨愁煞人。
天涯路远人何在,午夜梦回空扰惊。
作者:
男 loubingtt (我的生活与你无关) ★☆☆☆☆ -
盒子活跃会员
2003/12/12 16:50:23
9楼: 恩。这方面的资料我还有。但要找。再说你不是要我一个个的抄上来吧。不过例子还有几个
----------------------------------------------
按此在新窗口浏览图片
作者:
男 hbqckzj (醉里寻欢) ★☆☆☆☆ -
盒子活跃会员
2003/12/12 18:36:51
10楼: 兄弟,只要有就狂贴啊!3QU!
----------------------------------------------
秋风清 秋月明,几上闲琴黯生尘。
夜来忽忆少年事,陌上柳绿草青青。
相逢一笑言未语,携手相看自倾心。
二十二载云烟里,云含风韵烟含情。
风尘渺落风霜浸,但闻长空雁长鸣。
姿容如花颜如玉,只在平生梦里寻。
秋风清 秋月明,秋灯秋雨愁煞人。
天涯路远人何在,午夜梦回空扰惊。
作者:
男 bios (阿贡) ★☆☆☆☆ -
盒子中级会员
2003/12/12 18:39:30
11楼: 创造发贴 1000个 的 论坛新记录吧!
----------------------------------------------
按此在新窗口浏览图片
按此在新窗口浏览图片
作者:
男 hbqckzj (醉里寻欢) ★☆☆☆☆ -
盒子活跃会员
2003/12/13 7:48:24
12楼: 支持楼上bios的说法哈,同意的顶哈!

我找一些有关钩子的来顶先!
----------------------------------------------
秋风清 秋月明,几上闲琴黯生尘。
夜来忽忆少年事,陌上柳绿草青青。
相逢一笑言未语,携手相看自倾心。
二十二载云烟里,云含风韵烟含情。
风尘渺落风霜浸,但闻长空雁长鸣。
姿容如花颜如玉,只在平生梦里寻。
秋风清 秋月明,秋灯秋雨愁煞人。
天涯路远人何在,午夜梦回空扰惊。
作者:
男 hbqckzj (醉里寻欢) ★☆☆☆☆ -
盒子活跃会员
2003/12/13 7:50:09
13楼: 消息钩子函数入门篇  

--------------------------------------------------------------------------------
 
Windows系统是建立在事件驱动的机制上的,说穿了就是整个系统都是通过消息的传递来实现的。而钩子是Windows系统中非常重要的系统接口,用它可以截获并处理送给其他应用程序的消息,来完成普通应用程序难以实现的功能。钩子可以监视系统或进程中的各种事件消息,截获发往目标窗口的消息并进行处理。这样,我们就可以在系统中安装自定义的钩子,监视系统中特定事件的发生,完成特定的功能,比如截获键盘、鼠标的输入,屏幕取词,日志监视等等。可见,利用钩子可以实现许多特殊而有用的功能。因此,对于高级编程人员来说,掌握钩子的编程方法是很有必要的。 

钩子的类型 
  一. 按事件分类,有如下的几种常用类型 
  (1) 键盘钩子和低级键盘钩子可以监视各种键盘消息。 
  (2) 鼠标钩子和低级鼠标钩子可以监视各种鼠标消息。 
  (3) 外壳钩子可以监视各种Shell事件消息。比如启动和关闭应用程序。 
  (4) 日志钩子可以记录从系统消息队列中取出的各种事件消息。 
  (5) 窗口过程钩子监视所有从系统消息队列发往目标窗口的消息。 
  此外,还有一些特定事件的钩子提供给我们使用,不一一列举。 
下面描述常用的Hook类型: 
1、WH_CALLWNDPROC和WH_CALLWNDPROCRET Hooks 
WH_CALLWNDPROC和WH_CALLWNDPROCRET Hooks使你可以监视发送到窗口过程的消息。系统在消息发送到接收窗口过程之前调用WH_CALLWNDPROC Hook子程,并且在窗口过程处理完消息之后调用WH_CALLWNDPRO 
CRET Hook子程。WH_CALLWNDPROCRET Hook传递指针到CWPRETSTRUCT结构,再传递到Hook子程。CWPRETSTRUCT结构包含了来自处理消息的窗口过程的返回值,同样也包括了与这个消息关联的消息参数。 
2、WH_CBT Hook 
在以下事件之前,系统都会调用WH_CBT Hook子程,这些事件包括: 
1. 激活,建立,销毁,最小化,最大化,移动,改变尺寸等窗口事件; 
2. 完成系统指令; 
3. 来自系统消息队列中的移动鼠标,键盘事件; 
4. 设置输入焦点事件; 
5. 同步系统消息队列事件。 
Hook子程的返回值确定系统是否允许或者防止这些操作中的一个。 
3、WH_DEBUG Hook 
在系统调用系统中与其他Hook关联的Hook子程之前,系统会调用WH_DEBUG Hook子程。你可以使用这个Hook来决定是否允许系统调用与其他Hook关联的Hook子程。 
4、WH_FOREGROUNDIDLE Hook 
当应用程序的前台线程处于空闲状态时,可以使用WH_FOREGROUNDIDLE Hook执行低优先级的任务。当应用程序的前台线程大概要变成空闲状态时,系统就会调用WH_FOREGROUNDIDLE Hook子程。 
5、WH_GETMESSAGE Hook 
应用程序使用WH_GETMESSAGE Hook来监视从GetMessage or PeekMessage函数返回的消息。你可以使用WH_GETMESSAGE Hook去监视鼠标和键盘输入,以及其他发送到消息队列中的消息。 
6、WH_JOURNALPLAYBACK Hook 
WH_JOURNALPLAYBACK Hook使应用程序可以插入消息到系统消息队列。可以使用这个Hook回放通过使用WH_JOURNALRECORD Hook记录下来的连续的鼠标和键盘事件。只要WH_JOURNALPLAYBACK Hook已经安装,正常的鼠标和键盘事件就是无效的。WH_JOURNALPLAYBACK Hook是全局Hook,它不能象线程特定Hook一样使用。WH_JOURNALPLAYBACK Hook返回超时值,这个值告诉系统在处理来自回放Hook当前消息之前需要等待多长时间(毫秒)。这就使Hook可以控制实时事件的回放。WH_JOURNALPLAYBACK是system-wide local hooks,它們不會被注射到任何行程位址空間。(估计按键精灵是用这个hook做的) 
7、WH_JOURNALRECORD Hook 
WH_JOURNALRECORD Hook用来监视和记录输入事件。典型的,可以使用这个Hook记录连续的鼠标和键盘事件,然后通过使用WH_JOURNALPLAYBACK Hook来回放。WH_JOURNALRECORD Hook是全局Hook,它不能象线程特定Hook一样使用。WH_JOURNALRECORD是system-wide local hooks,它們不會被注射到任何行程位址空間。 
8、WH_KEYBOARD Hook 
在应用程序中,WH_KEYBOARD Hook用来监视WM_KEYDOWN and WM_KEYUP消息,这些消息通过GetMessage or PeekMessage function返回。可以使用这个Hook来监视输入到消息队列中的键盘消息。 
9、WH_KEYBOARD_LL Hook 
WH_KEYBOARD_LL Hook监视输入到线程消息队列中的键盘消息。 
10、WH_MOUSE Hook 
WH_MOUSE Hook监视从GetMessage 或者 PeekMessage 函数返回的鼠标消息。使用这个Hook监视输入到消息队列中的鼠标消息。 
11、WH_MOUSE_LL Hook 
WH_MOUSE_LL Hook监视输入到线程消息队列中的鼠标消息。 
12、WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks 
WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以监视菜单,滚动条,消息框,对话框消息并且发现用户使用ALT+TAB or ALT+ESC 组合键切换窗口。WH_MSGFILTER Hook只能监视传递到菜单,滚动条,消息框的消息,以及传递到通过安装了Hook子程的应用程序建立的对话框的消息。WH_SYSMSGFILTER Hook监视所有应用程序消息。WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以在模式循环期间过滤消息,这等价于在主消息循环中过滤消息。通过调用CallMsgFilter function可以直接的调用WH_MSGFILTER Hook。通过使用这个函数,应用程序能够在模式循环期间使用相同的代码去过滤消息,如同在主消息循环里一样。 
13、WH_SHELL Hook 
外壳应用程序可以使用WH_SHELL Hook去接收重要的通知。当外壳应用程序是激活的并且当顶层窗口建立或者销毁时,系统调用WH_SHELL Hook子程。 
WH_SHELL 共有5种情況: 
1. 只要有个top-level、unowned 窗口被产生、起作用、或是被摧毁; 
2. 当Taskbar需要重画某个按钮; 
3. 当系统需要显示关于Taskbar的一个程序的最小化形式; 
4. 当目前的键盘布局状态改变; 
5. 当使用者按Ctrl+Esc去执行Task Manager(或相同级别的程序)。 
按照惯例,外壳应用程序都不接收WH_SHELL消息。所以,在应用程序能够接收WH_SHELL消息之前,应用程序必须调用SystemParametersInfo function注册它自己。 
以上是13种常用的hook类型! 
  二. 按使用范围分类,主要有线程钩子和系统钩子 
  (1) 线程钩子监视指定线程的事件消息。 
  (2) 系统钩子监视系统中的所有线程的事件消息。因为系统钩子会影响系统中所有的应用程序,所以钩子函数必须放在独立的动态链接库(DLL) 
中。这是系统钩子和线程钩子很大的不同之处。 
   几点需要说明的地方: 
  (1) 如果对于同一事件(如鼠标消息)既安装了线程钩子又安装了系统钩子,那么系统会自动先调用线程钩子,然后调用系统钩子。 
  (2) 对同一事件消息可安装多个钩子处理过程,这些钩子处理过程形成了钩子链。当前钩子处理结束后应把钩子信息传递给下一个钩子函数。而且最近安装的钩子放在链的开始,而最早安装的钩子放在最后,也就是后加入的先获得控制权。 
  (3) 钩子特别是系统钩子会消耗消息处理时间,降低系统性能。只有在必要的时候才安装钩子,在使用完毕后要及时卸载。 
编写钩子程序 
   编写钩子程序的步骤分为三步:定义钩子函数、安装钩子和卸载钩子。 
  1.定义钩子函数 
  钩子函数是一种特殊的回调函数。钩子监视的特定事件发生后,系统会调用钩子函数进行处理。不同事件的钩子函数的形式是各不相同的。下面以鼠标钩子函数举例说明钩子函数的原型: 
LRESULT CALLBACK HookProc(int nCode ,WPARAM wParam,LPARAM lParam) 
参数wParam和 lParam包含所钩消息的信息,比如鼠标位置、状态,键盘按键等。nCode包含有关消息本身的信息,比如是否从消息队列中移出。 
我们先在钩子函数中实现自定义的功能,然后调用函数 CallNextHookEx.把钩子信息传递给钩子链的下一个钩子函数。CallNextHookEx.的原型如下: 
LRESULT CallNextHookEx( HHOOK hhk, int nCode, WPARAM wParam, LPARAM lParam ) 
参数 hhk是钩子句柄。nCode、wParam和lParam 是钩子函数。 
当然也可以通过直接返回TRUE来丢弃该消息,就阻止了该消息的传递。 
2.安装钩子 
  在程序初始化的时候,调用函数SetWindowsHookEx安装钩子。其函数原型为: 
HHOOK SetWindowsHookEx( int idHook,HOOKPROC lpfn, INSTANCE hMod,DWORD dwThreadId ) 
参数idHook表示钩子类型,它是和钩子函数类型一一对应的。比如,WH_KEYBOARD表示安装的是键盘钩子,WH_MOUSE表示是鼠标钩子等等。 
  Lpfn是钩子函数的地址。 
  HMod是钩子函数所在的实例的句柄。对于线程钩子,该参数为NULL;对于系统钩子,该参数为钩子函数所在的DLL句柄。 
   dwThreadId 指定钩子所监视的线程的线程号。对于全局钩子,该参数为NULL。 
  SetWindowsHookEx返回所安装的钩子句柄。 
  3.卸载钩子 
   当不再使用钩子时,必须及时卸载。简单地调用函数 BOOL UnhookWindowsHookEx( HHOOK hhk)即可。 
   
值得注意的是线程钩子和系统钩子的钩子函数的位置有很大的差别。线程钩子一般在当前线程或者当前线程派生的线程内,而系统钩子必须放在独立的动态链接库中,实现起来要麻烦一些。 

----------------------------------------------
秋风清 秋月明,几上闲琴黯生尘。
夜来忽忆少年事,陌上柳绿草青青。
相逢一笑言未语,携手相看自倾心。
二十二载云烟里,云含风韵烟含情。
风尘渺落风霜浸,但闻长空雁长鸣。
姿容如花颜如玉,只在平生梦里寻。
秋风清 秋月明,秋灯秋雨愁煞人。
天涯路远人何在,午夜梦回空扰惊。
作者:
男 hbqckzj (醉里寻欢) ★☆☆☆☆ -
盒子活跃会员
2003/12/13 8:01:43
14楼: 系统挂钩捕捉键盘操作(转载)
    
在WINDOWS系统下,应用程序常常要截获其他程序的消息,并加以处理(例如跟踪键盘或鼠标的按键状况等)。现在,我们假设在前台进行正常操作,在后台利用HOOK程序为系统安装一个键盘挂钩,当有按键操作时,系统发给键盘挂钩对应的消息,而这些消息被HOOK程序截获,并加以相应的处理,这样就可以监视键盘的使用状况了。

  一.实现方法
  DELPHI提供了强大的可视化集成开发环境,它使得在Windows下的应用程序开发变得更加广泛,因此我们将用DELPHI编写一个动态链接库,然后在主程序中加以调用以实现系统挂钩的设置。具体步骤如下:
  * 用DELPHI创建一个使用键盘挂钩的动态链接库HK.DLL
  * 用DELPHI编写一个使用上述DLL的可执行文件HOOK.EXE
  二.实现步骤
  1.创建动态链接库
  * 选择FILE菜单中的NEW选项,选择DLL产生一个新的模板,保存为HK.DPR

  library HK .
  uses
   SysUtils,
   Classes,
   hkproc in 'hkproc.pas'; //挂钩函数在文件中的定义
   exports //DLL的输出函数
   EnableHotKeyHook,
   DisableHotKeyHook;
 
  begin
   hNextHookProc :=0;
   Assign(f,'c:.txt');//将捕获的键值存入C盘的“code.txt”文件中
   Reset(f); //初始化“code.txt”文件
   procSaveExit := ExitProc; //DLL释放时解除挂钩
   ExitProc := @HotKeyHookExit;
  end.

  * 选择FILE菜单中的NEW选项,选择UNIT生成HKPROC.PAS

  unit hkproc;

  interface
  uses

   Windows,Messages;

  var

   f :file of char;

  c:char;

  i :integer;

  j :integer;

   hNextHookProc : HHook;

   procSaveExit : Pointer;

  function KeyboardHookHandler(iCode : Integer;

   wParam : WPARAM;

   lParam : LPARAM) : LRESULT; stdcall  export;

  function EnableHotKeyHook : BOOL  export 

  function DisableHotKeyHook : BOOL; export 

  procedure HotKeyHookExit  far 

  implementation

  function KeyboardHookHandler(iCode : Integer;
   WParam : WPARAM;
   lParam : LPARAM) : LRESULT stdcall  export;
  const
   _KeyPressMask = $80000000 
  begin
   Result :=0;
   if iCode <0 then
   begin
   Result :=CallNextHookEx(hNextHookProc,iCode,
   wParam,lParam);
   Exit;
   end;
   if((lParam and _KeyPressMask)=0) then
   begin
   i:=getkeystate($10); //返回Shift键的状态
   j:=getkeystate($14); //返回Caps Lock键的状态
   if((j and 1)=1 )then //判断CapsLock是否按下
   begin
   //判断Shift 是否按下
   if ((i and _KeyPressMask)=_KeyPressMask) then
   begin
   if (wparam<65) then //判断是字母键还是数字键
   begin
   c:=chr(wparam-16);
   end
   else
   begin
   c:= chr(wparam+32);
   end;
   end
   else
   begin
   if (wparam<65) then
   begin
   c:=chr(wparam);
   end
   else
   begin
   c:=chr(wparam);
   end;
   end;
   end
   else
   begin
   if ((i and _KeyPressMask)=_KeyPressMask) then
   begin
   if (wparam<65) then
   begin
   c:=chr(wparam-16);
   end
   else
   begin
   c:= chr(wparam);
   end;
   end
   else
   begin
   if (wparam<65) then
   begin
   c:=chr(wparam);
   end
   else
   begin
   c:=chr(wparam+32);
   end;
   end;
   end;
   seek(f,FileSize(f));
   write(f,c); //将捕获的键码存入文件
   end;
  end;

  function EnableHotKeyHook:BOOL;export;
  begin
  Result:=False;
  if hNextHookProc <> 0 then exit;
  hNextHookProc:=SetWindowsHookEx(WH_KEYBOARD,
  KeyboardHookHandler,Hinstance,0);
  Result:=hNextHookProc <>0 
  end;

  function DisableHotKeyHook:BOOL; export;
  begin
   if hNextHookPRoc <> 0 then
   begin
   UnhookWindowshookEx(hNextHookProc);
   hNextHookProc:=0;
   Messagebeep(0);
   Messagebeep(0);
   end;
   Result:=hNextHookPRoc=0;
  end;

  
  procedure HotKeyHookExit;
  begin
   if hNextHookProc <> 0 then DisableHotKeyHook;
   close(f); //关闭文件并自动解除挂钩
   ExitProc:=procSaveExit;
  end;
  end.

  * 将程序编译后生成一个名为HK.DLL的动态链接库文件并存入“c:”目录下。


   2.创建调用DLL的EXE程序HOOK.EXE

  * 选择FILE菜单中的NEW选项,在New Items窗口中,选择Application选项。在窗体Form中,加入两个按键,一个定义为挂钩,另一个定义为解脱,同时加入一个文本框以提示挂钩的设置状况。将Unit1存为“c:.pas”,其相应的代码如下:

  

  unit hk;

  interface

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

  type
   TForm1 = class(TForm)
   Button1: TButton;
   Button2: TButton;
   Edit1: TEdit;
   procedure Button1Click(Sender: TObject);
   procedure Button2Click(Sender: TObject);
  
   private
   { Private declarations }

   public
   { Public declarations }

   end;

  var
   Form1: TForm1;

  implementation

  {$R *.DFM}

  function EnableHotKeyHook : BOOL;external 'HK.dll';
  //声明HOOK . DLL中的两函数
  function DisableHotKeyHook :BOOL;external 'HK.dll';

  procedure TForm1.Button1Click(Sender: TObject);
  begin
   if EnableHotKeyHook() then
   begin
   edit1.text :='设置挂钩'
   end
  end;

  procedure TForm1.Button2Click(Sender: TObject);
  begin
   if DisableHotKeyHook() then
   begin
   edit1.Text :='挂钩解脱'
   end
  end;
  end.

  * 选取Views菜单中的Project Source,将Project1存为“c:.dpr”,其代码如下:

  

  program hook;
  uses
   Forms,
   hk in 'hk.pas' {Form1};
  {$R *.RES}

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

  * 编译生成HOOK.EXE 程序并存入“c:”目录下。预先用“记事本”在“c:”目录下建立CODE.TXT文件,运行HOOK程序并单击“挂钩”键,文本框提示“设置系统挂钩”,这时启动写字板等应用程序,所键入的字母和数字将被记录在CODE.TXT文件中。

  单击“解脱”键,文本框显示“挂钩解脱”,程序将停止对键盘的捕获。

  点击示意图

  三. 结束语

  将上述例子稍加改动,就可为系统安装其他类型的挂钩,同时为了增强程序的隐蔽性,可利用DELPHI中丰富的控件,将上述程序运行后,只需在屏幕右下部时钟处显示一个图标,就可以跟踪键盘等系统部件的工作状况了。

==================================================================

在许多系统中,出于安全或其它原因,常常要求随时对键盘进行监控,一个专
业的监控程序必须具备两点,一是实时;二是作为指示图标运行。实际应用中把利
用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:.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中正常运行。

----------------------------------------------
秋风清 秋月明,几上闲琴黯生尘。
夜来忽忆少年事,陌上柳绿草青青。
相逢一笑言未语,携手相看自倾心。
二十二载云烟里,云含风韵烟含情。
风尘渺落风霜浸,但闻长空雁长鸣。
姿容如花颜如玉,只在平生梦里寻。
秋风清 秋月明,秋灯秋雨愁煞人。
天涯路远人何在,午夜梦回空扰惊。
作者:
男 hbqckzj (醉里寻欢) ★☆☆☆☆ -
盒子活跃会员
2003/12/13 8:03:11
15楼: 用DELPHI编制钩子函数


Windows消息管理机构提供了能使应用程序访问控制消息流μ

'c4所谓的钩子(HOOK)机制。钩子有多种,分别用于捕获某一特定类型或某一范围的消息。如:键盘消息,鼠标消息等。我们这里仅以键盘钩子的使用为例,讨论在DELPHI下怎样编写DLL程序和怎样在自己的程序中安装使用键盘钩子函数,并讨论了不同程序使用同一DLL文件时怎样共享数据。

一、 钩子过滤函数的编写说明

由于钩子过滤函数必须在独立的模块中,也就是说我们必须首先生成一个DLL框架,然后再在其中加入钩子函数代码以及其他相关函数代码。我们这里以键盘钩子过滤函数的编写为例来说明。具体步骤如下:

1、先生成一个DLL框架

2、编写自己的键盘钩子过滤函数

钩子过滤函数必须是回调函数,其函数的声明为:

function KeyHookProc(

iCode:Integer;

wParam:WPARAM;

lParam:LPARAM ) : LRESULT; stdcall ;export ;

在生成的DLL框架中加入自己的键盘钩子处理函数处理键盘消息。

代码如下:…

if(iCode>=0) then begin

Result:=0; //初始化返回值

// 在这里加入自己的代码

end else

begin

Result:=CallNextHook(hOldKeyHook

iCode

wParam

lParam);

// hOldKeyHook是保存的原键盘过滤函数

end;

3、 安装键盘钩子过滤函数

为安装一个钩子键盘过滤函数应调用SetWindowsHookEx函数(适用于Windows3.0的SetWindowsHook钩子安装函数现在已经废弃不用)。该函数的原形如下:

HHOOK SetWindowsHookEx(

int idHook

// 安装的钩子类型

HOOKPROC lpfn

// 钩子过滤函数地址

HINSTANCE hMod

// 任务句柄

DWORD dwThreadId // 钩子用于的目的

);

需要说明的是:通常应该调用MakeProcInstance函数以获取一个输出函数的前导码的入口地址,再将此地址作为SetWindowsHookEx的第二个参数lpfn。但由于Delphi提供了"灵巧调用(smart callback)",使得MakeProcInstance可以省去,而直接将钩子过滤函数名用作入口地址。

这样当应用程序用GetMessage或PeekMessage函数从消息队列中读消息或有按键消息(WM_KEYDOWN或WM_KEYUP)要处理时,系统就要调用钩子过滤函数KeyHookProc处理键盘消息。

4、 卸载钩子过滤函数。

当钩子函数不再需要时,应调用UnHookWindowsHookProc卸载安装的钩子以释放系统资源。

完整的程序清单如下

Library KEYHOOK;

uses Windows;

const BUFFER_SIZE=16*1024;

const HOOK_MEM_FILENAME='SAMPLE KEY_HOOK_MEM_FILE';

const HOOK_MUTEX_NAME ='SAMPLE KEY_HOOK_MUTEX_NAME';

type

TShared=record

Keys : array[0..BUFFER_SIZE] of Char;

KeyCount : Integer;

end;

PShared=^TShared;

var

MemFile

HookMutex : THandle;

hOldKeyHook : HHook;

ProcSaveExit : Pointer;

Shared : PShared;


//键盘钩子过滤函数

function KeyHookProc(iCode: Integer; wParam: WPARAM ; lParam: LPARAM):LRESULT

; stdcall; export;

const KeyPressMask = $80000000;

begin

if iCode < 0 then

Result := CallNextHookEx(hOldKeyHook

iCode

wParam

lParam)

else begin

if ((lParam and KeyPressMask)= 0) then // 键按下

begin

Shared^.Keys[Shared^.KeyCount]:=Char(wParam and $00ff);

Inc(Shared^.KeyCount);

if Shared^.KeyCount>=BUFFER_SIZE-1 then Shared^.KeyCount:=0;

end;

iCode:=-1;

Result := CallNextHookEx(hOldKeyHook

iCode

wParam

lParam);

end;

end;


// 设置钩子过滤函数

function EnableKeyHook : BOOL ; export;

begin

Shared^.KeyCount:=0; //初始化键盘指针

if hOldKeyHook=0 then begin

hOldKeyHook := SetWindowsHookEx(WH_KEYBOARD


KeyHookProc


HInstance


0);

end;

Result := (hOldKeyHook <> 0);

end;


//撤消钩子过滤函数

function DisableKeyHook: BOOL ; export;

begin

if hOldKeyHook<> 0 then

begin

UnHookWindowsHookEx(hOldKeyHook); // 解除 Keyboard Hook

hOldKeyHook:= 0;

Shared^.KeyCount:=0;

end;

Result := (hOldKeyHook = 0);

end;


//取得键盘缓冲区中击键的个数

function GetKeyCount :Integer ; export;

begin

Result:=Shared^.KeyCount;

end;


//取得键盘缓冲区的键

function GetKey(index:Integer) : Char ; export;

begin

Result:=Shared^.Keys[index];

end;


//清空键盘缓冲区

procedure ClearKeyString ; export;

begin

Shared^.KeyCount:=0;

end;


//DLL的退出处理过程

procedure KeyHookExit; far;

begin

if hOldKeyHook <> 0 then DisableKeyHook;

UnMapViewOfFile(Shared); // 释放内存映象文件

CloseHandle(MemFile); // 关闭映象文件

ExitProc := ProcSaveExit;

end;


exports // 定义输出函数

EnableKeyHook


DisableKeyHook


GetKeyCount


ClearKeyString


GetKey;


begin

// DLL 初始化部分

HookMutex:=CreateMutex(nil

True

HOOK_MUTEX_NAME);

// 通过建立内存映象文件以共享内存

MemFile:=OpenFileMapping(FILE_MAP_WRITE

False


HOOK_MEM_FILENAME);

if MemFile=0 then

MemFile:=CreateFileMapping($FFFFFFFF

nil

PAGE_READWRITE

0


SizeOf(TShared)

HOOK_MEM_FILENAME);

Shared:=MapViewOfFile(MemFile

File_MAP_WRITE

0

0

0);

ReleaseMutex(HookMutex);

CloseHandle(HookMutex);

ProcSaveExit := ExitProc; // 保存DLL的ExitProc

ExitProc := @KeyHookExit; // 设置DLL新的ExitProc

end.

// 源代码结束


二、 在自己的程序中使用编制好的键盘钩子过滤函数。

钩子函数编制好后,使用起来其实很简单:首先调用SetWindowsHookEx安装自己的钩子过滤函数,同时保存原先的钩子过滤函数地址。这时钩子函数就开始起作用了,它将按照你的要求处理键盘消息。程序运行完毕或不再需要监视键盘消息时,调用UnHookWindowsHookProc函数卸载所安装的钩子函数,同时恢复原来的钩子过滤函数地址。

下面就是使用在以上编制的钩子函数的例子:

unit Unit1;

interface

uses

Windows

Messages

SysUtils

Classes

Graphics

Controls

Forms

Dialogs


StdCtrls

ExtCtrls;

type

TForm1 = class(TForm)

Memo1: TMemo;

Panel1: TPanel;

bSetHook: TButton;

bCancelHook: TButton;

bReadKeys: TButton;

bClearKeys: TButton;

Panel2: TPanel;

procedure bSetHookClick(Sender: TObject);

procedure bCancelHookClick(Sender: TObject);

procedure bReadKeysClick(Sender: TObject);

procedure bClearKeysClick(Sender: TObject);

end;

var Form1: TForm1;


implementation

{$R *.DFM}

function EnableKeyHook : BOOL ; external 'KEYHOOK.DLL';

function DisableKeyHook : BOOL ; external 'KEYHOOK.DLL';

function GetKeyCount : Integer ; external 'KEYHOOK.DLL';

function GetKey(idx:Integer) : Char ; external 'KEYHOOK.DLL';

procedure ClearKeyString ; external 'KEYHOOK.DLL';


procedure TForm1.bSetHookClick(Sender: TObject); // 设置键盘钩子

begin

EnableKeyHook;

bSetHook.Enabled :=False;

bCancelHook.Enabled:=True;

bReadKeys.Enabled :=True;

bClearKeys.Enabled :=True;

Panel2.Caption:=' 键盘钩子已经设置';

end;


procedure TForm1.bCancelHookClick(Sender: TObject); // 卸载键盘钩子

begin

DisableKeyHook;

bSetHook.Enabled :=True;

bCancelHook.Enabled:=False;

bReadKeys.Enabled :=False;

bClearKeys.Enabled :=False;

Panel2.Caption:=' 键盘钩子没有设置';

end;


procedure TForm1.bReadKeysClick(Sender: TObject); // 取得击键的历史记录

var i:Integer;

begin

Memo1.Lines.Clear; // 在Memo1中显示击键历史记录

for i:=0 to GetKeyCount-1 do

Memo1.Text:=Memo1.Text+GetKey(i);

end;


procedure TForm1.bClearKeysClick(Sender: TObject); // 清除击键历史记录

begin

Memo1.Clear;

ClearKeyString;

end;


end.

// 源代码结束


三、 Windows95下DLL中实现共享内存

在上面的钩子函数所在的DLL文件中,需要使用共享内存,即,所有击键的记录存储在同一个数据段中。为什么要这样做呢?这是因为Windows95的DLL调用方法与Windows3.X的方法不同。每个进(线)程在登录某动态连接库时都会为该动态连接库传入一个新的实例句柄(即DLL数据段的句柄)。这使得DLL各个实例之间互不干扰,但是这对那些所有DLL实例共享一组变量带来一些困难。为了解决这个问题,我们在这儿通过建立内存映射文件的方法来解决。即使用Windows的OpenFileMapping、CreateFileMapping和

MapViewOfFile三个函数来实现。使用方法如下:



MemFile是THandle类型,Shared是指针类型,HOOK_MEM_FILENAME是一常量串



MemFile:=OpenFileMapping(FILE_MAP_WRITE

False


HOOK_MEM_FILENAME); //打开内存映射文件

if MemFile=0 then //打开失败则创建内存映射文件

MemFile:=CreateFileMapping($FFFFFFFF

nil

PAGE_READWRITE

0


SizeOf(TShared)

HOOK_MEM_FILENAME);

//映射文件到变量

Shared:=MapViewOfFile(MemFile

File_MAP_WRITE

0

0

0);


到此为止,你已经知道用Delphi编制钩子函数有多么容易。最后不得不提醒大家:钩子函数虽然功能比较强,但如果使用不当将会严重影响系统的效率,所以要尽量避免使用系统钩子。非要使用不可时也应该格外小心,应使之尽可能小地影响系统的运行。

----------------------------------------------
秋风清 秋月明,几上闲琴黯生尘。
夜来忽忆少年事,陌上柳绿草青青。
相逢一笑言未语,携手相看自倾心。
二十二载云烟里,云含风韵烟含情。
风尘渺落风霜浸,但闻长空雁长鸣。
姿容如花颜如玉,只在平生梦里寻。
秋风清 秋月明,秋灯秋雨愁煞人。
天涯路远人何在,午夜梦回空扰惊。
作者:
男 hbqckzj (醉里寻欢) ★☆☆☆☆ -
盒子活跃会员
2003/12/13 8:04:39
16楼: 建立键盘鼠标动作记录与回放 

内容:很多的教学软件或系统监视软件可以自动记录回放用户的输入文字或点击按钮等操作操作,这个功能的实现是使用 
了Windows的Hook函数。 
Windows提供API函数SetwindowsHookEx来建立一个Hook,通过这个函数可以将一个程序添加到Hook链中监视Windows 
消息,函数语法为: 
SetWindowsHookEx(idHook: Integer; lpfn: TFNHookProc; hmod: HINST; dwThreadId: DWORD) 
其中参数idHook指定建立的监视函数类型。通过Windows MSDN帮助可以看到,SetwindowsHookEx函数提供15种不同 
的消息监视类型,在这里我们将使用WH_JOURNALRECORD和WH_JOURNALPLAYBACK来监视键盘和鼠标操作。参数lpfn指定消 
息函数,在相应的消息产生后,系统会调用该函数并将消息值传递给该函数供处理。函数的一般形式为: 
Hookproc (code: Integer; wparam: WPARAM; lparam: LPARAM): LRESULT stdcall; 
其中code为系统指示标记,wParam和lParam为附加参数,根据不同的消息监视类型而不同。只要在程序中建立这样 
一个函数再通过SetwindowsHookEx函数将它加入到消息监视链中就可以处理消息了。 
在不需要监视系统消息时需要调用提供UnHookWindowsHookEx来解除对消息的监视。 
WH_JOURNALRECORD和WH_JOURNALPLAYBACK类型是两种相反的Hook类型,前者获得鼠标、键盘动作消息,后者回放鼠 
标键盘消息。所以在程序中我们需要建立两个消息函数,一个用于纪录鼠标键盘操作并保存到一个数组中,另一个用于 
将保存的操作返给系统回放。 
下面来建立程序,在Delphi中建立一个工程,在Form1上添加3个按钮用于程序操作。另外再添加一个按钮控件和一 
个Edit控件用于验证操作。 
下面是Form1的全部代码 

unit Unit1; 

interface 

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

type 
TForm1 = class(TForm) 
Button1: TButton; 
Button2: TButton; 
Button3: TButton; 
Edit1: TEdit; 
Button4: TButton; 
procedure FormCreate(Sender: TObject); 
procedure Button1Click(Sender: TObject); 
procedure Button2Click(Sender: TObject); 
procedure Button3Click(Sender: TObject); 
private 
{ Private declarations } 
public 
{ Public declarations } 
end; 

var 
Form1: TForm1; 

EventArr:array[0..1000]of EVENTMSG; 
EventLog:Integer; 
PlayLog:Integer; 
hHook,hPlay:Integer; 
recOK:Integer; 
canPlay:Integer; 
bDelay:Bool; 
implementation 

{$R *.DFM} 
Function PlayProc(iCode:Integer;wParam:wParam;lParam:lParam):LRESULT;stdcall; 
begin 
canPlay:=1; 
Result:=0; 

if iCode < 0 then //必须将消息传递到消息链的下一个接受单元 
Result := CallNextHookEx(hPlay,iCode,wParam,lParam) 
else if iCode = HC_SYSMODALON then 
canPlay:=0 
else if iCode = HC_SYSMODALOFF then 
canPlay:=1 
else if ((canPlay =1 )and(iCode=HC_GETNEXT)) then begin 
if bDelay then begin 
bDelay:=False; 
Result:=50; 
end; 
pEventMSG(lParam)^:=EventArr[PlayLog]; 
end 
else if ((canPlay = 1)and(iCode = HC_SKIP))then begin 
bDelay := True; 
PlayLog:=PlayLog+1; 
end; 
if PlayLog>=EventLog then begin 
UNHookWindowsHookEx(hPlay); 
end; 
end; 

function HookProc(iCode:Integer;wParam:wParam;lParam:lParam):LRESULT;stdcall; 
begin 
recOK:=1; 
Result:=0; 

if iCode < 0 then 
Result := CallNextHookEx(hHook,iCode,wParam,lParam) 
else if iCode = HC_SYSMODALON then 
recOK:=0 
else if iCode = HC_SYSMODALOFF then 
recOK:=1 
else if ((recOK>0) and (iCode = HC_ACTION)) then begin 
EventArr[EventLog]:=pEventMSG(lParam)^; 
EventLog:=EventLog+1; 

if EventLog>=1000 then begin 
UnHookWindowsHookEx(hHook); 
end; 
end; 
end; 

procedure TForm1.FormCreate(Sender: TObject); 
begin 
Button1.Caption:='纪录'; 
Button2.Caption:='停止'; 
Button3.Caption:='回放'; 
Button4.Caption:='范例'; 
Button2.Enabled:=False; 
Button3.Enabled:=False; 
end; 

procedure TForm1.Button1Click(Sender: TObject); 
begin 
EventLog:=0; 
//建立键盘鼠标操作消息纪录链 
hHook:=SetwindowsHookEx(WH_JOURNALRECORD,HookProc,HInstance,0); 
Button2.Enabled:=True; 
Button1.Enabled:=False; 
end; 

procedure TForm1.Button2Click(Sender: TObject); 
begin 
UnHookWindowsHookEx(hHook); 
hHook:=0; 

Button1.Enabled:=True; 
Button2.Enabled:=False; 
Button3.Enabled:=True; 
end; 

procedure TForm1.Button3Click(Sender: TObject); 
begin 
PlayLog:=0; 
//建立键盘鼠标操作消息纪录回放链 
hPlay:=SetwindowsHookEx(WH_JOURNALPLAYBACK,PlayProc, 
HInstance,0); 

Button3.Enabled:=False; 
end; 

end. 

代码添加完毕后,运行程序,点击“纪录”按钮开始纪录操作,这时你可以在文本控件中输入一些文字或者点击 
“范例”按钮,然后点击“停止”按钮停止纪录,再点击“回放”按钮就可以讲先前所做的操作回放。 
在上面的程序中,HookProc是纪录操作的消息函数,每当有鼠标键盘消息发生时,系统都会调用该函数,消息信 
息就保存在地址lParam中,我们可以讲消息保存在一个数组中。PlayProc是消息回放函数,当系统可以执行消息回放 
时调用该函数,程序就将先前纪录的消息值返回到lParam指向的区域中,系统就会执行该消息,从而实现了消息回放。 

----------------------------------------------
秋风清 秋月明,几上闲琴黯生尘。
夜来忽忆少年事,陌上柳绿草青青。
相逢一笑言未语,携手相看自倾心。
二十二载云烟里,云含风韵烟含情。
风尘渺落风霜浸,但闻长空雁长鸣。
姿容如花颜如玉,只在平生梦里寻。
秋风清 秋月明,秋灯秋雨愁煞人。
天涯路远人何在,午夜梦回空扰惊。
作者:
男 hbqckzj (醉里寻欢) ★☆☆☆☆ -
盒子活跃会员
2003/12/13 8:11:06
17楼: 这个是上次虚渺兄弟贴给我的,我改了一下,可以屏蔽左右Windows键了,完整的贴出来:

以下例程可以实现禁止用户用ALT+TAB或ALT+ESCAPE键切换程序,并且可以屏蔽左右windows键:

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    btn1: TButton;
    btn2: TButton;
  procedure Button1Click(Sender: TObject);
  procedure Button2Click(Sender: TObject);
  procedure FormDestroy(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
  public
      { Public declarations }end;
{按键消息的结构,Delphi中也没有,自己定义吧。这也就我为什么说用C写
这样的程序更好的原因之一。还必须注意的是这个结构在Windows NT 4 sp3以上系统
中才能使用}
tagKBDLLHOOKSTRUCT = packed record
vkCode: DWORD;//虚拟键值
scanCode: DWORD;//扫描码值(没有用过,我也不懂^_^)
{一些扩展标志,这个值比较麻烦,MSDN上说得也不太明白,但是
根据这个程序,这个标志值的第六位数(二进制)为1时ALT键按下为0相反。}
flags: DWORD;
time: DWORD;//消息时间戳
dwExtraInfo: DWORD;//和消息相关的扩展信息
end;
KBDLLHOOKSTRUCT = tagKBDLLHOOKSTRUCT;
PKBDLLHOOKSTRUCT = ^KBDLLHOOKSTRUCT;

//这个是低级键盘钩子的索引值,Delphi中没有,必须自己定义
const WH_KEYBOARD_LL = 13;
//定义一个常量好和上面哪个结构中的flags比较而得出ALT键是否按下
const LLKHF_ALTDOWN = $20;

var
  Form1: TForm1;
  hhkLowLevelKybd: HHOOK;
implementation
{$R *.dfm}

{
功能:低级键盘钩子的回调函数,在里面过滤消息
参数:nCode 是Hook的标志
WParam 表示消息的类型
LParam 是一个指向我们在上面定义的哪个结构KBDLLHOOKSTRUCT的指针
返回值:如果不是0的话windows就把这个消息丢掉,程序就不会再收到这个消息了。
}
function LowLevelKeyboardProc(nCode: Integer;
WParam: WPARAM;LParam: LPARAM):LRESULT; stdcall;
var
  fEatKeystroke: BOOL;
  p: PKBDLLHOOKSTRUCT;
begin
  Result := 0;
  fEatKeystroke := FALSE;
  p := PKBDLLHOOKSTRUCT (lParam);
  //nCode值为HC_ACTION时表示WParam和LParam参数包涵了按键消息
  if (nCode = HC_ACTION) then
  begin
    //拦截按键消息并测试是否是左windows、右windows、Ctrl+Esc、Alt+Tab、和Alt+Esc功能键。
    case wParam of
      WM_KEYDOWN,
      WM_SYSKEYDOWN,
      WM_KEYUP,
      WM_SYSKEYUP:
      fEatKeystroke :=
      (p.vkCode = VK_rwin) or (p.vkCode = VK_lwin) or
      ((p.vkCode = VK_TAB) and ((p.flags and LLKHF_ALTDOWN) <> 0)) or
      ((p.vkCode = VK_ESCAPE) and ((p.flags and LLKHF_ALTDOWN) <> 0)) or
      ((p.vkCode = VK_ESCAPE) and ((GetKeyState(VK_CONTROL) and $8000) <> 0));
     end;
  end;
  if fEatKeystroke = True then
    Result := 1;
  if nCode <> 0 then
    Result := CallNextHookEx(0, nCode, wParam, lParam);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
//设置低级键盘钩子
  if hhkLowLevelKybd = 0 then
  begin
    hhkLowLevelKybd := SetWindowsHookExW(WH_KEYBOARD_LL,
    LowLevelKeyboardProc, Hinstance, 0);
    if hhkLowLevelKybd <> 0 then
      MessageBox(Handle, '低级键盘钩子设置成功!', '提示', MB_OK)
    else
      MessageBox(Handle, '低级键盘钩子设置失败!', '提示', MB_OK);
    end
  else
    MessageBox(Handle, '低级键盘钩子已设置!', '提示', MB_OK);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  //卸载低级键盘钩子
  if hhkLowLevelKybd <> 0 then
    if UnhookWindowsHookEx(hhkLowLevelKybd) <> False then
    begin
      MessageBox(Handle, '低级键盘钩子卸载成功!', '提示', MB_OK);
      hhkLowLevelKybd := 0;
    end
    else
      MessageBox(Handle, '低级键盘钩子卸载失败!', '提示', MB_OK)
  else
    MessageBox(Handle, '没有发现低级键盘钩子!', '提示', MB_OK);
end;


procedure TForm1.FormDestroy(Sender: TObject);
begin
//在Form关闭的时候检测,如果没有卸载钩子就卸载之
if hhkLowLevelKybd <> 0 then
  UnhookWindowsHookEx(hhkLowLevelKybd);
end;


上面例程在WINXP和DELPHI 7.0中使用通过。
----------------------------------------------
秋风清 秋月明,几上闲琴黯生尘。
夜来忽忆少年事,陌上柳绿草青青。
相逢一笑言未语,携手相看自倾心。
二十二载云烟里,云含风韵烟含情。
风尘渺落风霜浸,但闻长空雁长鸣。
姿容如花颜如玉,只在平生梦里寻。
秋风清 秋月明,秋灯秋雨愁煞人。
天涯路远人何在,午夜梦回空扰惊。
作者:
男 hbqckzj (醉里寻欢) ★☆☆☆☆ -
盒子活跃会员
2003/12/13 8:17:03
18楼:    
 
 关于钩子函数的讲解 
 
 
这是win32汇编语言中关于钩子函数的讲解,是丛windows的底层讲的,对于c,vb,delphi以至于易语言,原理都相同,只是语法的问题。
   

   WINDOWS钩子函数 

  
  --------------------------------------------------------------------------------
  本课中我们将要学习WINDOWS钩子函数的使用方法。WINDOWS钩子函数的功能非常强大,有了它您可以探测其它进程并且改变其它进程的行为。 
  理论:
  WINDOWS的钩子函数可以认为是WINDOWS的主要特性之一。利用它们,您可以捕捉您自己进程或其它进程发生的事件。通过“钩挂”,您可以给WINDOWS一个处理或过滤事件的回调函数,该函数也叫做“钩子函数”,当每次发生您感兴趣的事件时,WINDOWS都将调用该函数。一共有两种类型的钩子:局部的和远程的。 
  局部钩子仅钩挂您自己进程的事件。 
  远程的钩子还可以将钩挂其它进程发生的事件。远程的钩子又有两种: 
  基于线程的 它将捕获其它进程中某一特定线程的事件。简言之,就是可以用来观察其它进程中的某一特定线程将发生的事件。 
  系统范围的 将捕捉系统中所有进程将发生的事件消息。 
  安装钩子函数将会影响系统的性能。监测“系统范围事件”的系统钩子特别明显。因为系统在处理所有的相关事件时都将调用您的钩子函数,这样您的系统将会明显的减慢。所以应谨慎使用,用完后立即卸载。还有,由于您可以预先截获其它进程的消息,所以一旦您的钩子函数出了问题的话必将影响其它的进程。记住:功能强大也意味着使用时要负责任。
  在正确使用钩子函数前,我们先讲解钩子函数的工作原理。当您创建一个钩子时,WINDOWS会先在内存中创建一个数据结构,该数据结构包含了钩子的相关信息,然后把该结构体加到已经存在的钩子链表中去。新的钩子将加到老的前面。当一个事件发生时,如果您安装的是一个局部钩子,您进程中的钩子函数将被调用。如果是一个远程钩子,系统就必须把钩子函数插入到其它进程的地址空间,要做到这一点要求钩子函数必须在一个动态链接库中,所以如果您想要使用远程钩子,就必须把该钩子函数放到动态链接库中去。当然有两个例外:工作日志钩子和工作日志回放钩子。这两个钩子的钩子函数必须在安装钩子的线程中。原因是:这两个钩子是用来监控比较底层的硬件事件的,既然是记录和回放,所有的事件就当然都是有先后次序的。所以如果把回调函数放在DLL中,输入的事件被放在几个线程中记录,所以我们无法保证得到正确的次序。故解决的办法是:把钩子函数放到单个的线程中,譬如安装钩子的线程。
  钩子一共有14种,以下是它们被调用的时机: 
  WH_CALLWNDPROC 当调用SendMessage时 
  WH_CALLWNDPROCRET 当SendMessage的调用返回时 
  WH_GETMESSAGE 当调用GetMessage 或 PeekMessage时 
  WH_KEYBOARD 当调用GetMessage 或 PeekMessage 来从消息队列中查询WM_KEYUP 或 WM_KEYDOWN 消息时 
  WH_MOUSE 当调用GetMessage 或 PeekMessage 来从消息队列中查询鼠标事件消息时 
  WH_HARDWARE 当调用GetMessage 或 PeekMessage 来从消息队列种查询非鼠标、键盘消息时 
  WH_MSGFILTER 当对话框、菜单或滚动条要处理一个消息时。该钩子是局部的。它时为那些有自己的消息处理过程的控件对象设计的。 
  WH_SYSMSGFILTER 和WH_MSGFILTER一样,只不过是系统范围的 
  WH_JOURNALRECORD 当WINDOWS从硬件队列中获得消息时 
  WH_JOURNALPLAYBACK 当一个事件从系统的硬件输入队列中被请求时 
  WH_SHELL 当关于WINDOWS外壳事件发生时,譬如任务条需要重画它的按钮. 
  WH_CBT 当基于计算机的训练(CBT)事件发生时 
  WH_FOREGROUNDIDLE 由WINDOWS自己使用,一般的应用程序很少使用 
  WH_DEBUG 用来给钩子函数除错 
  现在我们知道了一些基本的理论,现在开始讲解如何安装/卸载一个钩子。 
  要安装一个钩子,您可以调用SetWindowHookEx函数。该函数的原型如下: 
  SetWindowsHookEx proto HookType:DWORD, pHookProc:DWORD, hInstance:DWORD, ThreadID:DWORD 
  HookType 是我们上面列出的值之一,譬如: WH_MOUSE, WH_KEYBOARD 
  pHookProc 是钩子函数的地址。如果使用的是远程的钩子,就必须放在一个DLL中,否则放在本身代码中 
  hInstance 钩子函数所在DLL的实例句柄。如果是一个局部的钩子,该值为NULL 
  ThreadID 是您安装该钩子函数后想监控的线程的ID号。该参数可以决定该钩子是局部的还是系统范围的。如果该值为NULL,那么该钩子将被解释成系统范围内的,那它就可以监控所有的进程及它们的线程。如果您指定了您自己进程中的某个线程ID 号,那该钩子是一个局部的钩子。如果该线程ID是另一个进程中某个线程的ID,那该钩子是一个全局的远程钩子。这里有两个特殊情况:WH_JOURNALRECORD 和 WH_JOURNALPLAYBACK总是代表局部的系统范围的钩子,之所以说是局部,是因为它们没有必要放到一个DLL中。WH_SYSMSGFILTER 总是一个系统范围内的远程钩子。其实它和WH_MSGFILTER钩子类似,如果把参数ThreadID设成0的话,它们就完全一样了。 
  如果该函数调用成功的话,将在eax中返回钩子的句柄,否则返回NULL。您必须保存该句柄,因为后面我们还要它来卸载钩子。
  要卸载一个钩子时调用UnhookWidowHookEx函数,该函数仅有一个参数,就是欲卸载的钩子的句柄。如果调用成功的话,在eax中返回非0值,否则返回NULL。
  现在您知道了如何安装和卸载一个钩子了,接下来我们将看看钩子函数。. 
  只要您安装的钩子的消息事件类型发生,WINDOWS就将调用钩子函数。譬如您安装的钩子是WH_MOUSE类型,那么只要有一个鼠标事件发生时,该钩子函数就会被调用。不管您安装的时那一类型钩子,钩子函数的原型都时是一样的: 
  HookProc proto nCode:DWORD, wParam:DWORD, lParam:DWORD 
   
  nCode 指定是否需要处理该消息 
  wParam 和 lParam 包含该消息的附加消息 
  HookProc 可以看作是一个函数名的占位符。只要函数的原型一致,您可以给该函数取任何名字。至于以上的几个参数及返回值的具体含义各种类型的钩子都不相同。譬如: 
  WH_CALLWNDPROC 
  nCode 只能是HC_ACTION,它代表有一个消息发送给了一个窗口 
  wParam 如果非0,代表正被发送的消息 
  lParam 指向CWPSTRUCT型结构体变量的指针 
  return value: 未使用,返回0 
  WH_MOUSE 
  nCode 为HC_ACTION 或 HC_NOREMOVE 
  wParam 包含鼠标的事件消息 
  lParam 指向MOUSEHOOKSTRUCT型结构体变量的指针 
  return value: 如果不处理返回0,否则返回非0值 
  所以您必须查询您的WIN32 API 指南来得到不同类型的钩子的参数的详细定义以及它们返回值的意义。这里还有一个问题需要注意:所有的钩子都串在一个链表上,最近加入的钩子放在链表的头部。当一个事件发生时,WINDOWS将按照从链表头到链表尾调用的顺序。所以您的钩子函数有责任把消息传到下一个链中的钩子函数。当然您可以不这样做,但是您最好明白这时这么做的原因。在大多数的情况下,最好把消息事件传递下去以便其它的钩子都有机会获得处理这一消息的机会。调用下一个钩子函数可以调用函数CallNextHookEx。该函数的原型如下: 
  CallNextHookEx proto hHook:DWORD, nCode:DWORD, wParam:DWORD, lParam:DWORD 
  hHook 时是您自己的钩子函数的句柄。利用该句柄可以遍历钩子链。 
  nCode, wParam and lParam 您只要把传入的参数简单传给CallNextHookEx即可。 
  请注意:对于远程钩子,钩子函数必须放到DLL中,它们将从DLL中映射到其它的进程空间中去。当WINDOWS映射DLL到其它的进程空间中去时,不会把数据段也进行映射。简言之,所有的进程仅共享DLL的代码,至于数据段,每一个进程都将有其单独的拷贝。这是一个很容易被忽视的问题。您可能想当然的以为,在DLL中保存的值可以在所有映射该DLL的进程之间共享。在通常情况下,由于每一个映射该DLL的进程都有自己的数据段,所以在大多数的情况下您的程序运行得都不错。但是钩子函数却不是如此。对于钩子函数来说,要求DLL的数据段对所有的进程也必须相同。这样您就必须把数据段设成共享的,这可以通过在链接开关中指定段的属性来实现。在MASM中您可以这么做: 
  /SECTION:<section name>, S
  已初期化的段名是.data,未初始化的段名是.bss。`加入您想要写一个包含钩子函数的DLL,而且想使它的未初始化的数据段在所有进程间共享,您必须这么做: 
  link /section:.bss,S /DLL /SUBSYSTEM:WINDOWS ..........
  S 代表该段是共享段。 
  例子:
  一共有两个模块:一个是GUI部分,另一个是安装和卸载钩子的DLL。 
  ;--------------------------------------------- 主程序的源代码部分-------------------------------------- 
  .386 
  .model flat,stdcall 
  option casemap:none 
  include \masm32\include\windows.inc 
  include \masm32\include\user32.inc 
  include \masm32\include\kernel32.inc 
  include mousehook.inc 
  includelib mousehook.lib 
  includelib \masm32\lib\user32.lib 
  includelib \masm32\lib\kernel32.lib 

  wsprintfA proto C :DWORD,:DWORD,:VARARG 
  wsprintf TEXTEQU <wsprintfA> 

  .const 
  IDD_MAINDLG equ 101 
  IDC_CLASSNAME equ 1000 
  IDC_HANDLE equ 1001 
  IDC_WNDPROC equ 1002 
  IDC_HOOK equ 1004 
  IDC_EXIT equ 1005 
  WM_MOUSEHOOK equ WM_USER+6 

  DlgFunc PROTO :DWORD,:DWORD,:DWORD,:DWORD 

  .data 
  HookFlag dd FALSE 
  HookText db "&Hook",0 
  UnhookText db "&Unhook",0 
  template db "%lx",0 

  .data? 
  hInstance dd ? 
  hHook dd ? 
  .code 
  start: 
   invoke GetModuleHandle,NULL 
   mov hInstance,eax 
   invoke DialogBoxParam,hInstance,IDD_MAINDLG,NULL,addr DlgFunc,NULL 
   invoke ExitProcess,NULL 

  DlgFunc proc hDlg:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD 
   LOCAL hLib:DWORD 
   LOCAL buffer[128]:byte 
   LOCAL buffer1[128]:byte 
   LOCAL rect:RECT 
   .if uMsg==WM_CLOSE 
   .if HookFlag==TRUE 
   invoke UninstallHook 
   .endif 
   invoke EndDialog,hDlg,NULL 
   .elseif uMsg==WM_INITDIALOG 
   invoke GetWindowRect,hDlg,addr rect 
   invoke SetWindowPos, hDlg, HWND_TOPMOST, rect.left, rect.top, rect.right, rect.bottom, SWP_SHOWWINDOW 
   .elseif uMsg==WM_MOUSEHOOK 
   invoke GetDlgItemText,hDlg,IDC_HANDLE,addr buffer1,128 
   invoke wsprintf,addr buffer,addr template,wParam 
   invoke lstrcmpi,addr buffer,addr buffer1 
   .if eax!=0 
   invoke SetDlgItemText,hDlg,IDC_HANDLE,addr buffer 
   .endif 
   invoke GetDlgItemText,hDlg,IDC_CLASSNAME,addr buffer1,128 
   invoke GetClassName,wParam,addr buffer,128 
   invoke lstrcmpi,addr buffer,addr buffer1 
   .if eax!=0 
   invoke SetDlgItemText,hDlg,IDC_CLASSNAME,addr buffer 
   .endif 
   invoke GetDlgItemText,hDlg,IDC_WNDPROC,addr buffer1,128 
   invoke GetClassLong,wParam,GCL_WNDPROC 
   invoke wsprintf,addr buffer,addr template,eax 
   invoke lstrcmpi,addr buffer,addr buffer1 
   .if eax!=0 
   invoke SetDlgItemText,hDlg,IDC_WNDPROC,addr buffer 
   .endif 
   .elseif uMsg==WM_COMMAND 
   .if lParam!=0 
   mov eax,wParam 
   mov edx,eax 
   shr edx,16 
   .if dx==BN_CLICKED 
   .if ax==IDC_EXIT 
   invoke SendMessage,hDlg,WM_CLOSE,0,0 
   .else 
   .if HookFlag==FALSE 
   invoke InstallHook,hDlg 
   .if eax!=NULL 
   mov HookFlag,TRUE 
   invoke SetDlgItemText,hDlg,IDC_HOOK,addr UnhookText 
   .endif 
   .else 
   invoke UninstallHook 
   invoke SetDlgItemText,hDlg,IDC_HOOK,addr HookText 
   mov HookFlag,FALSE 
   invoke SetDlgItemText,hDlg,IDC_CLASSNAME,NULL 
   invoke SetDlgItemText,hDlg,IDC_HANDLE,NULL 
   invoke SetDlgItemText,hDlg,IDC_WNDPROC,NULL 
   .endif 
   .endif 
   .endif 
   .endif 
   .else 
   mov eax,FALSE 
   ret 
   .endif 
   mov eax,TRUE 
   ret 
  DlgFunc endp 

  end start 

  ;----------------------------------------------------- DLL的源代码部分 -------------------------------------- 
  .386 
  .model flat,stdcall 
  option casemap:none 
  include \masm32\include\windows.inc 
  include \masm32\include\kernel32.inc 
  includelib \masm32\lib\kernel32.lib 
  include \masm32\include\user32.inc 
  includelib \masm32\lib\user32.lib 

  .const 
  WM_MOUSEHOOK equ WM_USER+6 

  .data 
  hInstance dd 0 

  .data? 
  hHook dd ? 
  hWnd dd ? 

  .code 
  DllEntry proc hInst:HINSTANCE, reason:DWORD, reserved1:DWORD 
   .if reason==DLL_PROCESS_ATTACH 
   push hInst 
   pop hInstance 
   .endif 
   mov eax,TRUE 
   ret 
  DllEntry Endp 

  MouseProc proc nCode:DWORD,wParam:DWORD,lParam:DWORD 
   invoke CallNextHookEx,hHook,nCode,wParam,lParam 
   mov edx,lParam 
   assume edx:PTR MOUSEHOOKSTRUCT 
   invoke WindowFromPoint,[edx].pt.x,[edx].pt.y 
   invoke PostMessage,hWnd,WM_MOUSEHOOK,eax,0 
   assume edx:nothing 
   xor eax,eax 
   ret 
  MouseProc endp 

  InstallHook proc hwnd:DWORD 
   push hwnd 
   pop hWnd 
   invoke SetWindowsHookEx,WH_MOUSE,addr MouseProc,hInstance,NULL 
   mov hHook,eax 
   ret 
  InstallHook endp 

  UninstallHook proc 
   invoke UnhookWindowsHookEx,hHook 
   ret 
  UninstallHook endp 

  End DllEntry 

  ;---------------------------------------------- DLL的Makefile文件 ---------------------------------------------- 

  NAME=mousehook 
  $(NAME).dll: $(NAME).obj 
   Link /SECTION:.bss,S /DLL /DEF:$(NAME).def /SUBSYSTEM:WINDOWS /LIBPATH:c:\masm\lib $(NAME).obj 
  $(NAME).obj: $(NAME).asm 
   ml /c /coff /Cp $(NAME).asm 
   

  分析:
  该应用程序的主窗口中包括三个编辑控件,它们将分别显示当前鼠标光标所在位置的窗口类名、窗口句柄和窗口过程的地址。还有两个按钮:“Hook”和“Eixt”。当您按下Hook时,应用程序将钩挂鼠标输入的事件消息,该按钮的文本将变成“Unhook”。当您把鼠标关标滑过一个窗口时,该窗口的有关消息将显示在主窗口中。当您按下“Unhook”时,应用程序将卸载钩子。 主窗口使用一个对话框来作为它的主窗口。它自定义了一个消息WM_MOUSEHOOK,用来在主窗口和DLL之间传递消息。当主窗口接收到该消息时,wParam中包含了光标所在位置的窗口的句柄。当然这是我们做的安排。我这么做只是为了方便。您可以使用您自己的方法在主应用程序和DLL之间进行通讯。 
   .if HookFlag==FALSE 
   invoke InstallHook,hDlg 
   .if eax!=NULL 
   mov HookFlag,TRUE 
   invoke SetDlgItemText,hDlg,IDC_HOOK,addr UnhookText 
   .endif 

  该应用程序有一个全局变量,HookFlag,它用来监视钩子的状态。如果安装来钩子它就是TRUE,否则是FALSE。 当用户按下Hook按钮时,应用程序检查钩子是否已经安装。如果还没有的话,它将调用DLL中引出的函数InstallHook来安装它。注意我们把主对话框的句柄传递给了DLL,这样这个钩子DLL就可以把WM_MOUSEHOOK消息传递给正确的窗口了。当应用程序加载时,钩子DLL也同时加载。时机上当主程序一旦加载到内存中后,DLL就立即加载。DLL的入口点函数载主程序的第一条语句执行前就前执行了。所以当主程序执行时,DLL已经初始化好了。我们载入口点处放入如下代码: 

   .if reason==DLL_PROCESS_ATTACH 
   push hInst 
   pop hInstance 
   .endif 

  该段代码把DLL自己的实例句柄放到一个全局变量中保存。由于入口点函数是在所有函数调用前被执行的,所以hInstance总是有效的。我们把该变量放到.data中,使得每一个进程都有自己一个该变量的值。因为当鼠标光标停在一个窗口上时,钩子DLL被映射进进程的地址空间。加入在DLL缺省加载的地址处已经加载其它的DLL,那钩子DLL将要被映射到其他的地址。hInstance将被更新成其它的值。当用户按下Unhook再按下Hook时,SetWindowsHookEx将被再次调用。这一次,它将把新的地址作为实例句柄。而在例子中这是错误的,DLL装载的地址并没有变。这个钩子将变成一个局部的,您只能钩挂发生在您窗口中的鼠标事件,这是很难让人满意的 。 

  InstallHook proc hwnd:DWORD 
   push hwnd 
   pop hWnd 
   invoke SetWindowsHookEx,WH_MOUSE,addr MouseProc,hInstance,NULL 
   mov hHook,eax 
   ret 
  InstallHook endp 

  InstallHook 函数非常简单。它把传递过来的窗口句柄保存在hWnd中以备后用。接着调用SetWindowsHookEx函数来安装一个鼠标钩子。该函数的返回值放在全局变量hHook中,将来在UnhookWindowsHookEx中还要使用。在调用SetWindowsHookEx后,鼠标钩子就开始工作了。无论什么时候发生了鼠标事件,MouseProc函数都将被调用: 

  MouseProc proc nCode:DWORD,wParam:DWORD,lParam:DWORD 
   invoke CallNextHookEx,hHook,nCode,wParam,lParam 
   mov edx,lParam 
   assume edx:PTR MOUSEHOOKSTRUCT 
   invoke WindowFromPoint,[edx].pt.x,[edx].pt.y 
   invoke PostMessage,hWnd,WM_MOUSEHOOK,eax,0 
   assume edx:nothing 
   xor eax,eax 
   ret 
  MouseProc endp 

  钩子函数首先调用CallNextHookEx函数让其它的钩子处理该鼠标事件。然后,调用WindowFromPoint函数来得到给定屏幕坐标位置处的窗口句柄。注意:我们用lParam指向的MOUSEHOOKSTRUCT型结构体变量中的POINT成员变量作为当前的鼠标位置。在我们调用PostMessage函数把WM_MOUSEHOOK消息发送到主程序。您必须记住的一件事是:在钩子函数中不要使用SendMessage函数,它会引起死锁。MOUSEHOOKSTRUCT的定义如下: 

  MOUSEHOOKSTRUCT STRUCT DWORD 
   pt POINT <> 
   hwnd DWORD ? 
   wHitTestCode DWORD ? 
   dwExtraInfo DWORD ? 
  MOUSEHOOKSTRUCT ENDS 
   

  pt 是当前鼠标所在的屏幕位置。 
  hwnd 是将接收鼠标消息的窗口的句柄。通常它是鼠标所在处的窗口,但是如果窗口调用了SetCapture,鼠标的输入将到向到这个窗口。因我们不用该成员变量而是用WindowFromPoint函数。 
  wHitTestCode 指定hit-test值,该值给出了更多的鼠标位置值。它指定了鼠标在窗口的那个部位。该值的完全列表,请参考WIN32 API 指南中的WM_NCHITTEST消息。 
  dwExtraInfo 该值包含了相关的信息。一般该值由mouse_event函数设定,可以调用GetMessageExtraInfo来获得。 

  
  当主窗口接收到WM_MOUSEHOOK 消息时,它用wParam参数中的窗口句柄来查询窗口的消息。 

   .elseif uMsg==WM_MOUSEHOOK 
   invoke GetDlgItemText,hDlg,IDC_HANDLE,addr buffer1,128 
   invoke wsprintf,addr buffer,addr template,wParam 
   invoke lstrcmpi,addr buffer,addr buffer1 
   .if eax!=0 
   invoke SetDlgItemText,hDlg,IDC_HANDLE,addr buffer 
   .endif 
   invoke GetDlgItemText,hDlg,IDC_CLASSNAME,addr buffer1,128 
   invoke GetClassName,wParam,addr buffer,128 
   invoke lstrcmpi,addr buffer,addr buffer1 
   .if eax!=0 
   invoke SetDlgItemText,hDlg,IDC_CLASSNAME,addr buffer 
   .endif 
   invoke GetDlgItemText,hDlg,IDC_WNDPROC,addr buffer1,128 
   invoke GetClassLong,wParam,GCL_WNDPROC 
   invoke wsprintf,addr buffer,addr template,eax 
   invoke lstrcmpi,addr buffer,addr buffer1 
   .if eax!=0 
   invoke SetDlgItemText,hDlg,IDC_WNDPROC,addr buffer 
   .endif 

  为了避免重绘文本时的抖动,我们把已经在编辑空间中线时的文本和我们将要显示的对比。如果相同,就可以忽略掉。得到类名调用GetClassName,得到窗口过程调用GetClassLong并传入GCL_WNDPROC标志,然后把它们格式化成文本串并放到相关的编辑空间中去。 

   invoke UninstallHook 
   invoke SetDlgItemText,hDlg,IDC_HOOK,addr HookText 
   mov HookFlag,FALSE 
   invoke SetDlgItemText,hDlg,IDC_CLASSNAME,NULL 
   invoke SetDlgItemText,hDlg,IDC_HANDLE,NULL 
   invoke SetDlgItemText,hDlg,IDC_WNDPROC,NULL 

  当用户按下Unhook后,主程序调用DLL中的UninstallHook函数。该函数调用UnhookWindowsHookEx函数。然后,它把按钮的文本换回“Hook”,HookFlag的值设成FALSE再清除掉编辑控件中的文本。
  链接器的开关选项如下: 

   Link /SECTION:.bss,S /DLL /DEF:$(NAME).def /SUBSYSTEM:WINDOWS 

  它指定.bss段作为一个共享段以便所有映射该DLL的进程共享未初始化的数据段。如果不用该开关,您DLL中的钩子就不能正常工作了。 
   如果要用全局钩子,一定要放在dll中的,不然其他进程的消息就不能得到了,但是易语言目前还不能做dll,还是用其他语言(比如汇编,vb,vc,delphi等)做个dll,就可以了,最好还是用自己做的dll,其他人做的dll并不一定适合的。

----------------------------------------------
秋风清 秋月明,几上闲琴黯生尘。
夜来忽忆少年事,陌上柳绿草青青。
相逢一笑言未语,携手相看自倾心。
二十二载云烟里,云含风韵烟含情。
风尘渺落风霜浸,但闻长空雁长鸣。
姿容如花颜如玉,只在平生梦里寻。
秋风清 秋月明,秋灯秋雨愁煞人。
天涯路远人何在,午夜梦回空扰惊。
作者:
男 hbqckzj (醉里寻欢) ★☆☆☆☆ -
盒子活跃会员
2003/12/13 8:24:19
19楼: 再贴一个VB的例程哈,其实VB和delphi很多地方是相同的,大家可以作为参考哈

声明
Public Declare Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA" (ByVal idHook As Long, ByVal lpfn As Long, ByVal hmod As Long, ByVal dwThreadId As Long) As Long

说明
安装钩子过程

其它
'---------------------------------
'Form 
'安装钩子
Private sub AddHook()
  '键盘钩子
  lHook(0) = SetWindowsHookEx(WH_KEYBOARD_LL, AddressOf CallKeyHookProc, App.hInstance, 0)
  '鼠标钩子
  lHook(1) = SetWindowsHookEx(WH_MOUSE_LL, AddressOf CallMouseHookProc, App.hInstance, 0)
End Sub
'卸钩子
Private sub DelHook()
  UnhookWindowsHookEx lHook(0)
  UnhookWindowsHookEx lHook(1)
End Sub
'---------------------------------
'模块
Public Declare Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA" (ByVal idHook As Long, ByVal lpfn As Long, ByVal hmod As Long, ByVal dwThreadId As Long) As Long
Public Declare Function UnhookWindowsHookEx Lib "user32" (ByVal hHook As Long) As Long
Public Declare Function GetKeyState Lib "user32" (ByVal nVirtKey As Long) As Integer
Public Declare Function CallNextHookEx Lib "user32" (ByVal hHook As Long, ByVal ncode As Long, ByVal wParam As Long, lParam As Any) As Long
Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (lpvDest As Any, ByVal lpvSource As Long, ByVal cbCopy As Long)
Public Type KEYMSGS
       vKey As Long          '虚拟码  (and &HFF)
       sKey As Long          '扫描码
       flag As Long          '键按下:128 抬起:0
       time As Long          'Window运行时间
End Type
Public Type MOUSEMSGS
       X As Long            'x座标
       Y As Long            'y座标
       a As Long
       b As Long
       time As Long         'Window运行时间
End Type
Public Type POINTAPI
    X As Long
    Y As Long
End Type
Public Const WH_KEYBOARD_LL = 13
Public Const WH_MOUSE_LL = 14
Public Const Alt_Down = &H20
'-----------------------------------------
'消息
Public Const HC_ACTION = 0
Public Const HC_SYSMODALOFF = 5
Public Const HC_SYSMODALON = 4
'键盘消息
Public Const WM_KEYDOWN = &H100
Public Const WM_KEYUP = &H101
Public Const WM_SYSKEYDOWN = &H104
Public Const WM_SYSKEYUP = &H105
'鼠标消息
Public Const WM_MOUSEMOVE = &H200
Public Const WM_LBUTTONDOWN = &H201
Public Const WM_LBUTTONUP = &H202
Public Const WM_LBUTTONDBLCLK = &H203
Public Const WM_RBUTTONDOWN = &H204
Public Const WM_RBUTTONUP = &H205
Public Const WM_RBUTTONDBLCLK = &H206
Public Const WM_MBUTTONDOWN = &H207
Public Const WM_MBUTTONUP = &H208
Public Const WM_MBUTTONDBLCLK = &H209
Public Const WM_MOUSEACTIVATE = &H21
Public Const WM_MOUSEFIRST = &H200
Public Const WM_MOUSELAST = &H209
Public Const WM_MOUSEWHEEL = &H20A
Public Declare Function GetKeyNameText Lib "user32" Alias "GetKeyNameTextA" (ByVal lParam As Long, ByVal lpBuffer As String, ByVal nSize As Long) As Long
Public strKeyName As String * 255
Public Declare Function GetActiveWindow Lib "user32" () As Long
Public keyMsg As KEYMSGS
Public MouseMsg As MOUSEMSGS
Public lHook(1) As Long
'----------------------------------------
'模拟鼠标
Private Const MOUSEEVENTF_LEFTDOWN = &H2
Private Const MOUSEEVENTF_LEFTUP = &H4
Private Const MOUSEEVENTF_ABSOLUTE = &H8000 '  absolute move
Private Declare Sub mouse_event Lib "user32" (ByVal dwFlags As Long, ByVal dx As Long, ByVal dy As Long, ByVal cButtons As Long, ByVal dwExtraInfo As Long)
Private Declare Function ClientToScreen Lib "user32" (ByVal hwnd As Long, lpPoint As POINTAPI) As Long
'--------------------------------------
'模拟按键
Private Declare Sub keybd_event Lib "user32" (ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As Long)
'鼠标钩子    
Public Function CallMouseHookProc(ByVal code As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
    Dim pt As POINTAPI

    If code = HC_ACTION Then
      CopyMemory MouseMsg, lParam, LenB(MouseMsg)
      
      Form1.txtMsg(1).Text = "X=" + Str(MouseMsg.X) + " Y=" + Str(MouseMsg.Y)
      Form1.txtHwnd(1) = Format(wParam, "0")
      
      If wParam = WM_MBUTTONDOWN Then                      '把中键改为左键
           mouse_event MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0
           CallMouseHookProc = 1
      End If
      
      If wParam = WM_MBUTTONUP Then
          mouse_event MOUSEEVENTF_LEFTUP, 0, 0, 0, 0
          CallMouseHookProc = 1
      End If
        
    End If
    
    If code <> 0 Then
      CallMouseHookProc = CallNextHookEx(0, code, wParam, lParam)
    End If
  
End Function
'键盘钩子
Public Function CallKeyHookProc(ByVal code As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
    Dim lKey As Long
    Dim strKeyName As String * 255
    Dim strLen As Long
   
    If code = HC_ACTION Then
      CopyMemory keyMsg, lParam, LenB(keyMsg)
      Select Case wParam
        Case WM_SYSKEYDOWN, WM_KEYDOWN, WM_SYSKEYUP, WM_KEYUP:
        
             
            lKey = keyMsg.sKey And &HFF           '扫描码
            lKey = lKey * 65536
            strLen = GetKeyNameText(lKey, strKeyName, 250)
            Form1.txtMsg(0).Text = "键名:" + Left(strKeyName, strLen) + " 虚拟码:" + Format(keyMsg.vKey And &HFF, "0") + " 扫描码:" + Format(lKey / 65536, "0")
            
            Form1.txtHwnd(0) = ""
            If (GetKeyState(vbKeyControl) And &H8000) Then
               Form1.txtHwnd(0) = Form1.txtHwnd(0) + "Ctrl "
            End If
            
            If (keyMsg.flag And Alt_Down) <> 0 Then
               Form1.txtHwnd(0) = Form1.txtHwnd(0) + "Alt "
            End If
            
            If (GetKeyState(vbKeyShift) And &H8000) Then
              Form1.txtHwnd(0) = Form1.txtHwnd(0) + "Shift"
            End If
              
            'keyMsg.vKey And &HFF   虚拟码
            'lKey / 65536           扫描码
            
            If (keyMsg.vKey And &HFF) = vbKeyY Then       '把Y键替换为N
               If wParam = WM_SYSKEYDOWN Or wParam = WM_KEYDOWN Then
                  keybd_event vbKeyN, 0, 0, 0
               End If
               CallKeyHookProc = 1        '屏蔽按键
            End If
            
       End Select
    End If
   
    If code <> 0 Then
      CallKeyHookProc = CallNextHookEx(0, code, wParam, lParam)
    End If
  End Function
----------------------------------------------
秋风清 秋月明,几上闲琴黯生尘。
夜来忽忆少年事,陌上柳绿草青青。
相逢一笑言未语,携手相看自倾心。
二十二载云烟里,云含风韵烟含情。
风尘渺落风霜浸,但闻长空雁长鸣。
姿容如花颜如玉,只在平生梦里寻。
秋风清 秋月明,秋灯秋雨愁煞人。
天涯路远人何在,午夜梦回空扰惊。
作者:
男 hbqckzj (醉里寻欢) ★☆☆☆☆ -
盒子活跃会员
2003/12/13 8:55:04
20楼: 再贴:

学用钩子函数

    钩子函数并不是什么高深的技术,在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 测试通过 


----------------------------------------------
秋风清 秋月明,几上闲琴黯生尘。
夜来忽忆少年事,陌上柳绿草青青。
相逢一笑言未语,携手相看自倾心。
二十二载云烟里,云含风韵烟含情。
风尘渺落风霜浸,但闻长空雁长鸣。
姿容如花颜如玉,只在平生梦里寻。
秋风清 秋月明,秋灯秋雨愁煞人。
天涯路远人何在,午夜梦回空扰惊。
作者:
男 bios (阿贡) ★☆☆☆☆ -
盒子中级会员
2003/12/13 11:42:25
21楼: 好极了 可以做一个 HOOK 葵花宝典了 顶顶!
----------------------------------------------
按此在新窗口浏览图片
按此在新窗口浏览图片
作者:
男 hbqckzj (醉里寻欢) ★☆☆☆☆ -
盒子活跃会员
2003/12/13 13:10:46
22楼: 俺搜索了一上午,感觉值得贴上来的也就这么多了

大家如果有这方面比较好的资料,欢迎贴上来!谢谢大家!

另:我有一个英文版的WIN32API电子书,23.6M,不知道站上有没有,不知道值不值得传上来。
----------------------------------------------
秋风清 秋月明,几上闲琴黯生尘。
夜来忽忆少年事,陌上柳绿草青青。
相逢一笑言未语,携手相看自倾心。
二十二载云烟里,云含风韵烟含情。
风尘渺落风霜浸,但闻长空雁长鸣。
姿容如花颜如玉,只在平生梦里寻。
秋风清 秋月明,秋灯秋雨愁煞人。
天涯路远人何在,午夜梦回空扰惊。
作者:
男 loubingtt (我的生活与你无关) ★☆☆☆☆ -
盒子活跃会员
2003/12/13 14:33:10
23楼: 呵呵。比我的还多啊。我好有个shell钩子要不要啊
----------------------------------------------
按此在新窗口浏览图片
作者:
男 hbqckzj (醉里寻欢) ★☆☆☆☆ -
盒子活跃会员
2003/12/13 14:41:16
24楼: 要哈,怎么不要,就像BIOS说的一样,贴到一千楼哈!

只要是钩子相关的,欢迎贴上来!日后让站长收起来做个专辑哈!
----------------------------------------------
秋风清 秋月明,几上闲琴黯生尘。
夜来忽忆少年事,陌上柳绿草青青。
相逢一笑言未语,携手相看自倾心。
二十二载云烟里,云含风韵烟含情。
风尘渺落风霜浸,但闻长空雁长鸣。
姿容如花颜如玉,只在平生梦里寻。
秋风清 秋月明,秋灯秋雨愁煞人。
天涯路远人何在,午夜梦回空扰惊。
作者:
男 bios (阿贡) ★☆☆☆☆ -
盒子中级会员
2003/12/13 17:17:12
25楼: 有什么好东西 只管上传 大家都需要!
----------------------------------------------
按此在新窗口浏览图片
按此在新窗口浏览图片
作者:
男 shiro (比卡丘) ★☆☆☆☆ -
普通会员
2004/3/24 15:54:55
26楼: 不错不错,COPY到不少好东西,HOOK要好好学学~
----------------------------------------------
巧克力PIKA
作者:
男 lyl910 (颠三倒四) ★☆☆☆☆ -
盒子活跃会员
2004/5/1 0:05:56
27楼: [Error] hookproc.pas(15): Unsatisfied forward or external declaration: 'keyboardhook'
[Error] hookproc.pas(17): Unsatisfied forward or external declaration: 'setkeyhook'
[Fatal Error] keyspy.dpr(7): Could not compile used unit 'hookproc.pas'

我采用14楼的代码 出现这个错误 怎么办?
----------------------------------------------
向大家学习。
作者:
男 fox7899 (fox7899) ★☆☆☆☆ -
盒子活跃会员
2004/6/6 11:52:20
28楼: 钩子是好东西。咱的论坛更好
----------------------------------------------
-
作者:
男 root_lh (Lhxs) ★☆☆☆☆ -
盒子活跃会员
2004/8/2 14:03:23
29楼: 能否打包上来哦。
----------------------------------------------
中流一壶,千金争挈。宁为铅刀,毋为楮叶。错节盘根,利器斯别。识时务者,呼为俊杰!
作者:
男 sjh2003 (单翼天使) ★☆☆☆☆ -
普通会员
2005/7/3 3:00:01
30楼: 好东西 怎么我编译通过不了阿? 我用的7.0版的
----------------------------------------------
学海无涯,苦作舟;苦海无边,回头是岸?
作者:
女 inrm (英壬) ★☆☆☆☆ -
普通会员
2005/9/2 16:55:37
32楼: 这种帖子才堪称“精华”。
----------------------------------------------
《英壬画板 inRm3D 》三维交互几何教学软件 http://www.inrm3d.cn
作者:
男 wodesnake (wodesnake) ★☆☆☆☆ -
盒子活跃会员
2005/9/13 22:38:24
33楼: 谁能帮我讲解一下,14楼朋友发的原代码中的
function keyboardhook(icode:integer;wparam:wparam;

lparam:lparam):lresult;stdcall;export;

export有什么作用了

谢谢
----------------------------------------------
-
作者:
男 zhengyun (老大) ★☆☆☆☆ -
普通会员
2006/7/18 16:47:50
34楼: 请问高手,钩子如果用到屏幕取词上,用delphi怎么可以实现?文字
----------------------------------------------
wo aho fwa
作者:
男 loien (loien) ★☆☆☆☆ -
普通会员
2006/7/18 22:21:01
35楼: 看看书,你就会明白的
----------------------------------------------
The life is dominate by me, the world is just fascinating because of me!
作者:
男 sudaliang (sudaliang) ★☆☆☆☆ -
盒子活跃会员
2006/9/27 9:12:07
36楼: ding!!!!!!!!!!1
----------------------------------------------
-真想做只感性的驴子!!!
作者:
男 xaonline (BEITA) ★☆☆☆☆ -
普通会员
2006/10/6 6:42:04
37楼:  
   果真经典!
----------------------------------------------
『學然後知不足』
作者:
男 atao (我本无名) ★☆☆☆☆ -
普通会员
2006/10/22 21:07:01
38楼: 这样的贴子,才是真正的好帖子
----------------------------------------------
-
作者:
女 emmazhong (emma) ★☆☆☆☆ -
普通会员
2006/10/27 15:05:24
39楼: 我是第1000位点击者
----------------------------------------------
-
作者:
女 emmazhong (emma) ★☆☆☆☆ -
普通会员
2006/10/27 15:06:05
40楼: 错了,是10000
----------------------------------------------
-
作者:
男 bsense (财神) ★☆☆☆☆ -
普通会员
2006/11/7 16:45:18
41楼: 经典好帖
----------------------------------------------
-
作者:
男 jack_zh (菜鸟) ★☆☆☆☆ -
普通会员
2006/11/10 23:28:20
42楼: 网上有本书《DELPHI下深入WINDOWS核心编程》,里面有详细讲解,下一本看下吧。
----------------------------------------------
-
作者:
男 hjcrfsoft (5ddq.com) ★☆☆☆☆ -
盒子活跃会员
2006/11/24 23:09:40
43楼: 好东西太多了.
要是有一个字就能说明白就好了
我看了n久才看完的
小"蔡"学习了.大家辛苦了
----------------------------------------------
我的大全[5ddq.com],我的主页!
作者:
男 kuainq (好汉做事痛痛快快!) ★☆☆☆☆ -
普通会员
2007/7/17 14:46:59
44楼: 为1000楼而努力不懈!
----------------------------------------------
-
作者:
男 seehero (苦不苦,想想红军二万五) ★☆☆☆☆ -
普通会员
2007/7/24 11:21:12
45楼: 好东东,谢谢楼主
----------------------------------------------
-
作者:
男 yeyun (野云) ★☆☆☆☆ -
盒子活跃会员
2008/4/18 12:08:05
46楼: 太好了..收藏学习.谢谢楼主各位的无私奉献...辛苦了.
----------------------------------------------
-
作者:
男 goy (老猪) ★☆☆☆☆ -
盒子活跃会员
2009/9/5 10:43:51
47楼: 最近正在研究这方面的东西 收藏了 !
----------------------------------------------
-
作者:
男 wei77316 (远洋方舟) ★☆☆☆☆ -
普通会员
2009/11/19 1:33:35
48楼: 要是直接打包就好了,辛苦了
----------------------------------------------
-
作者:
女 cughuan (wulianzi) ▲▲▲▲▲ -
普通会员
2011/6/13 15:01:40
51楼: 有用钩子实现对下载文件的捕获么??
----------------------------------------------
-
作者:
男 xmxx1 (西门笑雪) ▲▲▲▲▲ -
禁用账号
2011/6/18 16:30:36
52楼: ……
被禁用帐号,帖子内容自动屏蔽!
……

----------------------------------------------
发布广告,禁用帐号!
作者:
男 xmxx1 (西门笑雪) ▲▲▲▲▲ -
禁用账号
2011/6/18 16:31:25
53楼: ……
被禁用帐号,帖子内容自动屏蔽!
……

----------------------------------------------
发布广告,禁用帐号!
信息
登陆以后才能回复
Copyright © 2CCC.Com 盒子论坛 v3.0.1 版权所有 页面执行93.75毫秒 RSS