Windows7における、グローバルフックの問題点
Windows 7ではグローバルフックで捕まえたメッセージを300ms以内で処理出来ないことが10回続いた場合、そのプロセスは強制的にフックを解除され二度と登録できません。
今回の話題はそんな感じです。[ソース元]
http://bit.ly/d3a1HW
http://bit.ly/bS4BsP
解決案
解決方法として考え得るものを列挙します。
- レジストリを操作して、300msを10000msくらいに変更する
- プロセスを数分ごとに再起動させる
- RawInput系のAPIを代替手段として用いる
- 300ms以内で処理できるようにアプリケーションを改善する
解決案に対する考察
- 1 は、ユーザ視点でない限りまず採られない方法だと思います
- 2 は手っ取り早く、現実的に解決させる方法であると思います
- 3 はWindowsXP以降で実現するのに適しているかもしれませんが、それ以前のOSにも対応するのであれば、OSによってRawInputとグローバルフックを切り替えると良いかもしれません
- 4 のような、「短い時間で最良のパフォーマンスを」というような思想は、技術者なら常に頭に入れておく必要があると思います
RawInputManager
RawInput系のAPIを使用してグローバルフックに似た動きをする、C#やVB.Netで使えるDLL、「RawInputManager」を作成して公開します。
ダウンロードは以下のエントリから行ってください。
[RawInputManager]
RawInputManager使用上の注意
- Windows XP, Windows Server 2003以降でのみ使用可能です
- .NET Framework 2.0が必要となります
- DLLはx86でコンパイルされています
- CodeProjectのHookManagerに似せた実装になっていますが、メッセージを「ハンドルされた」として握りつぶすことはRawInputManagerではできません
[Ref: codeproject] http://bit.ly/y9hNoM - HookManagerに似せた実装ということで、捕まえられるイベントのデバイスはマウスとキーボードのみです
グローバルフックとの違い
ホットキーのKeyDownを捕まえられないことがあります。
具体的にはAlt+TabのTab-KeyDown等が来ないことがあります。
この現象はWindowsVista以降で発生する可能性があります。
KeyUpは捕まえられるのでそちらを判定すると良いかもしれません。
多分、原因はコレ(↓)です
[Using the IgnoreAltTab Fix] http://bit.ly/yROOL6
RawInputManagerの使用例
前述の通り、RawInputManagerはCodeProjectのHookManagerに似せた実装を行っており、同じように使用することができます。
以下にRawInputManagerを使用する例を示します。
using System; using System.Windows.Forms; using com.yu1row.tools; public class frmTest : Form { ... public frmTest() { InitializeComponent(); RawInputManager.KeyDown += new KeyEventHandler(RawInputManager_KeyDown); RawInputManager.MouseDown += new MouseEventHandler(RawInputManager_MouseDown); } void RawInputManager_MouseDown(object sender, MouseEventArgs e) { Console.WriteLine("Click: {0}", e.Button); } void RawInputManager_KeyDown(object sender, KeyEventArgs e) { Console.WriteLine("KeyDown: {0}", e.KeyData); } ... }
KeyDownのKeyEventArgs
RawInputManagerのKeyEventArgsのKeyEventArgsは.NET Frameworkのクラスをそのまま使用していますが、メンバのAlt, Control, Shiftはtrueになることはありません。
もしAlt, Control, Shiftキーが押されているかをハンドリングする場合、GetKeyStateを使用してキーの状態を調べるようにする方法があります。
以下にコード例を示します。
using System; using System.Windows.Forms; using com.yu1row.tools; public class frmTest2 : Form { [System.Runtime.InteropServices.DllImport("user32")] private static extern short GetKeyState(int vKey); private const int VK_SHIFT = 0x10; private const int VK_CONTROL = 0x11; private const int VK_MENU = 0x12; ... public frmTest2() { InitializeComponent(); RawInputManager.KeyDown += new KeyEventHandler(RawInputManager_KeyDown); } void RawInputManager_KeyDown(object sender, KeyEventArgs e) { bool isDownShift = ((GetKeyState(VK_SHIFT) & 0x80) == 0x80 ? true : false); bool isDownControl = ((GetKeyState(VK_CONTROL) & 0x80) == 0x80 ? true : false); bool isDownMenu = ((GetKeyState(VK_MENU) & 0x80) == 0x80 ? true : false); if (isDownShift) { Console.WriteLine("Pressed Shift key."); } if (isDownControl) { Console.WriteLine("Pressed Control key."); } if (isDownMenu) { Console.WriteLine("Pressed Alt key."); } Console.WriteLine("KeyDown: {0}", e.KeyData); } ... }
0 件のコメント:
コメントを投稿