none
ウィンドウフックの結果が正しく変換できない RRS feed

  • 質問

  • C#を使ったウィンドウフックのプログラムを作成しています。クラスライブラリプロジェクトで以下のようにコードを入力しました。
    	[StructLayout(LayoutKind.Sequential)]
    	public struct POINT
    	{
    		public int x;
    		public int y;
    	}
    	[StructLayout(LayoutKind.Sequential)]
    	public struct MSG
    	{
    		public IntPtr hWnd;
    		public uint message;
    		public IntPtr wParam;
    		public IntPtr lParam;
    		public uint time;
    		public POINT pt;
    	}
    
    	public class HookEventArgs : EventArgs
    	{
    		public MSG msg;
    	}
    	public delegate void HookEventHandler(object sender, HookEventArgs e);
    
    	public class Hook : IDisposable
    	{
    		public delegate int HookProc(int code, IntPtr wParam, IntPtr lParam);
    
    		protected IntPtr m_hhook = IntPtr.Zero;
    		protected HookProc m_filterFunc = null;
    		protected const int WH_CALLWNDPROC = 4;
    		protected const int WH_CALLWNDPROCRET = 12;
    
    		[DllImport("kernel32.dll")]
    		static extern int GetCurrentThreadId();
    		[DllImport("user32.dll")]
    		protected static extern IntPtr SetWindowsHookEx(int code, HookProc func, IntPtr hInstance, int threadID);
    		public Hook()
    		{
    			m_filterFunc = new HookProc(CoreHookProc);
    			m_hhook = SetWindowsHookEx(WH_CALLWNDPROCRET, m_filterFunc, IntPtr.Zero, GetCurrentThreadId());
    		}
    
    		[DllImport("user32.dll")]
    		protected static extern int UnhookWindowsHookEx(IntPtr hhook);
    		public void Dispose()
    		{
    			UnhookWindowsHookEx(m_hhook);
    		}
    
    		public event HookEventHandler HookInvoked;
    		[DllImport("user32.dll")]
    		protected static extern int CallNextHookEx(IntPtr hhook, int code, IntPtr wParam, IntPtr lParam);
    		protected unsafe int CoreHookProc(int code, IntPtr wParam, IntPtr lParam)
    		{
    			if (HookInvoked != null && code == 0) {
    				HookEventArgs e = new HookEventArgs();
    				//MSG *p = (MSG*)lParam;
    				e.msg = (MSG)Marshal.PtrToStructure(lParam, typeof(MSG));
    				if(e.msg.message == 0x0200)	// WM_MOUSEMOVE
    					HookInvoked(this, e);
    			}
    
    			return CallNextHookEx(m_hhook, code, wParam, lParam);
    		}
    	}
    
    

    これで一応ウィンドウイベントがフックされ、CoreHookProcメソッドが呼び出されるのですが、lParamの変換がうまくいかず、MSG構造体にしっくり来ない値(ptの値が明らかに大きすぎるなど)が代入されます。unsafeによるポインタの直接変換でも結果は同じでした。どの部分に問題があるかわかるでしょうか。
    2011年7月22日 8:36

回答

すべての返信

  • WH_CALLWNDPROCRETで呼び出されるコールバックはCallWndRetProc()でありそのlParam引数はCWPRETSTRUCT構造体とドキュメントにありますが。

    CWPRETSTRUCT構造体はTank2005さんがC#で定義したMSG構造体とは形が異なるようです。

    • 回答としてマーク Tank2005 2011年7月22日 14:14
    2011年7月22日 9:34
  • もしかして、CWPRETSTRUCT::messageがWM_MOUSEMOVEの際に処理をしたいのでしょうか? であればその時のパラメーターはコールバック引数のlParamではなく、CWPRETSTRUCT::lParamの方に入っているかと。またその値の読み方も間違っているのでWM_MOUSEMOVEのリンク先を参照してください。

    SetWindowsHookEx()のhInstance引数をIntPtr.Zeroに設定しているということは、自身のプロセスのウィンドウプロシージャをフックしたいのでしょうか。そのウィンドウもC#で動作しているのであればフックせずともForm.WndProc()メソッドをオーバーライドするだけで実現できるのかもしれません。やりたいことにもよりますが。

    2011年7月22日 9:54
  • MouseのHookは以下が参考になります。

    [How to set a Windows hook in Visual C# .NET]
      http://support.microsoft.com/kb/318804

    [Global System Hooks in .NET]
      http://www.codeproject.com/KB/system/globalsystemhook.aspx

    [Windows Hooks in the .NET Framework]
      http://msdn.microsoft.com/ja-jp/magazine/cc188966(en-us).aspx

    何れもSample Source Codeがありますよ。

    2011年7月22日 11:08
  • hInstanceが0なのはMSDNのサンプルソースを参考にしたからです。

    ちなみにWM_MOUSEMOVEが指定しているのは、たんにpt値を調べるのにはわかりやすいイベントであったためで、特に深い意味はありません。

    2011年7月22日 14:17
  • ちょっと何を言っているのか意味が分かりません。

    回答としてマークされたということは解決したのでしょうか? なぜ本題に触れないのか理解できません。

    hInstanceは適切な値を設定する必要があります。nullが適切な場合もありますが。

    WM_MOUSEMOVEがわかりやすいイベントとのことですが、イベントごとに値の読み方が違うためわかりやすいとか関係ありません。必要なイベントを参照すべきです。また、わかりやすいイベントのはずが、その読み出しすらわかっていないわけですから、どういう基準で選択されたのか理解できませんでした。

    2011年7月22日 14:36
  • 念のため。
    ローカルフックだけでよいのですよね?
    (もしくは、マウス or キーボードのイベントフックだけ)

    ほかのプロセスなど、通常のグローバルフックは、.NET Framework アプリケーションでは今もできなかったはず。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    2011年7月22日 15:00
    モデレータ
  • CWPRETSTRUCTに変換すべきところをMSGへと変換してしまったのが、プログラム上の誤りであることがわかったからです。仮にMSGが正しければ、カーソル位置を指すMSG.ptの値は有効であるはずなので、フックされた時点での位置の比較が行いやすいだろうと判断したためです。言葉足らずで申し訳ございませんでした。
    2011年7月22日 23:35
  • グローバルフックは.NETだけでは無理とのことですので、C++によるプログラムも試してみたいと思います。
    2011年7月22日 23:37
  • グローバルフックは.NETだけでは無理とのこと

    この情報は、すでに参考にされている http://support.microsoft.com/kb/318804 の下の方に書いてあります。
    参考までに。

    # グローバルフックはネイティブの DLL である必要があるので、C++ で書くという選択とともに、EXE+DLL の構成にしなければなりません。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    2011年7月23日 7:43
    モデレータ