Que dois-je faire pour que mes procédures de hook WH_SHELL ou WH_CBT reçoivent des événements d’autres processus?

J’essaie d’utiliser SetWindowsHookEx pour configurer un hook WH_SHELL afin d’être averti des événements HSHELL_WINDOWCREATED et HSHELL_WINDOWDESTROYED échelle du système. Je passe 0 pour l’argument final dwThreadId qui, selon les documents , devrait “associer la procédure de hook à tous les threads existants exécutés sur le même bureau que le thread appelant”. Je transmets également le HInstance à ma DLL ( HInstance in Delphi) pour le paramètre hMod , comme tous les exemples que j’ai examinés.

Pourtant, je ne reçois que des notifications de fenêtres créées par ma propre application et, le plus souvent, mes tests ont pour conséquence que le processus du bureau est en feu une fois mon application fermée. Avant de vous demander, j’appelle UnhookWindowsHookEx . J’appelle également toujours CallNextHookEx depuis mon gestionnaire.

Je lance mon application de test à partir d’un compte utilisateur limité, mais jusqu’à présent, je n’ai trouvé aucun indice indiquant que cela jouerait un rôle … (même si cela me surprend vraiment)

AFAICT, j’ai tout fait par le livre (évidemment je n’ai pas fait mais jusqu’ici je ne vois pas où).

J’utilise Delphi (2007) mais je pense que cela ne devrait pas vraiment avoir d’importance.

EDIT: Peut être aurais-je dû mentionner cela auparavant: j’ai téléchargé et essayé quelques exemples (bien qu’il n’y ait malheureusement pas beaucoup de disponibles pour Delphi – en particulier aucun pour WH_SHELL ou WH_CBT ). Bien qu’ils ne bloquent pas le système comme mon application de test le fait, ils ne capturent toujours pas les événements des autres processus (même si je peux vérifier avec ProcessExplorer qu’ils peuvent y être chargés correctement). Il semble donc que quelque chose ne va pas avec la configuration de mon système ou que les exemples ne soient pas corrects ou qu’il est tout simplement impossible de capturer des événements provenant d’autres processus. Quelqu’un peut-il m’éclairer?

EDIT2: OK, voici la source de mon projet de test.

La DLL contenant la procédure de hook:

 library HookHelper; uses Windows; {$R *.res} type THookCallback = procedure(ACode, AWParam, ALParam: Integer); stdcall; var WndHookCallback: THookCallback; Hook: HHook; function HookProc(ACode, AWParam, ALParam: Integer): Integer; stdcall; begin Result := CallNextHookEx(Hook, ACode, AWParam, ALParam); if ACode < 0 then Exit; try if Assigned(WndHookCallback) // and (ACode in [HSHELL_WINDOWCREATED, HSHELL_WINDOWDESTROYED]) then and (ACode in [HCBT_CREATEWND, HCBT_DESTROYWND]) then WndHookCallback(ACode, AWParam, ALParam); except // plop! end; end; procedure InitHook(ACallback: THookCallback); register; begin // Hook := SetWindowsHookEx(WH_SHELL, @HookProc, HInstance, 0); Hook := SetWindowsHookEx(WH_CBT, @HookProc, HInstance, 0); if Hook = 0 then begin // ShowMessage(SysErrorMessage(GetLastError)); end else begin WndHookCallback := ACallback; end; end; procedure UninitHook; register; begin if Hook  0 then UnhookWindowsHookEx(Hook); WndHookCallback := nil; end; exports InitHook, UninitHook; begin end. 

Et la forme principale de l’application utilisant le crochet:

 unit MainFo; interface uses Windows, SysUtils, Forms, Dialogs, Classes, Controls, Buttons, StdCtrls; type THookTest_Fo = class(TForm) Hook_Btn: TSpeedButton; Output_Lbx: TListBox; Test_Btn: TButton; procedure Hook_BtnClick(Sender: TObject); procedure Test_BtnClick(Sender: TObject); public destructor Destroy; override; end; var HookTest_Fo: THookTest_Fo; implementation {$R *.dfm} type THookCallback = procedure(ACode, AWParam, ALParam: Integer); stdcall; procedure InitHook(const ACallback: THookCallback); register; external 'HookHelper.dll'; procedure UninitHook; register; external 'HookHelper.dll'; procedure HookCallback(ACode, AWParam, ALParam: Integer); stdcall; begin if Assigned(HookTest_Fo) then case ACode of // HSHELL_WINDOWCREATED: HCBT_CREATEWND: HookTest_Fo.Output_Lbx.Items.Add('created handle #' + IntToStr(AWParam)); // HSHELL_WINDOWDESTROYED: HCBT_DESTROYWND: HookTest_Fo.Output_Lbx.Items.Add('destroyed handle #' + IntToStr(AWParam)); else HookTest_Fo.Output_Lbx.Items.Add(Format('code: %d, WParam: $%x, LParam: $%x', [ACode, AWParam, ALParam])); end; end; procedure THookTest_Fo.Test_BtnClick(Sender: TObject); begin ShowMessage('Boo!'); end; destructor THookTest_Fo.Destroy; begin UninitHook; // just to make sure inherited; end; procedure THookTest_Fo.Hook_BtnClick(Sender: TObject); begin if Hook_Btn.Down then InitHook(HookCallback) else UninitHook; end; end. 

