Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Start-Process does not populate the process object's exit code with NoNewWindow switch #20400

Closed
5 tasks done
unlikelier opened this issue Sep 29, 2023 · 21 comments
Closed
5 tasks done
Labels
Needs-Triage The issue is new and needs to be triaged by a work group. Resolution-Fixed The issue is fixed. WG-Cmdlets general cmdlet issues

Comments

@unlikelier
Copy link

unlikelier commented Sep 29, 2023

Prerequisites

Steps to reproduce

I noticed that the Start-Process cmdlet sometimes does not populate the ExitCode property in the output, I believe it should.
This happed at least when -PassThru, -Wait, and -NoNewWindow switches are provided. -NoNewWindow seems to be the reason, since the exit code is available without it.

Steps to reproduce

You can see this behavior with this simple test script

Set-Content -Value "exit 1" -Path script.ps1
$p = start-process "pwsh" script.ps1 -PassThru -NoNewWindow -Wait
$p.ExitCode  # Should output: 1
$p = start-process "pwsh" script.ps1 -PassThru -Wait
$p.ExitCode # Outputs: 1

Expected behavior

PS C:\> set-content -Value "exit 1" -Path script.ps1
PS C:\> $p = start-process "pwsh" script.ps1 -PassThru -NoNewWindow -Wait
PS C:\> $p.ExitCode
1
PS C:\> $p = start-process "pwsh" script.ps1 -PassThru -Wait
PS C:\> $p.ExitCode
1

Actual behavior

PS C:\> set-content -Value "exit 1" -Path script.ps1
PS C:\> $p = start-process "pwsh" script.ps1 -PassThru -NoNewWindow -Wait
PS C:\> $p.ExitCode
PS C:\> $p = start-process "pwsh" script.ps1 -PassThru -Wait
PS C:\> $p.ExitCode
1

Error details

No response

Environment data

PS C:\> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      7.4.0-preview.5
PSEdition                      Core
GitCommitId                    7.4.0-preview.5
OS                             Microsoft Windows 10.0.20348
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

Visuals

No response

@unlikelier unlikelier added the Needs-Triage The issue is new and needs to be triaged by a work group. label Sep 29, 2023
@mklement0
Copy link
Contributor

mklement0 commented Sep 29, 2023

Although the symptoms have changed a bit, the root cause is likely the same as in:

That is, it seems that an exception occurs behind the scenes due to the process handle not being available (for reasons unknown to me), which PowerShell quietly swallows, causing $null to be returned from .ExitCode (and .Handle).

This now seems to always happen with -NoNewWindow, whereas it previously didn't happen if -Wait was specified too.

That is, both ($p = start-process "pwsh" script.ps1 -PassThru -NoNewWindow -Wait).Handle and $p = start-process "pwsh" script.ps1 -PassThru -NoNewWindow; $p.WaitForExit(); $p.Handle now output nothing, and therefore also return $null via .ExitCode.

In Windows PowerShell (and seemingly also earlier versions of PS Core), the -Wait variant (only) does work.

The problem does NOT occur when System.Diagnostics.Process is used directly, so this can serve as a workaround

# NOTE: You may also have to set the working dir. to PowerShell's.
$p = [System.Diagnostics.Process]::Start('pwsh', (Convert-Path 'script.ps1'))
$p.WaitForExit()
"Exit code: [$($p.ExitCode)]" # OK

Update: @derkveenhof found a simpler workaround: cache the process handle:

$p = Start-Process -NoNewWindow -PassThru pwsh '-c whoami'
$dummy = $p.Handle # Cache the handle
$p.WaitForExit()
"Exit code: [$($p.ExitCode)]" # OK

However, it should be noted that these workarounds aren't fully equivalent, though it will probably often not make a difference: -Wait waits for the child process as well as any of ITS child processes, which .WaiForExit() and Wait-Process do not. See:

@shoddyguard
Copy link

We've just hit this too (as we have PowerShell Core set to auto-update).
Unfortunately we use Start-Process in a lot of places so it's going to be quite painful to implement the above workaround 😞

@prayaas-a
Copy link

+1

2 similar comments
@alexvergilis
Copy link

+1

@chrispy81
Copy link

+1

@jlpetz
Copy link

jlpetz commented Nov 21, 2023

what the heck, this is pretty bad. How did this slip past testing? I have a number of scripts using '-wait' which are now all bonkers, because the ExitCode and various other properties on System.Diagnostics.Process objects (returned by start-process) are now missing since 7.4 PowerShell update. Can this please be fixed this ASAP

@jlpetz
Copy link

jlpetz commented Nov 21, 2023

Weird, is this as known issue too?

In one of my scripts I tried swapping from in Start-Process

$runresult = Start-Process "$initExe" -ArgumentList "$initExeArgs" -wait -NoNewWindow -PassThru
if ($runresult.exitcode -ne 0)

to a simple call operator ('&'), thinking this would do the trick

& "$initExe" $initExeArgs
$runresult = $LASTEXITCODE
if ($runresult -ne 0)

And while it works in a Terminal prompt, where the EXE sends prompt output to the terminal and then waits for input (as below)

Enter password for your user: 

