SetWindowsHookEx 函数的第一个参数表示钩子类型, 共有 14 种选择, 前面我们已经用过两种:
WH_KEYBOARD、WH_MOUSE.

系统会为每一种类型的钩子建立一个表(那就是 14 个表), 譬如某个应用程序启动了键盘钩子, 我们自己的程序也启动了键盘钩子, 同样是键盘钩子就会进入同一个表. 这个表(可能不止一个, 可能还会有鼠标钩子等等)就是传说中的"钩子链".

假如某个钩子链中共进来了三个钩子(譬如是: 钩子A、钩子B、钩子C 依次进来), 最后进来的 "钩子C" 会先执行.
是不是先进后出? 我觉得应该说成: 后进先出! 这有区别吗? 有! 因为先进来的不一定出得来.
最后进了的钩子会最先得到执行, 先前进来的钩子(钩子A、钩子B)能不能得到执行那还得两说, 这得有正在执行的 "钩子C" 说了算.
如果 "钩子C" 的函数中包含了 CallNextHookEx 语句, 那么 "钩子A、钩子B" 就有可能得以天日; 不然就只有等着相应的
UnhookWindowsHookEx 来把它们带走(我想起赵本山的小品...).

这时你也许会想到: 这样太好了, 我以后就不加 CallNextHookEx , 只让自己的钩子"横行"; 但如果是你的钩子先进去的呢?
所以 Windows 建议: 钩子函数要调用 CallNextHookEx, 并把它的返回值当作钩子函数自己的返回值.

CallNextHookEx 同时要给钩子链中的下一个(或许应该叫上一个)钩子传递参数(譬如在键盘消息中按了哪个键). 一个键盘钩子和鼠标钩子的参数一样吗? 当然不一样, 所以它们也不在一个 "链" 中啊; 同一个链中的钩子的类型肯定是一样的.



再聊聊钩子函数的返回值:
在这之前, 钩子函数的返回值, 我们都是遵循 Windows 的惯例, 返回了 CallNextHookEx 的返回值.
如果 CallNextHookEx 成功, 它会返回下一个钩子的返回值, 是个连环套;
如果 CallNextHookEx 失败, 会返回 0, 这样钩子链也就断了, 只有当前钩子还在执行任务.

不同类型的钩子函数的返回值是不同的, 对键盘钩子来讲如果返回一个非 0 的值, 表示它处理完以后就把消息给消灭了.
换句话说:
如果给键盘的钩子函数 Result := 0; 说明消息被钩子拦截并处理后就给 "放" 了;
如果给键盘的钩子函数 Result := 1; 说明消息被钩子拦截并处理后又给 "杀" 了.

在下面的例子中, 我们干脆不使用 CallNextHookEx (反正暂时就我一个钩子), 直接给返回值!



这是接下来例子的演示动画:



动画中, 我在三种状态下分别给 Memo 输入了字母 a
当没启动钩子时, Memo 是可以正常输入的;
当钩子函数返回 0, 钩上以后, 先执行了钩子函数的功能(返回键值), 字母 a 也能输入成功;
当钩子函数返回非 0 值(譬如1), 钩上以后, 就只执行了钩子函数的功能(返回键值), 可怜的 Memo 不知道发生了什么.

但这里又有了新问题: 钩子函数返回键值时怎么...不是一个?
先提醒: 在前面用 Beep 测试时, 你有没有发现那个声音也不只一次, 这是一个道理.
因为按一次键就会发出两个消息: WM_KEYDOWN、WM_KEYUP, 我们没有指定拦截哪个, 就都拦截了.
那怎么区分这两个消息呢? 秘密在键盘钩子函数的第三个参数 lParam 里面, 等下一个话题再研究吧.


