Sending keys to a DirectX Game
Asked Answered
J

2

5

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);
}
Jaclyn answered 1/2, 2016 at 19:34 Comment(0)
J
8

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://mcmap.net/q/1946981/-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;
    }
}
Jaclyn answered 14/4, 2019 at 7:51 Comment(3)
it still knows it is a fake key :(Wariness
@KamuranSönecek It's probably using raw input apis, or other stuffs, hooks, etc to detect it, various ways to do so. Go kernel github.com/oblitum/Interception github.com/jasonpang/Interceptor Then you can do even very low level hotkeys.Jaclyn
No problem, It's been a time since 2016-2019 :)Jaclyn
O
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.

Oneida answered 1/2, 2016 at 20:29 Comment(4)
But SendInput doesn't allow you to directly send an input to the application even If It's in the background does It?Jaclyn
No. There's no general way to send keyboard input to a non-foreground app. You'll need to use an application specific automation system for that. For most apps UI Automation is likely, but DX games often aren't accessible.Oneida
Correct. PostMessage will also post the notification without affecting the actual input state. If the app looks at the actual input it will miss your keystroke.Oneida
I see. Thanks. I guess the only solution I can do If I want to afk in a game and do something else is to create a new desktop where I focus that app. Cheers.Jaclyn

© 2022 - 2024 — McMap. All rights reserved.