Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 40d4b20

Browse files
authored
Allow ConfigurationManager to load when GetEntryAssembly returns null. (dotnet#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 dotnet#25027
1 parent 9449633 commit 40d4b20

File tree

3 files changed

+72
-21
lines changed

3 files changed

+72
-21
lines changed

src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ClientConfigPaths.cs

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5+
using System.Diagnostics;
56
using System.Globalization;
67
using System.IO;
78
using System.Reflection;
9+
using System.Runtime.InteropServices;
810
using System.Security;
911

1012
namespace System.Configuration
@@ -32,7 +34,6 @@ private ClientConfigPaths(string exePath, bool includeUserConfig)
3234
_includesUserConfig = includeUserConfig;
3335

3436
Assembly exeAssembly = null;
35-
string applicationFilename = null;
3637

3738
if (exePath != null)
3839
{
@@ -42,39 +43,42 @@ private ClientConfigPaths(string exePath, bool includeUserConfig)
4243
{
4344
throw ExceptionUtil.ParameterInvalid(nameof(exePath));
4445
}
45-
46-
applicationFilename = ApplicationUri;
4746
}
4847
else
4948
{
5049
// Exe path wasn't specified, get it from the entry assembly
5150
exeAssembly = Assembly.GetEntryAssembly();
5251

53-
if (exeAssembly == null)
54-
throw new PlatformNotSupportedException();
55-
56-
HasEntryAssembly = true;
52+
if (exeAssembly != null)
53+
{
54+
HasEntryAssembly = true;
5755

58-
// The original .NET Framework code tried to get the local path without using Uri.
59-
// If we ever find a need to do this again be careful with the logic. "file:///" is
60-
// used for local paths and "file://" for UNCs. Simply removing the prefix will make
61-
// local paths relative on Unix (e.g. "file:///home" will become "home" instead of
62-
// "/home").
63-
string configBasePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, exeAssembly.ManifestModule.Name);
64-
Uri uri = new Uri(configBasePath);
56+
// The original .NET Framework code tried to get the local path without using Uri.
57+
// If we ever find a need to do this again be careful with the logic. "file:///" is
58+
// used for local paths and "file://" for UNCs. Simply removing the prefix will make
59+
// local paths relative on Unix (e.g. "file:///home" will become "home" instead of
60+
// "/home").
61+
string configBasePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, exeAssembly.ManifestModule.Name);
62+
Uri uri = new Uri(configBasePath);
6563

66-
if (uri.IsFile)
67-
{
64+
Debug.Assert(uri.IsFile);
6865
ApplicationUri = uri.LocalPath;
69-
applicationFilename = uri.LocalPath;
7066
}
7167
else
7268
{
73-
ApplicationUri = Uri.EscapeDataString(configBasePath);
69+
// An EntryAssembly may not be found when running from a custom host.
70+
// Try to find the native entry point.
71+
using (Process currentProcess = Process.GetCurrentProcess())
72+
{
73+
ApplicationUri = currentProcess.MainModule?.FileName;
74+
}
7475
}
7576
}
7677

77-
ApplicationConfigUri = ApplicationUri + ConfigExtension;
78+
if (!string.IsNullOrEmpty(ApplicationUri))
79+
{
80+
ApplicationConfigUri = ApplicationUri + ConfigExtension;
81+
}
7882

7983
// In the case when exePath was explicitly supplied, we will not be able to
8084
// construct user.config paths, so quit here.
@@ -84,7 +88,7 @@ private ClientConfigPaths(string exePath, bool includeUserConfig)
8488
if (!_includesUserConfig) return;
8589

8690
bool isHttp = StringUtil.StartsWithOrdinalIgnoreCase(ApplicationConfigUri, HttpUri);
87-
SetNamesAndVersion(applicationFilename, exeAssembly, isHttp);
91+
SetNamesAndVersion(exeAssembly, isHttp);
8892
if (isHttp) return;
8993

9094
// Create a directory suffix for local and roaming config of three parts:
@@ -227,7 +231,7 @@ private static string GetTypeAndHashSuffix(string exePath)
227231
return suffix;
228232
}
229233

230-
private void SetNamesAndVersion(string applicationFilename, Assembly exeAssembly, bool isHttp)
234+
private void SetNamesAndVersion(Assembly exeAssembly, bool isHttp)
231235
{
232236
Type mainType = null;
233237

src/libraries/System.Configuration.ConfigurationManager/tests/System.Configuration.ConfigurationManager.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@
7878
<Compile Include="System\Configuration\ConfigurationElementCollectionTests.cs" />
7979
<Compile Include="System\Configuration\ConfigurationElementTests.cs" />
8080
<Compile Include="System\Configuration\ConfigurationPropertyAttributeTests.cs" />
81+
<Compile Include="System\Configuration\CustomHostTests.cs" />
8182
<Compile Include="System\Configuration\ConfigurationPropertyTests.cs" />
8283
<Compile Include="System\Configuration\ConfigurationTests.cs" />
8384
<Compile Include="System\Configuration\BasicCustomSectionTests.cs" />
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System.Reflection;
6+
using System.Runtime.InteropServices;
7+
using Microsoft.DotNet.RemoteExecutor;
8+
using Xunit;
9+
10+
namespace System.Configuration.Tests
11+
{
12+
/// <summary>
13+
/// Tests ConfigurationManager works even when Assembly.GetEntryAssembly() returns null.
14+
/// </summary>
15+
public class CustomHostTests
16+
{
17+
[Fact]
18+
[SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Does not apply to .NET Framework.")]
19+
public void FilePathIsPopulatedCorrectly()
20+
{
21+
RemoteExecutor.Invoke(() =>
22+
{
23+
MakeAssemblyGetEntryAssemblyReturnNull();
24+
25+
string expectedFilePathEnding = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
26+
"dotnet.exe.config" :
27+
"dotnet.config";
28+
29+
Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
30+
Assert.EndsWith(expectedFilePathEnding, config.FilePath);
31+
}).Dispose();
32+
}
33+
34+
/// <summary>
35+
/// Makes Assembly.GetEntryAssembly() return null using private reflection.
36+
/// </summary>
37+
private static void MakeAssemblyGetEntryAssemblyReturnNull()
38+
{
39+
typeof(Assembly)
40+
.GetField("s_forceNullEntryPoint", BindingFlags.NonPublic | BindingFlags.Static)
41+
.SetValue(null, true);
42+
43+
Assert.Null(Assembly.GetEntryAssembly());
44+
}
45+
}
46+
}

0 commit comments

Comments
 (0)