Help us understand the problem. What is going on with this article?

C#にてマウスとキーボードを操りし者

More than 3 years have passed since last update.

はじめに

C#でマウスとキーボードをグローバルフックしてその入力を監視したり、横取りしてアレコレしたかった。
この前のやつからの発展版で、マウスだけではなく、キーボードフックも追加。
さらにSendInput 関数を利用したマウスとキーボードのシミュレーションのクラスも作成。

やっていること

各コード

マウスのフック

MouseHook.cs
namespace MMFrame.Windows.GlobalHook
{
    /// <summary>
    /// マウスのグローバルフックに関するクラス
    /// </summary>
    public static class MouseHook
    {
        /// <summary>
        /// P/Invoke
        /// </summary>
        private static class NativeMethods
        {
            /// <summary>
            /// フックプロシージャのデリゲート
            /// </summary>
            /// <param name="nCode">フックプロシージャに渡すフックコード</param>
            /// <param name="msg">フックプロシージャに渡す値</param>
            /// <param name="msllhookstruct">フックプロシージャに渡す値</param>
            /// <returns>フックチェーン内の次のフックプロシージャの戻り値</returns>
            public delegate System.IntPtr MouseHookCallback(int nCode, uint msg, ref MSLLHOOKSTRUCT msllhookstruct);

            /// <summary>
            /// アプリケーション定義のフックプロシージャをフックチェーン内にインストールします。
            /// フックプロシージャをインストールすると、特定のイベントタイプを監視できます。
            /// 監視の対象になるイベントは、特定のスレッド、または呼び出し側スレッドと同じデスクトップ内のすべてのスレッドに関連付けられているものです。
            /// </summary>
            /// <param name="idHook">フックタイプ</param>
            /// <param name="lpfn">フックプロシージャ</param>
            /// <param name="hMod">アプリケーションインスタンスのハンドル</param>
            /// <param name="dwThreadId">スレッドの識別子</param>
            /// <returns>関数が成功すると、フックプロシージャのハンドルが返ります。関数が失敗すると、NULL が返ります。</returns>
            [System.Runtime.InteropServices.DllImport("user32.dll")]
            public static extern System.IntPtr SetWindowsHookEx(int idHook, MouseHookCallback lpfn, System.IntPtr hMod, uint dwThreadId);

            /// <summary>
            /// 現在のフックチェーン内の次のフックプロシージャに、フック情報を渡します。
            /// フックプロシージャは、フック情報を処理する前でも、フック情報を処理した後でも、この関数を呼び出せます。
            /// </summary>
            /// <param name="hhk">現在のフックのハンドル</param>
            /// <param name="nCode">フックプロシージャに渡すフックコード</param>
            /// <param name="msg">フックプロシージャに渡す値</param>
            /// <param name="msllhookstruct">フックプロシージャに渡す値</param>
            /// <returns>フックチェーン内の次のフックプロシージャの戻り値</returns>
            [System.Runtime.InteropServices.DllImport("user32.dll")]
            public static extern System.IntPtr CallNextHookEx(System.IntPtr hhk, int nCode, uint msg, ref MSLLHOOKSTRUCT msllhookstruct);

            /// <summary>
            /// SetWindowsHookEx 関数を使ってフックチェーン内にインストールされたフックプロシージャを削除します。
            /// </summary>
            /// <param name="hhk">削除対象のフックプロシージャのハンドル</param>
            /// <returns>関数が成功すると、0 以外の値が返ります。関数が失敗すると、0 が返ります。</returns>
            [System.Runtime.InteropServices.DllImport("user32.dll")]
            [return: System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)]
            public static extern bool UnhookWindowsHookEx(System.IntPtr hhk);
        }

        /// <summary>
        /// マウスの状態の構造体
        /// </summary>
        public struct StateMouse
        {
            public Stroke Stroke;
            public int X;
            public int Y;
            public uint Data;
            public uint Flags;
            public uint Time;
            public System.IntPtr ExtraInfo;
        }

        /// <summary>
        /// 挙動の列挙型
        /// </summary>
        public enum Stroke
        {
            MOVE,
            LEFT_DOWN,
            LEFT_UP,
            RIGHT_DOWN,
            RIGHT_UP,
            MIDDLE_DOWN,
            MIDDLE_UP,
            WHEEL_DOWN,
            WHEEL_UP,
            X1_DOWN,
            X1_UP,
            X2_DOWN,
            X2_UP,
            UNKNOWN
        }

