Skip to content

Fix Environment.GetCommandLineArgs()[0] to report the host invocation name - #131671

Open
xoofx wants to merge 5 commits into
dotnet:mainfrom
xoofx:fix-environment-argv0
Open

Fix Environment.GetCommandLineArgs()[0] to report the host invocation name#131671
xoofx wants to merge 5 commits into
dotnet:mainfrom
xoofx:fix-environment-argv0

Conversation

@xoofx

@xoofx xoofx commented Jul 31, 2026

Copy link
Copy Markdown
Member

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 by Environment.GetCommandLineArgs(). The dotnet muxer continues to use the managed application path for element zero, and all remaining elements retain their existing behavior.

Implementation

  • Adds the well-known HOST_INVOCATION_NAME host runtime property.
  • Captures the apphost argv[0] in hostpolicy before host-mode argument parsing.
  • Queries the property from CoreCLR when initializing the managed command-line array.
  • Retains the existing assembly/bundle-path fallback when a host does not provide the property.
  • Documents the new host runtime property.

The complete native argument vector is intentionally not preserved or exposed. The resulting behavior is:

Command line Environment.GetCommandLineArgs()
dotnet.exe foo.dll 1 2 3 foo.dll, 1, 2, 3
foo 1 2 3 foo, 1, 2, 3
../bar/special_foo.exe 1 2 3, with special_foo.exe symlinked to foo.exe ../bar/special_foo.exe, 1, 2, 3

This does not change:

  • Managed Main arguments
  • Environment.ProcessPath
  • Bundle probing or resolution
  • Native hosting API signatures
  • The meaning of exePath
  • Behavior for hosts that do not implement the new property

Testing

  • Built Checked CoreCLR.
  • Built the libraries and Checked host components/host tests.
  • Ran the focused muxer command-line test and all 19 HostActivation.Tests.SymbolicLinks tests successfully.
  • Manually verified the resulting command-line arrays with both dotnet and corerun.

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.

Copilot AI review requested due to automatic review settings July 31, 2026 19:56
@dotnet-policy-service dotnet-policy-service Bot added the community-contribution Indicates that the PR has been added by a community member label Jul 31, 2026
@azure-pipelines

Copy link
Copy Markdown
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.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_NAME for 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.

Comment thread src/coreclr/vm/corhost.cpp
Copilot AI review requested due to automatic review settings July 31, 2026 20:06

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_NAME is surfaced unconditionally for non-libhost modes. If argv[0] is empty (which is legal on some platforms/launchers), this would make the property appear present and cause Environment.GetCommandLineArgs()[0] to become an empty string rather than falling back to the assembly/bundle path. Consider treating an empty invocation_name as "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 corerun is launched in an unusual way where argv[0] is the empty string, this implementation would report HOST_INVOCATION_NAME as present (length 1) and CoreCLR would then use an empty string for Environment.GetCommandLineArgs()[0]. Consider returning -1 when invocation_name is 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;
    }

@jkotas

jkotas commented Jul 31, 2026

Copy link
Copy Markdown
Member

The complete native argument vector is intentionally not preserved or exposed. For example:

dotnet app.dll arg

now produces an Environment.GetCommandLineArgs() array equivalent to:

[ "dotnet", "arg" ]

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)

Copilot AI review requested due to automatic review settings July 31, 2026 21:12
@xoofx

xoofx commented Jul 31, 2026

Copy link
Copy Markdown
Member Author

Fixed via 6ef8b95

Note

This comment was generated with AI assistance.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 dotnet muxer. Since the muxer doesn’t provide HOST_INVOCATION_NAME (per this doc and the updated tests), it may help to add an explicit example so readers don’t infer that dotnet app.dll arg will make GetCommandLineArgs()[0] become dotnet.
`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.

Comment thread src/coreclr/hosts/corerun/corerun.cpp Outdated
Copilot AI review requested due to automatic review settings August 1, 2026 06:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 > 0 guard suggests apphost could be invoked without argv[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 capture argv[0] in apphost mode.
    if (init.host_mode == host_mode_t::apphost && argc > 0)
        args.invocation_name = argv[0];

Copilot AI review requested due to automatic review settings August 1, 2026 06:58

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-Host community-contribution Indicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Environment.ProcessPath / Environment.GetCommandLineArgs()[0] discrepancies

3 participants