Le problème est que votre DLL de raccordement est en fait chargée dans plusieurs espaces d’adressage différents. Chaque fois que Windows détecte un événement dans un processus étranger qui doit être traité par votre hook, il charge la DLL hook dans ce processus (s’il n’est pas déjà chargé, bien sûr).

Cependant, chaque processus a son propre espace d’adressage. Cela signifie que le pointeur de fonction de rappel que vous avez passé dans InitHook () n’a de sens que dans le contexte de votre EXE (c’est pourquoi cela fonctionne pour les événements de votre application). Dans tout autre processus, ce pointeur est un déchet ; il peut pointer vers un emplacement de mémoire invalide ou (pire) dans une section de code aléatoire. Le résultat peut être une violation d’access ou une corruption de mémoire silencieuse.

En règle générale, la solution consiste à utiliser une sorte de communication interprocessus (IPC) pour notifier correctement votre fichier EXE. La manière la plus simple pour votre cas serait de poster un message et de ranger les informations nécessaires (événement et HWND) dans son WPARAM / LPARAM. Vous pouvez soit utiliser un WM_APP + n ou en créer un avec RegisterWindowMessage (). Assurez-vous que le message est publié et non envoyé pour éviter les blocages.

Cela pourrait être tertiaire à votre question, mais comme vous le voyez, les crochets sont très difficiles à corriger – si vous pouvez éviter de l’utiliser par tous les moyens, faites-le. Vous allez rencontrer toutes sortes de problèmes avec eux, en particulier sur Vista où vous devrez traiter avec UIPI.

Juste pour clarifier quelque chose que “efotinis” a mentionné à propos de la publication de messages dans votre processus – le wParam et le lParam que vous publiez dans votre processus principal ne peuvent pas être des pointeurs, ils peuvent simplement être des “chiffres”.

Par exemple, supposons que vous accrochez le message WM_WINDOWPOSCHANGING, Windows vous transmet un pointeur vers un WINDOWPOS dans le lparam. Vous ne pouvez pas simplement renvoyer ce lparam à votre processus principal car la mémoire vers laquelle pointe le lparam n’est valide que dans le processus qui reçoit le message.

C’est ce que signifiait “efotinis” quand il disait “fourrez les informations nécessaires (événement et HWND) dans sa WPARAM / LPARAM”. Si vous souhaitez transmettre des messages plus complexes, vous devrez utiliser d’autres IPC (comme les canaux nommés, les fichiers TCP ou les fichiers mappés en mémoire).

Lol, il semble que l’erreur est dans le code de test.

Si vous créez deux boutons distincts, un pour Init et un pour UnInit (je préfère Exit).

 procedure THooktest_FO.UnInitClick(Sender: TObject); begin UninitHook; end; procedure THooktest_FO.InitClick(Sender: TObject); begin InitHook(HookCallback) end; 

Démarrez l’application. Cliquez sur Init, puis sur le bouton Test, la sortie suivante est affichée:

 created handle #1902442 destroyed handle #1902442 created handle #1967978 created handle #7276488 

Ensuite, le messagebox est affiché.

Si vous cliquez sur OK, vous obtenez:

 destroyed handle #1967978 

HTH

J’ai trouvé la documentation de base Delphi pour SetWindowsHookEx. Mais le texte est un peu vague.

 function SetWindowsHookEx(idHook: Integer; lpfn: TFNHookProc; hmod: HInst; dwThreadId: DWORD): HHOOK; 
  • hmod: Un handle vers le module (une DLL) contenant la fonction hook pointée par le paramètre lpfn. Ce paramètre doit être défini sur zéro si dwThreadId identifie un thread créé par le processus en cours et qu’un dlpfn pointe vers une fonction hook située dans le code associé au processus en cours.

  • dwThreadId: identifiant du thread auquel la fonction hook sera associée. Si ce paramètre est défini sur zéro, le hook sera un hook système associé à tous les threads existants.

Au fait, pour le paramètre hmod, vous devriez avoir utilisé un handle de module. (HINSTANCE pointe vers le handle de l’application).

 hand := GetModuleHandle('hookhelper.dll'); Hook := SetWindowsHookEx(WH_SHELL, @HookProc, hand, 0); 

Mais bien que la main diffère de HINSTANCE, elle montre toujours le même résultat.