グローバルフックのプロシージャ(DLL内)からEXE側関数は呼び出せないのですか

[戻る]
原田 2014/09/03(水) 14:26:17
Windows 8.1上のVisual Studio 2013での質問です。よろしくお願いします。

グローバルフックを使用してマウスイベントを取得したいのですが、DLL内のフック プロ
シージャからEXE側の関数を呼び出すとフックイベントの発生が止まります。

EXE側の関数を呼び出しているのは、グルーバルフック用のDLLをアプリごとに作成したく
ないと思ったためです。

EXEからDLL内のフック登録用関数を呼び出す際にコールバック用関数のアドレスを渡して
おき、DLL内のフック プロシージャからそのアドレスの関数を呼び出せば、DLLを共用で
きて便利だろうなーと思いそのような作りにしました。


しかし、期待通りには動作しません。DLL内でマウスカーソル位置にTextOutしてみたとこ
ろ、実行直後に1瞬だけ文字が描画されるのですが、すぐに描画が止まります。

EXE側関数の呼び出しをやめると、マウスカーソルを動きに合わせて連続して文字が描画
されます。

なお、EXE側の関数は、まだ中身は空で何も処理はしていません。return しているだけで
す。

どういった原因が考えられるでしょうか?

YuO [E-Mail] 2014/09/04(木) 10:01:02
フックプロシージャは,
・32bitアプリケーションからフックを32bitアプリケーションに行った場合
・64bitアプリケーションからフックを32bitアプリケーションに行った場合
は対象のスレッドで実行されます。
このことはMSDNに記述があるので,当然理解されているとは思いますが……。
ref) http://msdn.microsoft.com/en-
us/library/windows/desktop/ms644990(v=vs.85).aspx
> If a 32-bit application installs a global hook on 64-bit Windows, the 32-bit 
hook is injected into each 32-bit process (the usual security boundaries apply).
> If a 64-bit application installs a global hook on 64-bit Windows, the 64-bit 
hook is injected into each 64-bit process, while all 32-bit processes use a 
callback to the hooking application.

異なるプロセス空間にあるため,当然ながら元のプロセスの関数を直接呼び出すことはで
きません。
一番単純な方法は,ユーザー定義のメッセージを元のスレッドに対してPostすることでし
ょう。


ただ,そもそもグローバルフックはデバッグ目的以外に使うなと書いてあります。
グローバルフックが必要になるデバッグというのも,デバッガを作る等を除くと考えにく
いですが。
ref) http://msdn.microsoft.com/en-
us/library/windows/desktop/ms644959(v=vs.85).aspx
> You should use global hooks only for debugging purposes; otherwise, you should 
avoid them.

原田 2014/09/04(木) 18:39:29
質問者です。お世話になります。

YuOさんの仰る通り、メッセージを利用すれば上手く動作します。ただ、DLLのフックプロ
シジャからEXE側の関数を呼び出すと正常に動作しない原因が知りたいと思ったため質問
させていただきました。

> 異なるプロセス空間にあるため,当然ながら元のプロセスの関数を....

私の説明が下手でした。すいません。
再度ここで説明させてください。

えーと、マウスのグローバルフックを利用したいのですが、フックプロシジャ関数はDLL
内に置かなければなりません。そこで、フックプロシジャをDLLとして外出しにしまし
た。アプリケーション本体のMyApp.exeと、フックプロシジャだけを収めたMyAppHook.DLL
という具合です。

この状態でMyApp.exeを実行すると、期待通りに他のアプリケーションのマウスイベント
も取得できました。試しに、MyAppHook.DLL内のフックプロシジャでマウスカーソル位置
にTextOutで文字を書き込んでみたところ、きちんとマウス位置に文字が出力されまし
た。マウスの動きに合わせて文字がどんどんと出力されます。

次に、DLLを色々なアプリケーションで共用したいと考えました。

そこで、MyApp.exeからMyAppHook.DLL内のフック登録関数を呼び出す際にコールバック先
のアドレスを渡せるように引数を追加しました。

さらに、MyAppHook.DLL内のフックプロシジャでは、フックイベントが発生した際に、
MyApp.exeから渡されたコールバック先のアドレスを呼び出すようにしました。

上記の修正後にMyApp.exeを実行すると、MyApp.exe内のコールバック関数での描画が行わ
れました。

つまり、MyAppHook.DLL側からMyApp.exeの関数の呼び出しは成功しています。ただ、一定
回数の呼び出し後に、DLL側のフックプロシジャーが呼び出されなくなります。一瞬だけ
動作することもあれば数十秒動作することもあります。

知りたいのは、なぜフックの発生が止まるのかです。

MSDNの、

  http://msdn.microsoft.com/ja-jp/library/h90dkhs0(v=vs.90).aspx

によると、

- 抜粋ここから -
Win32 DLL は、呼び出し側プロセスのアドレス空間に割り当てられます。既定では、DLL 
を使用するプロセスごとに、すべての DLL のグローバル変数および静的変数のインスタ
ンスが作成されます。
- 抜粋ここまで -

とあり、DLLとの間でアドレスでの関数呼び出しをしても問題なさそうに思えるのです。

AR 2014/09/05(金) 09:45:04
マウスジェスチャのアプリケーションを作った時に似たような経験をしました。
しかし、残念ながら何故フックが呼ばれなくなるかという疑問には、申し訳ありませんが
答えられません。
追いきれませんでした。

この現象はWindows7以降で発生するOS依存の現象と認識しています。
(Windows7の初期の頃はもっと酷かったがWindows updateを重ねるうちに改善されてきた)
対処方法としては、フックの登録と開放をこまめにやるという手段を採って現在では問題
なく動作しています。
参考までにどうぞ。

原田 2014/09/07(日) 01:05:18
[[解決]]
YuOさん、ARさん、アドバイスいただきありがとうございました。

DLLからアプリケーション側の関数をアドレスで呼び出すのは止めにしました。YuOさんのアドバ
イス通りに、メッセージでフック関連の情報を渡すようにしました。

メッセージを介した方法だと安定しています。

質問の内容は解決していませんが、やりたいことを別の仕組みで実現できましたので、これで解
決とさせていただきます。

ありがとうございました。

古いスレッドには追加発言できません。