How do I send keys to an active window in Powershell? - powershell

I want my script to open an application and send keys to it after it's opened. Currently, if I run the script, it opens the app but does not send the keys.
If I run just the send keys after the app has already been opened, it works.
Here is what I've got so far:
Start-Process -FilePath "C:\Program Files\VMware\VMware Horizon View Client\vmware-view.exe" -Wait -WindowStyle Normal
$wshell = New-Object -ComObject wscript.shell;
$wshell.AppActivate('VMware')
Sleep 1
$wshell.SendKeys('~')
Sleep 3
$wshell.SendKeys('username')
Sleep 2
$wshell.SendKeys('{TAB}')
Sleep 1
$wshell.SendKeys('password')

By using -Wait with Start-Process, you're blocking the call until the launched process terminates.
Thus, your attempts to send keystrokes will invariably fail, because they'll be sent after the target program has terminated.
Therefore:
Don't use -Wait
Use -PassThru, which makes Start-Process emit a process-information object representing the newly launched process, whose .ID property contains the PID (process ID).
For more reliable targeting, you can pass a PID to $wshell.AppActivate()
The general caveat applies: sending keystrokes, i.e. simulating user input is inherently unreliable.
$ps = Start-Process -PassThru -FilePath "C:\Program Files\VMware\VMware Horizon View Client\vmware-view.exe" -WindowStyle Normal
$wshell = New-Object -ComObject wscript.shell
# Wait until activating the target process succeeds.
# Note: You may want to implement a timeout here.
while (-not $wshell.AppActivate($ps.Id)) {
Start-Sleep -MilliSeconds 200
}
$wshell.SendKeys('~')
Sleep 3
$wshell.SendKeys('username')
Sleep 2
$wshell.SendKeys('{TAB}')
Sleep 1
$wshell.SendKeys('password')

Related

PowerShell: waiting for output redirect file to unlock after killing process

I have a PowerShell script that:
Starts a new process and redirects the output to two files
Waits with a timeout for the process to complete
Kills the process if it timed out
Reads the contents from the redirected output files
It looks something like this:
$_timeout = 30
$_stdoutfile = "./stdout"
$_stderrfile = "./stderr"
# Start the process
$_process = Start-Process powershell.exe -ArgumentList "-file ""$_cmdfile""" -PassThru -NoNewWindow -RedirectStandardError "$_stderrfile" -RedirectStandardOutput "$_stdoutfile"
# Wait for it to complete, or time out
$_timeout_error = $null
$_process | Wait-Process -Timeout $_timeout -ErrorAction SilentlyContinue -ErrorVariable _timeout_error
# Check if it timed out
if ($_timeout_error) {
# Kill it
$_process | Stop-Process -Force
# (1)
# Wait for the process to exit after the kill command
$_kill_timeout_error = $null
$_process | Wait-Process -Timeout 10 -ErrorAction SilentlyContinue -ErrorVariable _kill_timeout_error
# If the process is still running 10 seconds after the kill command die with an error
if ($_kill_timeout_error) {
Write-Error "Failed to terminate process (waited for 10 seconds after initiating termination)."
Exit 1
}
}
# (2)
# Read the stdout and stderr content that was output
$_stdout = [System.IO.File]::ReadAllText("$_stdoutfile")
$_stderr = [System.IO.File]::ReadAllText("$_stderrfile")
# Delete the files after reading them
Remove-Item -Force "$_stdoutfile"
Remove-Item -Force "$_stderrfile"
The majority of this works properly; the process is killed as expected if it runs too long. The problem I'm having is with the ReadAllText functions. They work fine if the process exits on its own, but if they were killed due to a timeout, these functions fail with the error:
The process cannot access the file
'C:\myfilename' because it is being used by another process.
I figured that perhaps it takes the OS a couple seconds to unlock the files after the process is killed, so I inserted a sleep-poll loop at # (2), but often several minutes later, they're still locked.
#Santiago Squarzon suggested (in the comments) a way to read the file while it's still locked, which may work, but I also need to be able to delete them after reading them.
So my questions are:
Is there a way to get these files to naturally unlock more quickly after killing the process?
Is there a way to force-unlock these files with a separate PowerShell command/function?
Unrelated, but is the part in my code around the comment # (1) necessary (waiting for the process to stop after the kill command), or will Stop-Process block until the process is actually stopped?

Powershell script waiting for user input and wont exit

