dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.

Home Page:https://docs.microsoft.com/dotnet/core/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Starting a .NET process with CreateProcessW using lpApplicationName ends up in messed up commandline args

MichalPetryka opened this issue · comments

Description

Starting a .NET process with CreateProcessW using lpApplicationName for the executable results in all the args APIs in .NET treating the 1st commandline arg as the executable name and skipping it for Main args. The real executable name isn't present anywhere.
I'm not sure how exactly does using CreateProcessW like this affect the OS APIs but on the managed side this behaviour is counterintuitive.

Reproduction Steps

Launch this code with CreateProcessW by passing the executable in lpApplicationName and some args like "123":

Console.WriteLine(JsonSerializer.Serialize(args));
Console.WriteLine(JsonSerializer.Serialize(Environment.GetCommandLineArgs()));
Console.WriteLine(JsonSerializer.Serialize(Environment.CommandLine.Split(' ')));

Expected behavior

Outputs:

[
  "123"
]
[
  "ConsoleApp1.exe",
  "123"
]
[
  "ConsoleApp1.exe",
  "123"
]

Actual behavior

Outputs:

[]
[
  "123"
]
[
  "123"
]

Regression?

Doesn't seem so, .NET Framework behaves the same.

Known Workarounds

Don't use lpApplicationName, pass the executable in lpCommandLine instead.

Configuration

.NET 9
Windows 10.0.19045 x64

Other information

No response

There likely isn't anything for .NET to do here and this is instead a quirk of how native works.

If you use the lpApplicationName parameter then it isn't part of the lpCommandLine.

It could potentially try and detect if the executable is part of the command line, but that might not be possible or trivial to do, especially not without cost.

I expect that you would see identical behavior in C/C++

Tagging subscribers to this area: @vitek-karas, @agocke, @VSadov
See info in area-owners.md if you want to be subscribed.

You are expected to include the executable name in the lpCommandLine argument for CreateProcessA/W. https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa suggests that by saying "Because argv[0] is the module name, C programmers generally repeat the module name as the first token in the command line.".

It is by design that the launched binary can be different from the argv[0] command line arguments. It allows Unix-like tricks like having multiple commands all pointing to the same binary. We have actually regressed this behavior in .NET Core and these tricks are not possible in .NET Core today. #101837 tracks fixing this regression.

It could potentially try and detect if the executable is part of the command line, but that might not be possible or trivial to do, especially not without cost.

It would make fixing #101837 impossible.