|
Delphi Programming / Object Pascal
[掲載 2007年09月23日] [更新 2015年03月08日] Delphi サンプルプログラム集 |
300_グローバルフック WH_CALLWNDPROC |
動作確認等 |
Windows 7 U64(SP1) + Delphi XE(UP1) Pro |
|
300_HookCallWndProc.zip [1,228 KB] 2015年03月08日版 (EXE 同梱) |
- 2007年10月29日
- コードをコピペして使用した場合に発生する [リストのインデックスが範囲を超えています] に対する修正
- 2008年07月14日
- [メッセージを選択してテスト] を追加
- 2008年12月20日
- [02] のプロジェクト名を変更.DLL 名が [01] と重複しないようにした
- 2009年07月27日
- 他のフックのサンプルと重複しないように MapFileName を変更
- コールバックの戻り値 (Result) を CallNextHookEx にした
- 2015年03月08日
- WM_USER を WM_APP に変更
- Windows 7 + Delphi XE で動作可能なコードに修正
- UIPI (ユーザーインターフェイス特権の分離) 対応コードを追加
- コード整備
|
01_グローバルフック WH_CALLWNDPROC |
WH_CALLWNDPROC フック関数のコールバック関数は,SendMessage によって呼出され,このフック関数を通過してから目的のウィンドウに送られます.受取ったメッセージの変更はできません.
本サンプルは,このフック関数を DLL 内に置くことによりグローバルフックとしています. |
グローバルフックとローカルフックの違いは,SetWindowsHookEx 関数の引数の違いです.
ローカルフックの場合は,次のように,第 3 引数の値を 0,第 4 引数の値を GetCurrentThreadID にすれば,多くの場合十分です.ローカルフックの場合,DLL にする必要はありません.
|
SetWindowsHookEx(WH_CALLWNDPROC, @MyHookProc, 0, GetCurrentThreadID);
|
図1
実行画面
|
リスト1
グローバルフック WH_CALLWNDPROC のテスト |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls, Buttons, ExtCtrls;
type
TForm1 = class(TForm)
ListView1: TListView;
Panel1: TPanel;
Button1: TButton;
Button2: TButton;
SpeedButton1: TSpeedButton;
procedure FormDestroy(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure SpeedButton1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
protected
procedure DisplayWindowInfo(ClassName, TitleStr: String);
function GetWindowTitleStr(hWindow: HWND): String;
function GetWindowClassNameStr(hWindow:HWND):string;
private
{ Private declarations }
hHookLib : THandle;
procedure WMAPPMessage(var Msg: TMessage); message WM_APP+200;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
// DLL内の呼出関数類
function StartCallWndProcHook(Wnd: HWND): Boolean; stdcall;
external 'PLHOOKCALLWNDPROC.DLL';
procedure StopCallWndProcHook; stdcall; external 'PLHOOKCALLWNDPROC.DLL';
//=============================================================================
// フォーム生成時の処理
// UIPI対応処理(管理者権限で実行してもフックを可能にする)
// クラス名とタイトルを一覧表示するためのListViewの設定
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
const
MSGFLT_ADD = 1;
MSGFLT_REMOVE = 2;
var
DLLHandle : THandle;
FuncFilter : function (msg: Cardinal; dwFlag: DWORD): BOOL; stdcall;
LStyle : LongInt;
begin
//UIPI(ユーザーインターフェイス特権の分離)対応処理
//このプログラムを管理者権限で実行してもメッセージの受信を可能にする
if CheckWin32Version(6, 0) then begin
DLLHandle := loadLibrary('user32.dll');
try
if DLLHandle > 0 then begin
@FuncFilter := GetProcAddress(DLLHandle, 'ChangeWindowMessageFilter');
if @FuncFilter <> nil then begin
FuncFilter(WM_APP+200, MSGFLT_ADD);
end;
end;
finally
FreeLibrary(DLLHandle);
end;
end;
Caption := 'WH_CALLWNDPROC テスト';
ListView1.ViewStyle := vsReport;
ListView1.Columns.Add;
ListView1.Columns[0].Caption := ' CLASS';
ListView1.Columns[0].Width := 130;
ListView1.Columns.Add;
ListView1.Columns[1].Caption := ' TITLE';
ListView1.Columns[1].Width := 161;
//垂直スクロールバーを最初から表示
LStyle := GetWindowLong(ListView1.Handle, GWL_STYLE);
ShowScrollBar(ListView1.Handle, SB_VERT, True);
SetWindowLong(ListView1.Handle, GWL_STYLE, LStyle);
end;
//=============================================================================
// フォーム破棄時の処理
// フックを解除
// DLLの解放
//=============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
if hHookLib <> 0 then begin
StopCallWndProcHook;
FreeLibrary(hHookLib);
hHookLib := 0;
end;
end;
//=============================================================================
// フックを有効にする
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
if hHookLib = 0 then begin
hHookLib := LoadLibrary('PLHOOKCALLWNDPROC.DLL');
StartCallWndProcHook(Handle);
end;
end;
//=============================================================================
// フックを無効にする
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
//フォームを破棄する時と同じ処理を実行
//このようにイベント用のメソッドを実行してもイベントとしては機能しない
FormDestroy(nil);
end;
//=============================================================================
// ListViewのアイテムをクリア
//=============================================================================
procedure TForm1.SpeedButton1Click(Sender: TObject);
begin
ListView1.Items.Clear;
ListView1.Update;
end;
//-----------------------------------------------------------------------------
// DLLでPostMessageで送ってきたメッセージを受取る
//
// DLL側で以下の値をセットしている
// Msg.wParam メッセージ識別子
// Msg.lParam メッセージを受取るウィンドウのハンドル値
//-----------------------------------------------------------------------------
procedure TForm1.WMAPPMessage(var Msg: TMessage);
var
LTargetWnd : HWND;
LClass : String;
LTitle : String;
begin
if Msg.Msg = WM_APP + 200 then begin
//ここのWM_XXXXの(メッセージ識別子)を変更してテストすれば
//WH_CALLWNDPROCフックがどんなメッセージを捕捉可能かを確認可能
if Msg.WParam = WM_ACTIVATE then begin
LTargetWnd := Msg.LParam;
LClass := GetWindowClassNameStr(LTargetWnd);
LTitle := GetWindowTitleStr(LTargetWnd);
DisplayWindowInfo(LClass, LTitle);
end;
end;
end;
//-----------------------------------------------------------------------------
// ListViewにCreateしたウィンドウのクラス名とタイトルを表示
//-----------------------------------------------------------------------------
procedure TForm1.DisplayWindowInfo(ClassName, TitleStr: String);
var
LItem : TListItem;
begin
LItem := ListView1.Items.Add;
LItem.Caption := ClassName;
LItem.SubItems.Add(TitleStr);
//ListViewの一番最後を表示
//ここでは実際にはMakeVisibleだけでOK
ListView1.Items[ListView1.Items.Count - 1].Selected := True;
ListView1.Items[ListView1.Items.Count - 1].Focused := True;
ListView1.Items[ListView1.Items.Count - 1].MakeVisible(True);
end;
//-----------------------------------------------------------------------------
// ウィンドウハンドルからウィンドウのクラス名を取得
//-----------------------------------------------------------------------------
function TForm1.GetWindowClassNameStr(hWindow: HWND): string;
var
Buffer : array[0..MAX_PATH-1] of Char;
Len : Integer;
begin
FillChar(Buffer, SizeOf(Buffer), #0);
Len := GetClassName(hWindow, Buffer, Length(Buffer));
if Len > 0 then Result := Buffer else Result := '';
end;
//-----------------------------------------------------------------------------
// ウィンドウハンドルからウィンドウのタイトルを取得
//-----------------------------------------------------------------------------
function TForm1.GetWindowTitleStr(hWindow: HWND): String;
var
Buffer : array[0..MAX_PATH-1] of Char;
Len : Integer;
begin
FillChar(Buffer, SizeOf(Buffer), #0);
Len := GetWindowText(hWindow, Buffer, Length(Buffer));
if Len > 0 then Result := Buffer else Result := '';
end;
end.
リスト2
サンプルプログラムで使用している DLL のプロジェクトソース |
library plHookCallWndProc;
uses
plHookCallWndProcUnit in 'plHookCallWndProcUnit.pas';
//-----------------------------------------------------------------------------
// 外部からDLL内のメソッドを利用可能にするためのオマジナイ
//-----------------------------------------------------------------------------
exports
StartCallWndProcHook,
StopCallWndProcHook;
end.
リスト3
DLL 本体のコードドは,グローバルフックのサンプルでほとんど同じ
違うのはコールバック関数の部分と,フック開始関数の引数等が違う場合がある |
unit plHookCallWndProcUnit;
interface
uses
Windows, Messages;
//外部のアプリケーションから使用する関数類
function StartCallWndProcHook(HostWnd: HWND): Boolean; stdcall;
procedure StopCallWndProcHook; stdcall;
implementation
//共有メモリの内容の構造体
type
PHookInfo = ^THookInfo;
THookInfo = record
HookHandle : HHOOK;
HostWnd : HWND;
end;
var
hMapFile : THandle; //メモリマップドファイルのハンドル
const
MapFileName = 'HookCallWndProc'; //メモリマップドファイル名
//-----------------------------------------------------------------------------
// 共有メモリアクセスのための準備
//
// 成功すると0を返す.失敗すると負数を返す.
// 負の値で処理を分岐できるようになっているが,このコードでは未使用.
//-----------------------------------------------------------------------------
function MapFileMemory(var AMapWnd: THandle; var ApMap: Pointer):integer;
begin
//メモリマップドファイルを開く
AMapWnd := OpenFileMapping(FILE_MAP_ALL_ACCESS, False, MapFileName);
if AMapWnd = 0 then begin
Result := -1;
end else begin
//メモリマップドファイルの割り当て
ApMap := MapViewOfFile(AMapWnd, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if ApMap = nil then begin
Result := -2;
CloseHandle(AMapWnd);
exit;
end;
Result := 0;
end;
end;
//-----------------------------------------------------------------------------
// 共有メモリアクセスの後始末
// 割当てたメモリがあればビューを解除
// ハンドルがあればクローズ
//-----------------------------------------------------------------------------
procedure UnMapFileMemory(AMapWnd: THandle; ApMap: Pointer);
begin
if ApMap <> nil then UnmapViewOfFile(ApMap);
if AMapWnd <> 0 then CloseHandle(AMapWnd);
end;
//-----------------------------------------------------------------------------
// フックのコールバック関数
// このDLLを使用したアプリにメッセージを送る
//
// nCode フックコード
// wPar カレントプロセスからのメッセージの場合0以外の値
// 別のプロセスからのメッセージの場合は0
// lPar メッセージ情報が格納されているTCWPStruct構造体のポインタ.
// 型をそのポインタ型のPCWPStructとしてもいいが,ここではIntegerのままと
// し,構造体から値を取得する時に変数LStructにそのポインタを代入
//
// メッセージがSendMessage関数によって送られる前にこのフック関数を通過する
// PostMessageで呼出元に送るのはメッセージの識別子とウィンドウハンドル
// フック関数のLPARAM等をそのまま送ってもよいが,メッセージ識別子とハンドルが
// 取得できれば通常の用途では十分と考えた
//-----------------------------------------------------------------------------
function MyHookProc(nCode: Integer; wPar: WPARAM; lPar: LPARAM): LRESULT; stdcall;
var
LpMap : Pointer;
LWnd : THANDLE;
LTargetWnd : HWND;
LpStruct : PCWPStruct;
LMsg : Integer;
begin
Result := 0;
if MapFileMemory(LWnd, LpMap) <> 0 then exit;
if nCode < 0 then begin
Result := CallNextHookEx(pHookInfo(LpMap)^.HookHandle, nCode, wPar, lPar);
end else begin
if nCode = HC_ACTION then begin
LpStruct := PCWPStruct(lPar);
LMsg := LpStruct^.message;
LTargetWnd := LpStruct^.hwnd;
if LTargetWnd <> pHookInfo(LpMap)^.HostWnd then begin
PostMessage(pHookInfo(LpMap)^.HostWnd, WM_APP + 200, LMsg, LTargetWnd);
end;
end;
Result := CallNextHookEx(pHookInfo(LpMap)^.HookHandle, nCode, wPar, lPar);
end;
UnMapFileMemory(LWnd, LpMap);
end;
//-----------------------------------------------------------------------------
// フック関数の登録
// 登録するフック関数はMyHookProc
//-----------------------------------------------------------------------------
function StartCallWndProcHook(HostWnd: HWND): Boolean; stdcall;
var
LMapWnd : THandle;
LpMap : Pointer;
begin
Result := False;
//メモリマップドファイル使用準備
MapFileMemory(LMapWnd, LpMap);
if LpMap = nil then begin
LMapWnd := 0;
end else begin
//フック情報構造体初期化とフック関数の登録
pHookInfo(LpMap)^.HostWnd := HostWnd;
//フックをインストール
//ローカルフックの場合は3番目の引数は0(null)でOK
//グローバルの場合はDLLのインスタンス
//4番目はフック対象のスレッドID(システム全体の時は0)
pHookInfo(LpMap)^.HookHandle := SetWindowsHookEx(WH_CALLWNDPROC,
@MyHookProc,
hInstance,
0);
//フック成功
if pHookInfo(LpMap)^.HookHandle > 0 then begin
Result := True;
end;
//メモリマップドファイル使用終了処理
UnMapFileMemory(LMapWnd, LpMap);
end;
end;
//-----------------------------------------------------------------------------
// フックの解除
//-----------------------------------------------------------------------------
procedure StopCallWndProcHook; stdcall;
var
LMapWnd : THandle;
LpMap : Pointer;
begin
//メモリマップドファイル使用準備
MapFileMemory(LMapWnd, LpMap);
if LpMap = nil then begin
LMapWnd := 0;
end else begin
//フック解除
if pHookInfo(LpMap)^.HookHandle > 0 then begin
UnhookWindowsHookEx(pHookInfo(LpMap)^.HookHandle);
end;
//メモリマップドファイル使用終了処理
UnMapFileMemory(LMapWnd, LpMap);
end;
end;
//-----------------------------------------------------------------------------
// ユニット初期化部
// メモリマップドファイルの作成
//
// High(NativeUInt)の値は
// 32ビットアプリでは$FFFFFFFF
// 64ヒットアプリでは$FFFFFFFFFFFFFFFF
//-----------------------------------------------------------------------------
initialization
begin
hMapFile := CreateFileMapping(High(NativeUInt),
nil,
PAGE_READWRITE,
0,
SizeOf(THookInfo),
MapFileName);
end;
//-----------------------------------------------------------------------------
// ユニット終了処理部
// メモリマップドファイルのクローズ
//-----------------------------------------------------------------------------
finalization
begin
if hMapFile <> 0 then CloseHandle(hMapFile);
end;
end.
他のプログラムのテストのために作成したものです.WH_CALLWNDPROC フック関数のフック対象メッセージをコンボボックスから選択するようにしています.別のメッセージを選択するとフックを中止しますので,再度フック開始ボタンをクリックします.
コンボボックスには Delphi 7 当時で扱える 227 の WM_XXXX メッセージリストをリソースから読み出してセットしていますが,WH_CALLWNDPROC でこれらの全てがフックできるわけではありません.マウスやキーボートの操作はフックできません.
|
|
図2
実行画面 |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls, Buttons, ExtCtrls;
type
THookWinProcForm = class(TForm)
Panel1: TPanel;
btnStart: TButton;
btnStop: TButton;
btnClear: TSpeedButton;
Panel2: TPanel;
ComboBox1: TComboBox;
ListView1: TListView;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure btnStartClick(Sender: TObject);
procedure btnStopClick(Sender: TObject);
procedure btnClearClick(Sender: TObject);
procedure ComboBox1CloseUp(Sender: TObject);
protected
procedure DisplayMessageInfo(AClass, ATitle: String; AwParam: Cardinal);
function GetWindowTitleStr(hWindow: HWND): String;
function GetWindowClassNameStr(hWindow: HWND): String;
private
hDllHandle : THANDLE;
procedure WMAPPMessage(var Msg: TMessage); message WM_APP+200;
public
end;
var
HookWinProcForm: THookWinProcForm;
implementation
{$R *.DFM}
// DLL内の呼出関数類
function StartWndProcHook(Wnd: HWND; AMsg: Integer): Boolean; stdcall;
external 'PLHOOKWNDPROCMSG.DLL';
procedure StopWndProcHook; stdcall; external 'PLHOOKWNDPROCMSG.DLL';
//=============================================================================
// フォーム生成時の処理
// リソースからWM_XXXのリストを取出してコンポボックスにセット
// ウィンドウメッセージリストのリソースファイルはMessageList.txt
// セットするのはWM_XXXの文字列とその整数値
//=============================================================================
procedure THookWinProcForm.FormCreate(Sender: TObject);
const
MSGFLT_ADD = 1;
MSGFLT_REMOVE = 2;
var
DLLHandle : THandle;
FuncFilter : function (msg: Cardinal; dwFlag: DWORD): BOOL; stdcall;
LResource : TResourceStream;
LList : TStringList;
i : Integer;
LText1 : String;
LText2 : String;
LPos : Integer;
LStyle : Integer;
begin
//UIPI(ユーザーインターフェイス特権の分離)対応処理
//このプログラムを管理者権限で実行してもメッセージの受信を可能にする
if CheckWin32Version(6, 0) then begin
DLLHandle := loadLibrary('user32.dll');
try
if DLLHandle > 0 then begin
@FuncFilter := GetProcAddress(DLLHandle, 'ChangeWindowMessageFilter');
if @FuncFilter <> nil then begin
FuncFilter(WM_APP + 200, MSGFLT_ADD);
end;
end;
finally
FreeLibrary(DLLHandle);
end;
end;
Caption := 'WH_CALLWNDPROC テスト';
LResource := TResourceStream.Create(hInstance, 'MessageList', 'TEXT');
LList := TStringList.Create;
ComboBox1.Items.Clear;
try
LList.LoadFromStream(LResource);
for i := 0 to LList.Count - 1 do begin
LPos := Pos('=', LList[i]);
LText1 := Trim(Copy(LList[i], 1, LPos - 1));
LText2 := Trim(Copy(LList[i], LPos + 1, Length(LList[i])));
ComboBox1.Items.AddObject(LText1, TObject(StrToInt(LText2)));
end;
finally
FreeAndNil(LResource);
FreeAndNil(LList);
end;
//垂直スクロールバーを最初から表示
LStyle := GetWindowLong(ListView1.Handle, GWL_STYLE);
ShowScrollBar(ListView1.Handle, SB_VERT, True);
SetWindowLong(ListView1.Handle, GWL_STYLE, LStyle);
end;
//=============================================================================
// フォーム破棄時の処理
//=============================================================================
procedure THookWinProcForm.FormDestroy(Sender: TObject);
begin
if hDllHandle <> 0 then begin
btnStart.Enabled := True;
btnStop.Enabled := False;
StopWndProcHook;
FreeLibrary(hDllHandle);
hDllHandle := 0;
end;
end;
//=============================================================================
// フックを有効にする
// フック開始関数引数は,このフォームのハンドルと,フック対象メッセージの値
//=============================================================================
procedure THookWinProcForm.btnStartClick(Sender: TObject);
var
LIndex : Integer;
LRevMsg : Integer;
begin
if ComboBox1.ItemIndex < 0 then exit;
if hDllHandle = 0 then begin
LIndex := ComboBox1.ItemIndex;
LRevMsg := Integer(ComboBox1.Items.Objects[LIndex]);
hDllHandle := LoadLibrary('PLHOOKWNDPROCMSG.DLL');
StartWndProcHook(Handle, LRevMsg);
btnStart.Enabled := False;
btnStop.Enabled := True;
end;
end;
//=============================================================================
// フックを無効にする
//=============================================================================
procedure THookWinProcForm.btnStopClick(Sender: TObject);
begin
//フォームを破棄する時と同じ処理を実行
//このようにイベント用のメソッドを実行してもイベントとしては機能しない
FormDestroy(nil);
end;
//=============================================================================
// ListViewのアイテムをクリア
//=============================================================================
procedure THookWinProcForm.btnClearClick(Sender: TObject);
begin
ListView1.Items.Clear;
ListView1.Update;
end;
//=============================================================================
// コンボボックスでフック対象メッセージを変更したら一度フックを止める
//=============================================================================
procedure THookWinProcForm.ComboBox1CloseUp(Sender: TObject);
begin
btnStopClick(nil);
end;
//-----------------------------------------------------------------------------
// ListViewにフックしたウインドウのクラス名とタイトル,wParam値を表示
// wParam値は,下位ワードと上位ワード値に分けて16進数で表示
//-----------------------------------------------------------------------------
procedure THookWinProcForm.DisplayMessageInfo(AClass, ATitle: String;
AwParam: Cardinal);
var
LAItem : TListItem;
LLow : WORD;
LHi : WORD;
begin
LLow := LOWORD(AwParam);
LHi := HIWORD(AwParam);
LAItem := ListView1.Items.Add;
LAItem.Caption := AClass;
LAItem.SubItems.Add (ATitle);
LAItem.SubItems.Add (Format('$%.4x ', [LHi]));
LAItem.SubItems.Add (Format('$%.4x ', [LLow]));
//ListViewの一番最後を表示
//ここでは実際にはMakeVisibleだけでOK
ListView1.Items[ListView1.Items.Count - 1].Selected := True;
ListView1.Items[ListView1.Items.Count - 1].Focused := True;
ListView1.Items[ListView1.Items.Count - 1].MakeVisible(True);
end;
//-----------------------------------------------------------------------------
// DLLからのメッセージを受け取る
//
// DLL側で以下の値をセットしている
// Msg.wParam メッセージを受取るウィンドウのハンドル値
// Msg.lParam メッセージ固有の値(wParam値)
//-----------------------------------------------------------------------------
procedure THookWinProcForm.WMAPPMessage(var Msg: TMessage);
var
LTargetWnd : HWND;
LClass : String;
LTitle : String;
LwParam : Cardinal;
begin
if Msg.Msg = WM_APP + 200 then begin
LTargetWnd := Msg.wParam;
LClass := GetWindowClassNameStr(LTargetWnd);
LTitle := GetWindowTitleStr(LTargetWnd);
LwParam := Msg.LParam;
DisplayMessageInfo(LClass, LTitle, LwParam);
end;
end;
//-----------------------------------------------------------------------------
// ウィンドウハンドルからウィンドウのクラス名を取得
//-----------------------------------------------------------------------------
function THookWinProcForm.GetWindowClassNameStr(hWindow: HWND): String;
var
Buffer : array[0..MAX_PATH-1] of Char;
Len : Integer;
begin
FillChar(Buffer, SizeOf(Buffer), #0);
Len := GetClassName(hWindow, Buffer, Length(Buffer));
if Len > 0 then Result := Buffer else Result := '';
end;
//-----------------------------------------------------------------------------
// ウィンドウハンドルからウィンドウのタイトルを取得
//-----------------------------------------------------------------------------
function THookWinProcForm.GetWindowTitleStr(hWindow: HWND): String;
var
Buffer : array[0..MAX_PATH-1] of Char;
Len : Integer;
begin
FillChar(Buffer, SizeOf(Buffer), #0);
Len := GetWindowText(hWindow, Buffer, Length(Buffer));
if Len > 0 then Result := Buffer else Result := '';
end;
end.
リスト5
ほとんど同じパターンの DLL のプロジェクトファイル |
library plHookWndProcMsg;
uses
plHookWndProcMsgUnit in 'plHookWndProcMsgUnit.pas';
//-----------------------------------------------------------------------------
// 外部からDLL内のメソッドを利用可能にするためのオマジナイ
//-----------------------------------------------------------------------------
exports
StartWndProcHook,
StopWndProcHook;
end.
リスト6
DLL 本体のコードドは,グローバルフックのサンプルでほとんど同じ
違うのはコールバック関数の部分と,フック開始関数の引数等が違う場合がある |
unit plHookWndProcMsgUnit;
interface
uses
Windows, Messages;
//外部のアプリケーションから使用する関数類
function StartWndProcHook(HostWnd: HWND; AMsg: Integer): Boolean; stdcall;
procedure StopWndProcHook; stdcall;
implementation
//共有メモリの内容の構造体
type
PHookInfo = ^THookInfo;
THookInfo = record
HookHandle : HHOOK;
HostWnd : HWND;
RevMessage : Integer;
end;
var
hMapFile : THandle; //メモリマップドファイルのハンドル
const
MapFileName = 'HookWndProcMsg'; //メモリマップドファイル名
//-----------------------------------------------------------------------------
// 共有メモリアクセスのための準備
//
// 成功すると0を返す.失敗すると負数を返す.
// 負の値で処理を分岐できるようになっているが,このコードでは未使用.
//-----------------------------------------------------------------------------
function MapFileMemory(var AMapWnd: THandle; var ApMap: Pointer):integer;
begin
//メモリマップドファイルを開く
AMapWnd := OpenFileMapping(FILE_MAP_ALL_ACCESS, False, MapFileName);
if AMapWnd = 0 then begin
Result := -1;
end else begin
//メモリマップドファイルの割り当て
ApMap := MapViewOfFile(AMapWnd, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if ApMap = nil then begin
Result := -2;
CloseHandle(AMapWnd);
exit;
end;
Result := 0;
end;
end;
//-----------------------------------------------------------------------------
// 共有メモリアクセスの後始末
// 割当てたメモリがあればビューを解除
// ハンドルがあればクローズ
//-----------------------------------------------------------------------------
procedure UnMapFileMemory(AMapWnd: THandle; ApMap: Pointer);
begin
if ApMap <> nil then UnmapViewOfFile(ApMap);
if AMapWnd <> 0 then CloseHandle(AMapWnd);
end;
//-----------------------------------------------------------------------------
// フックのコールバック関数
// このDLLを使用したアプリにメッセージを送る
//
// nCode フックコード
// wPar カレントプロセスからのメッセージの場合0以外の値
// 別のプロセスからのメッセージの場合は0
// lPar メッセージ情報が格納されているTCWPStruct構造体のポインタ.
// 型をそのポインタ型のPCWPStructとしてもいいが,ここではIntegerのままと
// し,構造体から値を取得する時に変数LStructにそのポインタを代入
//
// メッセージがSendMessage関数によって送られる前にこのフック関数を通過する
// PostMessageで呼出元に送るのはメッセージ発生元のハンドルとwParam値
// wParamにウィンドウ操作関係の情報がある
// lParamはウィンドウハンドルやコントロールのIDの場合が多い
//-----------------------------------------------------------------------------
function MyHookProc(nCode: Integer; wPar: WPARAM; lPar: LPARAM): LRESULT; stdcall;
var
LpMap : Pointer;
LWnd : THANDLE;
LTargetWnd : HWND;
LpStruct : PCWPStruct;
LMsg : Integer;
begin
Result := 0;
if MapFileMemory(LWnd, LpMap) <> 0 then exit;
if nCode < 0 then begin
Result := CallNextHookEx(pHookInfo(LpMap)^.HookHandle, nCode, wPar, lPar);
end else begin
if nCode = HC_ACTION then begin
if wPar = 0 then begin
LpStruct := PCWPStruct(lPar);
LMsg := LpStruct^.message;
LTargetWnd := LpStruct^.hwnd;
//LpStruct^.wParamはメッセージによって異なる固有の32ビット値
if LMsg = pHookInfo(LpMap)^.RevMessage then begin
PostMessage(pHookInfo(LpMap)^.HostWnd,
WM_APP+200,
LTargetWnd,
LpStruct^.wParam);
end;
end;
end;
Result := CallNextHookEx(pHookInfo(LpMap)^.HookHandle, nCode, wPar, lPar);
end;
UnMapFileMemory(LWnd, LpMap);
end;
//-----------------------------------------------------------------------------
// フック関数の登録
// 登録するフック関数はMyHookProc
// AMsgは調査対象のウィンドウメッセージの値(WM_XXXX)
//-----------------------------------------------------------------------------
function StartWndProcHook(HostWnd: HWND; AMsg: Integer): Boolean; stdcall;
var
LMapWnd : THandle;
LpMap : Pointer;
begin
Result := False;
//メモリマップドファイル使用準備
MapFileMemory(LMapWnd, LpMap);
if LpMap = nil then begin
LMapWnd := 0;
end else begin
//フック情報構造体初期化とフック関数の登録
pHookInfo(LpMap)^.HostWnd := HostWnd;
pHookInfo(LpMap)^.RevMessage := AMsg;
//フックをインストール
//ローカルフックの場合は3番目の引数は0(null)でOK
//グローバルの場合はDLLのインスタンス
//4番目はフック対象のスレッドID(システム全体の時は0)
pHookInfo(LpMap)^.HookHandle := SetWindowsHookEx(WH_CALLWNDPROC,
@MyHookProc,
hInstance,
0);
//フック成功
if pHookInfo(LpMap)^.HookHandle > 0 then begin
Result := True;
end;
//メモリマップドファイル使用終了処理
UnMapFileMemory(LMapWnd, LpMap);
end;
end;
//-----------------------------------------------------------------------------
// フックの解除
//-----------------------------------------------------------------------------
procedure StopWndProcHook; stdcall;
var
LMapWnd : THandle;
LpMap : Pointer;
begin
//メモリマップドファイル使用準備
MapFileMemory(LMapWnd, LpMap);
if LpMap = nil then begin
LMapWnd := 0;
end else begin
//フック解除
if pHookInfo(LpMap)^.HookHandle > 0 then begin
UnhookWindowsHookEx(pHookInfo(LpMap)^.HookHandle);
end;
//メモリマップドファイル使用終了処理
UnMapFileMemory(LMapWnd, LpMap);
end;
end;
//-----------------------------------------------------------------------------
// ユニット初期化部
// メモリマップドファイルの作成
//
// High(NativeUInt)の値は
// 32ビットアプリでは$FFFFFFFF
// 64ヒットアプリでは$FFFFFFFFFFFFFFFF
//-----------------------------------------------------------------------------
initialization
begin
hMapFile := CreateFileMapping(High(NativeUInt),
nil,
PAGE_READWRITE,
0,
SizeOf(THookInfo),
MapFileName);
end;
//-----------------------------------------------------------------------------
// ユニット終了処理部
// メモリマップドファイルのクローズ
//-----------------------------------------------------------------------------
finalization
begin
if hMapFile <> 0 then CloseHandle(hMapFile);
end;
end.
|