[release/v7.5] Set standard handles explicitly when starting a process with -NoNewWindow (#25324)

Co-authored-by: Dongbo Wang <dongbow@microsoft.com>
Co-authored-by: Travis Plunk <travis.plunk@microsoft.com>
This commit is contained in:
PowerShell Team Bot
2025-06-04 23:04:40 +00:00
committed by GitHub
co-authored by Dongbo Wang Travis Plunk
parent 8249040f1e
commit cd6b4d8f53
2 changed files with 52 additions and 6 deletions
@@ -1677,7 +1677,7 @@ namespace Microsoft.PowerShell.Commands
private SwitchParameter _loaduserprofile = SwitchParameter.Present;
/// <summary>
/// Starts process in a new window.
/// Starts process in the current console window.
/// </summary>
[Parameter(ParameterSetName = "Default")]
[Alias("nnw")]
@@ -1965,7 +1965,9 @@ namespace Microsoft.PowerShell.Commands
startInfo.WindowStyle = _windowstyle;
if (_nonewwindow)
// When starting a process as another user, the 'CreateNoWindow' property value is ignored and a new window is created.
// See details at https://learn.microsoft.com/dotnet/api/system.diagnostics.processstartinfo.createnowindow?view=net-9.0#remarks
if (_nonewwindow && _credential is null)
{
startInfo.CreateNoWindow = _nonewwindow;
}
@@ -2413,33 +2415,60 @@ namespace Microsoft.PowerShell.Commands
private void SetStartupInfo(ProcessStartInfo startinfo, ref ProcessNativeMethods.STARTUPINFO lpStartupInfo, ref int creationFlags)
{
bool hasRedirection = false;
// If we are starting a process using the current console window, we need to set its standard handles
// explicitly when they are not redirected because otherwise they won't be set and the new process will
// fail with the "invalid handle" error.
//
// However, if we are starting a process with a new console window, we should not explicitly set those
// standard handles when they are not redirected, but instead let Windows figure out the default to use
// when creating the process. Otherwise, the standard input handles of the current window and the new
// window will get weirdly tied together and cause problems.
bool hasRedirection = startinfo.CreateNoWindow
|| _redirectstandardinput is not null
|| _redirectstandardoutput is not null
|| _redirectstandarderror is not null;
// RedirectionStandardInput
if (_redirectstandardinput != null)
{
hasRedirection = true;
startinfo.RedirectStandardInput = true;
_redirectstandardinput = ResolveFilePath(_redirectstandardinput);
lpStartupInfo.hStdInput = GetSafeFileHandleForRedirection(_redirectstandardinput, FileMode.Open);
}
else if (startinfo.CreateNoWindow)
{
lpStartupInfo.hStdInput = new SafeFileHandle(
ProcessNativeMethods.GetStdHandle(-10),
ownsHandle: false);
}
// RedirectionStandardOutput
if (_redirectstandardoutput != null)
{
hasRedirection = true;
startinfo.RedirectStandardOutput = true;
_redirectstandardoutput = ResolveFilePath(_redirectstandardoutput);
lpStartupInfo.hStdOutput = GetSafeFileHandleForRedirection(_redirectstandardoutput, FileMode.Create);
}
else if (startinfo.CreateNoWindow)
{
lpStartupInfo.hStdOutput = new SafeFileHandle(
ProcessNativeMethods.GetStdHandle(-11),
ownsHandle: false);
}
// RedirectionStandardError
if (_redirectstandarderror != null)
{
hasRedirection = true;
startinfo.RedirectStandardError = true;
_redirectstandarderror = ResolveFilePath(_redirectstandarderror);
lpStartupInfo.hStdError = GetSafeFileHandleForRedirection(_redirectstandarderror, FileMode.Create);
}
else if (startinfo.CreateNoWindow)
{
lpStartupInfo.hStdError = new SafeFileHandle(
ProcessNativeMethods.GetStdHandle(-12),
ownsHandle: false);
}
if (hasRedirection)
{
@@ -2881,6 +2910,9 @@ namespace Microsoft.PowerShell.Commands
internal static class ProcessNativeMethods
{
[DllImport(PinvokeDllNames.GetStdHandleDllName, SetLastError = true)]
public static extern IntPtr GetStdHandle(int whichHandle);
[DllImport(PinvokeDllNames.CreateProcessWithLogonWDllName, CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool CreateProcessWithLogonW(string userName,
@@ -241,3 +241,17 @@ Describe "Environment Tests" -Tags "Feature" {
}
}
}
Describe "Bug fixes" -Tags "CI" {
## https://github.com/PowerShell/PowerShell/issues/24986
It "Error redirection along with '-NoNewWindow' should work for Start-Process" -Skip:(!$IsWindows) {
$errorFile = Join-Path -Path $TestDrive -ChildPath error.txt
$out = pwsh -noprofile -c "Start-Process -Wait -NoNewWindow -RedirectStandardError $errorFile -FilePath cmd -ArgumentList '/C echo Hello'"
## 'Hello' should be sent to standard output; 'error.txt' file should be created but empty.
$out | Should -BeExactly "Hello"
Test-Path -Path $errorFile | Should -BeTrue
(Get-Item $errorFile).Length | Should -Be 0
}
}