以前、紹介したキーフックやマウスフックは、
DLL側をVC6.0で作成したせいで、なにかしら気になってました....
ということで、今度こそC++ Builder6を使って作成します。
VC専用だった、『#pragma data_seg(".shared")〜〜』の代わりに、
共有メモリ(メモリマップドファイル)を使ってDLL側もBuilder6で作ります。
結局、何が出来るようになるかと言うと、
共有メモリに呼び出し側アプリのウィンドウハンドル等を書き込んで
DLL側から、フックしたメッセージを呼び出し側アプリにポストメッセージで送信します。
メッセージを受信したアプリはリストボックスにマウスボタンの種類とカーソル位置を表示します。
よーやく完成版という事で。
まずは、アプリ側から作成していきます。
←フォームはこんなのです。
上側のリストボックスで
DLLから受信したメッセージを表示します。
<(アプリ側) | MouseHookApp.h>
//---------------------------------------------------------------------------
#ifndef MouseHookAppH
#define MouseHookAppH
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
//---------------------------------------------------------------------------
// 共有メモリ用構造体
typedef struct {
HWND hwnd;
}SHARED_DATA;
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE 管理のコンポーネント
TListBox *ListBox1;
TButton *HookStartBtn;
TButton *HookStopBtn;
void __fastcall HookStartBtnClick(TObject *Sender);
void __fastcall HookStopBtnClick(TObject *Sender);
private: // ユーザー宣言
// DLLのインスタンスハンドル
HINSTANCE m_hInst;
// DLL関数呼び出し用の関数ポインタ宣言
__declspec(dllexport)void (*HookStart)(void); // フック開始呼び出し用
__declspec(dllexport)void (*HookStop)(void); // フック停止呼び出し用
HANDLE m_hMapping;
// 共有メモリ作成
void __fastcall SharedMem_Create(void);
// 共有メモリ削除
void __fastcall SharedMem_Delete(void);
// 共有メモリ書込
void __fastcall SharedMem_Write(void);
public: // ユーザー宣言
__fastcall TForm1(TComponent* Owner);
// マウスダウン受信用
void __fastcall WM_LButtonDown(TMessage msg);
// マウスアップ受信用
void __fastcall WM_LButtonUp(TMessage msg);
//---------------------------------------------------------------------------
// メッセージハンドラ
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(WM_USER+WM_LBUTTONDOWN, TMessage, WM_LButtonDown);
MESSAGE_HANDLER(WM_USER+WM_LBUTTONUP, TMessage, WM_LButtonUp);
END_MESSAGE_MAP(TForm)
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif
<(アプリ側) | MouseHookApp.cpp>
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "MouseHookApp.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
// コンストラクタ
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
// DLLの読み込み
m_hInst = LoadLibrary("MouseHook.dll");
// エラー処理
if ( m_hInst == NULL ){
ShowMessage("MouseHook.dll:読込失敗しました");
return;
}
// DLL関数のアドレスを取得
HookStart = (void (*)(void))GetProcAddress(m_hInst, "HookStart");
HookStop = (void (*)(void))GetProcAddress(m_hInst, "HookStop" );
// エラー処理
if ( HookStart == NULL || HookStop == NULL ){
ShowMessage("関数のアドレス取得に失敗しました");
FreeLibrary(m_hInst); // 読み込んだDLLを解放
m_hInst = NULL;
return;
}
}
//---------------------------------------------------------------------------
// フック開始ボタン
void __fastcall TForm1::HookStartBtnClick(TObject *Sender)
{
SharedMem_Create(); // 共有メモリ作成
SharedMem_Write(); // 共有メモリ書込
HookStart(); // フック開始
}
//---------------------------------------------------------------------------
// フック停止ボタン
void __fastcall TForm1::HookStopBtnClick(TObject *Sender)
{
SharedMem_Delete(); // 共有メモリ削除
HookStop(); // フック停止
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
// 共有メモリ作成
void __fastcall TForm1::SharedMem_Create(void)
{
// 共有メモリ作成
m_hMapping = CreateFileMapping(INVALID_HANDLE_VALUE,
NULL,
PAGE_READWRITE,
0, sizeof(SHARED_DATA), "HookTest");
if ( m_hMapping == NULL ) {
ShowMessage("共有メモリ作成失敗");
return;
}
// マッピング開始
SHARED_DATA *dat = (SHARED_DATA *)MapViewOfFile(m_hMapping,
FILE_MAP_ALL_ACCESS,
0, 0, sizeof(SHARED_DATA));
// 0クリア
ZeroMemory(dat, sizeof(SHARED_DATA));
// マッピング解除
UnmapViewOfFile(dat);
}
//---------------------------------------------------------------------------
// 共有メモリ削除
void __fastcall TForm1::SharedMem_Delete(void)
{
CloseHandle(m_hMapping);
m_hMapping = NULL;
}
//---------------------------------------------------------------------------
// 共有メモリ書込
void __fastcall TForm1::SharedMem_Write(void)
{
HANDLE hMapping = OpenFileMapping(FILE_MAP_WRITE, FALSE, "HookTest");
if ( hMapping == NULL ) {
ShowMessage("共有メモリが見つかりません");
return;
}
// マッピング開始
SHARED_DATA *dat = (SHARED_DATA *)MapViewOfFile(hMapping,
FILE_MAP_WRITE,
0, 0, sizeof(SHARED_DATA));
// ウィンドウハンドルを書き込みます
dat->hwnd = this->Handle;
// マッピング解除
UnmapViewOfFile(dat);
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
// 左マウスダウンイベント
void __fastcall TForm1::WM_LButtonDown(TMessage msg)
{
int x = msg.WParam; // 位置を取得
int y = msg.LParam;
AnsiString sx = IntToStr(x);
AnsiString sy = IntToStr(y);
ListBox1->Items->Add("左ダウン - 位置(" + sx + "," + sy + ")");
}
//---------------------------------------------------------------------------
// 左マウスアップイベント
void __fastcall TForm1::WM_LButtonUp(TMessage msg)
{
int x = msg.WParam; // 位置を取得
int y = msg.LParam;
AnsiString sx = IntToStr(x);
AnsiString sy = IntToStr(y);
ListBox1->Items->Add("左アップ - 位置(" + sx + "," + sy + ")");
}
//---------------------------------------------------------------------------
次に、DLL側を作成します。
ファイル > 新規作成 > その他で、『DLLウィザード』を選び、
『C++』と『VCLを使う』にチェックします。
<(DLL側) | MouseHookDLL.cpp>
//---------------------------------------------------------------------------
#include <vcl.h>
#include <windows.h>
#pragma hdrstop
#define DLLEXPORT extern "C" __declspec(dllexport)
//---------------------------------------------------------------------------
// 共有メモリ用構造体
typedef struct {
HWND hwnd; // ウィンドウハンドル読込用
}SHARED_DATA;
//---------------------------------------------------------------------------
// 宣言
// フック開始用
DLLEXPORT void __stdcall HookStart(void);
// フック停止用
DLLEXPORT void __stdcall HookStop(void);
// フック処理用 DLLにする事によってグローバルフックが出来るようになります
DLLEXPORT LRESULT CALLBACK MouseProc(int code, WPARAM wParam, LPARAM lParam);
// 共有メモリ取得
void __fastcall SharedMem_Read(void);
//---------------------------------------------------------------------------
HWND g_hwnd; // アプリ側のウィンドウハンドル
HHOOK g_hHook; // フックハンドル
HINSTANCE g_hInst;
//---------------------------------------------------------------------------
#pragma argsused
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)
{
if( reason == DLL_PROCESS_ATTACH ){
g_hInst = hinst;
}
return 1;
}
//---------------------------------------------------------------------------
// フック開始
DLLEXPORT void __stdcall HookStart(void)
{
g_hHook = SetWindowsHookEx(WH_MOUSE, (HOOKPROC)MouseProc, g_hInst, 0);
if ( g_hHook == NULL ){
ShowMessage("HookStart:フック開始は失敗しました");
return;
}
ShowMessage("HookStart:フック開始は成功しました");
}
//---------------------------------------------------------------------------
// フック処理
DLLEXPORT LRESULT CALLBACK MouseProc(int code, WPARAM wParam, LPARAM lParam)
{
// codeが0より小さい場合、関係ないのでCallNextHookExに渡します
if ( code < 0 ){
// (第1引数はNULLでOK)
return CallNextHookEx(NULL, code, wParam, lParam);
}
// この構造体から位置,ハンドル,ヒットテストコード,追加情報を取得出来ます
MOUSEHOOKSTRUCT *mmsg;
// lParamをMOUSEHOOKSTRUCT型でキャストし
// 構造体のメンバを指定出来るようにします
mmsg = (MOUSEHOOKSTRUCT *)lParam;
// 左ボタンダウンと位置をアプリ側に送信します
if ( wParam == WM_LBUTTONDOWN ){
WPARAM wp = mmsg->pt.x; // マウス位置X
LPARAM lp = mmsg->pt.y; // マウス位置Y
SharedMem_Read(); // 共有メモリからハンドル取得
// WM_USER+WM_LBUTTONDOWNメッセージとして送信
PostMessage(g_hwnd, WM_USER+WM_LBUTTONDOWN, wp, lp);
}
// 左ボタンアップと位置をアプリ側に送信します
if ( wParam == WM_LBUTTONUP ){
WPARAM wp = mmsg->pt.x; // マウス位置X
LPARAM lp = mmsg->pt.y; // マウス位置Y
SharedMem_Read(); // 共有メモリからハンドル取得
// WM_USER+WM_LBUTTONUPメッセージとして送信
PostMessage(g_hwnd, WM_USER+WM_LBUTTONUP, wp, lp);
}
// 最後は次のフックへ渡します(第1引数はNULLでOK)
return CallNextHookEx(NULL, code, wParam, lParam);
}
//---------------------------------------------------------------------------
// フック停止
DLLEXPORT void __stdcall HookStop(void)
{
BOOL bResult;
if ( g_hHook == NULL ) return;
bResult = UnhookWindowsHookEx(g_hHook);
if ( bResult == 0 ){
ShowMessage("HookStop:フック解除は失敗しました");
return;
}
ShowMessage("HookStop:フック解除は成功しました");
}
//---------------------------------------------------------------------------
// 共有メモリ取得
void __fastcall SharedMem_Read(void)
{
HANDLE hMapping = OpenFileMapping(FILE_MAP_READ, FALSE, "HookTest");
if ( hMapping == NULL ) {
ShowMessage("共有メモリが見つかりません");
return;
}
// マッピング開始
SHARED_DATA *dat = (SHARED_DATA *)MapViewOfFile(hMapping,
FILE_MAP_READ,
0, 0, sizeof(SHARED_DATA));
if ( dat == NULL ) {
ShowMessage("共有メモリのデータがありません");
return;
}
// アプリ側のウィンドウハンドルを取得します
g_hwnd = dat->hwnd;
// マッピング解除
UnmapViewOfFile(dat);
}
//---------------------------------------------------------------------------
←実行すると、このようになります。
・左クリックした際にDLLでフック。
・DLLからメッセージでアプリ側に送信。
・受信したアプリはリストボックスに種類と位置を表示。
ということで。
DLLやら、メッセージの送受信やら、共有メモリやら....
フックは、なにかと大変ですね。
そういえば、共有メモリ使ってるんだから
わざわざメッセージで送受信しなくても良いかもしれないような...
※参考
TListBox [リストボックス]