Powershell: Start-Process adds extra space to command line

Created on 4 Jul 2020  路  10Comments  路  Source: PowerShell/PowerShell

Steps to reproduce

Start-Process powershell.exe

# or

Start-Process pwsh.exe

In the new window run the following

Add-Type -Namespace Kernel32 -Name NativeMethods -MemberDefinition @'
[DllImport("Kernel32.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr GetCommandLineW();
'@

$linePtr = [Kernel32.NativeMethods]::GetCommandLinew()
$cmdLine = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($linePtr)

"'{0}'" -f $cmdLine

Expected behavior

'"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"'

# or

'"C:\Program Files\PowerShell\7\pwsh.exe"'

_Note: the path is dependent on where powershell is installed, this also affects v7_

Actual behavior

'"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" '

# or

'"C:\Program Files\PowerShell\7\pwsh.exe" '

The actual command line contains a trailing space. If you were to start the process with arguments the trailing space comes after the arguments. This is not a problem when using System.Diagnostics.Process directly or the call operator.

Environment data

Name                           Value
----                           -----
PSVersion                      5.1.17763.1007
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.17763.1007
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1

# or

Name                           Value
----                           -----
PSVersion                      7.0.1
PSEdition                      Core
GitCommitId                    7.0.1
OS                             Microsoft Windows 10.0.17763
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0鈥
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0
Issue-Bug Up-for-Grabs

Most helpful comment

Ah fair enough.

You may want to close https://github.com/PowerShell/PowerShell/issues/13097 as that鈥檚 a duplicate of this.

All 10 comments

After further testing, pwsh only has an issue when no arguments are supplied. WinPS has an issue even with the arguments but that's not really covered by this repo here. This is probably due to https://github.com/PowerShell/PowerShell/blob/58c371ca31d9e1b72da5d94821f2d670da5b4dfa/src/Microsoft.PowerShell.Commands.Management/commands/management/Process.cs#L2003 which has a format string of "{0} {1}".

something to do with formatting, this happens only when wrapped in quotes "'$cmdLine'" just $cmdLine works as expected.

That鈥檚 because you can鈥檛 see the space without the single quotes around it. I only did that format string to easily demonstrate what is happening.

yep correct.

and it wont be from https://github.com/PowerShell/PowerShell/blob/58c371ca31d9e1b72da5d94821f2d670da5b4dfa/src/Microsoft.PowerShell.Commands.Management/commands/management/Process.cs#L2003

targetMessage is displayed only when -WhatIf is used. This is from somewhere else.

Ah fair enough.

You may want to close https://github.com/PowerShell/PowerShell/issues/13097 as that鈥檚 a duplicate of this.

I believe the issue is in System.Diagnostics.Process as I see the same behavior that way and happens only if new windows is spawned.

I don't think so, when looking at the code PowerShell is actually calling CreateProcess here or CreateProcessWithLogon (if credentials were specified) here instead of using System.Diagnostics.Process to spawn the new process.

My guess as to what is happening is that the check for ArgumentList is true because while the ArgumentList is empty it is not null and the join operation is pretty much the same as ' ' -eq [string]::Join([String[]]@(), ' '). When the actual command line is being built it has a check on the argumentlist here. At this particular point in time arguments is equals to ' ' which is neither null or an empty string so it is appended to the command line to run. What I don't understand is why there's only 1 space, there should be 2 based on the assumption above. I don't have access to my dev environment so I can't step into the code to 100% check this but I don't think the issue lies in .NET but rather in PowerShell.

Sorry for the false alarm, just stepped through the code and I was wrong. By default Start-Process will use System.Diagnostics.Process unless other parameters are set which in this case it is not.

When running the code

$psi = [System.Diagnostics.ProcessStartInfo]@{
    FileName = 'pwsh'
    UseShellExecute = $true
}
[System.Diagnostics.Process]::Start($psi)

I see the same behaviour that Start-Process shows which indicates the problem is there and not in PowerShell. Even when forcing the code path to use the powershell call to CreateProcess by added -LoadUserProfile the command line args are fine.

My guess as to what is happening is that the check for ArgumentList is true because while the ArgumentList is empty it is not null and the join operation is pretty much the same as ' ' -eq [string]::Join([String[]]@(), ' ').

:-) Actually I was around this for hours with no luck and finally confirmed its in System.Diagnostics.Process by testing in that way...

Yea I should have listened to the experts :) thanks for your help.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

HumanEquivalentUnit picture HumanEquivalentUnit  路  3Comments

rudolfvesely picture rudolfvesely  路  3Comments

concentrateddon picture concentrateddon  路  3Comments

JohnLBevan picture JohnLBevan  路  3Comments

manofspirit picture manofspirit  路  3Comments