        /// <summary>
        /// マウスのグローバルフックを実行しているかどうかを取得、設定します。
        /// </summary>
        public static bool IsHooking
        {
            get;
            private set;
        }

        /// <summary>
        /// マウスのグローバルフックを中断しているかどうかを取得、設定します。
        /// </summary>
        public static bool IsPaused
        {
            get;
            private set;
        }

        /// <summary>
        /// マウスの状態を取得、設定します。
        /// </summary>
        public static StateMouse State;

        /// <summary>
        /// フックプロシージャ内でのイベント用のデリゲート
        /// </summary>
        /// <param name="msg">マウスに関するウィンドウメッセージ</param>
        /// <param name="msllhookstruct">低レベルのマウスの入力イベントの構造体</param>
        public delegate void HookHandler(ref StateMouse state);

        /// <summary>
        /// x 座標と y 軸座標の構造体
        /// </summary>
        [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
        private struct POINT
        {
            public int x;
            public int y;
        }

        /// <summary>
        /// 低レベルのマウスの入力イベントの構造体
        /// </summary>
        [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
        private struct MSLLHOOKSTRUCT
        {
            public POINT pt;
            public uint mouseData;
            public uint flags;
            public uint time;
            public System.IntPtr dwExtraInfo;
        }

        /// <summary>
        /// フックプロシージャのハンドル
        /// </summary>
        private static System.IntPtr Handle;

        /// <summary>
        /// 入力をキャンセルするかどうかを取得、設定します。
        /// </summary>
        private static bool IsCancel;

        /// <summary>
        /// 登録イベントのリストを取得、設定します。
        /// </summary>
        private static System.Collections.Generic.List<HookHandler> Events;

        /// <summary>
        /// フックプロシージャ内でのイベント
        /// </summary>
        private static event HookHandler HookEvent;

        /// <summary>
        /// フックチェーンにインストールするフックプロシージャのイベント
        /// </summary>
        private static event NativeMethods.MouseHookCallback hookCallback;

        /// <summary>
        /// フックプロシージャをフックチェーン内にインストールし、マウスのグローバルフックを開始します。
        /// </summary>
        /// <exception cref="System.ComponentModel.Win32Exception"></exception>
        public static void Start()
        {
            if (IsHooking)
            {
                return;
            }

            IsHooking = true;
            IsPaused = false;

            hookCallback = HookProcedure;
            System.IntPtr h = System.Runtime.InteropServices.Marshal.GetHINSTANCE(typeof(MouseHook).Assembly.GetModules()[0]);

            // WH_MOUSE_LL = 14
            Handle = NativeMethods.SetWindowsHookEx(14, hookCallback, h, 0);

            if (Handle == System.IntPtr.Zero)
            {
                IsHooking = false;
                IsPaused = true;

                throw new System.ComponentModel.Win32Exception();
            }
        }

        /// <summary>
        /// マウスのグローバルフックを停止し、フックプロシージャをフックチェーン内から削除します。
        /// </summary>
        public static void Stop()
        {
            if (!IsHooking)
            {
                return;
            }

            if (Handle != System.IntPtr.Zero)
            {
                IsHooking = false;
                IsPaused = true;

                ClearEvent();

                NativeMethods.UnhookWindowsHookEx(Handle);
                Handle = System.IntPtr.Zero;
                hookCallback -= HookProcedure;
            }
        }

        /// <summary>
        /// 次のフックプロシージャにフック情報を渡すのをキャンセルします。
        /// </summary>
        public static void Cancel()
        {
            IsCancel = true;
        }

        /// <summary>
        /// マウスのグローバルフックを中断します。
        /// </summary>
        public static void Pause()
        {
            IsPaused = true;
        }

        /// <summary>
        /// マウス操作時のイベントを追加します。
        /// </summary>
        /// <param name="hookHandler"></param>
        public static void AddEvent(HookHandler hookHandler)
        {
            if (Events == null)
            {
                Events = new System.Collections.Generic.List<HookHandler>();
            }

            Events.Add(hookHandler);
            HookEvent += hookHandler;
        }

        /// <summary>
        /// マウス操作時のイベントを削除します。
        /// </summary>
        /// <param name="hookHandler"></param>
        public static void RemoveEvent(HookHandler hookHandler)
        {
            if (Events == null)
            {
                return;
            }

            HookEvent -= hookHandler;
            Events.Remove(hookHandler);
        }

        /// <summary>
        /// マウス操作時のイベントを全て削除します。
        /// </summary>
        public static void ClearEvent()
        {
            if (Events == null)
            {
                return;
            }

            foreach (HookHandler e in Events)
            {
                HookEvent -= e;
            }

            Events.Clear();
        }

        /// <summary>
        /// フックチェーンにインストールするフックプロシージャ
        /// </summary>
        /// <param name="nCode">フックプロシージャに渡すフックコード</param>
        /// <param name="msg">フックプロシージャに渡す値</param>
        /// <param name="msllhookstruct">フックプロシージャに渡す値</param>
        /// <returns>フックチェーン内の次のフックプロシージャの戻り値</returns>
        private static System.IntPtr HookProcedure(int nCode, uint msg, ref MSLLHOOKSTRUCT s)
        {
            if (nCode >= 0 && HookEvent != null && !IsPaused)
            {
                State.Stroke = GetStroke(msg, ref s);
                State.X = s.pt.x;
                State.Y = s.pt.y;
                State.Data = s.mouseData;
                State.Flags = s.flags;
                State.Time = s.time;
                State.ExtraInfo = s.dwExtraInfo;

                HookEvent(ref State);

                if (IsCancel)
                {
                    IsCancel = false;

                    return (System.IntPtr)1;
                }
            }

            return NativeMethods.CallNextHookEx(Handle, nCode, msg, ref s);
        }

        /// <summary>
        /// マウスボタンの挙動を取得します。
        /// </summary>
        /// <param name="msg">マウスに関するウィンドウメッセージ</param>
        /// <param name="s">低レベルのマウスの入力イベントの構造体</param>
        /// <returns>マウスボタンの挙動</returns>
        private static Stroke GetStroke(uint msg, ref MSLLHOOKSTRUCT s)
        {
            switch (msg)
            {
                case 0x0200:
                    // WM_MOUSEMOVE
                    return Stroke.MOVE;
                case 0x0201:
                    // WM_LBUTTONDOWN
                    return Stroke.LEFT_DOWN;
                case 0x0202:
                    // WM_LBUTTONUP
                    return Stroke.LEFT_UP;
                case 0x0204:
                    // WM_RBUTTONDOWN
                    return Stroke.RIGHT_DOWN;
                case 0x0205:
                    // WM_RBUTTONUP
                    return Stroke.RIGHT_UP;
                case 0x0207:
                    // WM_MBUTTONDOWN
                    return Stroke.MIDDLE_DOWN;
                case 0x0208:
                    // WM_MBUTTONUP
                    return Stroke.MIDDLE_UP;
                case 0x020A:
                    // WM_MOUSEWHEE
                    return ((short)((s.mouseData >> 16) & 0xffff) > 0) ? Stroke.WHEEL_UP : Stroke.WHEEL_DOWN;
                case 0x20B:
                    // WM_XBUTTONDOWN
                    switch (s.mouseData >> 16)
                    {
                        case 1:
                            return Stroke.X1_DOWN;
                        case 2:
                            return Stroke.X2_DOWN;
                        default:
                            return Stroke.UNKNOWN;
                    }
                case 0x20C:
                    // WM_XBUTTONUP
                    switch (s.mouseData >> 16)
                    {
                        case 1:
                            return Stroke.X1_UP;
                        case 2:
                            return Stroke.X2_UP;
                        default:
                            return Stroke.UNKNOWN;
                    }
                default:
                    return Stroke.UNKNOWN;
            }
        }
    }
}

キーボードのフック

KeyboardHook.cs
namespace MMFrame.Windows.GlobalHook
{
    /// <summary>
    /// キーボードのグローバルフックに関するクラス
    /// </summary>
    public static class KeyboardHook
    {
        /// <summary>
        /// P/Invoke
        /// </summary>
        private static class NativeMethods
        {
            /// <summary>
            /// フックプロシージャのデリゲート
            /// </summary>
            /// <param name="nCode">フックプロシージャに渡すフックコード</param>
            /// <param name="msg">フックプロシージャに渡す値</param>
            /// <param name="msllhookstruct">フックプロシージャに渡す値</param>
            /// <returns>フックチェーン内の次のフックプロシージャの戻り値</returns>
            public delegate System.IntPtr KeyboardHookCallback(int nCode, uint msg, ref KBDLLHOOKSTRUCT kbdllhookstruct);

