2008年1月16日水曜日

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

前回の記事を見ていない方は「マウスの位置を取得し続ける(C#) その2」から
先にどうぞ。

前回は、フックを行うための準備まで実装しました。

今回はいよいよ、フック処理を実装します。

の前に、フック処理の開始 / 終了を切り替えるボタンを設置しましょう。
"Form1.cs"のデザイナを開き、"Button"を"Form1"上に設置します。
作成された"button1"をダブルクリックし"button1_Click"メソッドを作成します。

では、マウスフック処理を行うための準備をします。

まずは、"Form1.cs"に名前空間の修飾省略定義を行います。

/*** Form1.cs ***/
using System.Runtime.Interopesrvices;
次に、位置と、マウスフックを表す構造体を宣言します。

/*** Form1.cs ***/
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int x;
public int y;
}

[StructLayout(LayoutKind.Sequential)]
public class MouseHookStruct
{
public POINT pt;
public uint mouseData;
public uint flags;
public uint time;
public IntPtr dwExtraInfo;
}

構造体"POINT"は画面内の座標を表します。
同じく,"MouseHookStruct"はマウスフックを表します。

フックプロシージャのハンドルを保存しておくための、静的フィールドを用意します。

/*** Form1.cs ***/
private static IntPtr hHook = IntPtr.Zero;
次に,マウスに対するフックタイプを宣言します。

/*** Form1.cs ***/
private const int WH_MOUSE = 7;
フックタイプには,様々なものがあります。詳しくはMSDNを参照して下さい。

マウスフックを設定 / 削除するためのメソッドを作成します。

/*** Form1.cs ***/
private void SetMouseHook(HookMethods.HookProcedureDelegate proc)
{
// マウスフックを設定
hHook = HookMethods.SetWindowsHookEx(WH_MOUSE, proc, IntPtr.Zero, AppDomain.GetCurrentThreadId());
if (hHook == IntPtr.Zero)
{
MessageBox.Show("SetWindowsHookEx Failed.");
}
}

private void RemoveMouseHook()
{
// フックを削除
if (HookMethods.UnhookWindowsHookEx(hHook) == false)
{
MessageBox.Show("UnhookWindowsHookEx Failed.");
}
}


マウスフックプロシージャの実装を行います。

/*** Form1.cs ***/
public IntPtr MouseHookProc(int nCode, IntPtr wParam, IntPtr lParam)
{
if(nCode >= 0)
{
// コールバックからのデータを整理する.
MouseHookStruct MyMouseHookStruct = (MouseHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseHookStruct));

String strCaption = "x = " + MyMouseHookStruct.pt.x.ToString("d") + " : y = " + MyMouseHookStruct.pt.y.ToString("d");
this.Text = strCaption;
}
return HookMethods.CallNextHookEx(hHook, nCode, wParam, lParam);
}


これで、マウスフックの処理を行うための準備ができました。

では、実際にマウスフック処理を行ってみましょう。
"button1"が押されたらフックを開始し、再度押されたらフックを終了する処理を記述します。

/*** Form1.cs ***/
private void button1_Click(object sender, EventArgs e)
{
if (hHook == IntPtr.Zero)
{
SetMouseHook(MouseHookProc);
this.button1.Text = "Unhook Windows Hook";
}
else
{
RemoveMouseHook();
this.button1.Text = "Set Windwos Hook";
}
}


実行します。

以下のような警告がでますが、後ほど削除する部分なので、無視します。
'System.AppDomain.GetCurrentThreadId()' は古い形式です: 'AppDomain.GetCurrentThreadId has been deprecated because it does not provide a stable Id when managed threads are running on fibers (aka lightweight threads). To get a stable identifier for a managed thread, use the ManagedThreadId property on Thread. http://go.microsoft.com/fwlink/?linkid=14202'


"button1"を押すと、フックが開始されます。

"Form1"内でマウスを動かすと、その座標がタイトルに表示されます。


"Form1"外では、タイトルが変化しません。

もう一度"button1"を押すと、フックが終了します。


ローカルフックを用いて、マウスの位置を取得し続けることはできました。

.NETのみで実装した場合に比べて、動きがスムーズです。


次は、グローバルフックを用いた場合について説明しますが、
今回も長くなってしまったので、次回にまわしたいと思います。

0 件のコメント: