PowerShell Start-Process infinite execution of installer

81 views Asked by At

I have Inno Setup installer that run in very silent mode (it's working).

But when I'm running my Inno Setup installer through PowerShell script with admin rights – I get endless execution. Also, the following script code (SetEnvironmentVariable) is not executed even after successful installation.

$installerArguments = $requiredInstallerArguments + $additionalInstallerArguments

Start-Process -FilePath $installerFile.FullName -ArgumentList $installerArguments -Wait

[Environment]::SetEnvironmentVariable('WebPort', (GetValueByKey -array $additionalInstallerArguments -key "/webport"), [EnvironmentVariableTarget]::Machine)

Why does this happen and how to fix it?

2

There are 2 answers

0
Prodige69 On BEST ANSWER

Sometimes I have to use the .WaitForExit() method

$Process = Start-Process -FilePath $installerFile.FullName -ArgumentList $installerArguments -PassThru
$Process.WaitForExit()

Start-Process ... -Wait behaves differently than (Start-Process ... -PassThru).WaitForExit() / Start-Process ... -PassThru | Wait-Process: the former waits for the entire child process tree to terminate, whereas the latter return once the immediate child process has terminated (see GitHub issue #15555). Thus, if an installer spawns a child process that keeps running after the installer has exited, -Wait won't return until that child process terminates.

In SysInternals Process Monitor, you can use Tools->Process Tree to see these process trees.

0
mklement0 On

To complement Prodige69's helpful answer:

Another way to wait only for msiexec.exe (and child processes synchronously launched from it, if any) rather than its entire child process tree (msiexec.exe plus any child processes launched asynchronously from it, which is the cause of the problem here) is to use direct invocation or invocation via cmd.exe /c:

These are syntactically easier alternatives to using (Start-Process ... -PassThru).WaitForExit() or Start-Process ... -PassThru | Wait-Process, which also automatically reflect msiexec's process exit code in the automatic $LASTEXITCODE variable:

Note: I'm using msiexec.exe in the sample commands below, but the same applies analogously to any installer; in your case, with the direct invocation technique, substitute & $installerFile.FullName for msiexec, with the cmd.exe /c technique, substitute "$($installerFile.FullName)"

  • Using direct invocation, via a trick to make the call synchronous:
# To build an argument list *programmatically*, create an *array*
$installerArguments = '/i', 'sample.msi', '/qn'

# Executes *synchronously*, due to `| Out-Null`
# Process exit code is reported in $LASTEXITCODE afterwards.
# Equivalent of: 
#   msiexec /i sample.msi /qn | Out-Null
msiexec $installerArguments | Out-Null
  • Calling via cmd /c, which allows you to control quoting explicitly, which is required for property values that require partial quoting:
# Here, encode all arguments in a *single string*, using embedded double-quoting.
$installerArgumentList = '/i sample.msi /qn PROP="Value with spaces"'

# Executes *synchronously*, due to `cmd /c`
# Process exit code is reported in $LASTEXITCODE afterwards.
cmd /c "msiexec $installerArguments"

See this answer for background information.