8
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

[C#]Windowsフォームアプリでフォーム外マウス・キーボードイベントを取得する

Posted at

ドキュメントの多さを期待して古代のもので作ったらハマったのでメモ

環境

  • Visual Studio Community 2017 ver.15.7.5
  • .NET Framework 4.7.1

使うもの

コードの書き換え

古代のコード故に今のVSだとそのまま動かないらしいので,上記サイトのコメント欄を参考にし,一部を書き換える必要がある。
※解決法としてあるVSのデバッグオプションの項目はVS2017から消えたらしい(?)
各々,2箇所を以下のように変更する。

MouseHook.csの一部
	[DefaultEvent("MouseHooked")]
	public class MouseHook : System.ComponentModel.Component {
		[DllImport("user32.dll", SetLastError=true)]
		private static extern IntPtr SetWindowsHookEx(int hookType, MouseHookDelegate hookDelegate, IntPtr hInstance, uint threadId);
		[DllImport("user32.dll", SetLastError=true)]
		private static extern int CallNextHookEx(IntPtr hook, int code, MouseMessage message, ref MouseState state);
		[DllImport("user32.dll", SetLastError=true)]
		private static extern bool UnhookWindowsHookEx(IntPtr hook);
        /* 追加部分 */
        [DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)]
        internal static extern IntPtr LoadLibrary(string lpFileName);
        IntPtr module = LoadLibrary("user32.dll");
        /*---------*/
        private const int MouseLowLevelHook = 14;
		private delegate int MouseHookDelegate(int code, MouseMessage message, ref MouseState state);
		private IntPtr hook;
		private GCHandle hookDelegate;

		private static readonly object EventMouseHooked = new object();
		///<summary>マウスが入力されたときに発生する。</summary>
		public event MouseHookedEventHandler MouseHooked  {
			add {base.Events.AddHandler(EventMouseHooked, value);}
			remove {base.Events.RemoveHandler(EventMouseHooked, value);}
		}
		///<summary>
		///インスタンスを作成する。
		///</summary>
		///<exception cref="Win32Exception">フックに失敗しました。原因の詳細はエラーコードを参照してください。</exception>
		public MouseHook() {
			if (Environment.OSVersion.Platform != PlatformID.Win32NT)
				throw new PlatformNotSupportedException("Windows 98/Meではサポートされていません。");
			MouseHookDelegate handler = new MouseHookDelegate(CallNextHook);
			this.hookDelegate = GCHandle.Alloc(handler);
            //IntPtr module = Marshal.GetHINSTANCE(typeof(MouseHook).Assembly.GetModules()[0]); // ここを消すかコメントアウト
            this.hook = SetWindowsHookEx(MouseLowLevelHook, handler, module, 0);
            if (hook == IntPtr.Zero)
				throw new Win32Exception(Marshal.GetLastWin32Error());
		}
KeyboardHook.csの一部
	[DefaultEvent("KeyboardHooked")]
	public class KeyboardHook : Component {
		[DllImport("user32.dll", SetLastError=true)]
		private static extern IntPtr SetWindowsHookEx(int hookType, KeyboardHookDelegate hookDelegate, IntPtr hInstance, uint threadId);
		[DllImport("user32.dll", SetLastError=true)]
		private static extern int CallNextHookEx(IntPtr hook, int code, KeyboardMessage message, ref KeyboardState state);
		[DllImport("user32.dll", SetLastError=true)]
		private static extern bool UnhookWindowsHookEx(IntPtr hook);
        /* 追加部分 */
        [DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)]
        internal static extern IntPtr LoadLibrary(string lpFileName);
        IntPtr module = LoadLibrary("user32.dll");
        /*---------*/
        private delegate int KeyboardHookDelegate(int code, KeyboardMessage message, ref KeyboardState state);
		private const int KeyboardHookType = 13;
		private GCHandle hookDelegate;
		private IntPtr hook;
		private static readonly object EventKeyboardHooked = new object();
		///<summary>キーボードが操作されたときに発生する。</summary>
		public event KeyboardHookedEventHandler KeyboardHooked {
			add {base.Events.AddHandler(EventKeyboardHooked, value);}
			remove {base.Events.RemoveHandler(EventKeyboardHooked, value);}
		}
		///<summary>
		///KeyboardHookedイベントを発生させる。
		///</summary>
		///<param name="e">イベントのデータ。</param>
		protected virtual void OnKeyboardHooked(KeyboardHookedEventArgs e) {
			KeyboardHookedEventHandler handler = base.Events[EventKeyboardHooked] as KeyboardHookedEventHandler;
			if (handler != null)
				handler(this, e);
		}
		///<summary>
		///新しいインスタンスを作成する。
		///</summary>
		public KeyboardHook() {
			if (Environment.OSVersion.Platform != PlatformID.Win32NT)
				throw new PlatformNotSupportedException("Windows 98/Meではサポートされていません。");
			KeyboardHookDelegate callback = new KeyboardHookDelegate(CallNextHook);
			this.hookDelegate = GCHandle.Alloc(callback);
			//IntPtr module = Marshal.GetHINSTANCE(typeof(KeyboardHook).Assembly.GetModules()[0]); // ここを消すかコメントアウト
			this.hook = SetWindowsHookEx(KeyboardHookType, callback, module, 0);
		}

プロジェクトへの追加

  1. ファイル→追加→新しいプロジェクト→クラスライブラリ(.NET Framework)を作成
  2. ソリューションエクスプローラー上で追加したクラスライブラリを右クリック→追加→既存の項目でMouseHook.csKeyboardHook.csを追加
  3. 同様に右クリック→追加→参照で,System.DrawingSystem.Windows.Formsにチェックを入れてOK
  4. ソリューションエクスプローラー上でイベントを使うプロジェクト(元のプロジェクト)を右クリック→追加→参照→プロジェクトで今作成したクロスライブラリを追加
  5. ビルドする
  6. フォームデザインを開き,ツールボックスをクリック
  7. MouseHookKeyboardHookが追加されていればOK

MouseHookとKeyboardHookの利用

  1. 追加されたMouseHookKeyboardHookをダブルクリックし,フォームに追加する。正しくできていれば,フォームのデザイン画面の下にMouseHook1KeyboardHook1ができているはず
  2. 追加したMouseHook1KeyboardHook1をダブルクリックすると,Form1.cs上に各々のイベント検知コードが追加される

using HongliangSoft.Utilities.Gui;を書いておくと良い


using HongliangSoft.Utilities.Gui;が書いてある前提

private void mouseHook1_MouseHooked(object sender, MouseHookedEventArgs e) {
    if (e.Message == MouseMessage.LDown) {
        /* 左クリックを検知したときの動作 */
    }
}
private void keyboardHook1_KeyboardHooked(object sender, KeyboardHookedEventArgs e) {
    if(e.UpDown == KeyboardUpDown.Down) {
        if(e.KeyCode == Keys.Space) {
            /* スペースキーが押されたときの動作 */
        }
    }
}

その他

マウスの座標は特別なことをしなくてもCursorで取れるっぽい

8
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?