I am trying to run a script silently, its runs fine but then after its run it displays
Succeeded : 0
Press 'Enter' to continue
How can i check if succeeded and then send the enter key..
Note I am running this via the start process command as below but as it is waiting for the user to press enter it never exits:
Start-Process -Wait -FilePath "C:\windows\temp\abc.exe" -ArgumentList '/S','/v','/qn' -passthru
Your best bet is to check if your executable supports a command-line parameter (option) that skips the prompt at the end.
If there is none, you can try the following approach, but note that, as with all attempts to control an application via simulated user interaction, the solution is awkward and brittle:
# Comment this out to hide the verbose messages.
$VerbosePreference = 'Continue'
# Load helper assemblies.
Add-Type -ErrorAction Stop -AssemblyName Microsoft.VisualBasic, System.Windows.Forms
# Launch the external program.
# In this simple example, cmd.exe is invoked with its internal pause
# command, which waits for a keystroke to continue.
Write-Verbose 'Launching the external program asynchronously...'
# IMPORTANT: Do NOT use -Wait, as that will block execution
# indefinitely, and prevent you from sending the Enter keystroke.
$process = Start-Process -PassThru cmd '/c pause'
Write-Verbose 'Sleeping for as long as execution is expected to last at a minimum...'
Start-Sleep 5 # Adjust this as needed.
Write-Verbose 'Sending ENTER keystrokes until the window closes...'
while (-not $process.HasExited) {
# To be safe, activate the external program's window. If that fails, it must be closed already.
try { [Microsoft.VisualBasic.Interaction]::AppActivate($process.Id) } catch { break }
# Send the keystroke.
[System.Windows.Forms.SendKeys]::SendWait('{Enter}')
Start-Sleep -Milliseconds 200 # Sleep a little between attempts.
}
Write-Verbose 'The external program''s window is now closed.'

How to kill/stop a powershell process on no output for some period of time

Creating processes using powershell start-process command , which will executes the processes sequentially and output will be displayed in console. below is the code snippet:
foreach($module in $selectedModules){
$filepath = $basePath + $module
Start-Process -FilePath $filepath -ArgumentList $cmdArgs -Wait -NoNewWindow
}
If one process stuck, need to kill it so that next iteration process will start.If console output not updated for specified time range say 15 minutes, we can consider that process got hang and stop it. so that next process will begin.Tried with wait-process, but it needs predefined timeout value in place. any better way to find out stuck process ? or need to time with no output.

kill child processes when parent ends

My goal is to kill (or somehow gracefully shutdown) child processes that were started by powershell script, so that nothing will keep running after the parent process dies (either normally by hitting end of script or via crash or ctrl+c or any other way).
I've tried several approaches but none worked as expected:
# only one line was active at time, all other were commented
start-job -scriptblock { & 'notepad.exe' } # notepad.exe started, script continues to end, notepad.exe keep running
start-job -scriptblock { 'notepad.exe' } # notepad.exe not started, script continues to end
notepad.exe # notepad.exe started, script continues to end, notepad.exe keep running
& notepad.exe # notepad.exe started, script continues to end, notepad.exe keep running
start-Process -passthru -FilePath notepad.exe # notepad.exe started, script continues to end, notepad.exe keep running
# give script some time before ending
Write-Host "Begin of sleep section"
Start-Sleep -Seconds 5
Write-Host "End of sleep section"
You can to this kind of thing with a finally clause. A finally clause gets executed after a try block, even if the execution of the try block threw an exception or if the execution was aborted by the user.
So one way to approach your problem would be the following:
keep track of the process ids of the child processes, your script is spawning and
kill these processes in the finally clause.
try
{
$process = Start-Process 'notepad.exe' -PassThru
# give script some time before ending
Write-Host "Begin of sleep section"
Start-Sleep -Seconds 5
Write-Host "End of sleep section"
}
finally
{
# Kill the process if it still exists after the script ends.
# This throws an exception, if process ended before the script.
Stop-Process -Id $process.Id
}

Calling print verb in windows powershell without using Start-Sleep

I have an electron app (nodejs) and have a requirement to print pictures from it. I am using the powershell command:
$path = "C:\person.jpg";
Start-Process -FilePath $path -Verb Print | Out-Null;
Start-Sleep -s 150;
This works but as soon as I remove Start-Sleep, the print window opens for half a second and closes by itself. Then, if I don't do this and the window opens, even after the user presses the close button on the title bar, the powershell process still remains open until the timeout is completed. Is there a way to have this window open and to clean up it's memory when the user closes it?
Many Thans
Add the -Wait parameter to you Start-Process call. From Start-Process:
-Wait
Indicates that this cmdlet waits for the specified process and its descendants to complete before accepting more input. This parameter suppresses the command prompt or retains the window until the processes finish.
Otherwise, PowerShell fires up the process and moves on the script execution. That's the reason why you need a Start-Sleep. So, change your code to:
$path = "C:\person.jpg";
Start-Process -FilePath $path -Verb Print -Wait | Out-Null;
if you are using PowerShell 5.x you can also try `Out-PrinterĀ“ see this link for additional docu.
Hope that helps.