Had to stop production as I ran out of copilot credits
This commit is contained in:
@@ -0,0 +1,169 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user