Sending keys to a DirectX Game

10,194

Solution 1

Seems like this question is raising a lot of views, so here is my solution that I did before (I collected the inputs from a site, and made It to an enum for easier usage.):

/// <summary>
/// My own question as reference: https://stackoverflow.com/questions/35138778/sending-keys-to-a-directx-game
/// http://www.gamespp.com/directx/directInputKeyboardScanCodes.html
/// </summary>
public class Keyboard
{
    [Flags]
    public enum InputType
    {
        Mouse = 0,
        Keyboard = 1,
        Hardware = 2
    }

    [Flags]
    public enum KeyEventF
    {
        KeyDown = 0x0000,
        ExtendedKey = 0x0001,
        KeyUp = 0x0002,
        Unicode = 0x0004,
        Scancode = 0x0008,
    }

    [DllImport("user32.dll", SetLastError = true)]
    private static extern uint SendInput(uint nInputs, Input[] pInputs, int cbSize);

    [DllImport("user32.dll")]
    private static extern IntPtr GetMessageExtraInfo();

    /// <summary>
    /// DirectX key list collected out from the gamespp.com list by me.
    /// </summary>
    public enum DirectXKeyStrokes
    {
        DIK_ESCAPE = 0x01,
        DIK_1 = 0x02,
        DIK_2 = 0x03,
        DIK_3 = 0x04,
        DIK_4 = 0x05,
        DIK_5 = 0x06,
        DIK_6 = 0x07,
        DIK_7 = 0x08,
        DIK_8 = 0x09,
        DIK_9 = 0x0A,
        DIK_0 = 0x0B,
        DIK_MINUS = 0x0C,
        DIK_EQUALS = 0x0D,
        DIK_BACK = 0x0E,
        DIK_TAB = 0x0F,
        DIK_Q = 0x10,
        DIK_W = 0x11,
        DIK_E = 0x12,
        DIK_R = 0x13,
        DIK_T = 0x14,
        DIK_Y = 0x15,
        DIK_U = 0x16,
        DIK_I = 0x17,
        DIK_O = 0x18,
        DIK_P = 0x19,
        DIK_LBRACKET = 0x1A,
        DIK_RBRACKET = 0x1B,
        DIK_RETURN = 0x1C,
        DIK_LCONTROL = 0x1D,
        DIK_A = 0x1E,
        DIK_S = 0x1F,
        DIK_D = 0x20,
        DIK_F = 0x21,
        DIK_G = 0x22,
        DIK_H = 0x23,
        DIK_J = 0x24,
        DIK_K = 0x25,
        DIK_L = 0x26,
        DIK_SEMICOLON = 0x27,
        DIK_APOSTROPHE = 0x28,
        DIK_GRAVE = 0x29,
        DIK_LSHIFT = 0x2A,
        DIK_BACKSLASH = 0x2B,
        DIK_Z = 0x2C,
        DIK_X = 0x2D,
        DIK_C = 0x2E,
        DIK_V = 0x2F,
        DIK_B = 0x30,
        DIK_N = 0x31,
        DIK_M = 0x32,
        DIK_COMMA = 0x33,
        DIK_PERIOD = 0x34,
        DIK_SLASH = 0x35,
        DIK_RSHIFT = 0x36,
        DIK_MULTIPLY = 0x37,
        DIK_LMENU = 0x38,
        DIK_SPACE = 0x39,
        DIK_CAPITAL = 0x3A,
        DIK_F1 = 0x3B,
        DIK_F2 = 0x3C,
        DIK_F3 = 0x3D,
        DIK_F4 = 0x3E,
        DIK_F5 = 0x3F,
        DIK_F6 = 0x40,
        DIK_F7 = 0x41,
        DIK_F8 = 0x42,
        DIK_F9 = 0x43,
        DIK_F10 = 0x44,
        DIK_NUMLOCK = 0x45,
        DIK_SCROLL = 0x46,
        DIK_NUMPAD7 = 0x47,
        DIK_NUMPAD8 = 0x48,
        DIK_NUMPAD9 = 0x49,
        DIK_SUBTRACT = 0x4A,
        DIK_NUMPAD4 = 0x4B,
        DIK_NUMPAD5 = 0x4C,
        DIK_NUMPAD6 = 0x4D,
        DIK_ADD = 0x4E,
        DIK_NUMPAD1 = 0x4F,
        DIK_NUMPAD2 = 0x50,
        DIK_NUMPAD3 = 0x51,
        DIK_NUMPAD0 = 0x52,
        DIK_DECIMAL = 0x53,
        DIK_F11 = 0x57,
        DIK_F12 = 0x58,
        DIK_F13 = 0x64,
        DIK_F14 = 0x65,
        DIK_F15 = 0x66,
        DIK_KANA = 0x70,
        DIK_CONVERT = 0x79,
        DIK_NOCONVERT = 0x7B,
        DIK_YEN = 0x7D,
        DIK_NUMPADEQUALS = 0x8D,
        DIK_CIRCUMFLEX = 0x90,
        DIK_AT = 0x91,
        DIK_COLON = 0x92,
        DIK_UNDERLINE = 0x93,
        DIK_KANJI = 0x94,
        DIK_STOP = 0x95,
        DIK_AX = 0x96,
        DIK_UNLABELED = 0x97,
        DIK_NUMPADENTER = 0x9C,
        DIK_RCONTROL = 0x9D,
        DIK_NUMPADCOMMA = 0xB3,
        DIK_DIVIDE = 0xB5,
        DIK_SYSRQ = 0xB7,
        DIK_RMENU = 0xB8,
        DIK_HOME = 0xC7,
        DIK_UP = 0xC8,
        DIK_PRIOR = 0xC9,
        DIK_LEFT = 0xCB,
        DIK_RIGHT = 0xCD,
        DIK_END = 0xCF,
        DIK_DOWN = 0xD0,
        DIK_NEXT = 0xD1,
        DIK_INSERT = 0xD2,
        DIK_DELETE = 0xD3,
        DIK_LWIN = 0xDB,
        DIK_RWIN = 0xDC,
        DIK_APPS = 0xDD,
        DIK_BACKSPACE = DIK_BACK,
        DIK_NUMPADSTAR = DIK_MULTIPLY,
        DIK_LALT = DIK_LMENU,
        DIK_CAPSLOCK = DIK_CAPITAL,
        DIK_NUMPADMINUS = DIK_SUBTRACT,
        DIK_NUMPADPLUS = DIK_ADD,
        DIK_NUMPADPERIOD = DIK_DECIMAL,
        DIK_NUMPADSLASH = DIK_DIVIDE,
        DIK_RALT = DIK_RMENU,
        DIK_UPARROW = DIK_UP,
        DIK_PGUP = DIK_PRIOR,
        DIK_LEFTARROW = DIK_LEFT,
        DIK_RIGHTARROW = DIK_RIGHT,
        DIK_DOWNARROW = DIK_DOWN,
        DIK_PGDN = DIK_NEXT,

