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 第一代

Safari flex 不适用于 clearfix hack

iOS 应用程序初始屏幕方向不适用于横向

约束不适用于 UICollectionView