using System.Runtime.InteropServices; namespace AxCopilot.Services; internal static class ForegroundPasteHelper { private const int SW_RESTORE = 9; private const uint INPUT_KEYBOARD = 1; private const uint KEYEVENTF_KEYUP = 0x0002; private const ushort VK_CONTROL = 0x11; private const ushort VK_V = 0x56; public static async Task RestoreWindowAsync( IntPtr hwnd, CancellationToken ct, int initialDelayMs = 220, int settleDelayMs = 60, int maxAttempts = 4) { if (hwnd == IntPtr.Zero || !IsWindow(hwnd)) return false; await Task.Delay(initialDelayMs, ct); if (IsIconic(hwnd)) ShowWindow(hwnd, SW_RESTORE); for (var attempt = 0; attempt < maxAttempts; attempt++) { uint currentThread = GetCurrentThreadId(); uint targetThread = GetWindowThreadProcessId(hwnd, out _); var foreground = GetForegroundWindow(); uint foregroundThread = foreground != IntPtr.Zero ? GetWindowThreadProcessId(foreground, out _) : 0; var attachedTarget = false; var attachedForeground = false; try { if (targetThread != 0 && targetThread != currentThread) { AttachThreadInput(currentThread, targetThread, true); attachedTarget = true; } if (foregroundThread != 0 && foregroundThread != currentThread && foregroundThread != targetThread) { AttachThreadInput(currentThread, foregroundThread, true); attachedForeground = true; } BringWindowToTop(hwnd); SetForegroundWindow(hwnd); } finally { if (attachedForeground) AttachThreadInput(currentThread, foregroundThread, false); if (attachedTarget) AttachThreadInput(currentThread, targetThread, false); } await Task.Delay(settleDelayMs, ct); if (GetForegroundWindow() == hwnd) return true; } return GetForegroundWindow() == hwnd; } public static async Task PasteClipboardAsync( IntPtr hwnd, CancellationToken ct, int initialDelayMs = 220) { var restored = await RestoreWindowAsync(hwnd, ct, initialDelayMs); if (!restored) return false; await Task.Delay(40, ct); SendCtrlV(); return true; } private static void SendCtrlV() { var inputs = new INPUT[4]; inputs[0] = new INPUT { Type = INPUT_KEYBOARD, Ki = new KEYBDINPUT { wVk = VK_CONTROL } }; inputs[1] = new INPUT { Type = INPUT_KEYBOARD, Ki = new KEYBDINPUT { wVk = VK_V } }; inputs[2] = new INPUT { Type = INPUT_KEYBOARD, Ki = new KEYBDINPUT { wVk = VK_V, dwFlags = KEYEVENTF_KEYUP } }; inputs[3] = new INPUT { Type = INPUT_KEYBOARD, Ki = new KEYBDINPUT { wVk = VK_CONTROL, dwFlags = KEYEVENTF_KEYUP } }; SendInput((uint)inputs.Length, inputs, Marshal.SizeOf()); } [StructLayout(LayoutKind.Explicit, Size = 40)] private struct INPUT { [FieldOffset(0)] public uint Type; [FieldOffset(8)] public KEYBDINPUT Ki; } [StructLayout(LayoutKind.Sequential)] private struct KEYBDINPUT { public ushort wVk; public ushort wScan; public uint dwFlags; public uint time; public IntPtr dwExtraInfo; } [DllImport("user32.dll")] private static extern bool SetForegroundWindow(IntPtr hWnd); [DllImport("user32.dll")] private static extern bool BringWindowToTop(IntPtr hWnd); [DllImport("user32.dll")] private static extern IntPtr GetForegroundWindow(); [DllImport("user32.dll")] private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId); [DllImport("user32.dll")] private static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, [MarshalAs(UnmanagedType.Bool)] bool fAttach); [DllImport("user32.dll")] private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); [DllImport("user32.dll")] private static extern bool IsWindow(IntPtr hWnd); [DllImport("user32.dll")] private static extern bool IsIconic(IntPtr hWnd); [DllImport("kernel32.dll")] private static extern uint GetCurrentThreadId(); [DllImport("user32.dll")] private static extern uint SendInput(uint nInputs, [MarshalAs(UnmanagedType.LPArray)] INPUT[] pInputs, int cbSize); }