# Load required .NET assemblies for WinForms and Interop Add-Type -AssemblyName System.Windows.Forms Add-Type -AssemblyName System.Drawing Add-Type -AssemblyName System.Runtime.InteropServices [System.Windows.Forms.Application]::EnableVisualStyles() # --- Native Unicode typing via SendInput (layout-independent) --- Add-Type @" using System; using System.Runtime.InteropServices; public static class NativeInput { [StructLayout(LayoutKind.Sequential)] public struct INPUT { public uint type; // 1 = KEYBOARD public InputUnion U; } // The union MUST contain mouse/keyboard/hardware to have the correct size on x64. [StructLayout(LayoutKind.Explicit)] public struct InputUnion { [FieldOffset(0)] public MOUSEINPUT mi; [FieldOffset(0)] public KEYBDINPUT ki; [FieldOffset(0)] public HARDWAREINPUT hi; } [StructLayout(LayoutKind.Sequential)] public struct MOUSEINPUT { public int dx; public int dy; public uint mouseData; public uint dwFlags; public uint time; public IntPtr dwExtraInfo; } [StructLayout(LayoutKind.Sequential)] public struct KEYBDINPUT { public ushort wVk; // 0 when using KEYEVENTF_UNICODE public ushort wScan; // UTF-16 code unit for KEYEVENTF_UNICODE public uint dwFlags; // KEYEVENTF_UNICODE or | KEYEVENTF_KEYUP public uint time; public IntPtr dwExtraInfo; } [StructLayout(LayoutKind.Sequential)] public struct HARDWAREINPUT { public uint uMsg; public ushort wParamL; public ushort wParamH; } private const uint INPUT_KEYBOARD = 1; private const uint KEYEVENTF_UNICODE = 0x0004; private const uint KEYEVENTF_KEYUP = 0x0002; [DllImport("user32.dll", SetLastError = true)] private static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize); public static void SendUnicodeString(string s, int delayMs) { if (string.IsNullOrEmpty(s)) return; foreach (char ch in s) { // Build key-down (UNICODE) and key-up (UNICODE|KEYUP) for this char INPUT[] inputs = new INPUT[2]; inputs[0].type = INPUT_KEYBOARD; inputs[0].U.ki.wVk = 0; inputs[0].U.ki.wScan = ch; inputs[0].U.ki.dwFlags = KEYEVENTF_UNICODE; inputs[0].U.ki.time = 0; inputs[0].U.ki.dwExtraInfo = IntPtr.Zero; inputs[1].type = INPUT_KEYBOARD; inputs[1].U.ki.wVk = 0; inputs[1].U.ki.wScan = ch; inputs[1].U.ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP; inputs[1].U.ki.time = 0; inputs[1].U.ki.dwExtraInfo = IntPtr.Zero; uint sent = SendInput((uint)inputs.Length, inputs, Marshal.SizeOf(typeof(INPUT))); if (sent == 0) { throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); } if (delayMs > 0) { System.Threading.Thread.Sleep(delayMs); } } } } "@ # Types a string as real keyboard input using SendKeys (with a short delay between chars) function Type-Text { param( [Parameter(Mandatory = $true)] [string]$Text, [int]$DelayMs = 10 ) if ([string]::IsNullOrEmpty($Text)) { return } # Optional: normalize newlines to CR (some targets prefer \r over \n) $normalized = $Text -replace "`r?`n", "`r" [NativeInput]::SendUnicodeString($normalized, $DelayMs) } # Shows a form with one textbox per line and a "Copy" button next to each. # At the bottom there is a "Paste clipboard as keyboard input" button and a log box. function Show-TextWithCopyButtons { param( [Parameter()] [string[]]$InputText ) # Normalize to an array of lines (split on CR/LF across all provided items) $lines = @() if ($InputText) { foreach ($item in $InputText) { $lines += ($item -split "`r?`n") } } else { # Default: 10 empty lines if clipboard is empty $lines = @(for ($i = 1; $i -le 10; $i++) { "" }) } # Layout constants $textboxWidth = 300 $buttonWidth = 75 $padding = 10 $lineHeight = 35 $logBoxHeight = 150 # Compute form height and cap it to avoid oversized windows ### $formHeight = $lines.Count * $lineHeight + $logBoxHeight + 60 + 30 $formHeight = [Math]::Min($formHeight, 1000) # Create form $form = New-Object System.Windows.Forms.Form $form.Text = "Text with Copy Buttons v2" $form.Size = New-Object System.Drawing.Size(425, $formHeight) $form.StartPosition = "CenterScreen" $form.AutoScroll = $true $form.TopMost = $true # Log textbox $logBox = New-Object System.Windows.Forms.TextBox $logBox.Multiline = $true $logBox.ScrollBars = "Vertical" $logBox.ReadOnly = $true $logBox.Size = New-Object System.Drawing.Size(375, $logBoxHeight) $y = 10 foreach ($line in $lines) { $textBox = New-Object System.Windows.Forms.TextBox $textBox.Text = $line $textBox.Location = New-Object System.Drawing.Point($padding, $y) $textBox.Size = New-Object System.Drawing.Size($textboxWidth, 20) $form.Controls.Add($textBox) $button = New-Object System.Windows.Forms.Button $button.Text = "Copy" $button.Location = New-Object System.Drawing.Point(($padding + $textboxWidth + 10), ([int]$y - 1)) $button.Size = New-Object System.Drawing.Size($buttonWidth, 23) $currentTextBox = $textBox $currentLogBox = $logBox $button.Add_Click({ $text = $currentTextBox.Text if (![string]::IsNullOrWhiteSpace($text)) { [System.Windows.Forms.Clipboard]::SetText($text) $currentLogBox.AppendText("Copied: $text`r`n") } else { $currentLogBox.AppendText("Nothing to copy (empty line).`r`n") } }.GetNewClosure()) $form.Controls.Add($button) $y += $lineHeight } # Paste button $pasteButton = New-Object System.Windows.Forms.Button $pasteButton.Text = "Paste clipboard as keyboard input" $pasteButton.Size = New-Object System.Drawing.Size(250, 30) $pasteButton.Location = New-Object System.Drawing.Point($padding, ($y + 5)) $currentForm = $form $currentLog = $logBox $pasteButton.Add_Click({ try { $currentLog.AppendText("Reading clipboard...`r`n") $clipboardText = Get-Clipboard } catch { $currentLog.AppendText("Failed to read clipboard: $($_.Exception.Message)`r`n") return } if ([string]::IsNullOrEmpty($clipboardText)) { $currentLog.AppendText("Clipboard is empty. Nothing to type.`r`n") return } $currentLog.AppendText("Read: $clipboardText`r`n") $currentLog.AppendText("Minimizing window and typing...`r`n") $currentForm.WindowState = 'Minimized' Start-Sleep -Seconds 2 Type-Text -text $clipboardText $currentForm.WindowState = 'Normal' $currentLog.AppendText("Done typing.`r`n") }.GetNewClosure()) $form.Controls.Add($pasteButton) $y += 30 $logBox.Location = New-Object System.Drawing.Point($padding, ($y + 10)) $form.Controls.Add($logBox) $form.ShowDialog() | Out-Null } # Entry point try { $input = Get-Clipboard -ErrorAction Stop } catch { $input = $null } Show-TextWithCopyButtons -InputText $input