powershell how to close a .exe process - powershell

I want to start a .exe file from powershell and wait for it to finish and then continue to the next line in the powershell. However after the .exe file finished it's job the window remain there to be manually closed. Just like notepad. Here is what I got
start-process -FilePath notepad -Wait -NoNewWindow
echo "it's done!"
I specified -NoNewWindow but it still comes with a window and the powershell have to wait until I manually close it. How can I close it automatically? The .exe file is a third party app that I have no control over.
Please note that I want to wait for the task that the .exe file completes and then automatically close that window so that I can continue to the next line in powershell.
I also tried -WindowStyle hidden.
start-process -FilePath notepad -Wait -WindowStyle hidden
echo "it's done!"
But this is even worse I have to go to task manager to kill the hidden window without knowing it is completed or not.
Is there a parameter better than -NoNewWindow that can force no new window show up?
Thanks,

The script below will:
Open Notepad.
Wait for you to type "Finished".
Close Notepad.
You will need to install AutoIt. (I selected "x86" in the installer BTW.)
You will also need to adapt the Get-AU3ControlHandle call to suit your needs. The AutoIt Window Info Tool will be useful there. You may have to do some reading of the AutoIt documentation.
See also AutoIt Cmdlets for Windows PowerShell.
import-module "${env:ProgramFiles(x86)}\AutoIt3\AutoItX\AutoItX.psd1"
$p = Start-Process notepad -PassThru
# Wait for the window to open
while($p.MainWindowHandle -eq 0)
{
Start-Sleep 1
}
$c = Get-AU3ControlHandle -WinHandle $p.MainWindowHandle -Control "Edit1"
while($true)
{
$t = Get-AU3ControlText $p.MainWindowHandle $c
if ($p.HasExited)
{
break;
}
elseif ($t -eq "Finished")
{
$p.CloseMainWindow();
break;
}
else
{
Write-Output "Waiting 1 sec."
Start-Sleep 1
}
}
Write-Output "Done"

This is what I use to kill iTunes automatically. But, as mentioned in my comment, if you want to wait for it to be done, you'd have to add either a sleep or do some application-specific check to see that its job is done. What that check is depends on what the exe is doing.
$itunesProc = Get-Process -name iTunes -ErrorAction "Ignore"
if ($itunesProc -ne $null) {
echo "Stopping iTunes..."
Stop-Process -Name iTunes
} else {
echo "No running iTunes process found"
}

Related

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.'

Application doesn't get triggered from Powershell script, when invoking inside Do...Until

