170 lines
5.8 KiB
C#
170 lines
5.8 KiB
C#
using System.Runtime.InteropServices;
|
|
|
|
namespace PainoBar_Helper
|
|
{
|
|
/// <summary>
|
|
/// Helper class for injecting keyboard input directly into a Win32 console process.
|
|
/// Required for Pianobar Windows port which uses _getch() instead of stdin.
|
|
/// </summary>
|
|
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
|
|
|
|
/// <summary>
|
|
/// Sends keyboard input directly to a console process using Win32 API.
|
|
/// </summary>
|
|
/// <param name="processId">The process ID of the target console application</param>
|
|
/// <param name="text">The text to send (Enter key will be automatically appended)</param>
|
|
/// <returns>True if successful, false otherwise</returns>
|
|
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<INPUT_RECORD> inputRecords = new List<INPUT_RECORD>();
|
|
|
|
// 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
|
|
}
|
|
}
|