        // Mined these out of nowhere.
        DIK_LEFTMOUSEBUTTON = 0x100,
        DIK_RIGHTMOUSEBUTTON  = 0x101,
        DIK_MIDDLEWHEELBUTTON = 0x102,
        DIK_MOUSEBUTTON3 = 0x103,
        DIK_MOUSEBUTTON4 = 0x104,
        DIK_MOUSEBUTTON5 = 0x105,
        DIK_MOUSEBUTTON6 = 0x106,
        DIK_MOUSEBUTTON7 = 0x107,
        DIK_MOUSEWHEELUP = 0x108,
        DIK_MOUSEWHEELDOWN = 0x109,
    }

    /// <summary>
    /// Sends a directx key.
    /// http://www.gamespp.com/directx/directInputKeyboardScanCodes.html
    /// </summary>
    /// <param name="key"></param>
    /// <param name="KeyUp"></param>
    /// <param name="inputType"></param>
    public static void SendKey(DirectXKeyStrokes key, bool KeyUp, InputType inputType)
    {
        uint flagtosend;
        if (KeyUp)
        {
            flagtosend = (uint) (KeyEventF.KeyUp | KeyEventF.Scancode);
        }
        else
        {
            flagtosend = (uint) (KeyEventF.KeyDown | KeyEventF.Scancode);
        }

        Input[] inputs =
        {
            new Input
            {
                type = (int) inputType,
                u = new InputUnion
                {
                    ki = new KeyboardInput
                    {
                        wVk = 0,
                        wScan = (ushort) key,
                        dwFlags = flagtosend,
                        dwExtraInfo = GetMessageExtraInfo()
                    }
                }
            }
        };

        SendInput((uint) inputs.Length, inputs, Marshal.SizeOf(typeof(Input)));
    }

