6

I'm trying to make a C# application, which is going to control a game. That I'm trying to do is for example: Hold key A for 150ms, Hold left arrow for 500ms and so on. I was searching a lot and I found the following code. My program firstly target the game and then holding the keys.

I'm holding the keys this way:

Keyboard.HoldKey(Keys.Left);
Thread.sleep(500);
Keyboard.ReleaseKey(Keys.Left);

Here is the Keyboard class:

public class Keyboard
 {
    public Keyboard()
    {
    }

    [StructLayout(LayoutKind.Explicit, Size = 28)]
    public struct Input
    {
        [FieldOffset(0)]
        public uint type;
        [FieldOffset(4)]
        public KeyboardInput ki;
    }

    public struct KeyboardInput
    {
        public ushort wVk;
        public ushort wScan;
        public uint dwFlags;
        public long time;
        public uint dwExtraInfo;
    }

    const int KEYEVENTF_KEYUP = 0x0002;
    const int INPUT_KEYBOARD = 1;

    [DllImport("user32.dll")]
    public static extern int SendInput(uint cInputs, ref Input inputs, int cbSize);

    [DllImport("user32.dll")]
    static extern short GetKeyState(int nVirtKey);

    [DllImport("user32.dll")]
    static extern ushort MapVirtualKey(int wCode, int wMapType);


    public static bool IsKeyDown(Keys key)
    {
        return (GetKeyState((int)key) & -128) == -128;
    }

    public static void HoldKey(Keys vk)
    {
        ushort nScan = MapVirtualKey((ushort)vk, 0);

        Input input = new Input();
        input.type = INPUT_KEYBOARD;
        input.ki.wVk = (ushort)vk;
        input.ki.wScan = nScan;
        input.ki.dwFlags = 0;
        input.ki.time = 0;
        input.ki.dwExtraInfo = 0;
        SendInput(1, ref input, Marshal.SizeOf(input)).ToString();
    }

    public static void ReleaseKey(Keys vk)
    {
        ushort nScan = MapVirtualKey((ushort)vk, 0);

        Input input = new Input();
        input.type = INPUT_KEYBOARD;
        input.ki.wVk = (ushort)vk;
        input.ki.wScan = nScan;
        input.ki.dwFlags = KEYEVENTF_KEYUP;
        input.ki.time = 0;
        input.ki.dwExtraInfo = 0;
        SendInput(1, ref input, Marshal.SizeOf(input));
    }

    public static void PressKey(Keys vk)
    {
        HoldKey(vk);
        ReleaseKey(vk);
    }
}

and its working in notepad/browser etc, but it IS NOT working in any game, no matter fullscreen or window mode. Can you help me to figure out how I can hold keys in full screen apps/games? Thanks!

