こんにちは。
dll内でシステムフックをかけ、そのイベントが発生したウィンドウを持つアプリが
あらかじめ登録されているものかどうかを判定し、登録されていたものであった場合は
exeにメッセージを送り、登録されたものでない場合はそのままシステムへ返す処理を
dll内にて行いたいと考えております。
実際に作って実行してみると、自アプリのウィンドウについては正しく判定されているのですが、ほかアプリのウィンドウは、登録されていても登録されていない場合の値が返ってしまいます。
以前ここで質問したときに、
「フックプロシージャはイベントが発生したプロセス内?にて実行されるため、ほかで起きたイベントはdll側には通知されてこない」
との回答をいただきました。(まだ曖昧にしか理解できていません)
おそらくこのことが関係していると思うのですが、可能ならばこの処理はdll内にて行いたいので、解決方法などおわかりの方いらっしゃいましたらご指導お願いします。
環境は Win2K + SP3 / VC6++ / SDK
です。
一応、ほかウィンドウを持つアプリの判定に失敗しているソースの一部です。
//** アプリ名を格納する双方向リスト **//
typedef struct s_MouseAct{
char sName[30];
struct s_MouseAct *pPre;
struct s_MouseAct *pNext;
} MOUSEACT, *PMOUSEACT;
//** 双方向リストの最初と最後へのポインタの宣言 **//
extern PMOUSEACT pStart, pEnd;
//** フック関数 **//
// 中央ボタンが押されたら自作関数CheckTargetに対象ウィンドウのハンドルを送り、
// それが登録されたアプリのものかを調べる
EXPORT LRESULT CALLBACK MouseHookProc( int nCode, WPARAM wp, LPARAM lp){
if ( nCode < 0)
return CallNextHookEx( hMouseHook, nCode, wp, lp);
MOUSEHOOKSTRUCT *pmh;
pmh = ( MOUSEHOOKSTRUCT *)lp;
int nReturn = 0;
HWND hTarget = pmh->hwnd;
switch( wp){
case WM_MBUTTONDOWN:
nReturn = CheckTarget( hMain, hTarget);
if ( nReturn > 0){ // 登録されていたときはexeへメッセージを送る
PostMessage( hWndApp, WM_MDOWN, wp, lp);
return TRUE;
}
// 登録されていなければシステムに返す
break;
}
return CallNextHookEx( hMouseHook, nCode, wp, lp);
}
// 自作関数GetAppNameは、hTargetのウィンドウのファイル名を取得し、
// 成功するとTRUEを返し、sTargetNameにファイル名が入る
//** 登録されているかを調べる関数 **//
int CheckTarget( HWND hMain, HWND hTarget){
int nCount;
char sTargetName[30];
PMOUSEACT ptr; // 双方向リストMOUSEACTへのポインタ
int nReturn = GetAppName( hTarget, sTargetName);
// この時点でsTargetNameには正しくファイル名が入れられていてTRUEが返っている (*0)
switch ( nReturn){
case TRUE:
int i = 0;
for ( ptr = pStart ; ptr ; ptr = ptr->pNext ){
// sTargetNameが登録されているかを調べる
nCount = strcmp( sTargetName, ptr->sName);
if ( nCount == 0)
return i; // 登録されていれば何番目のリストかを返す (*1)
i++;
}
return -1; // 登録されていなければ-1を返す (*2)
}
return -2;
}
(*0)の時点ではsTargetNameには対象ウィンドウを持つアプリのファイル名が
問題なく入っていることを確認しています。
対象が自ウィンドウの場合は、(*1)で、登録されているリスト番号が返ってくるのですが、
対象がほかのアプリのウィンドウの場合は登録されていても、(*2)を通り、-1が返ってしまいます。
フックDLLは、イベントをフックするプロセスごとにロードされます。
別々のプロセスにロードされた各DLLの変数は共有されないため、
PMOUSEACT pStart, pEnd;
を、DLLがロードされるたびに初期化する必要があります。
わたしもdairygoodsさんと同じ原因だと思います。
PMOUSEACT pStart, pEnd;
とその確保先メモリがプロセス間で共有できていないためではないでしょうか?
dairygoods さん、そらぱ さんありがとうございます。
お陰様でようやく問題点が見えてきました。
そこで、pStartとpEndをフックをかけるときの、exeのウィンドウハンドルと同じように
すべてのDllコンテキストで共有できるようにすればよいのかと思うのですが、
pStartとpEndは、フック開始時にiniファイルから登録アプリ名を読み込み、その数に応じて
動的に確保したPMOUSEACTを割り当てる必要があります。
となると、この動的に確保した部分も共有されている必要がある訳ですよね?
現在は
#pragma data_seg(.MouHook)
HHOOK hMouseHook = NULL; //フックハンドル
HWND hWndApp = NULL;//メッセージを送信するアプリケーションハンドル
#pragma data_seg( )
と、セグメントを宣言して共有させているのですが
そうなるとこのdefファイルを使った方法では無理のような気がしてきました。
今はちょっと確かめられないので、夜にでも見直してみます。
> そうなるとこのdefファイルを使った方法では無理のような気がしてきました。
動的に確保した領域を共有セクション化するのは無理ですね。
先に書いたように、DLLがロードされるたびにiniファイルを読み込むか、
メモリマップドファイルを使用します。
(CreateFileMapping 辺りから調べられます)
dairygoods さん、ありがとうございます。
いろいろ考えた結果、CreateFileMappingを使おうかと思い、調べている中で疑問に思ったのですが、
フックdllはマウスメッセージをとらえるたびにその対象となるプロセスの仮想アドレス空間
内にそれぞれロードされるということは、フックdll内でメッセージを捕まえ対象を判別して・・・とするよりもdllはメッセージを捕まえてそれをEXEに通知するだけにして、
ほかの処理はできるだけアプリ側に任せてしまう方が効率がよいような気がしてきました。
こうすると対象が登録されていないときなどには対象アプリに対して元々のマウスイベントに応じたデフォルトのメッセージを送る必要が出てきますが。
この考えは間違っていますでしょうか?
せっかくいただいたアドバイスを無駄にするような方向転換で申し訳なくもあるのですが
引き続きご指導お願いします。
>この考えは間違っていますでしょうか?
間違ってません。
フックDLLみたいにデバッグの面倒なものの中に余分な処理を置かない、というのは
健全な発想だと思います。
渋木宏明(ひどり) さん、ありがとうございます。
dllでは、マウスメッセージを捕まえてexeに知らせるだけしか行わないとういう方向で
進めてゆこうと思います。