javapowershellwindows-installersynchronous

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:\MagtekCC\Files\Java\jre1.8.0_144.msi" /QN TRANSFORMS="jre1.8.0_144.mst" /L*V "C:\Temp\msilog.log"
Write-Host -NoNewLine "Done.`n" -ForegroundColor Cyan
Start-Sleep -s 2



  start-process msiexec.exe  "/i C:\MagtekCC\Files\Java\jre1.8.0_144.msi" -wait /QN TRANSFORMS="jre1.8.0_144.mst" /L*V "C:\Temp\msilog.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:\MagtekCC\Files\Java\jre1.8.0_144.ms ...
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : InvalidArgument: (:) [Start-Process], ParameterBindingException
        + FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.StartProcessCommand

Solution

  • 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]

    Therefore, you can use the following:

    # Note the `| Write-Output` at the end of the line, which ensures *synchronous* execution.
    msiexec.exe /i "C:\MagtekCC\Files\Java\jre1.8.0_144.msi" /QN TRANSFORMS="jre1.8.0_144.mst" /L*V "C:\Temp\msilog.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:

    cmd /c @"
    msiexec.exe /i "C:\MagtekCC\Files\Java\jre1.8.0_144.msi" /QN TRANSFORMS="jre1.8.0_144.mst" /L*V "C:\Temp\msilog.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:\MagtekCC\Files\Java\jre1.8.0_144.msi" /QN TRANSFORMS="jre1.8.0_144.mst" /L*V "C:\Temp\msilog.log"
    '@).ExitCode
    
    Write-Host -ForegroundColor Cyan "Installation finished with exit code $exitCode."
    

    Note how all arguments for misexec.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 ...
    

    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. If you'd rather suppress this information, pipe to Out-Null instead.