Delphiで共有変数ユニットの作成
次に、これを元にして、DelphiでDLLを作ってみます。Delphi IDEのメニューの[ファイル]-[新規作成]から[DLL]を選択してプロジェクトを作成します。ここでは、「mhook.dpr」という名前で保存します。
それから、簡単に変数を共有するためのユニットを作ります(IDEのメニューの[ファイル]-[新規作成]-[ユニット]をクリックします)。このユニットは「unit_share.pas」という名前で保存します。
ユニットの内容は次のとおりです。
unit unit_share; interface uses Windows, Messages, SysUtils; const MMF_NAME = 'mhook_share_data'; type // 共有したいデータの構造体を定義 PShareData = ^TShareData; TShareData = record hParent : THandle; hHook : THandle; end; var ShareData: PShareData = nil; // 共有データへのポインタ implementation var hCreate: THandle; // メモリマップドファイルのハンドル procedure BeginShareData; var bAlreadyExists: Boolean; begin // 共有メモリ(メモリマップドファイル)を作成 hCreate := CreateFileMapping($ffffffff, nil, PAGE_READWRITE or SEC_COMMIT, 0, SizeOf(TShareData), MMF_NAME); bAlreadyExists := (hCreate <> 0) and (GetLastError = ERROR_ALREADY_EXISTS); // メモリにマップする ShareData := MapViewOfFile(hCreate, FILE_MAP_ALL_ACCESS, 0, 0, 0); // 共有変数の初期化 if not bAlreadyExists then begin ShareData^.hParent := 0; ShareData^.hHook := 0; end; end; procedure EndShareData; begin if ShareData <> nil then UnmapViewOfFile(ShareData); if hCreate <> 0 then CloseHandle(hCreate); end; initialization BeginShareData; finalization EndShareData; end.
ユニットを初期化・破棄する「initialization」を定義し、DLLが読み込まれると強制的に共有メモリが作成されるという仕組みにしています。Visual C++なら、ほんの数行宣言を加えるとできるのですが、Delphi だとこのようなユニットを作らなければなりません。ですが、一度作ってしまえば、これを少し変えるだけで共有変数を実現できます。
フックDLLの作成
そして、肝心のフックを行うDLLのメインプログラムは次のように作ります。こちらはC言語のプログラムと大して変わりません。
library mhook; uses Windows, Messages, SysUtils, unit_share in 'unit_share.pas'; const WM_USER_MOUSE_ACTION = WM_USER + $1001; // フック関数 function HookProc(code: Integer; wp:WPalram; lp:LPalram): LRESULT; stdcall; var pms: PMouseHookStruct; begin pms := PMouseHookStruct(lp); PostMessage(ShareData^.hParent, WM_USER_MOUSE_ACTION, wp, pms^.wHitTestCode); Result := CallNextHookEx(ShareData^.hHook, code, wp, lp); end; // フックをインストールする関数 function HookInstall(Parent:THandle): BOOL; stdcall; begin if (ShareData = nil)or(Parent = 0) then begin Result := False; Exit; end; ShareData^.hParent := Parent; ShareData^.hHook := SetWindowsHookEx(WH_MOUSE, @HookProc, HInstance, 0); Result := (ShareData^.hHook > 0); end; // フックをアンインストールする関数 function HookUninstall: BOOL; stdcall; begin Result := True; if ShareData^.hHook <> 0 then begin UnhookWindowsHookEx(ShareData^.hHook); ShareData^.hHook := 0; end; end; // エクスポートする関数の一覧 exports HookInstall, HookUninstall; begin end.
ここで、もう一度フック関数を見てみます。
function HookProc(code: Integer; wp:WPalram; lp:LParam): LRESULT; stdcall; var pms: PMouseHookStruct; begin pms := PMouseHookStruct(lp); PostMessage(ShareData^.hParent, WM_USER_MOUSE_ACTION, wp, pms^.wHitTestCode); Result := CallNextHookEx(ShareData^.hHook, code, wp, lp); end;
はじめに作ったプログラムと違うところは、メッセージを親ウィンドウに投げる際の引数です。WPalamには、ウィンドウメッセージの種類を、LParamには、PMouseHookStruct.wHitTestCode
を指定しています。
ここを直接PMouseHookStruct
へのポインタを指定して投げると、親ウィンドウがメッセージを受け取り、データを参照する頃には、PMouseHookStruct
の内容が破棄されているためメモリのアクセス違反が起きてしまいます。そこで、必要なデータだけを投げるようにしています。
PMouseHookStruct
のメンバであるwHitTestCode
は、どの領域をクリックしたかという情報が入っています。マウスでフォームをリサイズしたかどうかを判別するために必要になります。