最終更新 2004 02/25
サンプルのダウンロード → BCB_MessageHook.lzh(136k)

【 プロジェクトの構築方法 】
BCB_MessageHook フォルダと BCB_MessageHookDLL フォルダを適当な場所に作り
そこにソースファイルをコピーします。
●BCB_MessageHook フォルダ
BCB メニューの「アプリケーションの新規作成(T)」を選んだ後
「すべて保存(V)」を選びます。
ファイルを保存する場所を BCB_MessageHook フォルダに変更してから
ソースファイル名はそのまま保存して、プロジェクトファイルだけ
BCB_MessageHook.bpr に名前を変更して保存します。
BCB のイベントはソースをコピーするだけでは認識してくれないので
FormCreate イベントと FormCloseQuery イベントの部分は
オブジェクトインスペクタのイベントから呼び出して、別途ソースを
貼り付けないといけません。
フォーム1にラベルを次のように配置します。
フォーム1はオブジェクトインスペクタで次のように設定します。
BorderIcons - biMinimize = false
BorderStyle = bsSingle
Caption = フック実行ウィンドウ
Height = 110
Width = 300
Position = poDefaultPosOnly
●BCB_MessageHookDLL フォルダ
BCB メニューの「新規作成(N)」を選んだ後、新規作成ダイアログの
「新規作成」タブから「DLL ウィザード」を選択します。
DLL ウィザードダイアログで「ソースの種類」を C++(+)、VCL を使う(V)
に設定します。
BCB メニューの「すべて保存(V)」を選びます。
ファイルを保存する場所を BCB_MessageHookDLL フォルダに変更してから
ソースファイル名はそのまま保存して、プロジェクトファイルだけ
BCB_MessageHookDLL.bpr に名前を変更して保存します。
そうしたら Unit1.cpp にサンプルソースを貼り付けてプロジェクトを
メイクします。
【 サンプルの実行方法 】
●BCB_MessageHook.exe
●BCB_MessageHookDLL.dll
●API_SetWindowsHookEx1.exe
を全て同じフォルダにコピーします。
そうしたら、API_SetWindowsHookEx1.exe を起動した後、BCB_MessageHook.exe
を実行します。
BCB_MessageHook の Unit1.h のソースコード
BCB_MessageHook の Unit1.cpp のソースコード
BCB_MessageHookDLL の Unit1.cpp のソースコード
【 VC++ との違い 】
BCB のメッセージフックは VC とはやり方が異なります。
グローバルフックを行うのにDLLを使う点は同じですが、共有メモリの扱い方が
異なります。
フックについての基本的なことは VC でのフック方法もご一読頂くとよいと思います。
BCB では #pragma data_seg が使えないのと、#pragma comment で共有メモリの
設定ができないので、共有メモリは別の方法で実装しなければいけません。
BCB では共有メモリを使うために、ファイルマッピングオブジェクトというものを
使います。
ファイルマッピングオブジェクトの実装には API 関数を使うので、VC でも
利用できます。
VC で紹介した方法では BCB との互換性はありませんが、こちらで紹介する方法は
少しソースをいじれば VC でも使用可能です。
【 ファイルマッピングオブジェクトの使い方 】
ファイルマッピングオブジェクトは次の流れで処理を行います。
1.CreateFileMapping() でファイルマッピングオブジェクトを作成
2.OpenFileMapping() でファイルマッピングオブジェクトを開く
3.MapViewOfFile() でファイルマッピングオブジェクトのポインタを取得
4.取得したポインタに値を設定したり取り出したりする
5.UnmapViewOfFile() でファイルマッピングオブジェクトのポインタを解放
6.CloseHandle() で OpenFileMapping() で取得したハンドルを閉じる
7.CloseHandle() で CreateFileMapping() で取得したハンドルを解放
ファイルマッピングオブジェクトに登録する値が複数ある場合は、適当な構造体を
作ってそこに値を入れてしまえば、その構造体ひとつだけで値をやり取りできます。
struct ShareDataStruct
{
HWND hWnd; //フックしたメッセージを返すウィンドウのハンドル
HHOOK hHook; //フックハンドル
WORD messageId; //フックしたメッセージのID
MSG message; //フックしたメッセージの値を保存
};
構造体を使わない場合は、ひとつの変数に対してファイルマッピングオブジェクトを
ひとつ作らないといけません。
それはさすがに面倒なので、やはり構造体で管理する方がよいです。
この構造体の値を CreateFileMapping() 関数を使ってファイルマッピング
オブジェクトに登録します。
HANDLE hFMObject = CreateFileMapping(
(HANDLE)0xFFFFFFFF,
NULL,
PAGE_READWRITE,
0,
sizeof(ShareDataStruct),
"ファイルマッピングオブジェクトの登録名"
);
|
これでファイルマッピングオブジェクトが作成できます。
CreateFileMapping() でファイルマッピングオブジェクトを作成したら
通常のファイルと同じように OpenFileMapping() でファイルを開きます。
ファイルマッピングオブジェクトの登録名 は OpenFileMapping() でも
使うので、ここに指定する値は #define するか、定数にしておくとよいです。
HANDLE hFmo = OpenFileMapping(
FILE_MAP_ALL_ACCESS,
false,
"ファイルマッピングオブジェクトの登録名"
);
|
CreateFileMapping() と OpenFileMapping() で取得できるハンドルは
別物なので、それぞれ別の変数に保存しておきます。
ファイルマッピングオブジェクトを開くと、値を取り出すことができるように
なります。
ファイルマッピングオブジェクトに登録した値を取り出すには MapViewOfFile()
を使います。
MapViewOfFile() に指定するのは OpenFileMapping() で取得したハンドルです。
ShareDataStruct* p = (ShareDataStruct*)::MapViewOfFile(
hFmo,
FILE_MAP_ALL_ACCESS,
0,
0,
0
);
|
値はポインタで返ってくるので、このポインタに値を指定すればファイルマッピング
オブジェクトに登録されている値も変わります。
MSG msg;
memset( &msg, 0, sizeof(msg) );
p->hWnd = hWnd;
p->hHook = SetWindowsHookEx(...);
p->messageId = 0;
p->message = msg;
|
値の設定や取得が終わったら UnmapViewOfFile() でポインタを解放します。
その後、CloseHandle( hFmo ) とやって OpenFileMapping() で取得した
ハンドルを閉じて、CloseHandle( hFMObject ) とやって、登録したファイル
マッピングオブジェクトを削除します。
【 フック用メッセージを登録 】
VC++ のサンプルでは、フックしたメッセージをそのまま呼び出し元のウィンドウに
送信していましたが、それではさすがに呼び出し元ウィンドウのメッセージなのか
フックしたウィンドウのメッセージなのか区別できないので少々問題があります。
そこでメッセージをフックした場合はこちら側で定義したメッセージを送信することで
呼び出し元で自分のウィンドウのメッセージなのか、フックしたメッセージなのかを
区別できるようにします。
一番簡単なのは WM_USER + 100 などのような値を使うのですが、DLLの場合は
いろいろと問題があります。
簡単に説明すると、WM_USER + ? の値を使うと、メッセージIDの衝突が起きます。
そこで RegisterWindowMessage( "登録名" ) を使って取得したメッセージIDを
使います。
「DLL側」
メッセージをフック
↓
フックしたメッセージの情報をファイルマッピングオブジェクトに保存
↓
RegisterWindowMessage() で取得したメッセージIDを呼び出し元に送信
↓
「呼び出し元」
受信したメッセージが RegisterWindowMessage() で取得したメッセージIDと
一致するか調べる ( DLL の GetHookMessageId() で取得 )
↓
一致したらファイルマッピングオブジェクトに保存したメッセージ情報を取得
( DLL の GetHookMessage() で取得 )
↓
そのメッセージごとに処理を行う
前回と違うのは、DLLから送信されるメッセージはフックしたメッセージ
そのものでなくて、「メッセージをフックしましたよ」という目印だけです。
ですから、どんなメッセージをフックしたのかは分かりません。
どんなメッセージをフックしたのかは、DLLの GetHookMessage() を使って
調べます。
【 DLL のソースを VC++ でも使うには 】
今回のDLLのソースは
#include<vcl.h>、#pragma hdrstop、#pragma argsused の行と
各関数の __stdcall を削除
int WINAPI DllEntryPoint を BOOL WINAPI DllMain に変更
以上のように変更すれば、VC++ でも使えます。
一応、呼び出し元のソースもDLLに合わせて変更する必要があるので
VC 用に変更した呼び出し元とDLLのソースをのせておきます。
呼び出し元のソース( プロジェクト名:MessageHook )
DLL のソース( プロジェクト名:MessageHookDLL )
【 TFormで受信したメッセージを処理するには 】
WndProc() メソッドをオーバーライドします。
BCB のフォーム(TForm)でメッセージを受信すると、最初にこの WndProc()
メソッドが呼び出されます。
この関数は virtual 宣言されているので、オーバーライドして内容を
変更することができます。
このメソッドの中で、DLLから「メッセージをフックしたよ」という
意味のメッセージを受信したときの処理を書きます。
サンプルを見れば実装方法はお分かりいただけると思います。
注意する点は、一番最初に親クラスの WndProc() を実行するという点です。
Form1 の FormCreate イベントなんかの処理は、親クラスの WndProc() で
処理されるので、最初に TForm::WndProc( Message ); を呼び出して
親クラスの処理を実行しておかないと、Form1 で設定したイベントが全く
実行されなくなってしまいます。
Form1 は TForm を継承しているので、TForm に定義されているイベントは
Form1 の WndProc() ではなく、TForm の WndProc() で処理されます。
|