Simulación de entrada de teclado y caracteres Unicode mediante SendInput en C#

Introducción

Este artículo explica cómo utilizar la función SendInput de Windows API para simular la entrada de texto Unicode y pulsaciones de teclas en aplicaciones. Aunque fue desarrollado inicialmente para permitir el ingreso de caracteres chinos en un videojuego, su funcionalidad es general y puede adaptarse a múltiples escenarios donde se requiera inyección de entrada a nivel del sistema.

Espacios de nombres requeridos

using static System.Threading.Thread;
using System.Runtime.InteropServices;

Estructuras y declaración de API

Para interactuar con la API de Windows, se definen estructuras que representan los tipos de eventos de entrada: teclado, ratón e hardware. Aquí solo nos enfocamso en entradas de teclado y caracteres Unicode.

[StructLayout(LayoutKind.Explicit)]
public struct INPUT
{
    [FieldOffset(0)] public uint type;
    [FieldOffset(4)] public KEYBDINPUT keyboard;
    [FieldOffset(4)] public MOUSEINPUT mouse;
    [FieldOffset(4)] public HARDWAREINPUT hardware;
}

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

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

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

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

Funciones de utilidad

A continuación se implementan métodos para enviar caracteres Unicode y simular pulsaciones completas de teclas (presión y liberación).

Enviar un carácter Unicode

Este método utiliza el flag KEYEVENTF_UNICODE (valor 4) para insertar un carácter directamente mediante su valor escaneado (scan code), útil para textos no ASCII como chino, japonés o árabe.

public void InyectarCaracter(char caracter)
{
    var entrada = new INPUT[2];

    // Pulsación (key down)
    entrada[0].type = 1; // Tipo: teclado
    entrada[0].keyboard.wVk = 0;          // wVk debe ser 0 cuando se usa Unicode
    entrada[0].keyboard.wScan = (ushort)caracter;
    entrada[0].keyboard.dwFlags = 0x0004; // KEYEVENTF_UNICODE
    entrada[0].keyboard.time = 0;
    entrada[0].keyboard.extraInfo = IntPtr.Zero;

    // Liberación (key up)
    entrada[1].type = 1;
    entrada[1].keyboard.wVk = 0;
    entrada[1].keyboard.wScan = (ushort)caracter;
    entrada[1].keyboard.dwFlags = 0x0006; // KEYEVENTF_UNICODE | KEYEVENTF_KEYUP
    entrada[1].keyboard.time = 0;
    entrada[1].keyboard.extraInfo = IntPtr.Zero;

    SendInput(2, entrada, Marshal.SizeOf<INPUT>());
}

Simular pulsación de una tecla virtual

Este método envía una tecla por su código virtual (por ejemplo, VK_RETURN, VK_ESCAPE), simulando primero la pulsación y luego la liberación con un breve retraso.

public void SimularTecla(int codigoVirtual)
{
    var evento = new INPUT();

    // Pulsar tecla
    evento.type = 1;
    evento.keyboard.wVk = (ushort)codigoVirtual;
    evento.keyboard.dwFlags = 0; // Key down
    SendInput(1, new[] { evento }, Marshal.SizeOf<INPUT>());
    
    Sleep(30);

    // Soltar tecla
    evento.keyboard.dwFlags = 2; // KEYEVENTF_KEYUP
    SendInput(1, new[] { evento }, Marshal.SizeOf<INPUT>());
    
    Sleep(30);
}

Uso y consideraciones

  • InyectarCaracter('文') permite introducir caracteres Unicode directamente en juegos o aplicaciones que no soportan IME.
  • SimularTecla(0x0D) presiona y libera la tecla Enter (código virtual 0x0D).
  • Los códigos de teclas virtuales están documentados en la documentación oficial de Microsoft.

Este enfoque funciona a nivel del sistema operativo y puede fallar si la aplicación objeitvo tiene medidas anti-trampa o ejecuta privilegios elevados.

Etiquetas: SendInput Windows API C# teclado virtual Unicode

Publicado el 8-30 21:18