[BlueLeaf1336]> PROBLEMS> PasteIt>

PasteIt > デスクトップでのダブルクリックを検出する

historyTOP

2007/02/01:作成
2007/02/02:続き
2007/02/03:不具合修正

とりあえずフックしてみるTOP

自分の作ったソフトの画面上でどんな風にマウスが動いているかは、何も考えずに OnMouseMove (もちろんDelphiでは) でかまいませんが、今回は相手がデスクトップなので、「フック」を行う必要があります。

最近ダウンロードした「Microsoft Platform SDK for Windows Server 2003 R2」にも詳しいやり方が書いてありますし、ネット上にも腐るほどサンプルがあります。でも、すでにやったことがあるので、それを焼きなおしただけでできました。

20070201PasteIt.zip(211,798bytes)(21,691bytes) 実行ファイルとソースコード。

DLL を使ってインストールしたフックプロシージャに飛んできたメッセージを、PostMessage で EXE に転送して、EXE ではそれを表示しています。(※Install したら UnInstall することを想定していますが、Install を連続でクリックしても Install の直前に UnInstall を行っていますので少しだけ安全にためせます。また終了時にも勝手に UnInstall しています。)

こんな画面ですが、いまやってみて「1回目のフックInstall」と「2回目以降のフックInstall」で動きが違うことに気づきました。「1回目のフックInstall」では、Installした後しばらくするとフックが解除されている(?)ような。おいおいと直していくことにします。

このテストプログラムでインストールするフックは、「MouseHook」と「CBTHook」です。「MouseHook」は、マウスで何かをするたびに(動かすとかクリックするとか)発生するメッセージを、自分で用意した関数を通過させることができます。「CBTHook」は、ウィンドウが作られたり破棄されたりアクティブにされたりといったときに発生するメッセージを(以下同様)。

ちなみに、フックした関数を通過しているときの情報は、PostMessage で EXE に飛ばしたときには 多分使えなくなっています。が、それはテストということで。

ところで、ネット上で簡単に見つかる「デスクトップのダブルクリック」を検出するサンプルでもコメントされていましたが、デスクトップの何にもない場所でダブルクリックしたのか、それともマイコンピュータやそのほかの何かのアイコンをダブルクリックしたのかを見分けるには、何かをしないといけないようです。ダブルクリック自体は、サンプルをそのままコンパイルしても検出できることを確認できますが、フォルダを開いた場合でも反応してしまうのは今回の場合問題があります。

コレを踏まえて、このテストプログラムでは MouseHook だけではなく CBTHook もインストールしています。

「何もない場所でダブルクリックしたら(普通は)何もおきないだろう、逆に何かのアイコンをダブルクリックした場合は、一瞬後には何らかのウィンドウが作成される」はず。つまり「ダブルクリックを検出(MouseHookでキャッチ)」したら「何らかのウィンドウが作成されないか(CBTHookでキャッチ)しばらく待って」何もおきなければ「何にもないところで(壁紙しかないところで)ダブルクリックされた」と判断できるだろう、

なので(多分) MouseHook と CBTHook がいるんじゃないかと思ったわけで。

フック自体は確認できましたが、このままではもちろん役に立ちません。まずは「単純ダブルクリック検出」からです。

単純なデスクトップのダブルクリックを検出TOP

基本方針は、インストールした MouseHook プロシージャにメッセージが飛んできたときの、マウス操作の対象ウィンドウが「デスクトップ」の場合(クラス名が Progman のはず)にだけ反応する方向で。

単純なデスクトップのダブルクリックを検出(続き)TOP

前回作ったのをチョコチョコと修正して、デスクトップのダブルクリックを検出できるようにしました。問題ありありですが。

その前に。前回気づいた "「1回目のフックInstall」と「2回目以降のフックInstall」で動きが違うことに気づきました。「1回目のフックInstall」では、Installした後しばらくするとフックが解除されている(?)..." 問題を修正しました。原因は(多分)「共有メモリで確保した構造体を FillChar(構造体, SizeOf(構造体), 0) で初期化していたこと」のようです。

フックをアンインストールするときにために SetWindowsHookEx が返してくるフックハンドルを保持しておくんですが、このハンドルを 0 で初期化していたわけです。アンインストール時に、そのハンドルが 0 以外なら...という判定を行うために。で、ついでに構造体全体をバキバキと FillChar していたのがだめだったようで。

なんとなくですが、String を SetLength した後、FillChar で 0 初期化すると、SetLength の効果がなくなってしまうのと似たような似てないような。

話を戻して、「デスクトップで左ボタンダブルクリック(WM_MBUTTONDBLCLK)と中ボタンダブルクリック(WM_MBUTTONDBLCLK)のタイミングで TMemo に時間を表示する」ようにしました。こんな感じに。

20070202PasteIt.zip(222,391bytes)(21,960bytes) 実行ファイルとソースコード。

さて、問題の「問題」ですが、何らかのプログラム(メモ帳とかその他)を起動して終了すると、フックが利かなくなってしまうんです。起動ではなくて「終了」がスイッチのようです。ためしに種類の違うプログラムを10個ぐらい立ち上げてデスクトップをダブルクリックしてみると、ちゃんと反応します。でも、どれかひとつでも終了させるとアウトです。

フックのインストールは、プログラムの起動時(FormCreate)で一回だけ行い、後は DLL の finalization でアンインストールしています。なんとなく怖くて FormDestroy でもアンインストールをしていますけど(DLL の finalization でそんなことをしてはだめ、なんていうタブーがあるかどうかは調べていません)。10秒に一回フックをインストールしなおすような対症療法ココにアリ、な解決方法も考えられますが、まあ、おいおいと。致命的な問題解決を先延ばしにするのは楽しいものです。

幸い、フォルダを開いて閉じるだけなら問題がない(ようにみえる)ので CBT HOOK を併用して、デスクトップにある何かのダブルクリックをより分けてみることにします。

単純なデスクトップのダブルクリックを検出(不具合修正)TOP

ふと思いついて1箇所直したところ、" 何らかのプログラム(メモ帳とかその他)を起動して終了すると、フックが利かなく "なる問題が発生しなくなりました(解決したのかどうかは知りません)。

20070203PasteIt.zip(221,018bytes)(20,582bytes) 実行ファイルとソースコード。

その1箇所とは、DLL の finalization で、安全のためと思って実行していたフックの(強制的な)アンインストール処理です。なぜ関連性があるのか正確には、否、まったく理解できてないのですが、雰囲気的には次のような感じかなぁと。

文章にしてみるとまったく持って???です。でも、そうじゃないかなぁと思って修正したことで実際に挙動が改善したかのように見えるということは、この考え方があさっての方向を見ているんじゃなく、かすっているんだと思うことにします。

あと、別件ですが、EXE と DLL のバージョン情報ファイル(Version.rc)を共有することにしました。片方しか直してなくてもプロジェクトグループでくくっているし、Delphi の高速コンパイルを無駄遣いして、暇つぶしに再構築してしまうのでついでならバージョンそろえちまうか、てなもんです。

EOFTOP