            /// <summary>
            /// アプリケーション定義のフックプロシージャをフックチェーン内にインストールします。
            /// フックプロシージャをインストールすると、特定のイベントタイプを監視できます。
            /// 監視の対象になるイベントは、特定のスレッド、または呼び出し側スレッドと同じデスクトップ内のすべてのスレッドに関連付けられているものです。
            /// </summary>
            /// <param name="idHook">フックタイプ</param>
            /// <param name="lpfn">フックプロシージャ</param>
            /// <param name="hMod">アプリケーションインスタンスのハンドル</param>
            /// <param name="dwThreadId">スレッドの識別子</param>
            /// <returns>関数が成功すると、フックプロシージャのハンドルが返ります。関数が失敗すると、NULL が返ります。</returns>
            [System.Runtime.InteropServices.DllImport("user32.dll")]
            public static extern System.IntPtr SetWindowsHookEx(int idHook, KeyboardHookCallback lpfn, System.IntPtr hMod, uint dwThreadId);

            /// <summary>
            /// 現在のフックチェーン内の次のフックプロシージャに、フック情報を渡します。
            /// フックプロシージャは、フック情報を処理する前でも、フック情報を処理した後でも、この関数を呼び出せます。
            /// </summary>
            /// <param name="hhk">現在のフックのハンドル</param>
            /// <param name="nCode">フックプロシージャに渡すフックコード</param>
            /// <param name="msg">フックプロシージャに渡す値</param>
            /// <param name="msllhookstruct">フックプロシージャに渡す値</param>
            /// <returns>フックチェーン内の次のフックプロシージャの戻り値</returns>
            [System.Runtime.InteropServices.DllImport("user32.dll")]
            public static extern System.IntPtr CallNextHookEx(System.IntPtr hhk, int nCode, uint msg, ref KBDLLHOOKSTRUCT kbdllhookstruct);

