2008年1月16日水曜日

マウスの位置を取得し続ける(C#) その2

前回の最後に、「マウスの位置が○○に来たら」のようにイベントを設定することができない、と書きましたが、これは誤りです。

正確には、「.NET Frameworkだけではできない」です。

このようなイベントを設定するためには、"フック"を用いるそうです。

フックについては「参照:KAB-studio フックのしくみ」が非常にわかりやすいので、知らない人は参照してみて下さい。

まとめると、
"フック"

システム内のメッセージを条件に合わせて拾い上げること

"ローカルフック"

自スレッドのみに対して働くフック

"グローバルフック"

全てのスレッドに対して働くフック

です。

前述のように「(範囲の内外を問わず)マウスの位置に応じて処理を行う」場合は、
グローバルフックを用いなければなりません。

しかし、「グローバルフックは .NET Framework ではサポートされていない」そうです。

・・・「.NET Framework では」?

つまり、.NET Framework 以外でグローバルフックを取得すればいいわけです。


まずはローカルフックのみを試してみます。
前回のプログラムと内容的には類似していますが、精度が違います。)

前回と同様に、新しいプロジェクトを開始します。
("ファイル" > "新しいプロジェクト" > "Windows フォームアプリケーション")

ソースコードを見やすくするために、フックを扱うクラス"HookMethods"を別に定義します。
("プロジェクト" > "クラスの追加" > "クラス" で名前を"HookMethods.cs"と入力)

名前空間の修飾省略定義を行う。

/*** HookMethods.cs ***/
using System.Runtime.InteropServices;
フックを扱うためのメソッドを3つ、"user32.dll"より呼び出します。

/*** HookMethods.cs ***/
// フックプロシージャのためのデリゲート
public delegate IntPtr HookProcedureDelegate(int nCode, IntPtr wParam, IntPtr lParam);

// フックプロシージャ"lpfn"をフックチェーン内にインストールする
// 返り値はフックプロシージャのハンドル
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr SetWindowsHookEx(int idHook, HookProcedureDelegate lpfn, IntPtr hInstance, int threadId);

// "SetWindowHookEx"でインポートされたフックプロシージャを削除する
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern bool UnhookWindowsHookEx(IntPtr idHook);

// 次のフックプロシージャにフック情報を渡す
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr CallNextHookEx(IntPtr idHook, int nCode, IntPtr wParam, IntPtr lParam);

3つのメソッドについて解説します。

"SetWindowsHookEx"と"UnhookWindowsEx"はセットで使われます。

"SetWindowsHookEx"は、フックタイプに関係するフックチェーンに対して、
フックプロシージャをインストールするためのメソッドです。

監視する対象はフックタイプで指定します。
フックタイプには,マウスを監視する"WH_MOUSE"やキーボードを監視する
"WH_KEYBOARD"があります。
1つ目の引数"iHook"に指定します。

フックした時の処理は、フックプロシージャの実装に委ねられます。
フックプロシージャへのポインタを2つ目の引数"lpfn"に指定します。
そのため、C#ではデリゲートを用います。
フックプロシージャの実装は、デリゲートの実装で行います。

"SetWindowsHookEx"メソッドの返り値は、
メソッドが成功した場合は、フックプロシージャのハンドルが、
メソッドが失敗した場合は、NULLが、
返ります。

"SetWindowsHookEx"で返されたフックプロシージャのハンドルを、
"UnhookWindowsHookEx"の引数として指定することで、フックが削除されます。

"CallNextHookEx"は、現在のフックチェーン内の次のフックプロシージャに、
フック情報を渡します。
他のプログラムが同じフックチェーンに対してフックしている場合、
他のプログラムのフックプロシージャに対してフック情報を渡さなければ、
そのフックプロシージャは実行されません。つまり、フックされません。
"CallNextHookEx"を実行しなければ、他のプログラムに影響を及ぼします。
そのため、フックプロシージャの実装コード内で、"CallNextHookEx"を呼び出す
必要があります。

これらのメソッドは、必ず3つセットで利用する必要があります。

これで、フックを行う準備はできました。


今回は長くなってしまったので、続きは次回にします。


参照:
MSDNフォーラム クリックしただけで別アプリケーション上の TextBox に文字列をペーストしたい
Stephen Toub : Low-Level Mouse Hook in C#
MSDN フック

0 件のコメント: