Delphiでのドジの例(SetWindowsHookEx編)

ふと思い付いたので(こんなんばっか)、キーボードの一部の動作をフックするプログラムを作ることにしました。 フックプログラムは作った事がなかったのですが、だいたいのところはWIN32 API用のヘルプの「フックの概要」を見れば分かります。

ふむふむと読んでいるとSetWindowsHookExを使えばいいと書いてありますんで、それを使えばよさそうです。 今回はOS全体にフックを行いたいので少しその点に気を付けなければなりません。

SetWindowsHookExのヘルプでは以下のことが書いてあります。

wThreadIDパラメータに0やほかのプロセスによって作成されたスレッドのIDが指定されているときには、 hkprcパラメータにはダイナミック リンク ライブラリ (DLL) 内のフック プロシージャを指すポインタを指定しなければなりません。
wThreadIDは0にしないと全体のメッセージに対してフックを行ってくれません。 って事はフックプロシージャはDLLにないといけないな。 って事なんで、プログラムをDLLとインタフェース部分に分けて作りました。LoadLibraryでDLLをリンクして、 GetProcAddressを使ってフックプロシージャのあるアドレスを知ります。

それで作ったプログラムがこれです。

間違っているフックプログラム
間違っているフックプログラムソース

しかーし、これがちゃんと動かないんだな(;_;)
自分のウインドウ上にある時にしかビープ音がならないんです。これじゃグローバルにフックしたことにならない……
ヘルプを見てみますが、あっている様に見えるんですな、こりが。 こうなったら。って事でメーリングリストのログと立ち読み(^^;をした結果、どうやら一部のコードに問題が あるような気がするのでそこを直してみました。
問題点を直して意図した通りに動くようにしたプログラムがこれです。

正しいフックプログラム
正しいフックプログラムソース

ソースを見ていただいて判ると思いますが、最初のプログラムではSetWindowsHookExを アプリケーション側で呼び出すのに対して、正しい方のプログラムではDLL側で呼び出します。

無知を承知で推測するんですが、多分SetWindowsHookExの第三引数(HINSTANCE hMod)がアプリケーション側のインスタンスでなく、DLL側のが必要なんでしょうね。

日本語版のWIN32 APIヘルプではhModの説明として

アプリケーション インスタンスのハンドル
hkprcが指すフック プロシージャを持つDLLを識別します。dwThreadIDパラメータに現在のプロセスによって作成されたスレッドが指定されていて、 現在のプロセスに関連付けられているコード内にフック プロシージャがあるときには、 hModパラメータにNULLを指定しなければなりません。
とあります。わかりにくい文章なんで適当に値を入れたのが敗因ですね。
ほんとにこれで正しいか自信がないんで、詳しい方のメールをお待ちしています。


「ここが間違っている」、「ここが判りにくい」などのご意見は、 yagura@geocities.co.jpまでメールをください。

戻る