[C#]今更使うことはないかもしれないWindowsフック

Windowsのグローバルなメッセージフックで少しはまったのでメモします（グローバルな要素とは）。
マサカリお願いします。

#ヘルパークラス（？）

``` InputHook.cs
public class InputHook : IDisposable
{
    private event HookProc KeyboardProcProperty;
    private event HookProc MouseProcProperty;

    private readonly IntPtr KeyboardHandle;
    private readonly IntPtr MouseHandle;

    public InputHook()
    {
        IntPtr hInstance = Marshal.GetHINSTANCE(System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0]);
        KeyboardHandle = SetWindowsHookEx(13, KeyboardProcProperty = KeyboardProc, hInstance, IntPtr.Zero);
        MouseHandle = SetWindowsHookEx(14, MouseProcProperty = MouseProc, hInstance, IntPtr.Zero);
    }

    public void Dispose()
    {
        UnhookWindowsHookEx(KeyboardHandle);
        UnhookWindowsHookEx(MouseHandle);
    }

    public delegate void MouseEvent(int x, int y);
    public event MouseEvent MouseMove;
    public event MouseEvent MouseLeftDown;
    public event MouseEvent MouseLeftUp;
    public event MouseEvent MouseLeftDoubleClick;
    public event MouseEvent MouseRightDown;
    public event MouseEvent MouseRightUp;
    public event MouseEvent MouseRightDoubleClick;
    public event MouseEvent MouseMiddleDown;
    public event MouseEvent MouseMiddleUp;
    public event MouseEvent MouseMiddleDoubleClick;

    public delegate void MouseWheelEvent(int x, int y, int delta);
    public event MouseWheelEvent MouseWheel;

    private IntPtr MouseProc(int nCode, IntPtr wParam, IntPtr lParam)
    {
        if (nCode < 0)
        {
            return CallNextHookEx(KeyboardHandle, nCode, wParam, lParam);
        }

        MSLLHOOKSTRUCT data = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));

        int x = data.pt.x;
        int y = data.pt.y;

        switch (wParam.ToInt32())
        {
            case 0x0200:
                MouseMove?.Invoke(x, y);
                break;
            case 0x0201:
                MouseLeftDown?.Invoke(x, y);
                break;
            case 0x0202:
                MouseLeftUp?.Invoke(x, y);
                break;
            case 0x0203:
                MouseLeftDoubleClick?.Invoke(x, y);
                break;
            case 0x0204:
                MouseRightDown?.Invoke(x, y);
                break;
            case 0x0205:
                MouseRightUp?.Invoke(x, y);
                break;
            case 0x0206:
                MouseRightDoubleClick?.Invoke(x, y);
                break;
            case 0x0207:
                MouseMiddleDown?.Invoke(x, y);
                break;
            case 0x0208:
                MouseMiddleUp?.Invoke(x, y);
                break;
            case 0x0209:
                MouseMiddleDoubleClick?.Invoke(x, y);
                break;
            case 0x020A:
                MouseWheel?.Invoke(x, y, (short)((data.mouseData >> 16) & 0xFFFF));
                break;
        }

        return CallNextHookEx(KeyboardHandle, nCode, wParam, lParam);
    }

    public delegate void KeyEvent(Key key);
    public event KeyEvent KeyDown;
    public event KeyEvent KeyUp;

    private IntPtr KeyboardProc(int nCode, IntPtr wParam, IntPtr lParam)
    {
        if (nCode < 0)
        {
            return CallNextHookEx(KeyboardHandle, nCode, wParam, lParam);
        }

        KBDLLHOOKSTRUCT data = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT));

        Key key = KeyInterop.KeyFromVirtualKey(data.vkCode);

        switch (wParam.ToInt32())
        {
            case 0x0100:
            case 0x0104:
                KeyDown?.Invoke(key);
                break;
            case 0x0101:
            case 0x0105:
                KeyUp?.Invoke(key);
                break;
        }

        return CallNextHookEx(MouseHandle, nCode, wParam, lParam);
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct POINT
    {
        public int x;
        public int y;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct MSLLHOOKSTRUCT
    {
        public POINT pt;
        public uint mouseData;
        public uint flags;
        public uint time;
        public IntPtr dwExtraInfo;
    }

    [Flags]
    private enum KBDLLHOOKSTRUCTFlags : uint
    {
        LLKHF_EXTENDED = 0x01,
        LLKHF_LOWER_IL_INJECTED = 0x02,
        LLKHF_INJECTED = 0x10,
        LLKHF_ALTDOWN = 0x20,
        LLKHF_UP = 0x80,
    }

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

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

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, IntPtr dwThreadId);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool UnhookWindowsHookEx(IntPtr hhk);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
}
```

#使い方

``` MainWindow.xaml.cs
private InputHook InputHook { get; }

public MainWindow()
{
    InitializeComponent();

    InputHook = new InputHook();
    InputHook.KeyDown += key => Debug.WriteLine($"down {key}");
    InputHook.KeyUp += key => Debug.WriteLine($"up {key}");
}

private void Window_Closed(object sender, EventArgs e)
{
    InputHook.Dispose();
}
```