12

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

それでも、自分のアプリによって作成されたウィンドウの通知を受け取ることはありません。ほとんどの場合、アプリを閉じると、テストの結果、デスクトッププロセスが炎上します。あなたが尋ねる前に、私は電話します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.
4

5 に答える 5

10

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

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

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

于 2008-11-21T23:11:02.353 に答える
2

「efotinis」がプロセスにメッセージを投稿することについて言及したことを明確にするために、メインプロセスに投稿する wParam と lParam はポインターにすることはできず、単に「数値」にすることができます。

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

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

于 2009-01-28T19:44:09.933 に答える
2

これはあなたの質問の 3 番目かもしれませんが、ご覧のとおり、フックを正しく使用するのは非常に困難です。特に UIPI に対処しなければならない Vista では、あらゆる種類の問題に遭遇することになります。

于 2008-12-14T19:52:44.650 に答える
0

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

1 つは Init 用、もう 1 つは UnInit 用の 2 つの別個のボタンを作成する場合 (私は 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

于 2008-11-21T10:08:19.530 に答える
0

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 とはハンドが異なりますが、それでも同じ結果を示します。

于 2008-11-21T13:45:06.380 に答える