Functions/GenXdev.Windows/Send-Keys.ps1

################################################################################
<#
.SYNOPSIS
    Sends keys to a window as if typed by user
 
.DESCRIPTION
    Sends keys to the active window or to the window of a specified process
    as if typed by the user.
 
    The text strings can contain control characters like {F11} or {ENTER}
    they will press the corresponding keys, unless -Escape is specified.
 
    Line feeds will automatically be converted to {ENTER} control codes.
    and Tabs to {TAB}.
 
.PARAMETER Keys
    The text to send
 
.PARAMETER Escape
    Escape control characters like {F11} or {ENTER} or modifiers like +(meaning shift), ^(meaning control), %(meaning alt)
 
.PARAMETER Process
    The process to send the keys to
 
.PARAMETER HoldKeyboardFocus
    Hold the keyboard focus to the target process main window when complete
 
.EXAMPLE
    Send-Keys -Keys "Hello World" -Escape
 
.EXAMPLE
    Send-Keys -Keys "Hello World" -Process (Get-Process code | Select -First 1)
#>

function Send-Keys {

    [CmdletBinding()]
    param (
        [Parameter(
            Position = 0,
            Mandatory,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName,
            ValueFromRemainingArguments,
            HelpMessage = "The text to send"
        )]
        [Alias("q", "Value", "Name", "Text", "Query", "Queries")]
        [string[]] $Keys,

        [Parameter(
            Mandatory = $false,
            HelpMessage = "Escape control characters like {F11} or {ENTER} or modifiers like +(meaning shift), ^(meaning control), %(meaning alt)"
        )]
        [switch] $Escape,

        [Parameter(
            Mandatory = $false,
            HelpMessage = "The process to send the keys to"
        )]
        [System.Diagnostics.Process] $Process,

        [Parameter(
            Mandatory = $false,
            HelpMessage = "Hold the keyboard focus to the target process main window when complete"
        )]
        [switch] $HoldKeyboardFocus,

        [Parameter(
            Mandatory = $false,
            HelpMessage = "Convert line feeds into Shift-Enter instead of just Enter"
        )]
        [switch] $ShiftEnter,

        [Parameter(
            Mandatory = $false,
            HelpMessage = "Optional extra delay between sending keys"
        )]
        [int] $DelayMilliSeconds = 0
    )

    begin {

        $windowProcess = $Process;
        $helper = New-Object -ComObject WScript.Shell;

        if ($null -ne $Process) {

            if ($windowProcess.MainWindowHandle -eq 0) {

                Start-Sleep 2 | Out-Null
            }

            while (($null -ne $windowProcess) -and ($windowProcess.MainWindowHandle -eq 0)) {

                $windowProcess = $windowProcess.Parent
            }

            if ($null -eq $windowProcess) {

                $windowProcess = (
                    Get-Process "$([IO.Path]::GetFileNameWithoutExtension($process.Path))" |
                    Where-Object {
                        ($PSItem.Id -ne $Process.Id) -and ($PSItem.MainWindowHandle -ne 0)
                    } |
                    Sort-Object -Property StartTime -Descending |
                    Select-Object -First 1
                );

                if ($null -eq $windowProcess) {

                    throw "could not find a window process to send the keys to"
                    return;
                }
            }

            try {
                $w = [GenXdev.Helpers.WindowObj]::new($windowProcess, $windowProcess.MainWindowTitle);
                $w.Show() | Out-Null;
                $w.SetForeground() | Out-Null
            }
            catch {

            }

            Set-ForegroundWindow -WindowHandle ($windowProcess.MainWindowHandle) | Out-Null

            [System.Threading.Thread]::Sleep(500);
        }
    }

    process {

        try {
            foreach ($key in $Keys) {

                $escapedQuery = $key -join " "

                if ($Escape -eq $true) {

                    $escapedQuery = $escapedQuery -replace '(\{)', '{{}'
                    $escapedQuery = $escapedQuery -replace '(\})', '{}}'
                }

                $escapedQuery = $escapedQuery -replace '`r', '`n'
                $escapedQuery = $escapedQuery -replace '`n`n', '`n'

                if ($ShiftEnter -eq $true) {

                    $escapedQuery = $escapedQuery -replace '`n', '+{ENTER}'
                }
                else {

                    $escapedQuery = $escapedQuery -replace '`n', '{ENTER}'
                }

                Write-Verbose "Sending keys: $escapedQuery"

                $helper.sendKeys($escapedQuery) | Out-Null

                if ($DelayMilliSeconds -gt 0) {

                    Start-Sleep -Milliseconds $DelayMilliSeconds
                }
            }
        }
        finally {
            if ($null -ne $windowProcess) {

                [System.Threading.Thread]::Sleep(1500);

                if (-not $HoldKeyboardFocus) {

                    try {
                        $psw = Get-PowershellMainWindow
                        $psw.SetForeground() | Out-Null
                        Set-ForegroundWindow -WindowHandle ($psw.Handle)
                    }
                    catch {}
                }
            }
        }
    }
}