Do not use '.NET Core' anymore in error messages (#68260)
authorVitek Karas <10670590+vitek-karas@users.noreply.github.com>
Mon, 25 Apr 2022 21:16:47 +0000 (14:16 -0700)
committerGitHub <noreply@github.com>
Mon, 25 Apr 2022 21:16:47 +0000 (14:16 -0700)
Added a test for a single-file FDD running over old hostfxr

Includes apphost version, .NET location and so on in error message. Similar to other improved error messages now.
Includes the apphost version and architecture in the UI dialog, just like we do if there's no runtime at all.

src/installer/tests/HostActivation.Tests/PortableAppActivation.cs
src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundledAppWithSubDirs.cs
src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/StaticHost.cs
src/native/corehost/apphost/apphost.windows.cpp
src/native/corehost/corehost.cpp

index b8fced8..9bdf4f7 100644 (file)
@@ -609,7 +609,9 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation
                 command.WaitForExit(true)
                     .Should().Fail()
                     .And.HaveStdErrContaining($"Showing error dialog for application: '{Path.GetFileName(appExe)}' - error code: 0x{expectedErrorCode}")
-                    .And.HaveStdErrContaining("To run this application, you need to install a newer version of .NET");
+                    .And.HaveStdErrContaining("You must install or update .NET to run this application.")
+                    .And.HaveStdErrContaining("App host version:")
+                    .And.HaveStdErrContaining("apphost_version=");
             }
         }
 
index 9442777..cf4f483 100644 (file)
@@ -4,6 +4,7 @@
 using System;
 using System.IO;
 using BundleTests.Helpers;
+using Microsoft.DotNet.Cli.Build;
 using Microsoft.DotNet.Cli.Build.Framework;
 using Microsoft.DotNet.CoreSetup.Test;
 using Microsoft.NET.HostModel.Bundle;
@@ -22,18 +23,22 @@ namespace AppHost.Bundle.Tests
 
         private void RunTheApp(string path, TestProjectFixture fixture)
         {
-            Command.Create(path)
-                .EnableTracingAndCaptureOutputs()
-                .EnvironmentVariable("DOTNET_ROOT", fixture.BuiltDotnet.BinPath)
-                .EnvironmentVariable("DOTNET_ROOT(x86)", fixture.BuiltDotnet.BinPath)
-                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
-                .Execute()
+            RunTheApp(path, fixture.BuiltDotnet)
                 .Should()
                 .Pass()
                 .And
                 .HaveStdOutContaining("Wow! We now say hello to the big world and you.");
         }
 
+        private CommandResult RunTheApp(string path, DotNetCli dotnet)
+        {
+            return Command.Create(path)
+                .EnableTracingAndCaptureOutputs()
+                .DotNetRoot(dotnet.BinPath)
+                .MultilevelLookup(false)
+                .Execute();
+        }
+
         [InlineData(BundleOptions.None)]
         [InlineData(BundleOptions.BundleNativeBinaries)]
         [InlineData(BundleOptions.BundleAllContent)]
@@ -51,6 +56,32 @@ namespace AppHost.Bundle.Tests
             RunTheApp(singleFile, fixture);
         }
 
+        [Fact]
+        public void Bundle_Framework_dependent_NoBundleEntryPoint()
+        {
+            var fixture = sharedTestState.TestFrameworkDependentFixture.Copy();
+            UseFrameworkDependentHost(fixture);
+            var singleFile = BundleHelper.BundleApp(fixture, BundleOptions.None);
+
+            string dotnetWithMockHostFxr = SharedFramework.CalculateUniqueTestDirectory(Path.Combine(TestArtifact.TestArtifactsPath, "guiErrors"));
+            using (new TestArtifact(dotnetWithMockHostFxr))
+            {
+                Directory.CreateDirectory(dotnetWithMockHostFxr);
+                var dotnetBuilder = new DotNetBuilder(dotnetWithMockHostFxr, sharedTestState.RepoDirectories.BuiltDotnet, "mockhostfxrFrameworkMissingFailure")
+                    .RemoveHostFxr()
+                    .AddMockHostFxr(new Version(2, 2, 0));
+                var dotnet = dotnetBuilder.Build();
+
+                // Run the bundled app (extract files)
+                RunTheApp(singleFile, dotnet)
+                    .Should()
+                    .Fail()
+                    .And.HaveStdErrContaining("You must install or update .NET to run this application.")
+                    .And.HaveStdErrContaining("App host version:")
+                    .And.HaveStdErrContaining("apphost_version=");
+            }
+        }
+
         [InlineData(BundleOptions.None)]
         [InlineData(BundleOptions.BundleNativeBinaries)]
         [InlineData(BundleOptions.BundleAllContent)]
index 71d365e..084a04d 100644 (file)
@@ -18,7 +18,7 @@ namespace AppHost.Bundle.Tests
         }
 
         [Fact]
-        private void Can_Run_App_With_StatiHost()
+        private void Can_Run_App_With_StaticHost()
         {
             var fixture = sharedTestState.TestFixture.Copy();
 
@@ -35,7 +35,7 @@ namespace AppHost.Bundle.Tests
         }
 
         [Fact]
