[C#] フォーム外のマウスイベントを取得する

■ プログラム Posted by ひぐま (Higmmer) on 2005-12-08 at 23:59:01

◆共有テーマ: プログラミング [コンピュータ]

DesktopCapture for Skype」では全画面の他に指定したウィンドウ又は任意の座標範囲をキャプチャ対象として設定することができますが、その際ウィンドウリストから選択したり座標を数値入力するのではなく、対象としたいウィンドウや座標範囲を直接クリックorドラッグすることで直感的に設定できるようにしたいと考えました。つまり、自分のフォーム外でのクリックやドラッグなどのマウスイベントを取得する必要があります。

これを実現するための手法としてはだいたい次の4つが考えられると思います。

  1. システムフックを利用する
  2. マウスキャプチャを利用する
  3. タイマーを使って定期的にマウスの位置を調べる
  4. 画面全体を覆う透明ウィンドウを作成する

最初に思いついたのは(1)の方法ですが、フォーム外のイベントを取得するにはグローバルフックを使用する必要があります。ところが.NET Frameworkの環境ではグローバルフックはサポートされないことが分かりました。このためだけにDLLを作成するのも大げさですし、下手にフックを操作してシステムが不安定になるのも避けたいところです。

そこで次に思い当たったのが(2)の方法です。リファレンスを参考にフォームのCaptureプロパティをtrueにすることでフォーム外のマウスイベントを取得できると思ったのですが、実際に試してみるとうまくいきません。このページにその理由が書いてありました。

つまり自分のフォーム外でマウスをキャプチャするにはボタンを押しっぱなしにしていなければならないのです。これでは目的の動作を実現することはできません。う~む、どうしたものか……と一度は行き詰まったのですが、いろいろ調べた結果、mouse_event関数を利用して擬似的に右ボタンを押しっぱなしにした状態を作り出すことで解決することができました。

以下にC#でそれを実現するコードを示します。

public class CaptureRegion { internal class NativeMethods { [DllImport("user32.dll")] extern public static void mouse_event(int dwFlags, int dx, int dy, int dwData, int dwExtraInfo); [DllImport("user32.dll")] extern public static int GetMessageExtraInfo(); public const int MOUSEEVENTF_RIGHTDOWN = 0x0008; public const int MOUSEEVENTF_RIGHTUP = 0x0010; } public static void SetMouseRButtonDown() { NativeMethods.mouse_event( NativeMethods.MOUSEEVENTF_RIGHTDOWN, 0, 0, 0, NativeMethods.GetMessageExtraInfo()); } public static void SetMouseRButtonUp() { NativeMethods.mouse_event( NativeMethods.MOUSEEVENTF_RIGHTUP, 0, 0, 0, NativeMethods.GetMessageExtraInfo()); } } public class Form1 : System.Windows.Forms.Form { private void Button1_Click(object sender, System.EventArgs e) { this.Capture = true; Cursor.Current = Cursors.Cross; CaptureRegion.SetMouseRButtonDown(); } }

あとは普通にMouseMoveやMouseUpイベントハンドラ内に目的の処理を記述すればOKです。最後にCaptureプロパティをfalseに戻すのと、SetMouseRButtonUp()を呼んでマウスボタンの状態を元に戻すのを忘れないようにして下さい。ちなみにDesktopCaptureの関連ウィンドウの上でボタンを離した時にポップアップメニューが表示されてしまうのは上記のように右クリックをエミュレーションしているためです。

余談になりますが(3)のタイマーを使用する方法は座標指定の精度を確保するのが難しいことやクリック先のウィンドウにフォーカスを奪われてしまうことから採用していません。また(4)は後から調べて見つけた方法で当初は思いつきませんでした。この全画面を透明ウィンドウで覆う方法なら枠を描画するのも簡単に行えるというメリットがあります(但し描画速度は遅くなると思われます)。興味がある方は試してみて下さい。

トラックバック

この記事について書く(FC2ブログユーザー)
※言及リンクの無いトラックバックは無効です

PageTop▲

コメント

PageTop▲

コメントの投稿

 
 
 
 
 
 (後で編集・削除したいなら必須)
 
  

PageTop▲