From: Eric Erhardt Date: Mon, 24 Feb 2020 22:52:08 +0000 (-0600) Subject: Allow ConfigurationManager to load when GetEntryAssembly returns null. (#32195) X-Git-Tag: submit/tizen/20210909.063632~9523 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=40d4b206233fcca34b3c8774c418668285b8f1a4;p=platform%2Fupstream%2Fdotnet%2Fruntime.git Allow ConfigurationManager to load when GetEntryAssembly returns null. (#32195) * Clean up unused variable in ClientConfigPaths. * Allow ConfigurationManager to load when GetEntryAssembly returns null. Attempt to find the native host to maintain behavior from netfx. Fix #25027 --- diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ClientConfigPaths.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ClientConfigPaths.cs index 1d37a0d..b6523ee 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ClientConfigPaths.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ClientConfigPaths.cs @@ -2,9 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; +using System.Runtime.InteropServices; using System.Security; namespace System.Configuration @@ -32,7 +34,6 @@ namespace System.Configuration _includesUserConfig = includeUserConfig; Assembly exeAssembly = null; - string applicationFilename = null; if (exePath != null) { @@ -42,39 +43,42 @@ namespace System.Configuration { throw ExceptionUtil.ParameterInvalid(nameof(exePath)); } - - applicationFilename = ApplicationUri; } else { // Exe path wasn't specified, get it from the entry assembly exeAssembly = Assembly.GetEntryAssembly(); - if (exeAssembly == null) - throw new PlatformNotSupportedException(); - - HasEntryAssembly = true; + if (exeAssembly != null) + { + HasEntryAssembly = true; - // The original .NET Framework code tried to get the local path without using Uri. - // If we ever find a need to do this again be careful with the logic. "file:///" is - // used for local paths and "file://" for UNCs. Simply removing the prefix will make - // local paths relative on Unix (e.g. "file:///home" will become "home" instead of - // "/home"). - string configBasePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, exeAssembly.ManifestModule.Name); - Uri uri = new Uri(configBasePath); + // The original .NET Framework code tried to get the local path without using Uri. + // If we ever find a need to do this again be careful with the logic. "file:///" is + // used for local paths and "file://" for UNCs. Simply removing the prefix will make + // local paths relative on Unix (e.g. "file:///home" will become "home" instead of + // "/home"). + string configBasePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, exeAssembly.ManifestModule.Name); + Uri uri = new Uri(configBasePath); - if (uri.IsFile) - { + Debug.Assert(uri.IsFile); ApplicationUri = uri.LocalPath; - applicationFilename = uri.LocalPath; } else { - ApplicationUri = Uri.EscapeDataString(configBasePath); + // An EntryAssembly may not be found when running from a custom host. + // Try to find the native entry point. + using (Process currentProcess = Process.GetCurrentProcess()) + { + ApplicationUri = currentProcess.MainModule?.FileName; + } } } - ApplicationConfigUri = ApplicationUri + ConfigExtension; + if (!string.IsNullOrEmpty(ApplicationUri)) + { + ApplicationConfigUri = ApplicationUri + ConfigExtension; + } // In the case when exePath was explicitly supplied, we will not be able to // construct user.config paths, so quit here. @@ -84,7 +88,7 @@ namespace System.Configuration if (!_includesUserConfig) return; bool isHttp = StringUtil.StartsWithOrdinalIgnoreCase(ApplicationConfigUri, HttpUri); - SetNamesAndVersion(applicationFilename, exeAssembly, isHttp); + SetNamesAndVersion(exeAssembly, isHttp); if (isHttp) return; // Create a directory suffix for local and roaming config of three parts: @@ -227,7 +231,7 @@ namespace System.Configuration return suffix; } - private void SetNamesAndVersion(string applicationFilename, Assembly exeAssembly, bool isHttp) + private void SetNamesAndVersion(Assembly exeAssembly, bool isHttp) { Type mainType = null; diff --git a/src/libraries/System.Configuration.ConfigurationManager/tests/System.Configuration.ConfigurationManager.Tests.csproj b/src/libraries/System.Configuration.ConfigurationManager/tests/System.Configuration.ConfigurationManager.Tests.csproj index 6d59c7b..ae77499 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/tests/System.Configuration.ConfigurationManager.Tests.csproj +++ b/src/libraries/System.Configuration.ConfigurationManager/tests/System.Configuration.ConfigurationManager.Tests.csproj @@ -78,6 +78,7 @@ + diff --git a/src/libraries/System.Configuration.ConfigurationManager/tests/System/Configuration/CustomHostTests.cs b/src/libraries/System.Configuration.ConfigurationManager/tests/System/Configuration/CustomHostTests.cs new file mode 100644 index 0000000..11d1add --- /dev/null +++ b/src/libraries/System.Configuration.ConfigurationManager/tests/System/Configuration/CustomHostTests.cs @@ -0,0 +1,46 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Reflection; +using System.Runtime.InteropServices; +using Microsoft.DotNet.RemoteExecutor; +using Xunit; + +namespace System.Configuration.Tests +{ + /// + /// Tests ConfigurationManager works even when Assembly.GetEntryAssembly() returns null. + /// + public class CustomHostTests + { + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Does not apply to .NET Framework.")] + public void FilePathIsPopulatedCorrectly() + { + RemoteExecutor.Invoke(() => + { + MakeAssemblyGetEntryAssemblyReturnNull(); + + string expectedFilePathEnding = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? + "dotnet.exe.config" : + "dotnet.config"; + + Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); + Assert.EndsWith(expectedFilePathEnding, config.FilePath); + }).Dispose(); + } + + /// + /// Makes Assembly.GetEntryAssembly() return null using private reflection. + /// + private static void MakeAssemblyGetEntryAssemblyReturnNull() + { + typeof(Assembly) + .GetField("s_forceNullEntryPoint", BindingFlags.NonPublic | BindingFlags.Static) + .SetValue(null, true); + + Assert.Null(Assembly.GetEntryAssembly()); + } + } +}