Hook 不适用于第三个应用程序
Posted
技术标签:
【中文标题】Hook 不适用于第三个应用程序【英文标题】:Hook doesn't work with an third application 【发布时间】:2020-01-20 15:53:52 【问题描述】:首先,对不起我的英语,但我会尽力而为。 我想从第三个应用程序中捕获附加到特定按钮的 BN_CLICKED 通知。 我能够在第三个应用程序上找到手柄按钮。 当我更改按钮的标识以使用我的应用程序上的按钮而不是另一个应用程序时,下面的代码运行良好!!! 请告诉我为什么,因为我会发疯。 提前谢谢你!
这是 DLL 代码:
WINHOOK.dll
library Winhook;
uses
Windows, Messages, SysUtils, vcl.dialogs, tlhelp32;
type
TPMsg = ^TCWPSTruct;
var
NextHook: HHOOK;
hbutton: hwnd;
//---------------------------------------------------------
//fonction de remplacement pour le traitement du message
function MsgFilterFunc(Code: Integer; MwParam: Wparam;
MlParam: Lparam): LRESULT; stdcall;
begin
Result := 0;
if TPMsg(MlParam)^.message = WM_COMMAND then
//si le handle correspond au bouton a surveiller
if TPMsg(mlParam)^.lParam = hbutton then
if hiword(TPMsg(mlParam)^.wParam)=BN_clicked then
showmessage('BN_CLICKED Activé');
Result := CallNextHookEx(NextHook, Code, MwParam
,MlParam);
end;
//---------------------------------------------------------
//Mise en place du hook avec WH_CALLWNDPROC pour interception
//des messages WM_COMMAND
function SetHook(hbt: hwnd; th: integer; MsgToSend: Integer):
Boolean; stdcall;
begin
Result := False;
hbutton := hbt;
//hook sur le thread donné par th
NextHook := SetWindowsHookEx(WH_CALLWNDPROC,
MsgFilterFunc, Hinstance,th);
if NextHook <> 0 then Result := True;
end;
//---------------------------------------------------------
//Suppression du hook
function FreeHook: Boolean; stdcall;
begin
Result := False;
if UnHookWindowsHookEx(NextHook) then
Result := true;
end;
exports
SetHook ,
FreeHook ;
begin
end.
这是程序代码:
unit Uprogatester;
interface
uses
Windows, Messages, SysUtils, Variants, Classes,
Graphics, Forms, Dialogs, StdCtrls, tlhelp32, ExtCtrls,
Controls, ComCtrls;
const
HookDemo = 'WINHOOK.dll';
const
WM_HOOKCREATE = WM_USER + 300;
Type
Pwindows = ^Twindows;
Twindows = record
windowhandle : hwnd;
windowtext : string;
windowclass : string;
end;
Type
TForm2 = class(TForm)
Button2: TButton;
BTNSETHook: TButton;
BTNUNSETHook: TButton;
Button1: TButton;
procedure BTNSETHookClick(Sender: TObject);
procedure BTNUNSETHookClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure IDHandle;
private
FHookSet: Boolean;
Déclarations privées
public
Déclarations publiques
end;
var
Form2: TForm2;
IDProc: Tprocessentry32;
IDThr: Tthreadentry32;
processid,processhandle,threadid: cardinal;
AWindows : PWindows;
function SetHook(Hbt: hwnd; IDTh: integer; MsgToSend: Integer): Boolean;
stdcall; external HookDemo;
function FreeHook: Boolean; stdcall; external HookDemo;
function getclasstext(wnd:hwnd): boolean;
function chercheboutonproc(wnd : hwnd;
Form : Tform2): bool;
$ifdef win32 stdcall; $endif
function enumwindowsproc(wnd : hwnd;
form : Tform2): bool;
$ifdef win32 stdcall; $endif
implementation
$R *.dfm
//------------------------------------------------------
// recherche du bouton
function chercheboutonproc(wnd : hwnd;
Form : Tform2): bool;
$ifdef win32 stdcall; $endif
begin
result := true;
if getclasstext(wnd) then
if awindows^.windowclass = 'TButton' then
begin
if awindows^.windowtext = 'Bouton a Tester' then
begin
result := false;
end;
end;
end;
//------------------------------------------------------
// recherche de la fenetre PARENT
function enumwindowsproc(wnd : hwnd;
form : Tform2): bool;
$ifdef win32 stdcall; $endif
begin
result := true;
if getclasstext(wnd) then
if awindows^.windowclass = 'TForm2' then
begin
if awindows^.windowtext = 'Programme a tester' then
begin
enumchildwindows(wnd,@chercheboutonproc,0);
result := false;
end;
end;
end;
//------------------------------------------------------
//Bouton Hook
procedure TForm2.BTNSETHookClick(Sender: TObject);
begin
enumwindows(@enumwindowsproc,longint(self));
idhandle;
showmessage(awindows^.WindowText+' / ' + awindows^.windowclass);
FHookSet := LongBool(SetHook(awindows^.windowhandle,
threadID, WM_HOOKCREATE));
if FHookSet then
begin
BTNSETHook.Enabled := false;
BTNUNSETHook.Enabled := true;
end;
end;
//------------------------------------------------------
//Bouton UNHook
procedure TForm2.BTNUNSETHookClick(Sender: TObject);
begin
FHookSet := FreeHook;
if FHookSet then
begin
BTNSETHook.Enabled := true;
BTNUNSETHook.Enabled := false;
end;
end;
//------------------------------------------------------
//Recherche handle ID processus et thread
procedure TForm2.IDHandle;
var shothdl: Thandle;
begin
//pour trouver une numero d'id d'un processus
IDProc.dwSize := sizeof(IDProc);
shothdl := createtoolhelp32snapshot(TH32CS_SNAPPROCESS, 0);
try
if shothdl=-1 then exit;
if process32first(shothdl,IDProc) then
begin
while process32next(shothdl,IDProc) do
begin
if (ansisametext(IDProc.szExeFile,'winhook.dll')) then
begin
processid := IDProc.th32ProcessID;
end;
end;
end;
finally closehandle(shothdl);
end;
//pour trouver le numero d'id du thread correspondant
IDThr.dwSize := sizeof(IDThr);
shothdl := createtoolhelp32snapshot(TH32CS_SNAPTHREAD, 0);
try
if shothdl=-1 then exit;
if thread32first(shothdl,IDThr) then
begin
while thread32next(shothdl,IDThr) do
begin
if IDThr.th32OwnerProcessID=processid then
begin
threadid := IDThr.th32ThreadID;
end;
end;
end;
finally closehandle(shothdl);
end;
end;
//------------------------------------------------------
procedure TForm2.FormCreate(Sender: TObject);
begin
FHookSet := false;
btnsethook.Enabled := true;
btnunsethook.Enabled := false;
end;
//------------------------------------------------------
procedure TForm2.FormDestroy(Sender: TObject);
begin
BTNUNSETHook.Click;
end;
//------------------------------------------------------
//recupération a chaque enumérartion
function getclasstext(wnd:hwnd): boolean;
var
buffer : array[0..99] of char;
nomclass : array[0..255] of char;
begin
result := true;
getclassname(wnd,nomclass,256);
getwindowtext(wnd,buffer,100);
new(awindows);
awindows^.windowhandle := wnd;
awindows^.windowtext := strpas(buffer);
awindows^.windowclass := strpas(nomclass);
end;
end.
problem when I execute with the third application
【问题讨论】:
您的枚举代码正在泄漏内存,并跳过CreateToolhelp32Snapshot()
报告的第一个进程和第一个线程。您还应该考虑使用SetWinEventHook()
而不是SetWindowsHookEx()
。无需 DLL,您可以更有效地过滤事件。
另外,您确定要挂接的线程 ID 的方式过于复杂。您可以在 Enum(Child)Windows()
回调中使用 GetWindowThreadProcessId()
来帮助您过滤窗口。 CreateToolhelp32Snapshot()
应该仅用于获取所需的进程 ID(如果有的话)。只要给定进程ID,就可以枚举属于该进程的窗口,给定标识就可以过滤特定按钮,然后直接获取其线程ID。
您必须将线程标识符 0 传递给 SetWindowsHookEx 以获得全局挂钩。当然,我不完全知道“IDThr.th32ThreadID”是什么,但我认为它没有返回 0。
@SertacAkyuz 在这种情况下,OP 不需要全局挂钩。只要 OP 获得正确的线程 ID 以进行挂钩,特定于线程的挂钩就可以正常工作。但是,在另一个进程中挂钩线程时仍然需要 DLL。
@Remy,好的,谢谢。 @ Franck - 然后我建议为 api 调用添加一些错误处理,这将有助于解决“为什么”。
【参考方案1】:
您试图在按钮本身上设置挂钩,但documentation 说,WM_COMMAND 被发送到按钮的父级。所以你应该在父节点上设置钩子。
【讨论】:
我不懂语言,但错误消息说“无法写入内存”。 @SertacAkyuz 新手用户经常显示与他们问题中的代码不同版本的屏幕截图。以上是关于Hook 不适用于第三个应用程序的主要内容,如果未能解决你的问题,请参考以下文章
MUI 自动完成功能不适用于 react-hook-form
React hook useRef 不适用于样式化组件和打字稿
Hololens - UserConsentVerifier 不适用于 Hololens 第一代