Skip to content

Commit de243ed

Browse files
committed
Updated assembly resolvers to use Assembly.Load instead of Assembly.LoadFrom
1 parent 02c2a18 commit de243ed

File tree

8 files changed

+100
-49
lines changed

8 files changed

+100
-49
lines changed

ConsoleTester/Program.cs

+9-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Reflection;
66
using System.Text;
77
using System.Threading.Tasks;
8+
using Xarial.XToolkit;
89
using Xarial.XToolkit.Helpers;
910
using Xarial.XToolkit.Reflection;
1011

@@ -25,7 +26,14 @@ static Program()
2526
System.Diagnostics.Debugger.Launch();
2627

2728
m_AssmResolver = new AssemblyResolver(AppDomain.CurrentDomain);
28-
m_AssmResolver.RegisterAssemblyReferenceResolver(new CustomAppConfigBindingRedirectReferenceResolver());
29+
//m_AssmResolver.RegisterAssemblyReferenceResolver(new CustomAppConfigBindingRedirectReferenceResolver());
30+
31+
var localPath = Path.GetDirectoryName(typeof(Program).Assembly.Location);
32+
33+
m_AssmResolver.RegisterAssemblyReferenceResolver(
34+
new LocalFolderReferencesResolver(FileSystemUtils.CombinePaths(localPath, @"..\..\..\Lib\bin\Debug"),
35+
AssemblyMatchFilter_e.PublicKeyToken | AssemblyMatchFilter_e.Culture, "", null,
36+
new string[] { localPath }));
2937
}
3038

3139
static void Main(string[] args)

ConsoleTester/README.md

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# How To Test AssemblyResolver
22

3+
## CustomAppConfigBindingRedirectReferenceResolver
4+
35
* Compile the **ConsoleTester** project. This project is referencing **Lib** dll with version 1.0.0.0
46
* Copy and overwrite the files from the **!** folder into the build folder
57
* **Lib.dll** reference is of version 1.1.0.0
@@ -21,4 +23,10 @@
2123
</configuration>
2224
~~~
2325
* Run the **ConsoleTester.exe** from the Windows File Explorer and attach to the process to debug
24-
* This is done so the default build process is not overriding the target files
26+
* This is done so the default build process is not overriding the target files
27+
28+
## LocalFolderReferencesResolver
29+
30+
* Compile the **ConsoleTester** project. This project is referencing **Lib** dll with version 1.0.0.0
31+
* Start the project
32+
* Delete the **Lib.dll** from the compile folder when program start (within static method or on the **Debugger.Launch**)

Tests/Utils.Tests/Utils.Tests.csproj

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
</PropertyGroup>
77

88
<ItemGroup>
9-
<PackageReference Include="nunit" Version="3.13.3" />
10-
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
11-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
9+
<PackageReference Include="nunit" Version="3.14.0" />
10+
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
11+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
1212
</ItemGroup>
1313

1414
<ItemGroup>

Tests/Wpf.Tests/Wpf.Tests.csproj

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
</PropertyGroup>
77

88
<ItemGroup>
9-
<PackageReference Include="nunit" Version="3.13.3" />
10-
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
11-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
9+
<PackageReference Include="nunit" Version="3.14.0" />
10+
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
11+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
1212
</ItemGroup>
1313

1414
<ItemGroup>

src/Utils/Helpers/AssemblyResolver.cs

+6-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public interface IReferenceResolver
3535
/// <summary>
3636
/// This is a helper class allowing to specify strategies for resolving the missing dlls
3737
/// </summary>
38-
public class AssemblyResolver
38+
public class AssemblyResolver : IDisposable
3939
{
4040
private readonly List<IReferenceResolver> m_AssemblyResolvers;
4141
private readonly AppDomain m_AppDomain;
@@ -94,5 +94,10 @@ private Assembly OnResolveMissingAssembly(object sender, ResolveEventArgs args)
9494

9595
return null;
9696
}
97+
98+
public void Dispose()
99+
{
100+
m_AppDomain.AssemblyResolve -= OnResolveMissingAssembly;
101+
}
97102
}
98103
}