Deepsy
  • 3,769
  • 7
  • 39
  • 71
  • 5
    You can't just google a piece of code, dump it into an app and expect it to work. Define `not working`? It's a very broad term, what exactly isn't working? – DGibbs May 02 '13 at 16:02
  • 1
    Games normally aquire keyboard via DirectX which is a very different way of handling. Sending Windows input messages to such apps is not useful. [E.g.](http://courses.washington.edu/css450/2008.Fall/web_contents/from_students/450Hints/DirectInputTutorial/DirectInputTutorial.pdf) [PDF]. – GSerg May 02 '13 at 16:05
  • Well I did everything else by myself, just I'm not used to windows API. By not working I mean my application is targeting the game and trying to hold/release the arrow game, but nothing happens ingame. If I press the arrow on my keyboard it works. – Deepsy May 02 '13 at 16:07
  • @GSerg can you give me a little example, how I should be able to do that? – Deepsy May 02 '13 at 16:09

4 Answers4

4

"Hold key A for 150ms, Hold left arrow for 500ms"

See if this works:

        Keyboard.HoldKey((byte)Keys.A, 150);
        Keyboard.HoldKey((byte)Keys.Left, 500);

Using:

public class Keyboard
{
    [DllImport("user32.dll", SetLastError = true)]
    static extern void keybd_event(byte bVk, byte bScan, int dwFlags, int dwExtraInfo);

    const int KEY_DOWN_EVENT = 0x0001; //Key down flag
    const int KEY_UP_EVENT = 0x0002; //Key up flag

    public static void HoldKey(byte key, int duration)
    {
        int totalDuration = 0;
        while (totalDuration < duration)
        {
            keybd_event(key, 0, KEY_DOWN_EVENT, 0);
            keybd_event(key, 0, KEY_UP_EVENT, 0);
            System.Threading.Thread.Sleep(PauseBetweenStrokes);
            totalDuration += PauseBetweenStrokes;
        }
    }
}
Idle_Mind
  • 38,363
  • 3
  • 29
  • 40
  • Hello. Thanks for the answer, but sadly the effect is the same as code posted in my first post: its working in notepad/browsers etc, but its not working in game windows. – Deepsy May 02 '13 at 16:35
  • I changed HoldKey() to rapidly press/release the key until duration has been met. See if that works any better... – Idle_Mind May 02 '13 at 16:52
  • Still the same, Seems the game are handling the key pressed in different way :( – Deepsy May 02 '13 at 17:15
  • Just noticed I didn't include "PauseBetweenStrokes": `const int PauseBetweenStrokes = 50;` I don't think it will make much difference, though. Sorry it didn't work...good luck! – Idle_Mind May 02 '13 at 17:21
  • Yeah, I tried with 1, 10 and 20 :( Seems not every game is able to handle such operations. – Deepsy May 02 '13 at 17:25
  • Supposedly these guys got SendInput() working with DirectInput [here](http://stackoverflow.com/questions/3644881/simulating-keyboard-with-sendinput-api-in-directinput-applications). – Idle_Mind May 02 '13 at 17:32
  • But can do the `KEY_DOWN_EVENT` which will keep the key pressed until there is another `KEY_UP_EVENT` for the same keycode and in between you can do the same for another key. Then you don't need pause between. Please see my reply below https://stackoverflow.com/a/74939988/3057246 – Vinod Srivastav Dec 28 '22 at 12:05
2

You can get it to work with that, this works great for holding down keys:

    public class Keyboard
{

    const int PauseBetweenStrokes = 50;
    [DllImport("user32.dll", SetLastError = true)]
    static extern void keybd_event(byte bVk, byte bScan, int dwFlags, int dwExtraInfo);

    const int KEY_DOWN_EVENT = 0x0001; //Key down flag
    const int KEY_UP_EVENT = 0x0002; //Key up flag

    public static void HoldKey(byte key, int duration)
    {
        keybd_event(key, 0, KEY_DOWN_EVENT, 0);
        System.Threading.Thread.Sleep(duration);
        keybd_event(key, 0, KEY_UP_EVENT, 0);
    }
}
darkarchon
  • 21
  • 1
0

I did it with Windown API and SendInput method.

Deepsy
  • 3,769
  • 7
  • 39
  • 71
0

If you define the Keyboard class as:

public static class Keyboard
    {   
        
        [DllImport("user32.dll", SetLastError = true)]
        static extern void keybd_event(byte bVk, byte bScan, int dwFlags, int dwExtraInfo);
        
        public static void Delay(int delay)
        {
            System.Threading.Thread.Sleep(delay);
        }
        
        public static void KeyDown(KEYCODE keycode)
        {
            keybd_event((byte)keycode, 0x0, 0, 0);// presses
        }
        
        public static void KeyPress(KEYCODE keycode, int delay = 0)
        {
            keybd_event((byte)keycode, 0x0, 0, 0);// presses
            System.Threading.Thread.Sleep(delay);
            keybd_event((byte)keycode, 0x0, 2, 0); //releases
        }
        
        public static void KeyUp(KEYCODE keycode)
        {
            keybd_event((byte)keycode, 0, 2, 0); //release
        }
        
        public static void Type(string message)
        {
            System.Windows.Forms.SendKeys.SendWait(message);
        }
            
    }

and the keycode as

public enum KEYCODE {
    VK_A = 0x41, VK_B = 0x42, VK_C = 0x43, VK_D = 0x44, VK_E = 0x45, VK_F = 0x46, VK_G = 0x47, 
    VK_H = 0x48, VK_I = 0x49, VK_J = 0x4A, VK_K = 0x4B, VK_L = 0x4C, VK_M = 0x4D, VK_N = 0x4E, VK_O = 0x4F,
    VK_P = 0x50, VK_Q = 0x51, VK_R = 0x52, VK_S = 0x53, VK_T = 0x54, VK_U = 0x55, VK_V = 0x56, VK_W = 0x57, 
    VK_X = 0x58, VK_Y = 0x59, VK_Z = 0x5A, VK_LSHIFT = 0xA0, VK_RSHIFT = 0xA1, VK_LCONTROL = 0xA2, VK_RCONTROL = 0xA3
}

You can run the following:

Keyboard.KeyDown(KEYCODE.VK_LSHIFT);
    Keyboard.KeyPress(KEYCODE.VK_V);
Keyboard.KeyUp(KEYCODE.VK_LSHIFT);
    Keyboard.KeyPress(KEYCODE.VK_I);
    Keyboard.KeyPress(KEYCODE.VK_N);
    Keyboard.KeyPress(KEYCODE.VK_O);
    Keyboard.KeyPress(KEYCODE.VK_D);

And it will print Vinod making V in capital since the shift is pressed. But if you trying to send multiple keystrokes like typing a text you can use the Keyboard.Type() instead like

Keyboard.Type("+vino+d");

and it will print VinoD making V and D capital for more help see the document here

Even I have written something called Robot.cs which has the complete code of Keyboard, Mouse & Process automation.

Vinod Srivastav
  • 3,644
  • 1
  • 27
  • 40