If I run that same '&' call embedded in a .ps1 script. It seems to have the Input/Output redirection issues, as the same EXE in the same terminal doesn't output the prompt text. Instead it just hangs waiting for input. It still works, but it's confusing as to why the prompt goes missing in that context?

@jlpetz
Copy link

jlpetz commented Nov 21, 2023

Ok, got it working similar to what @mklement0 posted in #20400 (comment)

The one difference is that to get the same behaviour in terms of screen output when running manually in a terminal AND when running it as part of a larger .ps1, I had to merge the WaitForExit() line (with a semicolon ';' on a single line). If I didn't do this, then the Output (input prompt) from the EXE was showing up on a PS prompt line (with the current directory prefix) when it was run manually in a terminal (but not when run in a large .ps1).

I have no idea why that would be the case. But by merging these onto a single line for some reason the EXE prompt goes to a new line (consistent with running the same command in a larger .ps1)

So in the end I went from

$runresult = Start-Process "$init" -ArgumentList "$initOptions" -wait -NoNewWindow -PassThru
if ($runresult.exitcode -ne 0)

to (leaving extra lines, as the initoptions as an array might help others)

$init = "c:\a path with spaces\init.exe"
$initoptions = @()
$initoptions += '-s'
$initoptions += '--abc'
$runresult = [System.Diagnostics.Process]::Start('pwsh', "-command & '$init' $initoptions") ; $runresult.WaitForExit()
if ($runresult.ExitCode -ne 0)
{
    Write-Host "ERROR: Exit code was non zero value - $($runresult.ExitCode)"
}

@softwarebear
Copy link

Just to add ... this doesn't happen on MacOS ...

Name                           Value
----                           -----
PSVersion                      7.4.0
PSEdition                      Core
GitCommitId                    7.4.0
OS                             Darwin 23.1.0 Darwin Kernel Version 23.1.0: Mon Oct  9 21:27:24 PDT 2023; root:xnu-10002.41.9~6/RELEASE_ARM64_T6000
Platform                       Unix
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

@mklement0
Copy link
Contributor

Good to know, @softwarebear.
Similarly, Linux seems to be unaffected (simple test command, for all platforms:
(Start-Process -Wait -NoNewWindow -PassThru whoami).ExitCode
Should print both the username and exit code 0)

@mklement0
Copy link
Contributor

@jlpetz, @derkveenhof found a simpler workaround - see my updated comment above.

@softwarebear
Copy link

softwarebear commented Nov 21, 2023

Just to add ... this doesn't happen on MacOS ...

Name                           Value
----                           -----
PSVersion                      7.4.0
PSEdition                      Core
GitCommitId                    7.4.0
OS                             Darwin 23.1.0 Darwin Kernel Version 23.1.0: Mon Oct  9 21:27:24 PDT 2023; root:xnu-10002.41.9~6/RELEASE_ARM64_T6000
Platform                       Unix
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

Just to be consistent ...

PS /Users/softwarebear/dev> (Start-Process -Wait -NoNewWindow -PassThru whoami).ExitCode
softwarebear                    
0
PS /Users/softwarebear/dev> (Start-Process -Wait -PassThru whoami).ExitCode                
softwarebear                    
0
PS /Users/softwarebear/dev> 

@mklement0
Copy link
Contributor

Thanks. On Unix-like platforms -NoNewWindow is implied, that's why both commands behave the same there.
On Windows - and that is the bug - only the form without -NoNewWindow reports an exit code (while - as expected - showing no whoami.exe output in the calling console, because it runs in a separate window).

@harmen-xb
Copy link

I am also experiencing this issue now after upgrading to PowerShell v7.4.0 x64 on Windows.

In PowerShell v7.3.10 it works fine:
image

In PowerShell v7.4.0 it doesn't return the ExitCode with -NoNewWindow, while it does return the ExitCode without -NoNewWindow:
image

@rkieslinger
Copy link

We have the same issue on Windows.

@shoddyguard
Copy link

I'm seeing this across a number of PoSh modules and platforms that have PowerShell runners.
This probably needs fixing at the source rather than being worked around.

@astrahan87
Copy link

+1

@santhonisz
Copy link

+1 on Windows Server 2022 and Windows 11

@mklement0
Copy link
Contributor

It looks like this will be fixed in v7.4.1:

@SteveL-MSFT SteveL-MSFT added KeepOpen The bot will ignore these and not auto-close WG-NeedsReview Needs a review by the labeled Working Group WG-Cmdlets general cmdlet issues Resolution-Fixed The issue is fixed. and removed WG-NeedsReview Needs a review by the labeled Working Group KeepOpen The bot will ignore these and not auto-close labels Apr 29, 2024
Copy link
Contributor

This issue has been marked as fixed and has not had any activity for 1 day. It has been closed for housekeeping purposes.

Copy link
Contributor

microsoft-github-policy-service bot commented May 1, 2024

📣 Hey @unlikelier, how did we do? We would love to hear your feedback with the link below! 🗣️

🔗 https://aka.ms/PSRepoFeedback

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs-Triage The issue is new and needs to be triaged by a work group. Resolution-Fixed The issue is fixed. WG-Cmdlets general cmdlet issues
Projects
None yet
Development

No branches or pull requests