src/Utils/Reflection/AppConfigBindingRedirectReferenceResolver.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public AppConfigBindingRedirectReferenceResolver() : this("")
2626
{
2727
}
2828

29-
public AppConfigBindingRedirectReferenceResolver(string name) : base(name)
29+
public AppConfigBindingRedirectReferenceResolver(string name, string[] filterDirs = null) : base(name, filterDirs)
3030
{
3131
}
3232

src/Utils/Reflection/AssemblyNameReferenceResolver.cs

+68-38
Original file line numberDiff line numberDiff line change
@@ -52,81 +52,111 @@ internal AssemblyInfo(AssemblyName name, string filePath, bool isLoaded)
5252
/// <remarks>Used in the logs</remarks>
5353
public string Name { get; }
5454

55+
private readonly string[] m_FilterDirs;
56+
5557
/// <summary>
5658
/// Default constructor
5759
/// </summary>
5860
/// <param name="name">Name of the resolver</param>
59-
public AssemblyNameReferenceResolver(string name)
61+
/// <param name="filterDirs">Only resolve the assembly if requesting assembly is in the specified directories</param>
62+
public AssemblyNameReferenceResolver(string name, string[] filterDirs = null)
6063
{
6164
if (string.IsNullOrEmpty(name))
6265
{
6366
name = this.GetType().FullName;
6467
}
6568

6669
Name = name;
70+
71+
m_FilterDirs = filterDirs;
6772
}
6873

