Hello i’m having issues with this line of code I been working on, for some reason is not working anymore I’m trying to invoke the MSI installer silently and wait till it’s done so I can execute next line of code I had it working but now is not, I tried executing start-process and using the -wait parameter but it’s returning with an error message, and when I call “msiexec.exe” it doesn’t have a -wait parameter
msiexec.exe /i "C:MagtekCCFilesJavajre1.8.0_144.msi" /QN TRANSFORMS="jre1.8.0_144.mst" /L*V "C:Tempmsilog.log" Write-Host -NoNewLine "Done.`n" -ForegroundColor Cyan Start-Sleep -s 2 start-process msiexec.exe "/i C:MagtekCCFilesJavajre1.8.0_144.msi" -wait /QN TRANSFORMS="jre1.8.0_144.mst" /L*V "C:Tempmsilog.log" Write-Host -NoNewLine "Done.`n" -ForegroundColor Cyan Start-Sleep -s 2 > Start-Process : A positional parameter cannot be found that accepts argument '/QN'. At line:1 char:1 + start-process msiexec.exe "/i C:MagtekCCFilesJavajre1.8.0_144.ms ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidArgument: (:) [Start-Process], ParameterBindingException + FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.StartProcessCommand
Advertisement
Answer
msiexec.exe
is unusual in that, while it has an extensive CLI (command-line interface) with many supported parameters, it is a GUI-subsystem executable that runs independently of any calling console.
PowerShell runs GUI-subsystem executables asynchronously when you invoke them directly; that is, unlike with console-subsystem executables, it doesn’t wait for the executable to terminate before continuing.
While Start-Process
-Wait
is indeed the typical way to invoke a GUI-subsystem executable synchronously, there is also a convenient shortcut:
If you pipe to Write-Output
:[1]
PowerShell will wait for the executable(‘s process) to terminate, even if it is a GUI-subsystem application.
- Note: If you know that the GUI application produces no stdout output – and not doing so is typical – you can also pipe to
Wait-Process
for better conceptual clarity (again, see footnote [1]).
- Note: If you know that the GUI application produces no stdout output – and not doing so is typical – you can also pipe to
Additionally, the automatic
$LASTEXITCODE
variable will properly reflect the process’ exit code (which not many GUI-subsystem executables meaningfully set, butmisexec.exe
does), allowing you to infer success (exit code0
) vs. failure (any nonzero exit code), by convention.- That said,
msiexec.exe
uses two nonzero exit codes that also indicate – qualified – success (see the docs for a full list of exit / error codes and their description):3010
(ERROR_SUCCESS_REBOOT_REQUIRED
)1641
(ERROR_SUCCESS_REBOOT_INITIATED
)
- That said,
Therefore, you can use the following:
# Note the `| Write-Output` at the end of the line, which ensures *synchronous* execution. msiexec.exe /i "C:MagtekCCFilesJavajre1.8.0_144.msi" /QN TRANSFORMS="jre1.8.0_144.mst" /L*V "C:Tempmsilog.log" | Write-Output $exitCode = $LASTEXITCODE Write-Host -ForegroundColor Cyan "Installation finished with exit code $exitCode."
Alternatively, pass the msiexec
command line to cmd.exe
via its /c
parameter, which:
- implicitly makes the call synchronous (and also sets
$LASTEXITCODE
) - preserves the original quoting, which is necessary if you pass property values with embedded spaces, e.g.
MYPROP="foo bar"
is then passed exactly as such, whereas PowerShell would convert this argument to"MYPROP=foo bar"
when it rebuilds the actual command line behind the scenes, whichmsiexec.exe
doesn’t recognize.
cmd /c @" msiexec.exe /i "C:MagtekCCFilesJavajre1.8.0_144.msi" /QN TRANSFORMS="jre1.8.0_144.mst" /L*V "C:Tempmsilog.log" "@ $exitCode = $LASTEXITCODE Write-Host -ForegroundColor Cyan "Installation finished with exit code $exitCode."
Note the use of a here-string (@"<newline>...<newline>"@
in this example to make embedded quoting easier.
The above solutions are more convenient alternatives to the following Start-Process -Wait
solution, which additionally uses -PassThru
to pass an object representing the launched process through, so that its .ExitCode
property can be checked:
$exitCode = (Start-Process -Wait -NoNewWindow -PassThru msiexec.exe @' /i "C:MagtekCCFilesJavajre1.8.0_144.msi" /QN TRANSFORMS="jre1.8.0_144.mst" /L*V "C:Tempmsilog.log" '@).ExitCode Write-Host -ForegroundColor Cyan "Installation finished with exit code $exitCode."
Note how all arguments for misexex.exe
are passed as a single string to Start-Process
(using a here-string for readability here), which positionally binds to the -ArgumentList
parameter.
While the -ArgumentList
parameter technically accepts an array of arguments – i.e. allows you to pass arguments individually – a long-standing bugs makes that unadvisable – see this answer.
As for what you tried:
Your Start-Process -Wait
call failed, because you ended up passing more than two positional arguments:
start-process msiexec.exe "/i ..." -wait /QN ...
msiexec.exe
positionally binds to the-FilePath
parameter.- Only the
"/i ..."
argument positionally argument binds to the-ArgumentList
parameter. - Since no other
Start-Process
parameters support positional binding,Start-Process
doesn’t know what to do with/QN
(and all subsequent positional (unnamed) arguments), which is what the error message in your question indicates.
Note: If you did want to pass parameters individually – which is best avoided, as stated above – you’d have to pass them as an array, separated by ,
(Start-Process -Wait msiexec.exe '/i', ...
)
[1] Note that any command in a subsequent pipeline segment works to ensure synchronous execution, because PowerShell always has to wait for the preceding segment’s command to terminate, so as to ensure that all input has been received; While it ultimately doesn’t matter what command you choose, Write-Output
, which in this case will typically be a no-op, has the advantage of passing stdout output through so that it becomes visible / can be captured, if the GUI application at hand produces such output, which is rare (append 2>&1
to the GUI-application call to also capture stderr output). As Bender the Greatest points out, some GUI applications do so in order to produce debugging output, by explicitly attaching to the caller’s console.