I am trying to build custom Windows System Utility script which offers some tasks with relevant keypress choices.
For cleanup task, I am trying to invoke CCleaner64.exe from this script, with it's correct switches as mentioned here. And the script I built so far is below:
$ScriptDir = Split-Path $MyInvocation.MyCommand.Path
if (-Not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator')) {
if ([int](Get-CimInstance -Class Win32_OperatingSystem | Select-Object -ExpandProperty BuildNumber) -ge 6000) {
$CommandLine = "-File `"" + $MyInvocation.MyCommand.Path + "`" " + $MyInvocation.UnboundArguments
Start-Process -FilePath PowerShell.exe -Verb Runas -ArgumentList $CommandLine
Exit
}
}
Set-Location $ScriptDir; Echo 'Current Directory: ' + (Get-Location | Out-String)
function SysUtilMenu {
param (
[string]$Title = 'Windows System Utility'
)
Clear-Host
Write-Host "============ $Title ==========="
Write-Host "1: Do task 1 here."
Write-Host "2: Cache/Temp Files Cleanup."
Write-Host "Q: Exit this Application."
}
Do
{
SysUtilMenu
$selection = Read-Host "Press key to run given task..."
switch ($selection)
{
'1' {
## Do task 1 here...
} '2' {
$CclnrApp64 = "$Env:ProgramFiles\CCleaner\CCleaner64.exe"
Start-Process $CclnrApp64 -ArgumentList '/Clean'
Start-Process $CclnrApp64 -ArgumentList '/Registry'
}
}
}
Until($selection -eq 'q')
But when I press '2', it doesn't invoke CCleaner64.exe in the taskbar, which I checked.
I don't get, why the same Start-Process line doesn't work in that script, but if I open the Powershell terminal separately and run below commands one-by-one, it works perfectly ?
$CclnrApp64 = "$Env:ProgramFiles\CCleaner\CCleaner64.exe"
Start-Process $CclnrApp64 -ArgumentList '/Clean'
Is it due to Script's Self-Elevation, I have taken care of setting the location of the script instead of C:\Windows\System32.
Suggestion with detailed explanation is greatly appreciated...
From the link you have added, the documentation under Command-line parameters for CCleaner operation pane focus, it says the switch should be /CLEANER, not /Clean, and since your code also shows the switch /Registry, I thought this is what you were after (to open the app on a particular tab page).
My initial thoughts were:
it is possible you need to add the -Wait switch so PowerShell will ony start the second command after the first one has completed.
so the ful command would be Start-Process -FilePath "$CclnrApp64" -ArgumentList '/Cleaner' -Wait
to try and use the call operator & instead of Start-Process like & "$CclnrApp64" "/CLEANER"
Both above have the paths in variable $CclnrApp64 in between quotes because $env:programfiles will usually expand to C:\Program Files which has a space in the path.
Under Command-line parameters for CCleaner Business and Technician Edition, there is also a switch called /Clean
If you have that version, the switch should clean up using whatever rules are rules defined in ccleaner.ini and optionally puts the results in log_filename.txt
However, on that same CCleaner page, there is also a listing of other parameters, especially for use in a commandline and as you have experimented using the /AUTO switch, it appears this is what you were after:
CCleaner runs silently and automatically, using the current set of saved options to clean the PC. CCleaner then exits.
A note about the /AUTO switch though:
When you run CCleaner.exe using the /AUTO parameter, CCleaner does not run the Registry cleaner. You cannot currently run the Registry cleaner through a command-line parameter
All this means there are several switches you can use with CCleaner, but they all serve a different purpose.
/CLEANER, /REGISTRY, /TOOLS and /OPTIONS are for opening the application at a certain pane
/AUTO (with optional /SHUTDOWN), /EXPORT and /DELETE (with optional /METHOD) are to have the application perform cleaning/delete actions
and for the Business and Technician Edition there is also
/analyze, /clean and /update

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.

How to "terminate" Get-Content -Wait after another command has finished in batch?

I'm writing on a batch script for Unity builds with Jenkins.
What I did so far
Unity has the problem that by default it it is not very verbous in -batchmode.
So in order to get the output into Jenkins I use -logFile to make Unity write to a specific logfile.
Until now I'm just able to read this file after a build succeeded or failed using
Unity.exe -batchmode -logFile JenkinsLOG.txt <more parameters>
type JenkinsLOG.txt
Now in order to get the content of JenkinsLOG.txt in realtime to the Jenkins log view I'm trying to use start to run the Unity process in a new console and use powershell Get-Content <file> -Wait to print the content of the logfile in realtime to the console:
start Unity.exe -batchmode -logFile JenkinsLOG.txt <more parameters>
powershell Get-Content JenkinsLOG.txt -Wait
this works great and I see the output in realtime appearing in Jenkins ...
But ... ofcourse the powershell command never terminates so the build process gets stuck still waiting for more lines beeing appended to JenkinsLOG.txt.
So my question is
Is there any possibility to tell this powershell command to terminate after the Unity process finished?
Get process id you want to monitor
Spin up a job that tails the log file
Loop the current console to read output from the job
Terminate the loop when the process exits
Clean up jobs
Here it is wrapped up in a function. There is probably a more elegant way, but I couldn't find another way to distinguish between a "timeout" exit of Wait-Process and a "process stopped" exit.
function TailFile-UntilProcessStops {
Param ($processID, $filePath)
$loopBlock = {
Param($filePath) Get-Content $filePath -Wait -Tail 0
}
$TailLoopJob = start-job -scriptBlock $loopBlock -ArgumentList $filePath
try {
do {
$TailLoopJob | Receive-Job
try {
Wait-Process -id $processID -ErrorAction Stop -Timeout 1
$waitMore = $false
} catch {
$waitMore = $true
}
} while($waitMore)
} finally {
Stop-Job $TailLoopJob
Remove-Job $TailLoopJob
}
}
Here is the test code with Notepad. Make sure the file exists, then modify it. Every time you save, the console should update with more data. Quit Notepad and control returns to the console.
$filename = 'h:\asdf\somefile.txt'
$process = start-process -FilePath 'notepad.exe' -ArgumentList #($filename) -PassThru
TailFile-UntilProcessStops -processID $process.id -filepath $filename

Disable close button of MSBuild child process

I have a PowerShell script that launches an MSBuild child process. I would like to disable the "close" button on the child process window, so that the user cannot interrupt the process. However, I have not been able to find any information indicating whether this is possible.
If someone could either confirm (and tell me how I would go about doing this) or deny whether this is possible I would greatly appreciate it.
MSBuild.exe is a console application, and as such by default it will run in a console window. You can't really "disable" the close button anymore than you could stop someone (with the right privileges) from just terminating the msbuild.exe process...
What you could do to mitigate some risk would be to use the the jobs feature that was introduced in PowerShell 2.0:
$job = Start-Job -ScriptBlock {
& msbuild app.csproj
if ($LASTEXITCODE -ne 0) { throw "MSBuild failed. Exit code: $LASTEXITCODE" }
}
This will schedule the script block to be run on a background thread of your PowerShell session and it will not show a window for msbuild. All of the output will be captured and held until you decide to retrieve the job. You can check the status of all background jobs with the Get-Job cmdlet, and receive the results with Receive-Job
Wait-Job $job # this line pauses PowerShell/your script until the job returns
$output = $job | Receive-Job
You can do whatever you want with the output - it is worth noting that the exception thrown if the msbuild exit status code is non-zero will be held until you receive the job, at which point it will be raised to your code like any other exception would be. You may want to consider wrapping your call to Receive-Job in a try/catch block to deal with a failed build.
Another option if you don't want a separate window to appear:
$buildArgs = "MySolution.sln", "/t:Build", "/p:Configuration=Debug"
Start-Process -FilePath "msbuild" -ArgumentList $buildArgs -NoNewWindow -Wait
Start-Process has other flags to control redirecting output as well.