using System.Runtime.InteropServices; namespace PainoBar_Helper { /// /// Helper class for injecting keyboard input directly into a Win32 console process. /// Required for Pianobar Windows port which uses _getch() instead of stdin. /// public static class Win32ConsoleHelper { #region Win32 API Imports [DllImport("kernel32.dll", SetLastError = true)] private static extern bool AttachConsole(uint dwProcessId); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool FreeConsole(); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool WriteConsoleInput( IntPtr hConsoleInput, INPUT_RECORD[] lpBuffer, uint nLength, out uint lpNumberOfEventsWritten); [DllImport("kernel32.dll", SetLastError = true)] private static extern IntPtr GetStdHandle(int nStdHandle); private const int STD_INPUT_HANDLE = -10; #endregion #region Structures [StructLayout(LayoutKind.Explicit)] private struct INPUT_RECORD { [FieldOffset(0)] public ushort EventType; [FieldOffset(4)] public KEY_EVENT_RECORD KeyEvent; } [StructLayout(LayoutKind.Sequential)] private struct KEY_EVENT_RECORD { public bool bKeyDown; public ushort wRepeatCount; public ushort wVirtualKeyCode; public ushort wVirtualScanCode; public char UnicodeChar; public uint dwControlKeyState; } private const ushort KEY_EVENT = 1; private const ushort VK_RETURN = 0x0D; #endregion #region Public Methods /// /// Sends keyboard input directly to a console process using Win32 API. /// /// The process ID of the target console application /// The text to send (Enter key will be automatically appended) /// True if successful, false otherwise public static bool SendConsoleInput(int processId, string text) { if (string.IsNullOrEmpty(text)) return false; try { // Attach to the target process's console if (!AttachConsole((uint)processId)) { int error = Marshal.GetLastWin32Error(); System.Diagnostics.Debug.WriteLine($"AttachConsole failed with error: {error}"); return false; } try { // Get the console input handle IntPtr hStdIn = GetStdHandle(STD_INPUT_HANDLE); if (hStdIn == IntPtr.Zero || hStdIn == new IntPtr(-1)) { System.Diagnostics.Debug.WriteLine("GetStdHandle failed"); return false; } // Build input records for each character + Enter key List inputRecords = new List(); // Add key events for each character in the text foreach (char c in text) { // Key down event inputRecords.Add(CreateKeyEvent(c, true)); // Key up event inputRecords.Add(CreateKeyEvent(c, false)); } // Add Enter key (VK_RETURN) at the end inputRecords.Add(CreateKeyEvent('\r', true, VK_RETURN)); inputRecords.Add(CreateKeyEvent('\r', false, VK_RETURN)); // Write all input records to the console input buffer uint written; bool success = WriteConsoleInput( hStdIn, inputRecords.ToArray(), (uint)inputRecords.Count, out written); if (!success) { int error = Marshal.GetLastWin32Error(); System.Diagnostics.Debug.WriteLine($"WriteConsoleInput failed with error: {error}"); return false; } System.Diagnostics.Debug.WriteLine($"Successfully wrote {written} input events for: {text}"); return true; } finally { // Always detach from the console FreeConsole(); } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"SendConsoleInput exception: {ex.Message}"); return false; } } #endregion #region Private Helper Methods private static INPUT_RECORD CreateKeyEvent(char character, bool keyDown, ushort virtualKeyCode = 0) { // If no virtual key code provided, use the character's value if (virtualKeyCode == 0) { virtualKeyCode = (ushort)char.ToUpper(character); } return new INPUT_RECORD { EventType = KEY_EVENT, KeyEvent = new KEY_EVENT_RECORD { bKeyDown = keyDown, wRepeatCount = 1, wVirtualKeyCode = virtualKeyCode, wVirtualScanCode = 0, UnicodeChar = character, dwControlKeyState = 0 } }; } #endregion } }