6974
/// <inheritdoc/>>
7075
public virtual Assembly Resolve(AppDomain appDomain, AssemblyName assmName, Assembly requestingAssembly)
7176
{
72-
var searchAssmName = GetReplacementAssemblyName(assmName, requestingAssembly, out string searchDir, out bool recursiveSearch);
73-
74-
if (searchAssmName != null)
77+
if (ShouldResolve(appDomain, assmName, requestingAssembly))
7578
{
76-
var matchedAssmNames = new List<AssemblyInfo>();
77-
78-
var replacementAssms = appDomain.GetAssemblies().Where(
79-
a => Match(a.GetName(), searchAssmName));
80-
81-
var exactMatch = replacementAssms.FirstOrDefault(a => CompareAssemblyNames(a.GetName(), searchAssmName));
79+
var searchAssmName = GetReplacementAssemblyName(assmName, requestingAssembly, out string searchDir, out bool recursiveSearch);
8280

83-
if (exactMatch != null)
81+
if (searchAssmName != null)
8482
{
85-
Trace.WriteLine($"Assembly '{searchAssmName}' is resolved to '{exactMatch.Location}' as exact match via '{Name}' resolver", Name);
83+
var matchedAssmNames = new List<AssemblyInfo>();
8684

87-
return exactMatch;
88-
}
89-
else
90-
{
91-
matchedAssmNames.AddRange(replacementAssms.Select(a => new AssemblyInfo(a.GetName(), a.Location, true)));
92-
}
85+
var replacementAssms = appDomain.GetAssemblies().Where(
86+
a => Match(a.GetName(), searchAssmName));
9387

94-
foreach (var name in EnumerateAssemblyByName(searchDir, recursiveSearch, searchAssmName))
95-
{
96-
if (CompareAssemblyNames(name.Name, searchAssmName))
88+
var exactMatch = replacementAssms.FirstOrDefault(a => CompareAssemblyNames(a.GetName(), searchAssmName));
89+
90+
if (exactMatch != null)
9791
{
98-
Trace.WriteLine($"Loading '{searchAssmName}' from '{name.FilePath}' as exact match via '{Name}' resolver", Name);
92+
Trace.WriteLine($"Assembly '{searchAssmName}' is resolved to '{exactMatch.Location}' as exact match via '{Name}' resolver", Name);
9993

100-
return Assembly.LoadFrom(name.FilePath);
94+
return exactMatch;
10195
}
102-
else
96+
else
10397
{
104-
matchedAssmNames.Add(name);
98+
matchedAssmNames.AddRange(replacementAssms.Select(a => new AssemblyInfo(a.GetName(), a.Location, true)));
10599
}
106-
}
107100

108-
var assmInfo = ResolveAmbiguity(matchedAssmNames, searchAssmName);
109-
110-
if (assmInfo != null)
111-
{
112-
if (assmInfo.IsLoaded)
101+
foreach (var name in EnumerateAssemblyByName(searchDir, recursiveSearch, searchAssmName))
113102
{
114-
Trace.WriteLine($"Loading '{assmInfo.Name}' from assembly name via '{Name}' resolver", Name);
115-
116-
return Assembly.Load(assmInfo.Name);
103+
if (CompareAssemblyNames(name.Name, searchAssmName))
104+
{
105+
Trace.WriteLine($"Loading '{searchAssmName}' from '{name.FilePath}' as exact match via '{Name}' resolver", Name);
106+
107+
return LoadAssembly(new AssemblyInfo(AssemblyName.GetAssemblyName(name.FilePath), name.FilePath, false));
108+
}
109+
else
110+
{
111+
matchedAssmNames.Add(name);
112+
}
117113
}
118-
else
119-
{
120-
Trace.WriteLine($"Loading '{assmInfo.Name}' from file '{assmInfo.FilePath}' via '{Name}' resolver", Name);
121114

122-
return Assembly.LoadFrom(assmInfo.FilePath);
115+
var assmInfo = ResolveAmbiguity(matchedAssmNames, searchAssmName);
116+
117+
if (assmInfo != null)
118+
{
119+
return LoadAssembly(assmInfo);
123120
}
124121
}
125122
}
126123

127124
return null;
128125
}
129126

127+
/// <summary>
128+
/// Checks if assembly should be resolved by this resolver
129+
/// </summary>
130+
/// <param name="appDomain">App Domain</param>
131+
/// <param name="assmName">Assembly to resolve</param>
132+
/// <param name="requestingAssembly">Requesting assembly</param>
133+
/// <returns></returns>
134+
protected virtual bool ShouldResolve(AppDomain appDomain, AssemblyName assmName, Assembly requestingAssembly)
135+
{
136+
if (m_FilterDirs?.Any() == true && requestingAssembly != null)
137+
{
138+
var reqAssmFilePath = requestingAssembly.Location;
139+
140+
return m_FilterDirs.Any(f => FileSystemUtils.IsInDirectory(reqAssmFilePath, f));
141+
}
142+
else
143+
{
144+
return true;
145+
}
146+
}
147+
148+
/// <summary>
149+
/// Load the assembly
150+
/// </summary>
151+
/// <param name="assmInfo">Assembly information</param>
152+
/// <returns>Loaded assembly</returns>
153+
protected Assembly LoadAssembly(AssemblyInfo assmInfo)
154+
{
155+
Trace.WriteLine($"Loading '{assmInfo.Name}' from file '{assmInfo.FilePath}' [Loaded={assmInfo.IsLoaded}] via '{Name}' resolver", Name);
156+
157+
return Assembly.Load(assmInfo.Name);
158+
}
159+
130160
/// <summary>
131161
/// Gets the culture of the assembly name
132162
/// </summary>

src/Utils/Reflection/LocalFolderReferencesResolver.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public class LocalFolderReferencesResolver : AssemblyNameReferenceResolver
3131

3232
public LocalFolderReferencesResolver(string searchDir,
3333
AssemblyMatchFilter_e matchFilter = AssemblyMatchFilter_e.PublicKeyToken | AssemblyMatchFilter_e.Culture,
34-
string name = "", params string[] assemblyNameFilters) : base(name)
34+
string name = "", string[] assemblyNameFilters = null, string[] filterDirs = null) : base(name, filterDirs)
3535
{
3636
m_MatchFilter = matchFilter;
3737

0 commit comments

Comments
 (0)