// 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
_includesUserConfig = includeUserConfig;
Assembly exeAssembly = null;
- string applicationFilename = null;
if (exePath != null)
{
{
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.
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:
return suffix;
}
- private void SetNamesAndVersion(string applicationFilename, Assembly exeAssembly, bool isHttp)
+ private void SetNamesAndVersion(Assembly exeAssembly, bool isHttp)
{
Type mainType = null;
--- /dev/null
+// 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
+{
+ /// <summary>
+ /// Tests ConfigurationManager works even when Assembly.GetEntryAssembly() returns null.
+ /// </summary>
+ 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();
+ }
+
+ /// <summary>
+ /// Makes Assembly.GetEntryAssembly() return null using private reflection.
+ /// </summary>
+ private static void MakeAssemblyGetEntryAssemblyReturnNull()
+ {
+ typeof(Assembly)
+ .GetField("s_forceNullEntryPoint", BindingFlags.NonPublic | BindingFlags.Static)
+ .SetValue(null, true);
+
+ Assert.Null(Assembly.GetEntryAssembly());
+ }
+ }
+}