Fix Environment.GetCommandLineArgs()[0] to report the host invocation name - #131671
Fix Environment.GetCommandLineArgs()[0] to report the host invocation name#131671xoofx wants to merge 5 commits into
Conversation
|
Azure Pipelines: Successfully started running 4 pipeline(s). 12 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
There was a problem hiding this comment.
Pull request overview
This PR changes Environment.GetCommandLineArgs()[0] on CoreCLR to reflect the native host invocation name (argv[0]) when available, instead of the managed assembly (or single-file bundle path), by flowing the invocation name from the host to the runtime via a new host runtime property.
Changes:
- Add a new well-known host runtime property (
HOST_INVOCATION_NAME) and plumb it through hostpolicy to CoreCLR. - Update CoreCLR command-line initialization to prefer
HOST_INVOCATION_NAMEfor element 0 (with existing fallbacks). - Extend HostActivation symbolic-link testing (and the HelloWorld test asset) to validate the new behavior.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/native/corehost/hostpolicy/hostpolicy_context.h | Stores host invocation name in the hostpolicy context. |
| src/native/corehost/hostpolicy/hostpolicy_context.cpp | Serves HOST_INVOCATION_NAME via get_runtime_property and initializes it from parsed args. |
| src/native/corehost/hostpolicy/args.h | Adds invocation_name to the parsed arguments model. |
| src/native/corehost/hostpolicy/args.cpp | Captures argv[0] as the invocation name before host-mode argument parsing. |
| src/native/corehost/host_runtime_contract.h | Defines the HOST_PROPERTY_INVOCATION_NAME key. |
| src/coreclr/vm/corhost.cpp | Uses HostInformation::GetProperty(HOST_INVOCATION_NAME, …) to set Environment.GetCommandLineArgs()[0]. |
| src/coreclr/hosts/corerun/corerun.cpp | Provides HOST_INVOCATION_NAME from corerun and captures its argv[0]. |
| src/installer/tests/HostActivation.Tests/SymbolicLinks.cs | Asserts that [0] preserves the apphost invocation path and [1] remains the first managed arg. |
| src/installer/tests/Assets/Projects/HelloWorld/Program.cs | Adds a test hook to print Environment.GetCommandLineArgs() for verification. |
| docs/design/features/host-runtime-information.md | Documents the new HOST_INVOCATION_NAME property. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.
Suppressed comments (2)
src/native/corehost/hostpolicy/hostpolicy_context.cpp:131
HOST_INVOCATION_NAMEis surfaced unconditionally for non-libhost modes. Ifargv[0]is empty (which is legal on some platforms/launchers), this would make the property appear present and causeEnvironment.GetCommandLineArgs()[0]to become an empty string rather than falling back to the assembly/bundle path. Consider treating an emptyinvocation_nameas "not found" by returning -1 in that case as well.
if (::strcmp(key, HOST_PROPERTY_INVOCATION_NAME) == 0)
{
if (context->host_mode == host_mode_t::libhost)
return -1;
return pal::pal_utf8string(context->invocation_name, value_buffer, value_buffer_size);
}
src/coreclr/hosts/corerun/corerun.cpp:295
- If
corerunis launched in an unusual way whereargv[0]is the empty string, this implementation would reportHOST_INVOCATION_NAMEas present (length 1) and CoreCLR would then use an empty string forEnvironment.GetCommandLineArgs()[0]. Consider returning -1 wheninvocation_nameis empty so the runtime can fall back to the existing bundle/assembly behavior.
if (::strcmp(key, HOST_PROPERTY_INVOCATION_NAME) == 0)
{
pal::string_utf8_t value_utf8 = pal::convert_to_utf8(config->invocation_name.c_str());
size_t len = value_utf8.size() + 1;
if (value_buffer_size < len)
return len;
::strncpy(value_buffer, value_utf8.c_str(), len - 1);
value_buffer[len - 1] = '\0';
return len;
}
I do not think that this is the behavior we want. IIRC, the desired behavior we have discussed is captured in this table #101837 (comment) |
|
Fixed via 6ef8b95 Note This comment was generated with AI assistance. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 11 changed files in this pull request and generated no new comments.
Suppressed comments (1)
docs/design/features/host-runtime-information.md:45
- The new property description is a bit ambiguous about what happens when running via the
dotnetmuxer. Since the muxer doesn’t provideHOST_INVOCATION_NAME(per this doc and the updated tests), it may help to add an explicit example so readers don’t infer thatdotnet app.dll argwill makeGetCommandLineArgs()[0]becomedotnet.
`HOST_INVOCATION_NAME`
The name used to invoke an application host, corresponding to the native process's `argv[0]`. The apphost and `corerun` hosts provide this property for the first element returned by [`Environment.GetCommandLineArgs()`](https://learn.microsoft.com/dotnet/api/system.environment.getcommandlineargs). The `dotnet` muxer does not provide this property, preserving the managed application path as the first element.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.
Suppressed comments (1)
src/native/corehost/hostpolicy/args.cpp:24
- The new
argc > 0guard suggests apphost could be invoked withoutargv[0], but the rest of this function unconditionally assumes apphost has at least one argument (&argv[1],argc - 1). To avoid a misleading partial guard, either validate/return false for invalid inputs, or assert the invariant and always captureargv[0]in apphost mode.
if (init.host_mode == host_mode_t::apphost && argc > 0)
args.invocation_name = argv[0];
Hello .NET runtime folks,☺️
Back on the topic to try to fix #101837. I have used ChatGPT 5.6 Sol with guidance from the issue, my previous hacky PR and safest path analysis.
Summary
Environment.GetCommandLineArgs()[0]currently reports the managed assembly path, or the resolved bundle path for single-file applications, rather than the name used to invoke the native host. This is especially visible when an apphost is launched through a symbolic link.For apphost launches, this change preserves the native host's original
argv[0]and uses it only for element zero of the array returned byEnvironment.GetCommandLineArgs(). Thedotnetmuxer continues to use the managed application path for element zero, and all remaining elements retain their existing behavior.Implementation
HOST_INVOCATION_NAMEhost runtime property.argv[0]in hostpolicy before host-mode argument parsing.The complete native argument vector is intentionally not preserved or exposed. The resulting behavior is:
Environment.GetCommandLineArgs()dotnet.exe foo.dll 1 2 3foo.dll,1,2,3foo 1 2 3foo,1,2,3../bar/special_foo.exe 1 2 3, withspecial_foo.exesymlinked tofoo.exe../bar/special_foo.exe,1,2,3This does not change:
MainargumentsEnvironment.ProcessPathexePathTesting
HostActivation.Tests.SymbolicLinkstests successfully.dotnetandcorerun.The symbolic-link test verifies that element zero retains the original apphost invocation path while element one remains the existing managed argument.
Note
This PR description was drafted with AI assistance.