none
KeySend dans un jeu C# RRS feed

  • Question

  • Bonjour,

    Je suis actuellement sur la création d'un projet ou il me faut utiliser l'expression :SendKey

    J'ai réussi a l'utilisé au début sur plusieurs logiciels type bloc note, word, firefox et même un jeux nommé Warframe.

    Mais je ne sait pour quelle raison cela ne marche pas sur le jeux EuroTruck Simulator 2 ...

    j’envoie le code suivant :

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Windows.Forms;

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

            private void button1_Click(object sender, EventArgs e)
            {
                Thread.Sleep(5000);///Attendre 5 seconde avant d'envoyer le message.
                SendKeys.Send("Test dans le chat du jeux");
            }
        }
    }

    Mais rien ne s'affiche dans le chat du jeux ... Ni en partie solo (dans la console du jeux) ni en partie multijoueur  ...

    Auriez-vous une solution ?



    • Modifié Freezlex dimanche 5 août 2018 20:44
    dimanche 5 août 2018 20:34

Réponses

  • Non, comme j'ai dit, par P/Invoke (Interop)

    Par exemple, test avec Notepad : on le met en foreground et on envoie 'a' =>

    IntPtr hWndNotepad = FindWindow("Notepad", null);
    if (hWndNotepad != IntPtr.Zero)
    {
        IntPtr hWndEdit = FindWindowEx(hWndNotepad, IntPtr.Zero, "Edit", null);
        SwitchToThisWindow(hWndNotepad, true);
        SwitchToThisWindow(hWndEdit, true);
    
        INPUT[] currentInput = new INPUT[2];
        currentInput[0].type = INPUT_KEYBOARD;
        currentInput[0].inputUnion.ki.wVk = (short)Keys.A;
        currentInput[1] = currentInput[0];
        currentInput[1].inputUnion.ki.dwFlags = KEYEVENTF_KEYUP;
        SendInput(2, currentInput, Marshal.SizeOf(currentInput[0]));
    }

    avec les declarations :

    (ajouter

    using System.Runtime.InteropServices;

    au début)

    =>

    [DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
    
    [DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
    
    [DllImport("User32.dll", SetLastError = true)]
    public static extern int SendInput(int nInputs, [MarshalAs(UnmanagedType.LPArray)] INPUT[] pInput, int cbSize);
    
    public const int INPUT_KEYBOARD = 1;
    public const int KEYEVENTF_EXTENDEDKEY = 0x0001;
    public const int KEYEVENTF_KEYUP = 0x0002;
    public const int KEYEVENTF_UNICODE = 0x0004;
    
    [StructLayout(LayoutKind.Sequential)]
    public struct MOUSEINPUT
    {
        public int dx;
        public int dy;
        public int mouseData;
        public int dwFlags;
        public int time;
        public IntPtr dwExtraInfo;
    }
    
    [StructLayout(LayoutKind.Sequential)]
    public struct KEYBDINPUT
    {
        public short wVk;
        public short wScan;
        public int dwFlags;
        public int time;
        public IntPtr dwExtraInfo;
    }
    
    [StructLayout(LayoutKind.Sequential)]
    public struct HARDWAREINPUT
    {
        public int uMsg;
        public short wParamL;
        public short wParamH;
    }
    
    [StructLayout(LayoutKind.Sequential)]
    public struct INPUT
    {
        public int type;
        public INPUTUNION inputUnion;
    }
    
    [StructLayout(LayoutKind.Explicit)]
    public struct INPUTUNION
    {
        [FieldOffset(0)]
        public MOUSEINPUT mi;
        [FieldOffset(0)]
        public KEYBDINPUT ki;
        [FieldOffset(0)]
        public HARDWAREINPUT hi;
    }

    • Marqué comme réponse Freezlex mercredi 8 août 2018 14:44
    lundi 6 août 2018 09:45
  • Oui, je n'ai pas mis la definition de :

    [DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            public static extern bool SwitchToThisWindow(IntPtr hWnd, Boolean fAltTab);

    et Keys est dans :

    using System.Windows.Forms;

    • Marqué comme réponse Freezlex mercredi 8 août 2018 14:44
    mercredi 8 août 2018 09:00
  • Je viens de télécharger "Eurotruck Simulator 2" pour tester

    et apparemment il a besoin du scan code.

    J'ai fait ce test qui marche chez moi (envoi de H puis J (1 seconde) pour klaxon + phares) :

    IntPtr hWndEurotruck = FindWindow("prism3d", null);
    if (hWndEurotruck != IntPtr.Zero)
    {                   
        SwitchToThisWindow(hWndEurotruck, true);
        System.Threading.Thread.Sleep(1000);
       
        sendKey((short)Keys.H, 1000, false);
        sendKey((short)Keys.J, 1000, false);
    }

    avec : 

    [DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern uint MapVirtualKey(uint uCode, uint uMapType);
    
    public const int KEYEVENTF_SCANCODE = 0x0008;
    
    void sendKey(short wVk, int nSleep, bool bExtendedkey)
    {
        INPUT[] input = new INPUT[1];
        input[0].inputUnion.ki.wVk = wVk;
        input[0].inputUnion.ki.wScan = (short)MapVirtualKey((uint)wVk, 0);
        input[0].inputUnion.ki.dwFlags = KEYEVENTF_SCANCODE;
        if (bExtendedkey)
            input[0].inputUnion.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
        input[0].inputUnion.ki.time = 0;
        input[0].type = INPUT_KEYBOARD;
        SendInput(1, input, Marshal.SizeOf(input[0]));
        System.Threading.Thread.Sleep(nSleep);
    
        input[0].inputUnion.ki.wVk = wVk;
        input[0].inputUnion.ki.wScan = (short)MapVirtualKey((uint)wVk, 0);
        input[0].inputUnion.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP;
        if (bExtendedkey)
            input[0].inputUnion.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
        input[0].inputUnion.ki.time = 0;
        input[0].type = INPUT_KEYBOARD;
        SendInput(1, input, Marshal.SizeOf(input[0]));
        System.Threading.Thread.Sleep(20);
    }

    • Marqué comme réponse Freezlex lundi 13 août 2018 12:11
    samedi 11 août 2018 00:14

Toutes les réponses

  • SendInput (par P/Invoke) fonctionne théoriquement avec n'importe quelle appli,

    après l'avoir mise en foreground (focus sur le bon contrôle)

    (j'avais testé sur divers jeux sous Windows 10, en Admin)


    lundi 6 août 2018 09:20
  • En effet j'avais déjà vu cette méthode mais je doit bien avouer que je ne sait absolument pas comment on utilise cette méthode :(

    Deuxième problème j'ai vu que ils font référence en C++ mais rien en C# ... Cela veut dire que je doit convertir mon  projet en C++ ?

    lundi 6 août 2018 09:33
  • Non, comme j'ai dit, par P/Invoke (Interop)

    Par exemple, test avec Notepad : on le met en foreground et on envoie 'a' =>

    IntPtr hWndNotepad = FindWindow("Notepad", null);
    if (hWndNotepad != IntPtr.Zero)
    {
        IntPtr hWndEdit = FindWindowEx(hWndNotepad, IntPtr.Zero, "Edit", null);
        SwitchToThisWindow(hWndNotepad, true);
        SwitchToThisWindow(hWndEdit, true);
    
        INPUT[] currentInput = new INPUT[2];
        currentInput[0].type = INPUT_KEYBOARD;
        currentInput[0].inputUnion.ki.wVk = (short)Keys.A;
        currentInput[1] = currentInput[0];
        currentInput[1].inputUnion.ki.dwFlags = KEYEVENTF_KEYUP;
        SendInput(2, currentInput, Marshal.SizeOf(currentInput[0]));
    }

    avec les declarations :

    (ajouter

    using System.Runtime.InteropServices;

    au début)

    =>

    [DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
    
    [DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
    
    [DllImport("User32.dll", SetLastError = true)]
    public static extern int SendInput(int nInputs, [MarshalAs(UnmanagedType.LPArray)] INPUT[] pInput, int cbSize);
    
    public const int INPUT_KEYBOARD = 1;
    public const int KEYEVENTF_EXTENDEDKEY = 0x0001;
    public const int KEYEVENTF_KEYUP = 0x0002;
    public const int KEYEVENTF_UNICODE = 0x0004;
    
    [StructLayout(LayoutKind.Sequential)]
    public struct MOUSEINPUT
    {
        public int dx;
        public int dy;
        public int mouseData;
        public int dwFlags;
        public int time;
        public IntPtr dwExtraInfo;
    }
    
    [StructLayout(LayoutKind.Sequential)]
    public struct KEYBDINPUT
    {
        public short wVk;
        public short wScan;
        public int dwFlags;
        public int time;
        public IntPtr dwExtraInfo;
    }
    
    [StructLayout(LayoutKind.Sequential)]
    public struct HARDWAREINPUT
    {
        public int uMsg;
        public short wParamL;
        public short wParamH;
    }
    
    [StructLayout(LayoutKind.Sequential)]
    public struct INPUT
    {
        public int type;
        public INPUTUNION inputUnion;
    }
    
    [StructLayout(LayoutKind.Explicit)]
    public struct INPUTUNION
    {
        [FieldOffset(0)]
        public MOUSEINPUT mi;
        [FieldOffset(0)]
        public KEYBDINPUT ki;
        [FieldOffset(0)]
        public HARDWAREINPUT hi;
    }

    • Marqué comme réponse Freezlex mercredi 8 août 2018 14:44
    lundi 6 août 2018 09:45
  • Tout d'abord merci beaucoup pour t'as réponse mais maintenant je suis a nouveau bloqué ...

    J'ai construit un code mais en voulant le tester il m'affiche une erreur :

    SwitchToThisWindow(hWndNotepad, true); [le nom SwitchToThisWindow n’existe pas dans le contexte actuel] currentInput[0].inputUnion.ki.wVk = (short)Keys.A ; [le nom Keys n’existe pas dans le contexte actuel]


    Mon code:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Runtime.InteropServices;
    
    
    
    namespace ConsoleApplication1
    
    {
    
        class Program
        {
            [DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
    
            [DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
    
            [DllImport("User32.dll", SetLastError = true)]
            public static extern int SendInput(int nInputs, [MarshalAs(UnmanagedType.LPArray)] INPUT[] pInput, int cbSize);
    
            public const int INPUT_KEYBOARD = 1;
            public const int KEYEVENTF_EXTENDEDKEY = 0x0001;
            public const int KEYEVENTF_KEYUP = 0x0002;
            public const int KEYEVENTF_UNICODE = 0x0004;
    
            [StructLayout(LayoutKind.Sequential)]
            public struct MOUSEINPUT
            {
                public int dx;
                public int dy;
                public int mouseData;
                public int dwFlags;
                public int time;
                public IntPtr dwExtraInfo;
            }
    
            [StructLayout(LayoutKind.Sequential)]
            public struct KEYBDINPUT
            {
                public short wVk;
                public short wScan;
                public int dwFlags;
                public int time;
                public IntPtr dwExtraInfo;
            }
    
            [StructLayout(LayoutKind.Sequential)]
            public struct HARDWAREINPUT
            {
                public int uMsg;
                public short wParamL;
                public short wParamH;
            }
    
            [StructLayout(LayoutKind.Sequential)]
            public struct INPUT
            {
                public int type;
                public INPUTUNION inputUnion;
            }
    
            [StructLayout(LayoutKind.Explicit)]
            public struct INPUTUNION
            {
                [FieldOffset(0)]
                public MOUSEINPUT mi;
                [FieldOffset(0)]
                public KEYBDINPUT ki;
                [FieldOffset(0)]
                public HARDWAREINPUT hi;
            }
    
                static void Main(string[] args)
            {
                IntPtr hWndNotepad = FindWindow("Notepad", null);
                if (hWndNotepad != IntPtr.Zero)
                {
                    IntPtr hWndEdit = FindWindowEx(hWndNotepad, IntPtr.Zero, "Edit", null);
                    SwitchToThisWindow(hWndNotepad, true);
                    SwitchToThisWindow(hWndEdit, true);
    
                    INPUT[] currentInput = new INPUT[2];
                    currentInput[0].type = INPUT_KEYBOARD;
                    currentInput[0].inputUnion.ki.wVk = (short)Keys.A;
                    currentInput[1] = currentInput[0];
                    currentInput[1].inputUnion.ki.dwFlags = KEYEVENTF_KEYUP;
                    SendInput(2, currentInput, Marshal.SizeOf(currentInput[0]));
                }
            }
        }
    }
    
    Merci d'avance pour la réponse :)

    mercredi 8 août 2018 08:42
  • Oui, je n'ai pas mis la definition de :

    [DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            public static extern bool SwitchToThisWindow(IntPtr hWnd, Boolean fAltTab);

    et Keys est dans :

    using System.Windows.Forms;

    • Marqué comme réponse Freezlex mercredi 8 août 2018 14:44
    mercredi 8 août 2018 09:00
  • Bonjour,

    Vraiment désolé de revenir à la charge mais en fait ça ne marche toujours pas ...

    Sur notepad en effet ça fonctionne ! Mais j'ai essayé sur Word avec le nom du processus approprié : "WinWord" mais rien ne se passe ... Pas de code d'erreur, mais il ne prend pas en compte la touche envoyée :(

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Runtime.InteropServices;
    using System.Windows.Forms;
    using System.Threading;
    
    namespace ConsoleApplication1
    
    {
    
        class Program
    
        {
            [DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            public static extern bool SwitchToThisWindow(IntPtr hWnd, Boolean fAltTab);
    
            [DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
    
            [DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
    
            [DllImport("User32.dll", SetLastError = true)]
            public static extern int SendInput(int nInputs, [MarshalAs(UnmanagedType.LPArray)] INPUT[] pInput, int cbSize);
    
            public const int INPUT_KEYBOARD = 1;
            public const int KEYEVENTF_EXTENDEDKEY = 0x0001;
            public const int KEYEVENTF_KEYUP = 0x0002;
            public const int KEYEVENTF_UNICODE = 0x0004;
    
            [StructLayout(LayoutKind.Sequential)]
            public struct MOUSEINPUT
            {
                public int dx;
                public int dy;
                public int mouseData;
                public int dwFlags;
                public int time;
                public IntPtr dwExtraInfo;
            }
    
            [StructLayout(LayoutKind.Sequential)]
            public struct KEYBDINPUT
            {
                public short wVk;
                public short wScan;
                public int dwFlags;
                public int time;
                public IntPtr dwExtraInfo;
            }
    
            [StructLayout(LayoutKind.Sequential)]
            public struct HARDWAREINPUT
            {
                public int uMsg;
                public short wParamL;
                public short wParamH;
            }
    
            [StructLayout(LayoutKind.Sequential)]
            public struct INPUT
            {
                public int type;
                public INPUTUNION inputUnion;
            }
    
            [StructLayout(LayoutKind.Explicit)]
            public struct INPUTUNION
            {
                [FieldOffset(0)]
                public MOUSEINPUT mi;
                [FieldOffset(0)]
                public KEYBDINPUT ki;
                [FieldOffset(0)]
                public HARDWAREINPUT hi;
            }
    
                static void Main(string[] args)
            {
                IntPtr hWndNotepad = FindWindow("WinWord", null);
                if (hWndNotepad != IntPtr.Zero)
                {
                    IntPtr hWndEdit = FindWindowEx(hWndNotepad, IntPtr.Zero, "Edit", null);
                    SwitchToThisWindow(hWndNotepad, true);
                    SwitchToThisWindow(hWndEdit, true);
    
                    INPUT[] currentInput = new INPUT[2];
                    currentInput[0].type = INPUT_KEYBOARD;
                    currentInput[0].inputUnion.ki.wVk = (short)Keys.W;
                    currentInput[1] = currentInput[0];
                    currentInput[1].inputUnion.ki.dwFlags = KEYEVENTF_KEYUP;
                    SendInput(2, currentInput, Marshal.SizeOf(currentInput[0]));
    
                }
            }
    
        }
    }

    Alors ma question c'est est ce que j'ai oublié de mettre qqc dans mon code, le code n'est pas bon ou que le nom du processus n'est pas bon ?


    • Modifié Freezlex vendredi 10 août 2018 11:59
    vendredi 10 août 2018 11:58
  • Alors ma question c'est est ce que j'ai oublié de mettre qqc dans mon code, le code n'est pas bon ou que le nom du processus n'est pas bon ?

    Ce n'est pas le nom du processus, c'est le nom de la classe de fenêtre

    On trouve la classe avec Spy++ 

    Pour Word, c'est "OpusApp" et, Spy++ montre qu'il y a plusieurs fenêtres enfants de différentes classes

    Avec Word 2016, ça donne (mais sans doute différent pour autres versions, voir avec Spy++ sinon) :

    IntPtr hWndWord = FindWindow("OpusApp", null);
    if (hWndWord != IntPtr.Zero)
    {
        IntPtr hWndChid1 = FindWindowEx(hWndWord, IntPtr.Zero, "_WwF", null);
        IntPtr hWndChid2 = FindWindowEx(hWndChid1, IntPtr.Zero, "_WwB", null);
        IntPtr hWndChid3 = FindWindowEx(hWndChid2, IntPtr.Zero, "_WwG", null);
        SwitchToThisWindow(hWndWord, true);
        SwitchToThisWindow(hWndChid3, true);
    
        INPUT[] currentInput = new INPUT[2];
        currentInput[0].type = INPUT_KEYBOARD;
        currentInput[0].inputUnion.ki.wVk = (short)Keys.A;
        currentInput[1] = currentInput[0];
        currentInput[1].inputUnion.ki.dwFlags = KEYEVENTF_KEYUP;
        SendInput(2, currentInput, Marshal.SizeOf(currentInput[0]));
    }

    vendredi 10 août 2018 13:37
  • Yep super merci on avance petit a petit :)

    Mais malheureusement sur le fameux jeux toujours rien ... Je remarque qu'il y a plusieurs instances (3 exactement mais 1 seule "active" j'ai envie de dire) mais avec le même nom ... Alors est ce que il est "protégé" ? Ou il y a un autre problème ?

    vendredi 10 août 2018 16:20
  • Mais ce jeu a-t-il une classe de fenêtre pour la trouver avec FindWindow, puis pour la mettre en foreground ?
    vendredi 10 août 2018 16:54
  • Oui je suis allé regarder dans les propriété et j'ai vu "Nom de la classe : prism3d" ... En fait il arrive a ouvrir la fenêtre du jeu mais les touches ne sont pas envoyés ou alors ne sont pas interprétée par le jeux c'est pour ça que je ne comprend pas :/
    vendredi 10 août 2018 19:53
  • Je viens de télécharger "Eurotruck Simulator 2" pour tester

    et apparemment il a besoin du scan code.

    J'ai fait ce test qui marche chez moi (envoi de H puis J (1 seconde) pour klaxon + phares) :

    IntPtr hWndEurotruck = FindWindow("prism3d", null);
    if (hWndEurotruck != IntPtr.Zero)
    {                   
        SwitchToThisWindow(hWndEurotruck, true);
        System.Threading.Thread.Sleep(1000);
       
        sendKey((short)Keys.H, 1000, false);
        sendKey((short)Keys.J, 1000, false);
    }

    avec : 

    [DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern uint MapVirtualKey(uint uCode, uint uMapType);
    
    public const int KEYEVENTF_SCANCODE = 0x0008;
    
    void sendKey(short wVk, int nSleep, bool bExtendedkey)
    {
        INPUT[] input = new INPUT[1];
        input[0].inputUnion.ki.wVk = wVk;
        input[0].inputUnion.ki.wScan = (short)MapVirtualKey((uint)wVk, 0);
        input[0].inputUnion.ki.dwFlags = KEYEVENTF_SCANCODE;
        if (bExtendedkey)
            input[0].inputUnion.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
        input[0].inputUnion.ki.time = 0;
        input[0].type = INPUT_KEYBOARD;
        SendInput(1, input, Marshal.SizeOf(input[0]));
        System.Threading.Thread.Sleep(nSleep);
    
        input[0].inputUnion.ki.wVk = wVk;
        input[0].inputUnion.ki.wScan = (short)MapVirtualKey((uint)wVk, 0);
        input[0].inputUnion.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP;
        if (bExtendedkey)
            input[0].inputUnion.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
        input[0].inputUnion.ki.time = 0;
        input[0].type = INPUT_KEYBOARD;
        SendInput(1, input, Marshal.SizeOf(input[0]));
        System.Threading.Thread.Sleep(20);
    }

    • Marqué comme réponse Freezlex lundi 13 août 2018 12:11
    samedi 11 août 2018 00:14
  • Je ne te remercierais jamais assez ! C'est bon maintenant je comprend comment tu as fait et c'est vrai que j'ai encore pas mal de chose a apprendre dans ce domaine :D  Merci beaucoup ça marche niquel !
    lundi 13 août 2018 12:13