终于找到全局钩子导致其它程序奔溃的原因
前几天突然心血来潮想写个全局钩子的小程序,期间遇到个一直无法解决的问题,就是程序设置钩子没问题,一旦解除钩子就会导致其它运行的程序奔溃。短短几行代码,左改右改,该查的资料都查了,就是没有解决,中途时不时的想放弃不弄了,但是本着不找到问题原因不放弃的原则,最后终于找到问题所在。
费话不多说,本文目的是分享经验,没有技术含量,看到最后你才知道什么叫惊不惊喜意不意外。
主要代码:
dll
// 传递消息
function HookProc(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
begin
Result := CallNextHookEx(hhk, nCode, wParam, lParam);
end;
// 开始HOOK
function StartHook(pid: DWORD):Boolean; stdcall;
begin
hhk := SetWindowsHookEx(WH_CALLWNDPROC, @HookProc, HInstance, 0);
Result := hhk<>0;
end;
// 结束HOOK
function EndHook:Boolean; stdcall;
begin
Result := UnhookWindowsHookEx(hhk);
if Result then hhk := 0;
end;
症状:
运行的程序 一 一 奔溃,explorer.exe也不例外。
主要过程:
过程1:在项目中把所有无关的代码删除,只保留了设置钩子和取消钩子的代码
// 传递消息
function HookProc(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
begin
Result := CallNextHookEx(hhk, nCode, wParam, lParam);
end;
// 开始HOOK
function StartHook(pid: DWORD):Boolean; stdcall;
begin
hhk := SetWindowsHookEx(WH_CALLWNDPROC, @HookProc, HInstance, 0);
Result := hhk<>0;
end;
// 结束HOOK
function EndHook:Boolean; stdcall;
begin
Result := UnhookWindowsHookEx(hhk);
if Result then hhk := 0;
end;结果:设置钩子没问题,一旦解除钩子就会导致其它运行的程序奔溃。
过程2:另外新建项目把相关代码边复制边测试以便查找问题。
结果是把所有代码都移过去后发现程序运行正常了(解除钩子不会导致其它程序崩溃)。
心里想问题到底在哪里呀?这也不至于啊,就这样算了吧反正是正常了,再想想还是要找到问题,要是以后又遇到那不是又得折腾。
问题在这里:
经过2个项目代码进行排查,终于找到这该死的问题。
有问题的dll代码:
library MyHookdll;
uses
SysUtils,
Classes,
Windows, Vcl.Dialogs;
var
hhk: HHOOK = 0;
// 传递消息
function HookProc(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
begin
Result := CallNextHookEx(hhk, nCode, wParam, lParam);
end;
// 开始HOOK
function StartHook(pid: DWORD):Boolean; stdcall;
begin
hhk := SetWindowsHookEx(WH_CALLWNDPROC, @HookProc, HInstance, 0);
Result := hhk<>0;
end;
// 结束HOOK
function EndHook:Boolean; stdcall;
begin
Result := UnhookWindowsHookEx(hhk);
if Result then hhk := 0;
end;
exports
StartHook, EndHook;
begin
end.
dll正常代码:
library MyHookdll;
uses
SysUtils,
Classes,
Windows;
var
hhk: HHOOK = 0;
// 传递消息
function HookProc(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
begin
Result := CallNextHookEx(hhk, nCode, wParam, lParam);
end;
// 开始HOOK
function StartHook(pid: DWORD):Boolean; stdcall;
begin
hhk := SetWindowsHookEx(WH_CALLWNDPROC, @HookProc, HInstance, 0);
Result := hhk<>0;
end;
// 结束HOOK
function EndHook:Boolean; stdcall;
begin
Result := UnhookWindowsHookEx(hhk);
if Result then hhk := 0;
end;
exports
StartHook, EndHook;
begin
end.
对比2处代码,仅仅是引用了Vcl.Dialogs组件库便导致程序产生了致命的问题。
就问你们惊不惊喜意不意外。 全局钩子导致崩溃的原因分析及解决方案
通过对比两个DLL项目代码,发现问题根源在于Vcl.Dialogs单元的引用。以下是关键分析:
[*]崩溃原因:VCL单元包含消息循环机制,在全局钩子场景中会导致以下问题:
1. 被注入进程加载该DLL时,会初始化VCL框架
2. 不同进程共享VCL资源时产生冲突
3. 卸载DLL时资源释放顺序异常
[*]致命影响:
1. 调用UnhookWindowsHookEx时DLL卸载失败
2. 残留的VCL资源导致宿主进程内存损坏
3. 最终引发explorer.exe等进程崩溃
解决方案:
// 正确的DLL单元引用应仅保留核心模块
uses
Windows, SysUtils; // 移除非必要的VCL单元
经验总结
1. 全局钩子DLL必须保持最小化依赖
2. 避免在DLL中使用任何GUI相关单元
3. 调试时建议用OutputDebugString代替弹窗
4. 推荐使用纯API实现钩子功能
这个案例印证了:在系统级编程中,看似无关的单元引用可能造成毁灭性后果。建议通过Process Monitor等工具监控DLL加载行为,能更快定位此类隐蔽问题。
以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。 太实用了 不二如是 发表于 2025-4-24 10:21
太实用了
惭愧惭愧。
页:
[1]