            /// <summary>
            /// SetWindowsHookEx 関数を使ってフックチェーン内にインストールされたフックプロシージャを削除します。
            /// </summary>
            /// <param name="hhk">削除対象のフックプロシージャのハンドル</param>
            /// <returns>関数が成功すると、0 以外の値が返ります。関数が失敗すると、0 が返ります。</returns>
            [System.Runtime.InteropServices.DllImport("user32.dll")]
            [return: System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)]
            public static extern bool UnhookWindowsHookEx(System.IntPtr hhk);
        }

        /// <summary>
        /// キーボードの状態の構造体
        /// </summary>
        public struct StateKeyboard
        {
            public Stroke Stroke;
            public System.Windows.Forms.Keys Key;
            public uint ScanCode;
            public uint Flags;
            public uint Time;
            public System.IntPtr ExtraInfo;
        }

        /// <summary>
        /// 挙動の列挙型
        /// </summary>
        public enum Stroke
        {
            KEY_DOWN,
            KEY_UP,
            SYSKEY_DOWN,
            SYSKEY_UP,
            UNKNOWN
        }

        /// <summary>
        /// キーボードのグローバルフックを実行しているかどうかを取得、設定します。
        /// </summary>
        public static bool IsHooking
        {
            get;
            private set;
        }

        /// <summary>
        /// キーボードのグローバルフックを中断しているかどうかを取得、設定します。
        /// </summary>
        public static bool IsPaused
        {
            get;
            private set;
        }

        /// <summary>
        /// キーボードの状態を取得、設定します。
        /// </summary>
        public static StateKeyboard State;

        /// <summary>
        /// フックプロシージャ内でのイベント用のデリゲート
        /// </summary>
        /// <param name="msg">キーボードに関するウィンドウメッセージ</param>
        /// <param name="msllhookstruct">低レベルのキーボードの入力イベントの構造体</param>
        public delegate void HookHandler(ref StateKeyboard state);

