Windows CE(Compact Framework)でキーフック 2009

いくつかの技術的なトピックについて、昔の日記に結構アクセスがあるんですよね(・ω・)
ただ、中にはその後推奨する方法が変わっていたるものもあったりして(´д`;)
っで、たまには内容をアップデートしてみようと思います(・∀・)


とりあえず今回は以下の辺りに対するアップデートということで、Windows CE(Compact Framework)でのキーフックのやりかたについて(・∀・)

やりかた

Smart Device Framework(OpenNETCF)の中にKeyboardHookクラスが入っているんで、それを使え。
以上。


…っで終わってしまうんですが(´д`;)
それだけでもなんなので、自前でのKeyboardHookクラスの作り方についても書いてみます(・∀・)
とりあえずソース。

// Window Message
public enum WM
{
...
    KEYDOWN = 256,
    KEYUP = 257,
...
}
// Event Args
public class KeyDataEventArgs : EventArgs
{
    public bool Handled { get; set; }
    public WM KeyMessage { get; private set; }
    public int KeyCode { get; private set; }
    public int ScanCode { get; private set; }
    public int TimeStamp { get; private set; }

    public KeyDataEventArgs(WM keyMessage, int keyCode, int scanCode, int timeStamp)
    {
        this.KeyMessage = keyMessage;
        this.KeyCode = keyCode;
        this.ScanCode = scanCode;
        this.TimeStamp = timeStamp;
    }
}
// Hook Class
public class KeyboardHook
{
    private const int HC_ACTION = 0;
    private const int WH_KEYBOARD_LL = 20;

    private delegate int HookProc(int nCode, IntPtr wParam, IntPtr lParam);

    [StructLayout(LayoutKind.Sequential)]
    private struct KBDLLHOOKSTRUCT
    {
        public int vkCode;
        public int scanCode;
        public int flags;
        public int time;
        public int dwExtraInfo;
    }

    [DllImport("coredll.dll", SetLastError=true)]
    private static extern IntPtr GetModuleHandle(string lpModuleName);

    [DllImport("coredll.dll", SetLastError=true)]
    private static extern IntPtr SetWindowsHookEx(int hook, HookProc callback, IntPtr hMod, int dwThreadId);

    [DllImport("coredll.dll", SetLastError=true)]
    private static extern bool UnhookWindowsHookEx(IntPtr hhk);

    [DllImport("coredll.dll", SetLastError=true)]
    private static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);


    public event EventHandler<KeyDataEventArgs> KeyDetected;

    private IntPtr hHook;
    private readonly HookProc hookProc;


    public KeyboardHook()
    {
        this.hookProc = new HookProc( KeyboardHookProc );
    }

    ~KeyboardHook()
    {
        this.Dispose( false );
    }

    public void Dispose()
    {
        this.Dispose( true );
        GC.SuppressFinalize( this );
    }

    protected virtual void Dispose(bool disposing)
    {
        this.Enabled = false;
    }

    private int KeyboardHookProc(int nCode, IntPtr wParam, IntPtr lParam)
    {
        if( ( nCode == HC_ACTION ) && ( this.KeyDetected != null ) )
        {
            KBDLLHOOKSTRUCT khs = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure( lParam, typeof( KBDLLHOOKSTRUCT ) );
            KeyDataEventArgs args = new KeyDataEventArgs( (WM)((int)wParam), khs.vkCode, khs.scanCode, khs.time );

            KeyDetected( this, args );

            if( args.Handled )
            {
                return 1;
            }  
        }

        return CallNextHookEx( this.hHook, nCode, wParam, lParam );
    }

    public bool Enabled
    {
        get
        {
            return this.hHook != IntPtr.Zero;
        }
        set
        {
            if( value )
            {
                if( this.hHook == IntPtr.Zero )
                {
                    this.hHook = SetWindowsHookEx( WH_KEYBOARD_LL, this.hookProc, GetModuleHandle( null ), 0 );
                }
            }
            else
            {
                if( this.hHook != IntPtr.Zero )
                {
                    UnhookWindowsHookEx( this.hHook );
                    this.hHook = IntPtr.Zero;
                }
            }
        }
    }
}

解説は特にいらないですよね。
ちなみに、OpenNETCFのやつも大体こんな感じのハズ…っというか、それにインタフェースを似せてみました(・∀・)
イベントの所をちょっと自分の趣味に合わせているけれど(・ω・)


っで、使い方はこんな感じで。

public partial class Form1 : Form
{
    private readonly KeyboardHook keyboardHook = new KeyboardHook();

    public Form1()
    {
        InitializeComponent();

        this.keyboardHook.KeyDetected += KeyDetected;
        this.keyboardHook.Enabled = true;
    }

    private void KeyDetected(object sender, KeyDataEventArgs e)
    {
        System.Diagnostics.Debug.WriteLine( String.Format( "Message=[{0}], KeyCode=[{1}]", e.KeyMessage, e.KeyCode ) );

        // 無効化してみる
        if( e.KeyCode == (int)Keys.LWin )
        {
            e.Handled = true;
        }
    }
}

っということで、Smart Device Frameworkを使用せずに自前でキーフックしたければ、こんなカンジのクラスを用意すればOKです(・∀・)
まあ、Smart Device Frameworkで良いと思うけど(´ω`)


Smart Device Frameworkについては1系の初期の頃から知っているので、どんなクラスがあって、内部でどんなことをしているのかもある程度分かっているんだけど(・ω・)
Smart Device Frameworkの中には、Full Frameworkではサポートしているのに、Compact Frameworkでは未サポートな機能を代替するためのものも多くあったりしてね。
っというか、Compact Frameworkが標準で未サポートな所を減らしてくれれば、そういうのも不要になるんですが。
Win32レベルではCEでもサポートしているハズなのに、Compact Frameworkではサポートしていないとか、やめて欲しいです(#゚Д゚)


今でもそういうのが残っているけれど、Compact Framework 1.0時代は今以上に酷くて、色々とバッドノウハウがあったんだよね〜(´д`;)
まあ、マーシャリングが貧弱すぎたせいで、結局Native DLLを作るはめになったということも(つд`)


っで、僕チンはまだVisual Studio 2010βに触っていないんですが、Compact Framework関連の強化ポイントってどのあたりなんですかね(・∀・)?