-        private void Can_Run_SingleFile_App_With_StatiHost()
+        private void Can_Run_SingleFile_App_With_StaticHost()
         {
             var fixture = sharedTestState.TestFixture.Copy();
 
index a4fd290..c43f610 100644 (file)
@@ -70,16 +70,22 @@ namespace
         return false;
     }
 
-    pal::string_t get_runtime_not_found_message()
+    pal::string_t get_apphost_details_message()
     {
-        pal::string_t msg = INSTALL_NET_DESKTOP_ERROR_MESSAGE _X("\n\n")
-            _X("Architecture: ");
+        pal::string_t msg = _X("Architecture: ");
         msg.append(get_arch());
         msg.append(_X("\n")
             _X("App host version: ") _STRINGIFY(COMMON_HOST_PKG_VER) _X("\n\n"));
         return msg;
     }
 
+    pal::string_t get_runtime_not_found_message()
+    {
+        pal::string_t msg = INSTALL_NET_DESKTOP_ERROR_MESSAGE _X("\n\n");
+        msg.append(get_apphost_details_message());
+        return msg;
+    }
+
     void show_error_dialog(const pal::char_t *executable_name, int error_code)
     {
         pal::string_t gui_errors_disabled;
@@ -108,6 +114,7 @@ namespace
             dialogMsg = pal::string_t(INSTALL_OR_UPDATE_NET_ERROR_MESSAGE _X("\n\n"));
             pal::string_t line;
             pal::stringstream_t ss(g_buffered_errors);
+            bool foundCustomMessage = false;
             while (std::getline(ss, line, _X('\n')))
             {
                 const pal::char_t prefix[] = _X("Framework: '");
@@ -119,18 +126,23 @@ namespace
                 {
                     dialogMsg.append(line);
                     dialogMsg.append(_X("\n\n"));
+                    foundCustomMessage = true;
                 }
                 else if (utils::starts_with(line, custom_prefix, true))
                 {
                     dialogMsg.erase();
                     dialogMsg.append(line.substr(utils::strlen(custom_prefix)));
                     dialogMsg.append(_X("\n\n"));
+                    foundCustomMessage = true;
                 }
                 else if (try_get_url_from_line(line, url))
                 {
                     break;
                 }
             }
+
+            if (!foundCustomMessage)
+                dialogMsg.append(get_apphost_details_message());
         }
         else if (error_code == StatusCode::BundleExtractionFailure)
         {
index 907efcb..e8fa786 100644 (file)
@@ -84,12 +84,27 @@ bool is_exe_enabled_for_execution(pal::string_t* app_dll)
 #define CURHOST_EXE
 #endif
 
-void need_newer_framework_error()
+void need_newer_framework_error(const pal::string_t& dotnet_root, const pal::string_t& host_path)
 {
-    pal::string_t url = get_download_url();
-    trace::error(_X("  _ To run this application, you need to install a newer version of .NET Core."));
-    trace::error(_X(""));
-    trace::error(_X("  - %s&apphost_version=%s"), url.c_str(), _STRINGIFY(COMMON_HOST_PKG_VER));
+    trace::error(
+        INSTALL_OR_UPDATE_NET_ERROR_MESSAGE
+        _X("\n\n")
+        _X("App: %s\n")
+        _X("Architecture: %s\n")
+        _X("App host version: %s\n")
+        _X(".NET location: %s\n")
+        _X("\n")
+        _X("Learn about runtime installation:\n")
+        DOTNET_APP_LAUNCH_FAILED_URL
+        _X("\n\n")
+        _X("Download the .NET runtime:\n")
+        _X("%s&apphost_version=%s"),
+        host_path.c_str(),
+        get_arch(),
+        _STRINGIFY(COMMON_HOST_PKG_VER),
+        dotnet_root.c_str(),
+        get_download_url().c_str(),
+        _STRINGIFY(COMMON_HOST_PKG_VER));
 }
 
 #if defined(CURHOST_EXE)
@@ -211,7 +226,7 @@ int exe_start(const int argc, const pal::char_t* argv[])
         {
             // An outdated hostfxr can only be found for framework-related apps.
             trace::error(_X("The required library %s does not support single-file apps."), fxr.fxr_path().c_str());                    
-            need_newer_framework_error();
+            need_newer_framework_error(fxr.dotnet_root(), host_path);
             rc = StatusCode::FrameworkMissingFailure;
         }
     }
@@ -235,10 +250,12 @@ int exe_start(const int argc, const pal::char_t* argv[])
 
             rc = hostfxr_main_startupinfo(argc, argv, host_path_cstr, dotnet_root_cstr, app_path_cstr);
 
-            // This check exists to provide an error message for UI apps when running 3.0 apps on 2.0 only hostfxr, which doesn't support error writer redirection. 
+            // This check exists to provide an error message for apps when running 3.0 apps on 2.0 only hostfxr, which doesn't support error writer redirection.
+            // Note that this is not only for UI apps - on Windows we always write errors to event log as well (regardless of UI) and it uses
+            // the same mechanism of redirecting error writers.
             if (trace::get_error_writer() != nullptr && rc == static_cast<int>(StatusCode::FrameworkMissingFailure) && set_error_writer == nullptr)
             {
-                need_newer_framework_error();
+                need_newer_framework_error(fxr.dotnet_root(), host_path);
             }
         }
 #if !defined(FEATURE_STATIC_HOST)