        /// <summary>
        /// 低レベルのキーボードの入力イベントの構造体
        /// </summary>
        [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
        private struct KBDLLHOOKSTRUCT
        {
            public uint vkCode;
            public uint scanCode;
            public uint flags;
            public uint time;
            public System.IntPtr dwExtraInfo;
        }

        /// <summary>
        /// フックプロシージャのハンドル
        /// </summary>
        private static System.IntPtr Handle;

        /// <summary>
        /// 入力をキャンセルするかどうかを取得、設定します。
        /// </summary>
        private static bool IsCancel;

        /// <summary>
        /// 登録イベントのリストを取得、設定します。
        /// </summary>
        private static System.Collections.Generic.List<HookHandler> Events;

        /// <summary>
        /// フックプロシージャ内でのイベント
        /// </summary>
        private static event HookHandler HookEvent;

        /// <summary>
        /// フックチェーンにインストールするフックプロシージャのイベント
        /// </summary>
        private static event NativeMethods.KeyboardHookCallback hookCallback;

        /// <summary>
        /// フックプロシージャをフックチェーン内にインストールし、キーボードのグローバルフックを開始します。
        /// </summary>
        /// <exception cref="System.ComponentModel.Win32Exception"></exception>
        public static void Start()
        {
            if (IsHooking)
            {
                return;
            }

            IsHooking = true;
            IsPaused = false;

            hookCallback = HookProcedure;
            System.IntPtr h = System.Runtime.InteropServices.Marshal.GetHINSTANCE(typeof(KeyboardHook).Assembly.GetModules()[0]);

            // WH_KEYBOARD_LL = 13;
            Handle = NativeMethods.SetWindowsHookEx(13, hookCallback, h, 0);

            if (Handle == System.IntPtr.Zero)
            {
                IsHooking = false;
                IsPaused = true;

                throw new System.ComponentModel.Win32Exception();
            }
        }

        /// <summary>
        /// キーボードのグローバルフックを停止し、フックプロシージャをフックチェーン内から削除します。
        /// </summary>
        public static void Stop()
        {
            if (!IsHooking)
            {
                return;
            }

            if (Handle != System.IntPtr.Zero)
            {
                IsHooking = false;
                IsPaused = true;

                ClearEvent();

                NativeMethods.UnhookWindowsHookEx(Handle);
                Handle = System.IntPtr.Zero;
                hookCallback -= HookProcedure;
            }
        }

        /// <summary>
        /// 次のフックプロシージャにフック情報を渡すのをキャンセルします。
        /// </summary>
        public static void Cancel()
        {
            IsCancel = true;
        }

        /// <summary>
        /// キーボードのグローバルフックを中断します。
        /// </summary>
        public static void Pause()
        {
            IsPaused = true;
        }

        /// <summary>
        /// キーボード操作時のイベントを追加します。
        /// </summary>
        /// <param name="hookHandler"></param>
        public static void AddEvent(HookHandler hookHandler)
        {
            if (Events == null)
            {
                Events = new System.Collections.Generic.List<HookHandler>();
            }

            Events.Add(hookHandler);
            HookEvent += hookHandler;
        }

        /// <summary>
        /// キーボード操作時のイベントを削除します。
        /// </summary>
        /// <param name="hookHandler"></param>
        public static void RemoveEvent(HookHandler hookHandler)
        {
            if (Events == null)
            {
                return;
            }

            HookEvent -= hookHandler;
            Events.Remove(hookHandler);
        }

        /// <summary>
        /// キーボード操作時のイベントを全て削除します。
        /// </summary>
        public static void ClearEvent()
        {
            if (Events == null)
            {
                return;
            }

            foreach (HookHandler e in Events)
            {
                HookEvent -= e;
            }

            Events.Clear();
        }

        /// <summary>
        /// フックチェーンにインストールするフックプロシージャ
        /// </summary>
        /// <param name="nCode">フックプロシージャに渡すフックコード</param>
        /// <param name="msg">フックプロシージャに渡す値</param>
        /// <param name="msllhookstruct">フックプロシージャに渡す値</param>
        /// <returns>フックチェーン内の次のフックプロシージャの戻り値</returns>
        private static System.IntPtr HookProcedure(int nCode, uint msg, ref KBDLLHOOKSTRUCT s)
        {
            if (nCode >= 0 && HookEvent != null && !IsPaused)
            {
                State.Stroke = GetStroke(msg);
                State.Key = (System.Windows.Forms.Keys)s.vkCode;
                State.ScanCode = s.scanCode;
                State.Flags = s.flags;
                State.Time = s.time;
                State.ExtraInfo = s.dwExtraInfo;

                HookEvent(ref State);

                if (IsCancel)
                {
                    IsCancel = false;

                    return (System.IntPtr)1;
                }
            }

            return NativeMethods.CallNextHookEx(Handle, nCode, msg, ref s);
        }

        /// <summary>
        /// キーボードキーの挙動を取得します。
        /// </summary>
        /// <param name="msg">キーボードに関するウィンドウメッセージ</param>
        /// <returns>キーボードボタンの挙動</returns>
        private static Stroke GetStroke(uint msg)
        {
            switch (msg)
            {
                case 0x100:
                    // WM_KEYDOWN
                    return Stroke.KEY_DOWN;
                case 0x101:
                    // WM_KEYUP
                    return Stroke.KEY_UP;
                case 0x104:
                    // WM_SYSKEYDOWN
                    return Stroke.SYSKEY_DOWN;
                case 0x105:
                    // WM_SYSKEYUP
                    return Stroke.SYSKEY_UP;
                default:
                    return Stroke.UNKNOWN;
            }
        }
    }
}

マウスとキーボードのシミュレーション

InputSimulator.cs
namespace MMFrame.Windows.Simulation
{
    /// <summary>
    /// マウスとキーボード入力のシミュレーションに関するクラス
    /// </summary>
    public static class InputSimulator
    {
        /// <summary>
        /// P/Invoke
        /// </summary>
        private static class NativeMethods
        {
            /// <summary>
            /// 仮想キーコードをスキャンコード、または文字の値(ASCII 値)へ変換します。
            /// また、スキャンコードを仮想コードへ変換することもできます。
            /// </summary>
            /// <param name="wCode">仮想キーコードまたはスキャンコード</param>
            /// <param name="wMapType">実行したい変換の種類</param>
            /// <returns></returns>
            [System.Runtime.InteropServices.DllImport("user32.dll", EntryPoint = "MapVirtualKeyA")]
            public extern static int MapVirtualKey(int wCode, int wMapType);

