2012年2月14日火曜日

RawInputを.NETで使う

Windows7における、グローバルフックの問題点


Windows 7ではグローバルフックで捕まえたメッセージを300ms以内で処理出来ないことが10回続いた場合、そのプロセスは強制的にフックを解除され二度と登録できません。
今回の話題はそんな感じです。[ソース元]
http://bit.ly/d3a1HW
http://bit.ly/bS4BsP


解決案


解決方法として考え得るものを列挙します。
  1. レジストリを操作して、300msを10000msくらいに変更する
  2. プロセスを数分ごとに再起動させる
  3. RawInput系のAPIを代替手段として用いる
  4. 300ms以内で処理できるようにアプリケーションを改善する

解決案に対する考察


  • 1 は、ユーザ視点でない限りまず採られない方法だと思います
  • 2 は手っ取り早く、現実的に解決させる方法であると思います
  • 3 はWindowsXP以降で実現するのに適しているかもしれませんが、それ以前のOSにも対応するのであれば、OSによってRawInputとグローバルフックを切り替えると良いかもしれません
  • 4 のような、「短い時間で最良のパフォーマンスを」というような思想は、技術者なら常に頭に入れておく必要があると思います
自分のアプリでは、3と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 件のコメント:

コメントを投稿