トップ回答者
ウィンドウフックの結果が正しく変換できない

質問
-
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によるポインタの直接変換でも結果は同じでした。どの部分に問題があるかわかるでしょうか。
回答
-
WH_CALLWNDPROCRETで呼び出されるコールバックはCallWndRetProc()でありそのlParam引数はCWPRETSTRUCT構造体とドキュメントにありますが。
CWPRETSTRUCT構造体はTank2005さんがC#で定義したMSG構造体とは形が異なるようです。
- 回答としてマーク Tank2005 2011年7月22日 14:14
すべての返信
-
WH_CALLWNDPROCRETで呼び出されるコールバックはCallWndRetProc()でありそのlParam引数はCWPRETSTRUCT構造体とドキュメントにありますが。
CWPRETSTRUCT構造体はTank2005さんがC#で定義したMSG構造体とは形が異なるようです。
- 回答としてマーク Tank2005 2011年7月22日 14:14
-
もしかして、CWPRETSTRUCT::messageがWM_MOUSEMOVEの際に処理をしたいのでしょうか? であればその時のパラメーターはコールバック引数のlParamではなく、CWPRETSTRUCT::lParamの方に入っているかと。またその値の読み方も間違っているのでWM_MOUSEMOVEのリンク先を参照してください。
SetWindowsHookEx()のhInstance引数をIntPtr.Zeroに設定しているということは、自身のプロセスのウィンドウプロシージャをフックしたいのでしょうか。そのウィンドウもC#で動作しているのであればフックせずともForm.WndProc()メソッドをオーバーライドするだけで実現できるのかもしれません。やりたいことにもよりますが。
-
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がありますよ。
-
hInstanceが0なのはMSDNのサンプルソースを参考にしたからです。
ちなみにWM_MOUSEMOVEが指定しているのは、たんにpt値を調べるのにはわかりやすいイベントであったためで、特に深い意味はありません。
-
-
グローバルフックは.NETだけでは無理とのこと
この情報は、すでに参考にされている http://support.microsoft.com/kb/318804 の下の方に書いてあります。
参考までに。# グローバルフックはネイティブの DLL である必要があるので、C++ で書くという選択とともに、EXE+DLL の構成にしなければなりません。
質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。