            /// <summary>
            /// キーストローク、マウスの動き、ボタンのクリックなどを合成します。
            /// </summary>
            /// <param name="nInputs"><paramref name="pInputs"/> 配列内の構造体の数を指定します。</param>
            /// <param name="pInputs">INPUT 構造体の配列へのポインタを指定します。構造体はそれぞれキーボードまたはマウス入力ストリームに挿入されるイベントを表します。</param>
            /// <param name="cbsize">INPUT 構造体のサイズを指定します。cbSize パラメータの値が INPUT 構造体のサイズと等しくない場合、関数は失敗します。</param>
            [System.Runtime.InteropServices.DllImport("user32.dll")]
            public extern static void SendInput(int nInputs, Input[] pInputs, int cbsize);
        }

        /// <summary>
        /// シミュレートされたマウスイベントの構造体
        /// </summary>
        [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
        public struct MouseInput
        {
            public int X;
            public int Y;
            public int Data;
            public int Flags;
            public int Time;
            public int ExtraInfo;
        }

        /// <summary>
        /// シミュレートされたキーボードイベントの構造体
        /// </summary>
        [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
        public struct KeyboardInput
        {
            public short VirtualKey;
            public short ScanCode;
            public int Flags;
            public int Time;
            public int ExtraInfo;
        }

        /// <summary>
        /// キーボードやマウス以外の入力デバイスによって生成されたシミュレートされたメッセージの構造体
        /// </summary>
        [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
        public struct HardwareInput
        {
            public int uMsg;
            public short wParamL;
            public short wParamH;
        }

        /// <summary>
        /// キーストローク、マウスの動き、マウスクリックなどの入力イベントの構造体
        /// </summary>
        [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Explicit)]
        public struct Input
        {
            [System.Runtime.InteropServices.FieldOffset(0)]
            public int Type;

            [System.Runtime.InteropServices.FieldOffset(4)]
            public MouseInput Mouse;

            [System.Runtime.InteropServices.FieldOffset(4)]
            public KeyboardInput Keyboard;

            [System.Runtime.InteropServices.FieldOffset(4)]
            public HardwareInput Hardware;
        }

        /// <summary>
        /// マウス動作の列挙型
        /// </summary>
        public enum MouseStroke
        {
            MOVE = 0x0001,
            LEFT_DOWN = 0x0002,
            LEFT_UP = 0x0004,
            RIGHT_DOWN = 0x0008,
            RIGHT_UP = 0x0010,
            MIDDLE_DOWN = 0x0020,
            MIDDLE_UP = 0x0040,
            X_DOWN = 0x0080,
            X_UP = 0x0100,
            WHEEL = 0x0800
        }

        /// <summary>
        /// キーボード動作の列挙型
        /// </summary>
        public enum KeyboardStroke
        {
            KEY_DOWN = 0x0000,
            KEY_UP = 0x0002            
        }

        /// <summary>
        /// KEYEVENTF_UNICODE
        /// </summary>
        private const int KBD_UNICODE = 0x0004;

        /// <summary>
        /// マウス入力のイベントを追加します。
        /// </summary>
        /// <param name="inputs">入力イベントのリスト</param>
        /// <param name="flag">移動とクリックのオプション</param>
        /// <param name="data">シミュレートされたマウスイベントに関する情報(ホイール回転量又はXボタン番号)</param>
        /// <param name="absolute">マウス座標を絶対値で指定する場合は true</param>
        /// <param name="x">水平位置または移動量</param>
        /// <param name="y">垂直位置または移動量</param>
        public static void AddMouseInput(ref System.Collections.Generic.List<Input> inputs, MouseStroke flag, int data, bool absolute, int x, int y)
        {
            AddMouseInput(ref inputs, new System.Collections.Generic.List<MouseStroke> { flag }, data, absolute, x, y);
        }

        /// <summary>
        /// マウス入力のイベントを追加します。
        /// </summary>
        /// <param name="inputs">入力イベントのリスト</param>
        /// <param name="flags">移動とクリックのオプション</param>
        /// <param name="data">シミュレートされたマウスイベントに関する情報(ホイール回転量又はXボタン番号)</param>
        /// <param name="absolute">マウス座標を絶対値で指定する場合は true</param>
        /// <param name="x">水平位置または移動量</param>
        /// <param name="y">垂直位置または移動量</param>
        public static void AddMouseInput(ref System.Collections.Generic.List<Input> inputs, System.Collections.Generic.List<MouseStroke> flags, int data, bool absolute, int x, int y)
        {
            if (flags == null)
            {
                return;
            }

            int mouseFlags = 0;

            foreach (MouseStroke f in flags)
            {
                mouseFlags |= (int)f;
            }

            if (absolute)
            {
                // ABSOLUTE = 0x8000
                mouseFlags |= 0x8000;

                x *= (65535 / System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width);
                y *= (65535 / System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height);
            }

            AddMouseInput(ref inputs, mouseFlags, data, x, y, 0, 0);
        }

        /// <summary>
        /// マウス入力のイベントを追加します。
        /// </summary>
        /// <param name="inputs">入力イベントのリスト</param>
        /// <param name="flags">移動とクリックのオプション</param>
        /// <param name="data">シミュレートされたマウスイベントに関する情報(ホイール回転量又はXボタン番号)</param>
        /// <param name="x">水平位置または移動量</param>
        /// <param name="y">垂直位置または移動量</param>
        /// <param name="time">ミリ秒単位でのイベントのタイムスタンプ</param>
        /// <param name="extraInfo">マウスイベントに関連する追加の値</param>
        public static void AddMouseInput(ref System.Collections.Generic.List<Input> inputs, int flags, int data, int x, int y, int time, int extraInfo)
        {           
            Input input = new Input();
            input.Type = 0; // MOUSE = 0
            input.Mouse.Flags = flags;
            input.Mouse.Data = data;
            input.Mouse.X = x;
            input.Mouse.Y = y;
            input.Mouse.Time = time;
            input.Mouse.ExtraInfo = extraInfo;

            inputs.Add(input);
        }

        /// <summary>
        /// キーボード入力のイベントを追加します。
        /// </summary>
        /// <param name="inputs">入力イベントのリスト</param>
        /// <param name="srcStr">入力する文字列</param>
        public static void AddKeyboardInput(ref System.Collections.Generic.List<Input> inputs, string srcStr)
        {
            if (System.String.IsNullOrEmpty(srcStr))
            {
                return;
            }

            foreach (char s in srcStr)
            {
                AddKeyboardInput(ref inputs, (int)KeyboardStroke.KEY_DOWN | KBD_UNICODE, 0, (short)s, 0, 0);
                AddKeyboardInput(ref inputs, (int)KeyboardStroke.KEY_UP | KBD_UNICODE, 0, (short)s, 0, 0);
            }
        }

        /// <summary>
        /// キーボード入力のイベントを追加します。
        /// </summary>
        /// <param name="inputs">入力イベントのリスト</param>
        /// <param name="flags">キーボードの動作</param>
        /// <param name="key">入力するキー</param>
        public static void AddKeyboardInput(ref System.Collections.Generic.List<Input> inputs, KeyboardStroke flags, System.Windows.Forms.Keys key)
        {
            int keyboardFlags = (int)flags | KBD_UNICODE;
            short virtualKey = (short)key;
            short scanCode = (short)NativeMethods.MapVirtualKey(virtualKey, 0);

            AddKeyboardInput(ref inputs, keyboardFlags, virtualKey, scanCode, 0, 0);
        }

        /// <summary>
        /// キーボード入力のイベントを追加します。
        /// </summary>
        /// <param name="inputs">入力イベントのリスト</param>
        /// <param name="flags">キーストロークのオプション</param>
        /// <param name="virtualKey">仮想キーコード</param>
        /// <param name="scanCode">キーのハードウェアスキャンコード</param>
        /// <param name="time">ミリ秒単位でのイベントのタイムスタンプ</param>
        /// <param name="extraInfo">キーストロークに関連付けられた付加価値</param>
        public static void AddKeyboardInput(ref System.Collections.Generic.List<Input> inputs, int flags, short virtualKey, short scanCode, int time, int extraInfo)
        {
            Input input = new Input();
            input.Type = 1; // KEYBOARD = 1
            input.Keyboard.Flags = flags;
            input.Keyboard.VirtualKey = virtualKey;
            input.Keyboard.ScanCode = scanCode;
            input.Keyboard.Time = time;
            input.Keyboard.ExtraInfo = extraInfo;

            inputs.Add(input);
        }

        /// <summary>
        /// 入力イベントを実行します。
        /// </summary>
        public static void SendInput(System.Collections.Generic.List<Input> inputs)
        {
            Input[] inputArray = inputs.ToArray();
            SendInput(inputArray);
        }

        /// <summary>
        /// 入力イベントを実行します。
        /// </summary>
        public static void SendInput(Input[] inputs)
        {
            NativeMethods.SendInput(inputs.Length, inputs, System.Runtime.InteropServices.Marshal.SizeOf(inputs[0]));
        }
    }
}

こんな感じに使う

Form1.cs
using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace MMFrame
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        void hookMouseTest(ref MMFrame.Windows.GlobalHook.MouseHook.StateMouse s)
        {
            button1.Text = s.X + ", " + s.Y;

            if (s.Stroke == MMFrame.Windows.GlobalHook.MouseHook.Stroke.X1_DOWN)
            {
                MMFrame.Windows.GlobalHook.MouseHook.Cancel();
                textBox1.Text = "Disable X1_DOWN" + "\r\n" + textBox1.Text;
                return;
            }

            if (s.Stroke != MMFrame.Windows.GlobalHook.MouseHook.Stroke.MOVE)
            {
                textBox1.Text = s.Stroke + "\r\n" + textBox1.Text;
            }
        }