//示例代码:
unit Unit1;interfaceusesWindows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,Dialogs, StdCtrls;typeTForm1 = class(TForm)Button1: TButton;Button2: TButton;Memo1: TMemo;procedure FormDestroy(Sender: TObject);procedure Button1Click(Sender: TObject);procedure Button2Click(Sender: TObject);end;{钩子函数声明}function MyKeyHook(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;varForm1: TForm1;implementation{$R *.dfm}varhook: HHOOK;function MyKeyHook(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT;
beginForm1.Memo1.Lines.Add(IntToStr(wParam)); {参数二是键值}Result := 0; {分别测试返回 0 或非 0 这两种情况}
end;{派出钩子}
procedure TForm1.Button1Click(Sender: TObject);
beginhook := SetWindowsHookEx(WH_KEYBOARD, MyKeyHook, HInstance, GetCurrentThreadId);Memo1.Clear;Text := '钩子启动';
end;{收回钩子}
procedure TForm1.Button2Click(Sender: TObject);
beginUnhookWindowsHookEx(hook);Text := '钩子关闭';
end;{如果忘了收回钩子...}
procedure TForm1.FormDestroy(Sender: TObject);
beginif hook<>0 then UnhookWindowsHookEx(hook);
end;end.

小秘密: 发现没有, 这次在 SetWindowsHookEx 时, 第二参数(函数地址), 没有使用 @、也没有用 Addr, 怎么也行呢?
因为函数名本身就是个地址.


使用钩子函数[4] - 钩子链和 CallNextHookEx 的返回值相关推荐

  1. MATLAB中如何忽略函数中前几个不需要的返回值而只取需要的返回值,例如:只取函数第二个返回值,而忽略第一个返回值

    1 致谢 感谢网友honglei.chen的回答, 原文链接如下: https://www.ilovematlab.cn/thread-203269-1-1.html 2 前言 今天在学习神经网络算法 ...

  2. C语言基础入门48篇_20_函数入门:为什么使用函数?(函数使得程序模块化 使用函数封装细节,使得程序员只要面向函数的接口编程(参数与返回值),而不用关心函数内部细节)

    菜鸟和高手都会基本,那菜鸟为什么是菜鸟,高手为什么是高手呢?很大的区别在于,高手写的程序[结构性]更好,更容易维护.而函数就是C语言结构化的一种手段. 函数可以将大的计算任务划分为多个较小的任务(解耦 ...

  3. Python 函数参数有冒号 声明后有- 箭头 返回值注释 参数类型注释

    在python3.7 环境下 函数声明时能在参数后加冒号,如图: 1 def f(ham: str, eggs: str = 'eggs') -> str : 2 print("Ann ...

  4. Mysql函数示例(如何定义输入变量与返回值)

    Mysql 函数示例 简介 基于mysql实现函数的编写,如何定义输入变量,如何定义和使用返回值等. 获取男女信息 --1 获取男女信息 CREATE FUNCTION getGender(idcar ...

  5. python冒号声明类型_Python 函数参数有冒号 声明后有- 箭头 返回值注释 参数类型注释...

    在python3.7 环境下 函数声明时能在参数后加冒号,如图: 1 def f(ham: str, eggs: str = 'eggs') -> str : 2 print("Ann ...

  6. 深入理解PHP内核(十二)函数-函数的定义、传参及返回值

    原文链接:http://www.orlion.ga/344/ 一.函数的定义 用户函数的定义从function 关键字开始,如下 function foo($var) { echo $var; } 1 ...

  7. python学习之路---函数:定义def、调用、返回值return、参数说明、参数陷阱

    #函数 #可读性强,复用性强 #函数定义:定义了之后,可以在任何需要的地方调用 #格式: def 函数名(): 例子:def mylen(): # 没有返回长度,只能单纯的打印 # return 返回 ...

  8. js错误集锦 函数在一个map循环遍历中进行返回值返回 结果在调用该函数时收不到返回值

    错误代码: superviseFilter = () => {let company = this.props.cooperativeState.allCompanyif(this.state. ...

  9. python调用自定义函数返回值的类型_生成dll文件以及python对DLL中函数的调用(参数类型以及返回值)...

    工具:VS2010    python2.7 (若使用的python是64位的,生成的dll也要使用x64) 系统:win7pro 64bit 首先,dll工程的创建以及dll文件的生成: new p ...

最新文章

  1. object标签与embad掉钱_使用object或embed标签来播放SWF文件
  2. 安装sql2008 enterprise (English正式版)图解
  3. 解决ubuntu里面的sudo: /etc/sudoers is mode 0640, should be 0440
  4. 第一章:火狐浏览器 : 环境配置: FireFox 版本38 + jdk 7 + selenium 2.53.6 + selenum-version 2.48.2...
  5. [转] fedora linux下安装vmware tools的最好方法
  6. AtCoder Beginner Contest 211 E - Red Polyomino(暴力+状态记录)
  7. HDUOJ 1062 TEXT REVERSE
  8. 全球首发!惯性导航导论(剑桥大学)第十部分
  9. 建筑基坑工程设计计算与施工(一)
  10. ftp文件上传及下载工具类
  11. 计算机考研面试有英语听力吗,考研复试前如何准备英语听力和口语
  12. 如何更好地进行销售预测
  13. 小技巧——如何为foxmail中的文字编辑超链接
  14. 1>cl : 命令行 warning D9035: “Gm”选项已否决,并将在将来的版本中移除1>cl : 命令行 error D8016: “/ZI”和“/Gy-”命令行选项不兼容
  15. 高等数学-无穷级数思维导图
  16. ORB_SLAM2稠密建图(ORBSLAM2_with_pointcloud_map),并保存地图
  17. 使用Raw Image和Unity自带的视频播放插件Video Player
  18. Bifrost RAT 流量分析报告
  19. 普通Kriging算法笔记
  20. 字符串常量池、class常量池和运行时常量池

热门文章

  1. hdu3635 Dragon Balls(带权并查集)
  2. Hexo自定义页面的两种方法
  3. java端模拟http的get、post请求(转)
  4. validate插件深入学习-01 小白从看透一个插件开始
  5. 用原生 js jquery 实现知乎收起答案功能
  6. Java对象和XML之间的编排与反编排
  7. Atitit利用反射获取子类 集合 以及继承树
  8. 为什么世界上一些最好的科学家和程序员,在世人眼里,都有点不太正常,甚至行为混乱...
  9. 倒排索引统计与 Python 字典
  10. SQL Server 2008创建数据库