Skip to content
Advertisement

Java MSI silent installer

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]).
  • Additionally, the automatic $LASTEXITCODE variable will properly reflect the process’ exit code (which not many GUI-subsystem executables meaningfully set, but misexec.exe does), allowing you to infer success (exit code 0) 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)

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, which msiexec.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.

User contributions licensed under: CC BY-SA
10 People found this is helpful
Advertisement