        void hookKeyboardTest(ref MMFrame.Windows.GlobalHook.KeyboardHook.StateKeyboard s)
        {
            textBox1.Text = s.Stroke + " : " + s.Key + "\r\n" + textBox1.Text;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (MMFrame.Windows.GlobalHook.MouseHook.IsHooking)
            {
                MMFrame.Windows.GlobalHook.MouseHook.Stop();
                return;
            }

            MMFrame.Windows.GlobalHook.MouseHook.AddEvent(hookMouseTest);
            MMFrame.Windows.GlobalHook.MouseHook.Start();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            if (MMFrame.Windows.GlobalHook.KeyboardHook.IsHooking)
            {
                MMFrame.Windows.GlobalHook.KeyboardHook.Stop();
                return;
            }

            MMFrame.Windows.GlobalHook.KeyboardHook.AddEvent(hookKeyboardTest);
            MMFrame.Windows.GlobalHook.KeyboardHook.Start();
        }

        private void button3_Click(object sender, EventArgs e)
        {
            List<MMFrame.Windows.Simulation.InputSimulator.Input> inputs = new List<MMFrame.Windows.Simulation.InputSimulator.Input>();
            List<MMFrame.Windows.Simulation.InputSimulator.MouseStroke> flags = new List<MMFrame.Windows.Simulation.InputSimulator.MouseStroke>();

            flags.Add(MMFrame.Windows.Simulation.InputSimulator.MouseStroke.LEFT_DOWN);
            flags.Add(MMFrame.Windows.Simulation.InputSimulator.MouseStroke.LEFT_UP);
            flags.Add(MMFrame.Windows.Simulation.InputSimulator.MouseStroke.MOVE);

            MMFrame.Windows.Simulation.InputSimulator.AddMouseInput(ref inputs, flags, 0, false, 0, 50);
            MMFrame.Windows.Simulation.InputSimulator.AddKeyboardInput(ref inputs, "ゆっくりしていってね!!");

            MMFrame.Windows.Simulation.InputSimulator.SendInput(inputs);
        }
    }
}

test.png

おわりに

エクスプロージョン!

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away