C# 別スレッドでのマウスイベントのフック

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
black_cat

C# 別スレッドでのマウスイベントのフック

#1

投稿記事 by black_cat » 12年前

こんにちは。
現在、マウスイベントのグローバルフックを使用したアプリケーションを作成しているのですが、処理が重くなるのでThreadPriority.Highestを適用した別スレッドで処理させようと考えています。
そこで、以下のように実装しようとしてみたのですが思ったようにフックできません。


Form1.cs
(Form1にはロードイベント(Form1_Load)とクロージングイベント(Form1_FormClosing)を追加してあります。)

コード:

using System;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
	public partial class Form1 : Form
	{
		public Form1()
		{
			InitializeComponent();
		}

		MouseHook mouseHook;
		Thread mouseHookThread;

		private void Form1_Load(object sender, EventArgs e)
		{
			// Create the thread object. This does not start the thread.
			mouseHook = new MouseHook();
			mouseHookThread = new Thread(mouseHook.Start);
			// Start the worker thread.
			mouseHookThread.IsBackground = true;
			mouseHookThread.Priority = ThreadPriority.Highest;
			mouseHookThread.Start();
		}

		private void Form1_FormClosing(object sender, FormClosingEventArgs e)
		{
			mouseHook.End();
			mouseHookThread.Join();
		}
	}
}

HookMethods.cs

コード:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Windows.Forms;

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


		[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);


		[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
		public static extern IntPtr GetModuleHandle(String lpModuleName);
	}


	public class MouseHook
	{
		[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;
		}

		private static IntPtr hHook = IntPtr.Zero;
		private const int WH_MOUSE_LL = 14;



		public const int WM_LBUTTONDOWN = 0x201;
		public const int WM_LBUTTONUP = 0x202;
		public const int WM_RBUTTONDOWN = 0x204;
		public const int WM_RBUTTONUP = 0x205;
		public const int WM_MBUTTONDOWN = 0x207;
		public const int WM_MBUTTONUP = 0x208;
		public const int WM_MBUTTONDBLCLK = 0x209;
		public const int WM_MOUSEMOVE = 0x200;
		public const int WM_MOUSEWHEEL = 0x20A;

		static HookMethods.HookProcedureDelegate mouse_proc;



		// スレッド開始時に実行
		public void Start()
		{
			SetMouseHook();
			//####↓ここのループがないとフック削除時にUnhookWindowsHookEx()がfalseを返す####
			while (!_shouldStop) {
				Thread.Sleep(100);
			}
		}
		public void End()
		{
			RemoveMouseHook();
		}

		private void SetMouseHook()
		{
			using (Process process = Process.GetCurrentProcess())
			using (ProcessModule module = process.MainModule) {
				hHook = HookMethods.SetWindowsHookEx(
					WH_MOUSE_LL,
					mouse_proc = new HookMethods.HookProcedureDelegate(MouseHookProc),
					HookMethods.GetModuleHandle(module.ModuleName),
					0
				);
			}

			if (hHook == IntPtr.Zero) {
				MessageBox.Show("SetWindowsHookEx Failed.");
			}
			MessageBox.Show("Message");//####MessageBoxをエンターキーで閉じるまではフックできる####
		}

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

		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");

				switch ((int)wParam) {
					case WM_LBUTTONDOWN://左マウスダウン
						MessageBox.Show("LeftButton Down!");
						break;
				}
			}
			return HookMethods.CallNextHookEx(hHook, nCode, wParam, lParam);
		}
	}
}
気になる点が数点あるのですが、まずは全体の流れとして間違っているところはありませんでしょうか?
次に、Start()でのループはなぜ必要なのでしょうか?
Threadの仕組みをよく理解していないがために的外れな質問をしていたらすみません。

また、SetMouseHook()の最後で出したMessageBoxが出ている間だけフックできている理由は何なのでしょうか?
SetMouseHook()の後は特に何も処理をしていないので、MessageBoxが出ている間と消えた後では何も変化はないと思うのですが、どういうことが起こっているのでしょうか?

一気にいろいろと質問してしまって申し訳ないですが、どなたかご教授していただけないでしょうか?
よろしくお願いいたします。

“C言語何でも質問掲示板” へ戻る