WH_SHELLまたはWH_CBTフックプロシージャが他のプロセスからイベントを受信するようにするには、どうすればよいですか?

StackOverflow https://stackoverflow.com/questions/299370

  •  08-07-2019
  •  | 
  •  

質問

SetWindowsHookEx を使用して WH_SHELL フックを設定し、システム全体の HSHELL_WINDOWCREATED および HSHELL_WINDOWDESTROYEDの通知を取得しようとしています。 イベント。 dwThreadId 引数に0を渡します。 = "noreferrer">ドキュメント、「フックプロシージャを、呼び出しスレッドと同じデスクトップで実行されているすべての既存のスレッドに関連付ける」必要があります。また、私が見たすべての例と同様に、 hMod パラメーターのハンドルをDLL(Delphiの HInstance )に渡します。

まだ、私は自分のアプリで作成されたウィンドウについてのみ通知されます-そして、ほとんどの場合、私のテストは、アプリを閉じるとデスクトッププロセスが炎上するという結果になります。尋ねる前に、 UnhookWindowsHookEx を呼び出します。また、ハンドラ内から常に CallNextHookEx を呼び出します。

限られたユーザーアカウントからテストアプリを実行していますが、これまでのところ、これが役割を果たしていることを示すヒントは見つかりませんでした...(実際には驚きました)

AFAICT、私は本ですべてを行いました(明らかにそうではありませんでしたが、今のところどこにあるかわかりません)。

Delphi(2007)を使用していますが、それは実際問題ではないと思います。

編集:以前にこれについて言及したほうがいいかもしれません:いくつかの例をダウンロードして試してみました(残念ながら、Delphiで利用できるものは多くありません。特に WH_SHELL または WH_CBT )。私のテストアプリのようにシステムをクラッシュさせることはありませんが、他のプロセスからイベントをキャプチャしません(ProcessExplorerでそれらが正常にロードされることを確認できますが)。だから、私のシステム構成に何か問題があるか、例が間違っているか、他のプロセスからイベントをキャプチャすることが不可能なようです。誰でも私を啓発できますか?

EDIT2: OK、テストプロジェクトのソースは次のとおりです。

フックプロシージャを含むDLL:

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.

そして、フックを使用するアプリのメインフォーム:

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.
役に立ちましたか?

解決

問題は、フックDLLが実際にいくつかの異なるアドレススペースにロードされていることです。 Windowsは、フックで処理する必要のある外部プロセスでイベントを検出すると、フックDLLをそのプロセスに読み込みます(もちろん、まだ読み込まれていない場合)。

ただし、各プロセスには独自のアドレス空間があります。つまり、InitHook()で渡したコールバック関数ポインターは、EXEのコンテキストでのみ意味があることを意味します(そのため、アプリのイベントに対して機能します)。他のプロセスでは、ポインターは garbage です。無効なメモリの場所を指すか、(さらに悪いことに)ランダムなコードセクションを指す場合があります。結果は、アクセス違反またはサイレントメモリ破損のいずれかです。

通常、解決策は、ある種のインタープロセスを使用することです。通信(IPC)を使用して、EXEを正しく通知します。あなたのケースにとって最も簡単な方法は、メッセージを投稿し、必要な情報(イベントとHWND)をWPARAM / LPARAMに詰め込むことです。 WM_APP + nを使用するか、RegisterWindowMessage()で作成できます。デッドロックを回避するために、メッセージが投稿されて送信されていないことを確認してください。

他のヒント

これはあなたの質問の三次的なものかもしれませんが、あなたが見ているように、フックを正しく取得するのは非常に難しいです-何らかの方法でこれを使用しない場合は、実行してください。特にUIPIに対処しなければならないVistaで、それらのあらゆる種類の問題に遭遇するでしょう。

「efotinis」ということを明確にするだけです。あなたのプロセスにメッセージをポストバックすることについて言及しました-メインプロセスにポストするwParamとlParamはポインタではなく、単に「数字」にすることができます。

たとえば、WM_WINDOWPOSCHANGINGメッセージをフックするとします。Windowsはlparam内のWINDOWPOSへのポインターを渡します。 lparamが指すメモリは、メッセージを受信するプロセスでのみ有効であるため、そのlparamをメインプロセスにポストバックすることはできません。

これは「エフォティニス」です。彼が言ったときの意味必要な情報(イベントとHWND)をWPARAM / LPARAMに詰め込みます」。より複雑なメッセージを返したい場合は、他のIPC(名前付きパイプ、TCP、メモリマップファイルなど)を使用する必要があります。

笑、エラーはテストコードにあるようです。

2つの別個のボタンを作成する場合、1つはInit用、もう1つはUnInit用です(Exitが望ましい)。

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

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

アプリを起動します。 [初期化]をクリックし、[テスト]ボタンをクリックすると、次の出力が表示されます。

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

その後、メッセージボックスが表示されます。

[OK]をクリックすると、次のメッセージが表示されます:

destroyed handle #1967978

HTH

SetWindowsHookExのDelphiベースドキュメントを見つけました。しかし、テキストは少しあいまいです。

function SetWindowsHookEx(idHook: Integer; lpfn: TFNHookProc; 
  hmod: HInst; dwThreadId: DWORD): HHOOK;
  • hmod:lpfnパラメーターが指すフック関数を含むモジュール(DLL)へのハンドル。 dwThreadIdが現在のプロセスによって作成されたスレッドを識別する場合、このパラメーターはゼロに設定する必要があります。dlpfnは、現在のプロセスに関連付けられたコードにあるフック関数を指します。

  • dwThreadId:インストールされたフック関数が関連付けられるスレッドの識別子。このパラメーターをゼロに設定すると、フックは既存のすべてのスレッドに関連付けられたシステム全体のフックになります。

ところで、hmodパラメーターには、モジュールハンドルを使用する必要があります。 (HINSTANCEはアプリケーションハンドルを指します)。

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

しかし、手はHINSTANCEとは異なりますが、同じ結果が表示されます。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top