Functions/GenXdev.Windows/Send-Key.ps1
################################################################################ <# .SYNOPSIS Sends simulated keystrokes to a window or process. .DESCRIPTION Sends keystrokes to either the active window or a specified process window as if typed by a user. Supports special keys and keyboard modifiers through control sequences like {F11}, {ENTER}, etc. Can target specific processes and maintain keyboard focus. .PARAMETER KeysToSend The text or key sequences to send. Supports control sequences like {F11} and keyboard modifiers (+, ^, %). Can be piped or provided as array. .PARAMETER Escape When specified, escapes special characters so they are sent as literal text instead of being interpreted as control sequences. .PARAMETER Process Target process that should receive the keystrokes. If not specified, sends to the currently active window. .PARAMETER HoldKeyboardFocus Prevents returning keyboard focus to PowerShell after sending keys. .PARAMETER ShiftEnter Sends Shift+Enter instead of regular Enter for line breaks. .PARAMETER DelayMilliSeconds Adds delay between sending different key sequences. Useful for slower apps. .EXAMPLE Send-Key -KeysToSend "Hello World{ENTER}" -Process (Get-Process notepad) Sends text to Notepad followed by Enter key .EXAMPLE Send-Key "Special {F11} key" -Escape Sends literal "{F11}" rather than F11 key #> function Send-Key { [CmdletBinding(DefaultParameterSetName = "ByProcessName")] [Alias("sendkeys", "invokekeys")] param ( ######################################################################## [Parameter( Position = 0, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ValueFromRemainingArguments = $false, HelpMessage = "The text to send" )] [Alias("q", "Value", "Name", "Text", "Query", "Queries")] [ValidateNotNullOrEmpty()] [string[]] $KeysToSend, ######################################################################## [Parameter( Mandatory = $false, HelpMessage = "Escape control characters and modifiers" )] [switch] $Escape, ######################################################################## [Parameter( ParameterSetName = "ByProcessName", ValueFromPipelineByPropertyName = $true, HelpMessage = "Name of the process to get window information for" )] [ValidateNotNullOrEmpty()] [SupportsWildcards()] [string] $ProcessName, ######################################################################## [Parameter( ParameterSetName = "ByProcessId", ValueFromPipelineByPropertyName = $true, HelpMessage = "ID of the process to get window information for" )] [ValidateNotNull()] [Alias("Id", "PID")] [int] $ProcessId, ######################################################################## [Parameter( ParameterSetName = "ByWindowHandle", ValueFromPipelineByPropertyName = $true, HelpMessage = "Window handle to get information for" )] [ValidateNotNull()] [Alias("Handle", "hWnd")] [long] $WindowHandle, ######################################################################## [Parameter( Mandatory = $false, HelpMessage = "Hold keyboard focus on target window" )] [switch] $HoldKeyboardFocus, ######################################################################## [Parameter( Mandatory = $false, HelpMessage = "Use Shift+Enter instead of Enter" )] [switch] $ShiftEnter, ######################################################################## [Parameter( Mandatory = $false, HelpMessage = "Delay between different input strings in milliseconds" )] [ValidateRange(0, [int]::MaxValue)] [int] $DelayMilliSeconds = 0 ) begin { # initialize window handling variables $helper = New-Object -ComObject WScript.Shell # bring window to foreground $window = $null; if ($null -eq $window) { try { $invocationArguments = GenXdev.Helpers\Copy-IdenticalParamValues ` -FunctionName "Get-Window" ` -BoundParameters $PSBoundParameters ` -DefaultValues (Get-Variable -Scope Local -Name * -ErrorAction SilentlyContinue) if ((-not [string]::IsNullOrWhiteSpace($ProcessName)) -or ($ProcessId -ne 0) -or ($WindowHandle -ne 0)) { $window = Get-Window @invocationArguments -ErrorAction SilentlyContinue | Select-Object -First 1 } } catch { Write-Warning $_.Exception.Message } } if ($null -ne $window) { $window.Show(); $window.SetForeground() $null = Set-ForegroundWindow -WindowHandle $window.Handle Start-Sleep -Milliseconds 500 } } process { try { foreach ($key in $KeysToSend) { Write-Verbose "Processing key sequence: $key" try { # prepare key sequence for sending $escapedQuery = $key -join " " if ($Escape) { $escapedQuery = $escapedQuery -replace '(\{)', '{{}' $escapedQuery = $escapedQuery -replace '(\})', '{}}' } # normalize line endings $escapedQuery = $escapedQuery -replace '`r', '`n' $escapedQuery = $escapedQuery -replace '`n`n', '`n' # convert newlines to appropriate key sequences if ($ShiftEnter) { $escapedQuery = $escapedQuery -replace '`n', '+{ENTER}' } else { $escapedQuery = $escapedQuery -replace '`n', '{ENTER}' } Write-Verbose "Sending keys: $escapedQuery" $null = $helper.sendKeys($escapedQuery, $true) if ($DelayMilliSeconds -gt 0) { Start-Sleep -Milliseconds $DelayMilliSeconds } } catch { Write-Warning $_.Exception.Message } } } finally { if ($null -ne $window) { try { # restore PowerShell window focus if not holding focus if (-not $HoldKeyboardFocus) { try { Write-Verbose "Restoring PowerShell window focus" $psWindow = Get-PowershellMainWindow $null = $psWindow.SetForeground() $null = Set-ForegroundWindow -WindowHandle $psWindow.Handle } catch { Write-Warning $_.Exception.Message } } } catch { Write-Warning $_.Exception.Message } } } } } ################################################################################ |