Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/design/features/host-runtime-information.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ Directory containing the application. This is used for [`AppContext.BaseDirector

[Runtime identifier](https://learn.microsoft.com/dotnet/core/rid-catalog) for the application. This is used for [`RuntimeInformation.RuntimeIdentifier`](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.runtimeinformation.runtimeidentifier).

`ARGV0`

The name used to invoke an application host, corresponding to the native process's `argv[0]`. The apphost provides this property for the first element returned by [`Environment.GetCommandLineArgs()`](https://learn.microsoft.com/dotnet/api/system.environment.getcommandlineargs). Muxer-style hosts, including `dotnet` and `corerun`, do not provide this property, preserving the managed application path as the first element.

### Deps files

`APP_CONTEXT_DEPS_FILES`
Expand Down
22 changes: 10 additions & 12 deletions src/coreclr/vm/corhost.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#endif // !TARGET_UNIX

#include "nativelibrary.h"
#include "hostinformation.h"

#ifndef DACCESS_COMPILE

Expand Down Expand Up @@ -217,17 +218,11 @@ HRESULT CorHost2::ExecuteApplication(LPCWSTR pwzAppFullName,
}

/*
* This method processes the arguments sent to the host which are then used
* to invoke the main method.
* Note -
* [0] - points to the assemblyName that has been sent by the host.
* The rest are the arguments sent to the assembly.
* Also note, this might not always return the exact same identity as the cmdLine
* used to invoke the method.
*
* For example :-
* ActualCmdLine - Foo arg1 arg2.
* (Host1) - Full_path_to_Foo arg1 arg2
* This method constructs the array returned by Environment.GetCommandLineArgs().
* The first element is passed separately from argv as exePath and uses the native
* invocation name when provided by the host. Otherwise, the assembly or bundle path
* is used. The remaining elements come from argv, which contains the arguments to the
* main method.
*/
static PTRARRAYREF SetCommandLineArgs(PCWSTR pwzAssemblyPath, int argc, PCWSTR* argv)
{
Expand All @@ -242,7 +237,10 @@ static PTRARRAYREF SetCommandLineArgs(PCWSTR pwzAssemblyPath, int argc, PCWSTR*
// Record the command line.
SaveManagedCommandLine(pwzAssemblyPath, argc, argv);

PCWSTR exePath = Bundle::AppIsBundle() ? static_cast<PCWSTR>(Bundle::AppBundle->Path()) : pwzAssemblyPath;
StackSString invocationName;
PCWSTR exePath = HostInformation::GetProperty(HOST_PROPERTY_ARGV0, invocationName)
? invocationName.GetUnicode()
: (Bundle::AppIsBundle() ? static_cast<PCWSTR>(Bundle::AppBundle->Path()) : pwzAssemblyPath);

PTRARRAYREF result = NULL;
GCPROTECT_BEGIN(result);
Expand Down
7 changes: 7 additions & 0 deletions src/installer/tests/Assets/Projects/HelloWorld/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ public static void Main(string[] args)
Console.WriteLine($"AppContext.GetData({propertyName}) = {propertyValue}");
}
break;
case "print_command_line_args":
string[] commandLineArgs = Environment.GetCommandLineArgs();
for (int i = 0; i < commandLineArgs.Length; i++)
{
Console.WriteLine($"Environment.GetCommandLineArgs()[{i}] = {commandLineArgs[i]}");
}
break;
case "throw_exception":
// Disable core dumps - test is intentionally crashing
Utilities.CoreDump.Disable();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,23 @@ public void Muxer_Default()
var dotnet = HostTestContext.BuiltDotNet;
var appDll = sharedTestState.App.AppDll;

dotnet.Exec(appDll)
dotnet.Exec(appDll, "print_command_line_args")
.CaptureStdErr()
.CaptureStdOut()
.Execute()
.Should().Pass()
.And.HaveStdOutContaining("Hello World");
.And.HaveStdOutContaining("Hello World")
.And.HaveStdOutContaining($"Environment.GetCommandLineArgs()[0] = {appDll}")
.And.HaveStdOutContaining("Environment.GetCommandLineArgs()[1] = print_command_line_args");

dotnet.Exec("exec", appDll)
dotnet.Exec("exec", appDll, "print_command_line_args")
.CaptureStdErr()
.CaptureStdOut()
.Execute()
.Should().Pass()
.And.HaveStdOutContaining("Hello World");
.And.HaveStdOutContaining("Hello World")
.And.HaveStdOutContaining($"Environment.GetCommandLineArgs()[0] = {appDll}")
.And.HaveStdOutContaining("Environment.GetCommandLineArgs()[1] = print_command_line_args");
}

[Fact]
Expand Down
7 changes: 5 additions & 2 deletions src/installer/tests/HostActivation.Tests/SymbolicLinks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ public void Symlink_all_files_fx(string symlinkRelativePath)
symlinks.Add(new SymLink(symlinkPath, file));
}

var result = Command.Create(Path.Combine(testDir.Location, symlinkRelativePath, Path.GetFileName(sharedTestState.FrameworkDependentApp.AppExe)))
string appHostPath = Path.Combine(testDir.Location, symlinkRelativePath, Path.GetFileName(sharedTestState.FrameworkDependentApp.AppExe));
var result = Command.Create(appHostPath, "print_command_line_args")
.CaptureStdErr()
.CaptureStdOut()
.DotNetRoot(HostTestContext.BuiltDotNet.BinPath)
Expand All @@ -54,7 +55,9 @@ public void Symlink_all_files_fx(string symlinkRelativePath)
// * Unix: The apphost will look next to the resolved apphost for the app dll and find the real thing
result
.Should().Pass()
.And.HaveStdOutContaining("Hello World");
.And.HaveStdOutContaining("Hello World")
.And.HaveStdOutContaining($"Environment.GetCommandLineArgs()[0] = {appHostPath}")
.And.HaveStdOutContaining("Environment.GetCommandLineArgs()[1] = print_command_line_args");
}
finally
{
Expand Down
1 change: 1 addition & 0 deletions src/native/corehost/host_runtime_contract.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#define HOST_PROPERTY_BUNDLE_PROBE "BUNDLE_PROBE"
#define HOST_PROPERTY_BUNDLE_EXTRACTION_PATH "BUNDLE_EXTRACTION_PATH"
#define HOST_PROPERTY_ENTRY_ASSEMBLY_NAME "ENTRY_ASSEMBLY_NAME"
#define HOST_PROPERTY_ARGV0 "ARGV0"
#define HOST_PROPERTY_NATIVE_DLL_SEARCH_DIRECTORIES "NATIVE_DLL_SEARCH_DIRECTORIES"
#define HOST_PROPERTY_PINVOKE_OVERRIDE "PINVOKE_OVERRIDE"
#define HOST_PROPERTY_PLATFORM_RESOURCE_ROOTS "PLATFORM_RESOURCE_ROOTS"
Expand Down
3 changes: 3 additions & 0 deletions src/native/corehost/hostpolicy/args.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ bool parse_arguments(
pal::string_t managed_application_path;
if (init.host_mode == host_mode_t::apphost)
{
assert(argc > 0 && argv != nullptr);
args.invocation_name = argv[0];

// Find the managed app in the same directory
managed_application_path = init.host_info.app_path;

Expand Down
1 change: 1 addition & 0 deletions src/native/corehost/hostpolicy/args.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ struct arguments_t
host_mode_t host_mode;
pal::string_t app_root;
pal::string_t deps_path;
pal::string_t invocation_name;
pal::string_t managed_application;

int app_argc;
Expand Down
9 changes: 9 additions & 0 deletions src/native/corehost/hostpolicy/hostpolicy_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,14 @@ namespace
return pal::pal_utf8string(get_filename_without_ext(context->application), value_buffer, value_buffer_size);
}

if (::strcmp(key, HOST_PROPERTY_ARGV0) == 0)
{
if (context->invocation_name.empty())
return -1;

return pal::pal_utf8string(context->invocation_name, value_buffer, value_buffer_size);
}

if (::strcmp(key, HOST_PROPERTY_BUNDLE_EXTRACTION_PATH) == 0)
{
if (!bundle::info_t::is_single_file_bundle())
Expand Down Expand Up @@ -167,6 +175,7 @@ int hostpolicy_context_t::initialize(const hostpolicy_init_t &hostpolicy_init, c
application = args.managed_application;
host_mode = hostpolicy_init.host_mode;
host_path = hostpolicy_init.host_info.host_path;
invocation_name = args.invocation_name;
breadcrumbs_enabled = enable_breadcrumbs;

deps_json_t::rid_resolution_options_t rid_resolution_options
Expand Down
1 change: 1 addition & 0 deletions src/native/corehost/hostpolicy/hostpolicy_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ struct hostpolicy_context_t
pal::string_t clr_path;
host_mode_t host_mode;
pal::string_t host_path;
pal::string_t invocation_name;

bool breadcrumbs_enabled;
mutable std::unordered_set<pal::string_t> breadcrumbs;
Expand Down