+ Rispondi al Thread
Visualizzazione dei risultati da 1 a 2 su 2

Discussione: Corretta parametrizzazione INPUT per User32.SendMessage

  1. #1
    Sgrubak non è in linea Scolaretto
    Luogo
    Torrazza Piemonte
    Post
    303

    Corretta parametrizzazione INPUT per User32.SendMessage

    Buongiorno a tutti,
    nel tentativo di comandare un'altra applicazione alla pressione di un bottone sulla mia Form, ho imparato leggendo sul forum che la tecnica corretta è quella di ricorrere all'uso delle API.
    Ho quindi studiato le pagine della documentazione ufficiale e con l'aiuto di altri post in giro per il web sono arrivato a produrre quanto segue:
    codice:
            [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.Explicit)]
            public struct INPUT
            {
                [FieldOffset(0)]
                public int type;//0-MOUSEINPUT;1-KEYBDINPUT;2-HARDWAREINPUT     
                [FieldOffset(8)] //Sono in ambiente x64
                public KEYBDINPUT ki;
                [FieldOffset(8)]
                public MOUSEINPUT mi;
                [FieldOffset(8)]
                public HARDWAREINPUT hi;
            }
    
            [DllImport("user32.dll")]
            public static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);
            [DllImport("User32.dll")]
            public static extern int SetForegroundWindow(IntPtr point);
    L'utilizzo è il seguente:
    codice:
            private void CompilaOI()
            {
                Process p = Process.GetProcessesByName("notepad").FirstOrDefault();
                if (p != null)
                {
                        IntPtr h = p.MainWindowHandle;
                        int x = SetForegroundWindow(h);
                }
                    INPUT[] inputs = new INPUT[2];
                    //Imposto la pressione del tasto
                    inputs[0].type = 1; //Input da tastiera
                    inputs[0].ki.wVk = 0;
                    inputs[0].ki.wScan = 0x50;
                    inputs[0].ki.dwFlags = 0x0008;
                    //Imposto il rilascio del tasto
                    inputs[1].type = 1; //Input da tastiera
                    inputs[1].ki.wVk = 0; 
                    inputs[1].ki.wScan = 0x50; //Preso da qui
                    inputs[1].ki.dwFlags = 0x000A;
    
                    foreach (Articolo a in listaFiltrata)
                    {
                        if (a.Associazioni.Count == 0)
                        {
                            SendKeys.Send(a.Codice);
                            Thread.Sleep(500);
    
                            SendKeys.SendWait("{TAB}");
                            SendKeys.Send(a.Qta.ToString());
                            Thread.Sleep(500);
    
                            SendKeys.SendWait("{TAB}");
                            SendKeys.Send(a.DataFineProduzione.Value.ToString("ddMMyyyy"));
                            Thread.Sleep(500);
    
                            uint res = SendInput(2, inputs, Marshal.SizeOf(typeof(INPUT))); //res vale 0
    
                            //SendKeys.Send("{DOWN}");
                            Thread.Sleep(500);
    
                            SendKeys.SendWait("{TAB}");
                        }
                    }
                }
                else
                {
                    //Messaggio di errore;
                }
            }
    Ho impostato le pause per vedere se era una questione ti sincronizzazione, ma i SendKey.SendWait funzionano a dovere. È l'invio della [freccia giu] che fa i capricci. Come devo valorizzare correttamente la struttura? Una volta che ho capito, faccio in modo di usare SendMessage anche per le stringhe.

    Grazie in anticipo

  2. #2
    Sgrubak non è in linea Scolaretto
    Luogo
    Torrazza Piemonte
    Post
    303
    Continuando a provare, mi sono accorto che la definizione delle varie struct era errata. Riporto di seguito quella corretta:
    codice:
    [StructLayout(LayoutKind.Sequential)]
            public struct MOUSEINPUT
            {
                public int dx;
                public int dy;
                public uint mouseData;
                public uint dwFlags;
                public uint time;
                public IntPtr dwExtraInfo;
            }
    
            [StructLayout(LayoutKind.Sequential)]
            public struct KEYBDINPUT
            {
                public ushort wVk;
                public ushort wScan;
                public uint dwFlags;
                public uint time;
                public IntPtr dwExtraInfo;
            }
    
            [StructLayout(LayoutKind.Sequential)]
            public struct HARDWAREINPUT
            {
                public int uMsg;
                public short wParamL;
                public short wParamH;
            }
    
            [StructLayout(LayoutKind.Explicit)]
            public struct MouseKeybdHardwareInputUnion
            {
                [FieldOffset(0)]
                public MOUSEINPUT mi;
    
                [FieldOffset(0)]
                public KEYBDINPUT ki;
    
                [FieldOffset(0)]
                public HARDWAREINPUT hi;
            }
    
            [StructLayout(LayoutKind.Sequential)]
            public struct INPUT
            {
                public uint type;
                public MouseKeybdHardwareInputUnion mkhi;
            }
    
            [DllImport("user32.dll")]
            public static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);
            [DllImport("User32.dll")]
            public static extern int SetForegroundWindow(IntPtr point);
            [DllImport("kernel32.dll")]
            public static extern uint GetLastError();
            [DllImport("user32.dll")]
            public static extern IntPtr GetMessageExtraInfo();
    Il codice per la creazione dell'array di input. Imposta sia la pressione che il rilascio:
    codice:
                    //Ottengo la dmensione della struct da passare come parametro alla funzione SendInput
                    int size = Marshal.SizeOf(typeof(INPUT));
    
                    INPUT[] inputs = new INPUT[2];
                    //Imposto la pressione del tasto
                    inputs[0].type = 1; //Input da tastiera
                    inputs[0].mkhi.ki.wVk = 0x28; //Virtual-Key Code per [Freccia Giu] preso da https://docs.microsoft.com/it-it/windows/win32/inputdev/virtual-key-codes
                    inputs[0].mkhi.ki.wScan = 0;
                    inputs[0].mkhi.ki.dwFlags = 0;
                    inputs[0].mkhi.ki.dwExtraInfo = GetMessageExtraInfo();
                    //Imposto il rilascio del tasto
                    inputs[1].type = 1;
                    inputs[1].mkhi.ki.wVk = 0x28;
                    inputs[1].mkhi.ki.wScan = 0;
                    inputs[1].mkhi.ki.dwFlags = 0x0002;
                    inputs[1].mkhi.ki.dwExtraInfo = GetMessageExtraInfo();
    Utilizzo nel ciclo:
    codice:
                            uint res = SendInput((uint)inputs.Count(), inputs, size);
                            uint err = GetLastError();
                            if (res == 0)//Solo nel caso in cui res è 0, quindi non è stato inviato nessun input, mostro il risultato di GetLastError()
                            {
                                MessageBox.Show("Errore {0}(0x{0:X4}) durante la compilazione del documento.", err.ToString());
                                return;
                            }
    Con Notepad funziona perfettamente.

+ Rispondi al Thread

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi