I'm trying to find a way to get PowerShell not to spawn a command window when running an executable using Start-Process.
If I call the executable directly within the script (e.g. .\program.exe) then the program runs (with its arguments) and the output is returned to the PowerShell window.
If I use Start-Process the program spawns a command window where the program runs and returns it's output.
If I try and use the -NoNewWindow switch of Start-Process the script then errors out saying it can't find the exe file.
I would prefer to use Start-Process to have access to the -Wait switch, as the programs and configurations the script makes can take some time individually to finish, and I don't want later commands starting up.
This code runs the executable in a separate command window:
Start-Process DeploymentServer.UI.CommandLine.exe -ArgumentList "download --autoDownloadOn --autoDownloadStartTime $StartTime --autoDownloadEndTime $EndTime" -Wait
This code runs the exe within the PowerShell console:
.\DeploymentServer.UI.CommandLine.exe download --autoDownloadOn --autoDownloadStartTime $StartTime --autoDownloadEndTime $EndTime
If I add the -NoNewWindow to the Start-Process code
Start-Process DeploymentServer.UI.CommandLine.exe -ArgumentList "download --autoDownloadOn --autoDownloadStartTime $StartTime --autoDownloadEndTime $EndTime" -Wait -NoNewWindow
I get the following error:
Start-Process : This command cannot be executed due to the error: The system
cannot find the file specifie
At C:\Temp\SOLUS3Installv1.3.ps1:398 char:22
+ Start-Process <<<< DeploymentServer.UI.CommandLine.exe -ArgumentList "download --autoDownloadStartTime $StartTime --autoDownloadEndTime $EndTime" -Wait -NoNewWindow
+ CategoryInfo : InvalidOperation: (:) [Start-Process], InvalidOperationException
+ FullyQualifiedErrorId : InvalidOperationException,Microsoft.PowerShell.Commands.StartProcessCommand
You should prefix the executable name with the current directory when you use the -NoNewWindow switch:
Start-Process .\DeploymentServer.UI.CommandLine.exe -ArgumentList "download --autoDownloadOn --autoDownloadStartTime $StartTime --autoDownloadEndTime $EndTime" -Wait -NoNewWindow
Background information:
The first thing Start-Process tries to do is to resolve the value of the -FilePath parameter by PowerShell rules. If it succeeds, it replaces the value value passed with the full path to the command. If not, it leaves the value untouched.
In the Windows API there are two ways to start a new process: CreateProcess and ShellExecute. ShellExecute is the default, but if you use a cmdlet parameter that requires CreateProcess (for example, -NoNewWindow), then CreateProcess will be used. The difference between them, which matters for this question, is that when looking for a command to execute, CreateProcess uses the current process' working directory, while ShellExecute uses the specified working directory (which Start-Process by default passes based on the current filesystem-provider location, unless explicitly specified via -WorkingDirectory).
PS Test:\> 1..3 |
>> ForEach-Object {
>> New-Item -Path $_ -ItemType Directory | Out-Null
>> Add-Type -TypeDefinition #"
>> static class Test {
>> static void Main(){
>> System.Console.WriteLine($_);
>> System.Console.ReadKey(true);
>> }
>> }
>> "# -OutputAssembly $_\Test.exe
>> }
PS Test:\> [IO.Directory]::SetCurrentDirectory((Convert-Path 2))
PS Test:\> Set-Location 1
PS Test:\1> Start-Process -FilePath Test -WorkingDirectory ..\3 -Wait # Use ShellExecute. Print 3 in new windows.
PS Test:\1> Start-Process -FilePath .\Test -WorkingDirectory ..\3 -Wait # Use ShellExecute. Print 1 in new windows.
PS Test:\1> Start-Process -FilePath Test -WorkingDirectory ..\3 -Wait -NoNewWindow # Use CreateProcess.
2
PS Test:\1> Start-Process -FilePath .\Test -WorkingDirectory ..\3 -Wait -NoNewWindow # Use CreateProcess.
1
PowerShell does not update the current process' working directory when you change the current location for the FileSystem provider, so the directories can differ.
When you type:
Start-Process DeploymentServer.UI.CommandLine.exe -Wait -NoNewWindow
Start-Process cannot resolve DeploymentServer.UI.CommandLine.exe by PowerShell rules, since it does not look in the current FileSystem location by default. And it uses CreateProcess, since you specify -NoNewWindow switch. So, it ends up looking for DeploymentServer.UI.CommandLine.exe in the current process' working directory, which does not contains this file and thus causes an error.
Related
I executed the following three powershell commands. The first two commands returned no results, and the third command returned results. The main difference between the three commands is the use of the wait argument and parentheses.
PS C:\Users> Start-Process -FilePath 'msiexec' -ArgumentList '/I C:\Users\Downloads\Everything-1.4.1.1015.x64.msi -quiet' -PassThru | Foreach-Object -Process { $_.exitcode }
PS C:\Users> Start-Process -FilePath 'msiexec' -ArgumentList '/I C:\Users\Downloads\Everything-1.4.1.1015.x64.msi -quiet' -PassThru -wait | Foreach-Object -Process { $_.exitcode }
PS C:\Users> (Start-Process -FilePath 'msiexec' -ArgumentList '/I C:\Users\Downloads\Everything-1.4.1.1015.x64.msi -quiet' -PassThru -Wait) | Foreach-Object -Process { $_.exitcode }
1619
I test another two commands, and the difference between them was the use of parentheses. Both commands returned results no matter with parentheses.
PS C:\Users> Start-Process -FilePath 'msiexec' -ArgumentList '/I C:\Users\Downloads\Everything-1.4.1.1015.x64.msi -quiet' -PassThru | Foreach-Object -Process { $_.id }
22980
PS C:\Users> (Start-Process -FilePath 'msiexec' -ArgumentList '/I C:\Users\Downloads\Everything-1.4.1.1015.x64.msi -quiet' -PassThru -Wait) | Foreach-Object -Process { $_.id }
8064
Thanks for explanation for -wait parameter. I still confused with the difference caused by parentheses. Hope for more replies.
The main difference between the three commands is the use of the -Wait argument and parentheses
To build on Mathias R. Jessen's helpful comment:
It is primarily the use of Start-Process's -Wait switch that is required:
Without -Wait, Start-Process runs asynchronously.
Without -PassThru, Start-Process produces no output.
While -PassThru makes Start-Process output a System.Diagnostics.Process instance representing the newly launched process, unless -Wait is also present that instance's .ExitCode property has no value yet, because the launched process typically hasn't exited yet.
Additionally, parentheses ((...)) are required too, because Start-Process emits the System.Diagnostics.Process instance representing the newly launched process to the pipeline (as received by ForEach-Object) right away, and then waits for the process to exit. By using (...), the grouping operator, you're forcing a wait for Start-Process itself to exit, at which point the Process' instance .ExitCode property is available, thanks to -Wait.
In general, wrapping a command in (...) forces collecting its output in full, up front - which includes waiting for it to exit - before the results are passed through the pipeline (as opposed to the streaming (one-by-on output) behavior that is the default, which happens while the command is still running).
Therefore, the following works - but see the bottom section for a simpler alternative:
# Note: With (...), you could also pipe the output to ForEach-Object, as in
# your question, but given that there's by definition only *one*
# output object, that is unnecessary.
(
Start-Process -PassThru -Wait -FilePath 'msiexec' -ArgumentList '/i C:\Users\Downloads\Everything-1.4.1.1015.x64.msi -quiet'
).ExitCode
It follows from the above that using two separate statements would work too (given that any statement runs to completion before executing the next):
$process = Start-Process -PassThru -Wait -FilePath 'msiexec' -ArgumentList '/i C:\Users\Downloads\Everything-1.4.1.1015.x64.msi -quiet'
$process.ExitCode
msiexec.exe is unusual in that:
it is a GUI(-subsystem) executable (as opposed to a console(-subsystem) executable), which therefore - even when invoked directly - runs asynchronously.
yet it reports a meaningful process exit code that the caller may be interested in, requiring the caller to wait for its exit (termination) in order to determine this exit code.
As an aside: For invoking console applications, Start-Process is not the right tool in general, except in unusual scenarios - see this answer.
An simpler alternative to using msiexec with Start-Process -PassThru -Wait is to use direct invocation via cmd /c, which ensures both (a) synchronous invocation and (b) that PowerShell reflects msiexec's exit code in its automatic $LASTEXITCODE variable:
cmd /c 'msiexec /i C:\Users\Downloads\Everything-1.4.1.1015.x64.msi -quiet'
$LASTEXITCODE # output misexec's exit code
Note: If the msiexec command line needs to include PowerShell variable values, pass an expandable (double-quoted) string ("...") to cmd /c instead and - as with verbatim (single-quoted) string ('...') strings - use embedded double quoting around embedded arguments, as necessary.
$argList = "-file `"C:\Users\bdl\Desktop\jhansi\PowerShell_Scripts\dialog.ps1`""
Start powershell -argumentlist $argList -NoNewWindow
I am trying to open another powershell terminal from the current script to execute the current script output. Another powershell terminal is opening but it is blinking continuously. The above two lines of code i have written but it is blinking. please tell me where is the mistake in the above two lines.
You can try something like this:
$ArgList = "C:\Users\bdl\Desktop\jhansi\PowerShell_Scripts\dialog.ps1"
Start-Process -FilePath PowerShell -ArgumentList $ArgList -NoNewWindow -Wait
You can also check if the script called in PowerShell returns successfully or not by adding an exit code into it (Exit 0 means it succeed and Exit 1 if it fails) with:
$Exe = (Start-Process -FilePath PowerShell -ArgumentList $ArgList -NoNewWindow -Wait -PassThru).ExitCode
If ($Exe -ne 0)
{
Write-Host "An error has occured while running the script."
}
As the exit code other than 0 means the script didn't finish properly.
I have a process that would traditionally be run like so (in the command line):
filepath.exe #"respfile.resp"
where respfile.resp is a response file that has command line arguments for the executable.
Running the command like that works as desired in the command prompt.
However I am trying to use a powershell script to run multiple programs. Here is what I have:
if (Test-Path $respPath){
$executionResposne = Start-Process -NoNewWindow -Wait -PassThru -FilePath $bimlcExePath -ArgumentList $respPath
if ($executionResposne.ExitCode -eq 1){
Write-Output "Unable to successfully run the process. Exiting program."
return
}
}
and I am getting the following error message:
Error:: filepath\to\resp\file Data at the root level is invalid.
How can I make this work?
You need to embed the quotes for the interpreter:
-ArgumentList "#`"$respPath`""
I'm writing simple script to unarchive (rar) a project from Teamcenter to temp directory, then run specific program (Mentor), then archive again.
I've read a lot of examples about starting exe from PS, but they mostly relate to small exes like notepad, without dlls and other resources.
In Powershell Ise the script works perfectly. But when I call the script from teamcenter, Mentor is missing dlls.
Before I run Mentor, in the script, I do:
Get-ChildItem Env:
to check environment variables and all variables exist. I tried to set environments manually, like this:
$wf_classpath = Get-ChildItem Env:WF_CLASSPATH
[System.Environment]::SetEnvironmentVariable("WF_CLASSPATH", $wf_classpath.Value, "Process")
Does not work.
I tried to set homefolder:
$mentor = Start-Process $file.FullName -Wait -WorkingDirectory $workdir
Does not work.
Then I tried to call a batch file from the script with environments, does not work.
Try call cmd.exe /c ... does not work.
Full script here, works perfect only in Powershell Ise, if I call the script from other programs, exe does not start.
$shell = new-object -com shell.application
$invocation = $MyInvocation.MyCommand.Definition
$rootpath = $PSScriptRoot
$outpath = "$($PSScriptRoot)\out"
$pathtorar = "c:\Siemens\menutils\Rar.exe"
Remove-Item -Recurse -Force $outpath
New-Item $outpath -ItemType directory
$archive = get-childitem $rootpath | where { $_.extension -eq ".rar" } | Select-Object -First 1
$arglist = "x $($archive.FullName) $($outpath)"
Start-Process -FilePath $pathtorar -ArgumentList $arglist -Wait
Remove-Item -Recurse -Force $archive.FullName
$file = get-childitem $outpath -Recurse | where { $_.extension -eq ".prj" } | Select-Object -First 1
Write-Host "$(get-date -Format yyyy-MM-dd-hh-ss)
Start process: $($file.FullName)"
$mentor = Start-Process $file.FullName -Wait
$arglist = "a -m0 -r -ep1 $($archive.FullName) $($outpath)"
Start-Process -FilePath $pathtorar -ArgumentList $arglist -Wait
Remove-Item -Recurse -Force $outpath
Read-Host -Prompt "Press Enter to exit"
What's the difference between running the script from Powershell Ise and other programs?
How should I set environment variables to run the script from other scripts/programs?
Its probably that your Current directory is not correct and WorkingDirectory in my experience is buggy. The dll's will be obtained from the current directory if they are not at the regular system paths.
Use this function before Start-Process
[IO.Directory]::SetCurrentDirectory($Dir)
Here's what I'm trying to do:
#ECHO OFF
CALL powershell -ExecutionPolicy RemoteSigned -Command "$sh = new-object -com 'Shell.Application'; $sh.ShellExecute('powershell', '-NoExit -Command "$path = """HKLM:\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}""";echo $path"', '', 'runas')"
PAUSE
Basically, I want to have a batch file that I can double-click, which will run a powershell script that calls another powershell script but asks for admin privileges and runs that command as admin.
I'm having problems though, with the double-quotes I think... I've tried many things but can't seem to fix it, here's the powershell error message:
Bad numeric constant: 4D.
At line:1 char:57
+ $path = HKLM:\SYSTEM\CurrentControlSet\Control\Class\{4D <<<< 36E972-E325-11C
E-BFC1-08002BE10318};echo $path
+ CategoryInfo : ParserError: (4D:String) [], ParentContainsError
RecordException
+ FullyQualifiedErrorId : BadNumericConstant
PS C:\Windows\system32>
I would use the built-in command Start-Process rather than creating a shell object e.g.:
CALL powershell -ExecutionPolicy RemoteSigned -NoProfile -Command "& {Start-Process PowerShell -Verb runas -Arg '-NoExit -Command & {$path=''foo'';$path}'}"
For anything of significance the quoting is going to be annoying. Can you put the final script in a file and execute the script file using the -File parameter on PowerShell.exe?
I solved it, here's the long batch one-liner for my real problem, so people can see a real example:
CALL powershell -ExecutionPolicy RemoteSigned -Command "$sh = new-object -com 'Shell.Application'; $sh.ShellExecute('powershell', '-NoExit -Command ""$path = ''HKLM:\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}''; Get-Childitem $path -ErrorAction SilentlyContinue | Where { (Get-ItemProperty $_.PSPath DriverDesc) -Match ''VMnet'' } | Foreach { New-ItemProperty -ErrorAction SilentlyContinue $_.PSPath -Name ''*NdisDeviceType'' -Value ''1'' -PropertyType DWord }; netsh interface set interface name=''VMware Network Adapter VMnet1'' admin=DISABLED; netsh interface set interface name=''VMware Network Adapter VMnet1'' admin=ENABLED; netsh interface set interface name=''VMware Network Adapter VMnet8'' admin=DISABLED; netsh interface set interface name=''VMware Network Adapter VMnet8'' admin=ENABLED""', '', 'runas')"
P.S: In case anyone's wondering what it's for... I run this every time I install/update VMware Workstation to hide the virtual network adapters from appearing on the Network and Sharing Center in Windows Vista/7.