    /// <summary>
    /// Sends a directx key.
    /// http://www.gamespp.com/directx/directInputKeyboardScanCodes.html
    /// </summary>
    /// <param name="key"></param>
    /// <param name="KeyUp"></param>
    /// <param name="inputType"></param>
    public static void SendKey(ushort key, bool KeyUp, InputType inputType)
    {
        uint flagtosend;
        if (KeyUp)
        {
            flagtosend = (uint) (KeyEventF.KeyUp | KeyEventF.Scancode);
        }
        else
        {
            flagtosend = (uint) (KeyEventF.KeyDown | KeyEventF.Scancode);
        }

        Input[] inputs =
        {
            new Input
            {
                type = (int) inputType,
                u = new InputUnion
                {
                    ki = new KeyboardInput
                    {
                        wVk = 0,
                        wScan = key,
                        dwFlags = flagtosend,
                        dwExtraInfo = GetMessageExtraInfo()
                    }
                }
            }
        };

        SendInput((uint) inputs.Length, inputs, Marshal.SizeOf(typeof(Input)));
    }

    public struct Input
    {
        public int type;
        public InputUnion u;
    }

    [StructLayout(LayoutKind.Explicit)]
    public struct InputUnion
    {
        [FieldOffset(0)] public readonly MouseInput mi;
        [FieldOffset(0)] public KeyboardInput ki;
        [FieldOffset(0)] public readonly HardwareInput hi;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct MouseInput
    {
        public readonly int dx;
        public readonly int dy;
        public readonly uint mouseData;
        public readonly uint dwFlags;
        public readonly uint time;
        public readonly IntPtr dwExtraInfo;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct KeyboardInput
    {
        public ushort wVk;
        public ushort wScan;
        public uint dwFlags;
        public readonly uint time;
        public IntPtr dwExtraInfo;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct HardwareInput
    {
        public readonly uint uMsg;
        public readonly ushort wParamL;
        public readonly ushort wParamH;
    }
}

Solution 2

Your code isn't sending keystrokes. It's sending a message that the key changed without actually sending the input. This may work if the target window looks only at the message and ignores the actual key state. DirectX apps will generally pay attention to the actual keyboard state and will ignore your sent messages.

Instead use the SendInput API to inject keys into the input stream. Input injected at this level will go through the normal input processing and act like real keyboard input. Depending on the target app you may need to set the scancode in the KEYBDINPUT structure rather than just the virtual key. This is likely for a DX app.

For more information see the Simulating Input section in MSDN's keyboard input overview.

Share:
10,194
DreTaX
Author by

DreTaX

https://github.com/dretax

Updated on June 04, 2022

Comments

  • DreTaX
    DreTaX almost 2 years

    I got into a little trouble sending keys. It seems like when ever I want to send keys It doesn't work.

    I'm thinking about sending alphabetic characters such as T to bring the chat up from example, but If I write It to the enum, nothing happens whenever I use them. Any ideas?

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
    public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
    [DllImport("user32.dll", SetLastError = true)]
    public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
    /// <summary>
    /// Virtual Keys
    /// </summary>
    public enum VirtualKeyStates : int
    {
        VK_LBUTTON = 0x01,
        VK_RBUTTON = 0x02,
        VK_CANCEL = 0x03,
        VK_MBUTTON = 0x04,
        //
        VK_XBUTTON1 = 0x05,
        VK_XBUTTON2 = 0x06,
        //
        VK_BACK = 0x08,
        VK_TAB = 0x09,
        //
        VK_CLEAR = 0x0C,
        VK_RETURN = 0x0D,
        //
        VK_SHIFT = 0x10,
        VK_CONTROL = 0x11,
        VK_MENU = 0x12,
        VK_PAUSE = 0x13,
        VK_CAPITAL = 0x14,
        //
        VK_KANA = 0x15,
        VK_HANGEUL = 0x15, /* old name - should be here for compatibility */
        VK_HANGUL = 0x15,
        VK_JUNJA = 0x17,
        VK_FINAL = 0x18,
        VK_HANJA = 0x19,
        VK_KANJI = 0x19,
        //
        VK_ESCAPE = 0x1B,
        //
        VK_CONVERT = 0x1C,
        VK_NONCONVERT = 0x1D,
        VK_ACCEPT = 0x1E,
        VK_MODECHANGE = 0x1F,
        //
        VK_SPACE = 0x20,
        VK_PRIOR = 0x21,
        VK_NEXT = 0x22,
        VK_END = 0x23,
        VK_HOME = 0x24,
        VK_LEFT = 0x25,
        VK_UP = 0x26,
        VK_RIGHT = 0x27,
        VK_DOWN = 0x28,
        VK_SELECT = 0x29,
        VK_PRINT = 0x2A,
        VK_EXECUTE = 0x2B,
        VK_SNAPSHOT = 0x2C,
        VK_INSERT = 0x2D,
        VK_DELETE = 0x2E,
        VK_HELP = 0x2F,
        //
        VK_LWIN = 0x5B,
        VK_RWIN = 0x5C,
        VK_APPS = 0x5D,
        //
        VK_SLEEP = 0x5F,
        //
        VK_NUMPAD0 = 0x60,
        VK_NUMPAD1 = 0x61,
        VK_NUMPAD2 = 0x62,
        VK_NUMPAD3 = 0x63,
        VK_NUMPAD4 = 0x64,
        VK_NUMPAD5 = 0x65,
        VK_NUMPAD6 = 0x66,
        VK_NUMPAD7 = 0x67,
        VK_NUMPAD8 = 0x68,
        VK_NUMPAD9 = 0x69,
        VK_MULTIPLY = 0x6A,
        VK_ADD = 0x6B,
        VK_SEPARATOR = 0x6C,
        VK_SUBTRACT = 0x6D,
        VK_DECIMAL = 0x6E,
        VK_DIVIDE = 0x6F,
        VK_F1 = 0x70,
        VK_F2 = 0x71,
        VK_F3 = 0x72,
        VK_F4 = 0x73,
        VK_F5 = 0x74,
        VK_F6 = 0x75,
        VK_F7 = 0x76,
        VK_F8 = 0x77,
        VK_F9 = 0x78,
        VK_F10 = 0x79,
        VK_F11 = 0x7A,
        VK_F12 = 0x7B,
        VK_F13 = 0x7C,
        VK_F14 = 0x7D,
        VK_F15 = 0x7E,
        VK_F16 = 0x7F,
        VK_F17 = 0x80,
        VK_F18 = 0x81,
        VK_F19 = 0x82,
        VK_F20 = 0x83,
        VK_F21 = 0x84,
        VK_F22 = 0x85,
        VK_F23 = 0x86,
        VK_F24 = 0x87,
        //
        VK_NUMLOCK = 0x90,
        VK_SCROLL = 0x91,
        //
        VK_OEM_NEC_EQUAL = 0x92, // '=' key on numpad
        //
        VK_OEM_FJ_JISHO = 0x92, // 'Dictionary' key
        VK_OEM_FJ_MASSHOU = 0x93, // 'Unregister word' key
        VK_OEM_FJ_TOUROKU = 0x94, // 'Register word' key
        VK_OEM_FJ_LOYA = 0x95, // 'Left OYAYUBI' key
        VK_OEM_FJ_ROYA = 0x96, // 'Right OYAYUBI' key
        //
        VK_LSHIFT = 0xA0,
        VK_RSHIFT = 0xA1,
        VK_LCONTROL = 0xA2,
        VK_RCONTROL = 0xA3,
        VK_LMENU = 0xA4,
        VK_RMENU = 0xA5,
        //
        VK_BROWSER_BACK = 0xA6,
        VK_BROWSER_FORWARD = 0xA7,
        VK_BROWSER_REFRESH = 0xA8,
        VK_BROWSER_STOP = 0xA9,
        VK_BROWSER_SEARCH = 0xAA,
        VK_BROWSER_FAVORITES = 0xAB,
        VK_BROWSER_HOME = 0xAC,
        //
        VK_VOLUME_MUTE = 0xAD,
        VK_VOLUME_DOWN = 0xAE,
        VK_VOLUME_UP = 0xAF,
        VK_MEDIA_NEXT_TRACK = 0xB0,
        VK_MEDIA_PREV_TRACK = 0xB1,
        VK_MEDIA_STOP = 0xB2,
        VK_MEDIA_PLAY_PAUSE = 0xB3,
        VK_LAUNCH_MAIL = 0xB4,
        VK_LAUNCH_MEDIA_SELECT = 0xB5,
        VK_LAUNCH_APP1 = 0xB6,
        VK_LAUNCH_APP2 = 0xB7,
        //
        VK_OEM_1 = 0xBA, // ';:' for US
        VK_OEM_PLUS = 0xBB, // '+' any country
        VK_OEM_COMMA = 0xBC, // ',' any country
        VK_OEM_MINUS = 0xBD, // '-' any country
        VK_OEM_PERIOD = 0xBE, // '.' any country
        VK_OEM_2 = 0xBF, // '/?' for US
        VK_OEM_3 = 0xC0, // '`~' for US
        //
        VK_OEM_4 = 0xDB, // '[{' for US
        VK_OEM_5 = 0xDC, // '|' for US
        VK_OEM_6 = 0xDD, // ']}' for US
        VK_OEM_7 = 0xDE, // ''"' for US
        VK_OEM_8 = 0xDF,
        //
        VK_OEM_AX = 0xE1, // 'AX' key on Japanese AX kbd
        VK_OEM_102 = 0xE2, // "<>" or "|" on RT 102-key kbd.
        VK_ICO_HELP = 0xE3, // Help key on ICO
        VK_ICO_00 = 0xE4, // 00 key on ICO
        //
        VK_PROCESSKEY = 0xE5,
        //
        VK_ICO_CLEAR = 0xE6,
        //
        VK_PACKET = 0xE7,
        //
        VK_OEM_RESET = 0xE9,
        VK_OEM_JUMP = 0xEA,
        VK_OEM_PA1 = 0xEB,
        VK_OEM_PA2 = 0xEC,
        VK_OEM_PA3 = 0xED,
        VK_OEM_WSCTRL = 0xEE,
        VK_OEM_CUSEL = 0xEF,
        VK_OEM_ATTN = 0xF0,
        VK_OEM_FINISH = 0xF1,
        VK_OEM_COPY = 0xF2,
        VK_OEM_AUTO = 0xF3,
        VK_OEM_ENLW = 0xF4,
        VK_OEM_BACKTAB = 0xF5,
        //
        VK_ATTN = 0xF6,
        VK_CRSEL = 0xF7,
        VK_EXSEL = 0xF8,
        VK_EREOF = 0xF9,
        VK_PLAY = 0xFA,
        VK_ZOOM = 0xFB,
        VK_NONAME = 0xFC,
        VK_PA1 = 0xFD,
        VK_OEM_CLEAR = 0xFE,
        //
        VK_0 = 0x30,
        VK_1 = 0x31,
        VK_2 = 0x32,
        VK_3 = 0x33,
        VK_4 = 0x34,
        VK_5 = 0x35,
        VK_6 = 0x36,
        VK_7 = 0x37,
        VK_8 = 0x38,
        VK_9 = 0x39
    }
    ///summary> 
    /// Virtual Messages 
    /// </summary> 
    public enum WMessages : int
    /// <summary>
    /// Sends a message to the specified window
    /// </summary>
    /// <param name="winTitle">string Title of window</param>
    /// <param name="Key">Virtual key code of the key to send</param>
    public static void ControlSendMessage(string winTitle, VirtualKeyStates Key, bool shift)
    {
        IntPtr hWnd = FindWindow(null, winTitle);
        if (shift == true)
        {
            //send shift down
            SendMessage(hWnd, 0x100, (int)VirtualKeyStates.VK_SHIFT, 0);
            //send key down
            SendMessage(hWnd, 0x100, (int)Key, 0);
            //sleep 50ms
            Thread.Sleep(50);
            //send key up
            SendMessage(hWnd, 0x101, (int)Key, 0);
            //send shift up
            SendMessage(hWnd, 0x101, (int)VirtualKeyStates.VK_SHIFT, 0);
        }
        else
        {
            //send key down
            SendMessage(hWnd, 0x100, (int)Key, 0);
            //sleep 50ms
            Thread.Sleep(50);
            //send key up
            SendMessage(hWnd, 0x101, (int)Key, 0);
        }
    }
    /// <summary>
    /// Sends a Keydown message(0x100) to the specified window with a Virtual Key
    /// </summary>
    /// <param name="winTitle">Window Title</param>
    /// <param name="Key">Virtual Key to Send</param>
    public static void KeyDown(string winTitle, VirtualKeyStates Key)
    {
        IntPtr hWnd = FindWindow(null, winTitle);
        SendMessage(hWnd, 0x100, (int)Key, 0);
    }
    /// <summary>
    /// Sends a Keydup message(0x101) to the specified window with a Virtual Key
    /// </summary>
    /// <param name="winTitle">Window Title</param>
    /// <param name="Key">Virtual Key to Send</param>
    public static void KeyUp(string winTitle, VirtualKeyStates Key)
    {
        IntPtr hWnd = FindWindow(null, winTitle);
        SendMessage(hWnd, 0x101, (int)Key, 0);
    }
    /// <summary> 
    /// Sends a message to the specified handle 
    /// </summary> 
    public void _SendMessage(IntPtr handle, int Msg, int wParam, int lParam)
    {
        SendMessage(handle, Msg, wParam, lParam);
    }