Skip to content

Commit 95ab2dc

Browse files
committed
Added simple AssemblyReferenceResolver
1 parent de243ed commit 95ab2dc

13 files changed

+325
-12
lines changed

src/Utils/Licensing/Exceptions/InvalidSignatureException.cs

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
using System;
1+
//*********************************************************************
2+
//xToolkit
3+
//Copyright(C) 2023 Xarial Pty Limited
4+
//Product URL: https://xtoolkit.xarial.com
5+
//License: https://xtoolkit.xarial.com/license/
6+
//*********************************************************************
7+
8+
using System;
29
using Xarial.XToolkit.Reporting;
310

411
namespace Xarial.XToolkit.Licensing.Exceptions

src/Utils/Licensing/Exceptions/LicenseAuthorizationException.cs

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
using System;
1+
//*********************************************************************
2+
//xToolkit
3+
//Copyright(C) 2023 Xarial Pty Limited
4+
//Product URL: https://xtoolkit.xarial.com
5+
//License: https://xtoolkit.xarial.com/license/
6+
//*********************************************************************
7+
8+
using System;
29
using Xarial.XToolkit.Reporting;
310

411
namespace Xarial.XToolkit.Licensing.Exceptions

src/Utils/Licensing/Exceptions/LicenseFileCorruptedException.cs

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
using System;
1+
//*********************************************************************
2+
//xToolkit
3+
//Copyright(C) 2023 Xarial Pty Limited
4+
//Product URL: https://xtoolkit.xarial.com
5+
//License: https://xtoolkit.xarial.com/license/
6+
//*********************************************************************
7+
8+
using System;
29
using Xarial.XToolkit.Reporting;
310

411
namespace Xarial.XToolkit.Licensing.Exceptions

src/Utils/Licensing/Exceptions/LicenseFileMalformedException.cs

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
using System;
1+
//*********************************************************************
2+
//xToolkit
3+
//Copyright(C) 2023 Xarial Pty Limited
4+
//Product URL: https://xtoolkit.xarial.com
5+
//License: https://xtoolkit.xarial.com/license/
6+
//*********************************************************************
7+
8+
using System;
29
using Xarial.XToolkit.Reporting;
310

411
namespace Xarial.XToolkit.Licensing.Exceptions

src/Utils/Licensing/Exceptions/LicenseInstallationFailedException.cs

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
using System;
1+
//*********************************************************************
2+
//xToolkit
3+
//Copyright(C) 2023 Xarial Pty Limited
4+
//Product URL: https://xtoolkit.xarial.com
5+
//License: https://xtoolkit.xarial.com/license/
6+
//*********************************************************************
7+
8+
using System;
29
using Xarial.XToolkit.Reporting;
310

411
namespace Xarial.XToolkit.Licensing.Exceptions

src/Utils/Licensing/Exceptions/LicenseNotFoundException.cs

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
using System.IO;
1+
//*********************************************************************
2+
//xToolkit
3+
//Copyright(C) 2023 Xarial Pty Limited
4+
//Product URL: https://xtoolkit.xarial.com
5+
//License: https://xtoolkit.xarial.com/license/
6+
//*********************************************************************
7+
8+
using System.IO;
29
using Xarial.XToolkit.Reporting;
310

411
namespace Xarial.XToolkit.Licensing.Exceptions

src/Utils/Licensing/Exceptions/LicenseTrialExpiredException.cs

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
using System;
1+
//*********************************************************************
2+
//xToolkit
3+
//Copyright(C) 2023 Xarial Pty Limited
4+
//Product URL: https://xtoolkit.xarial.com
5+
//License: https://xtoolkit.xarial.com/license/
6+
//*********************************************************************
7+
8+
using System;
29
using Xarial.XToolkit.Reporting;
310

411
namespace Xarial.XToolkit.Licensing.Exceptions

src/Utils/Licensing/Exceptions/LicenseValidationException.cs

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
using System;
1+
//*********************************************************************
2+
//xToolkit
3+
//Copyright(C) 2023 Xarial Pty Limited
4+
//Product URL: https://xtoolkit.xarial.com
5+
//License: https://xtoolkit.xarial.com/license/
6+
//*********************************************************************
7+
8+
using System;
29
using System.Collections.Generic;
310
using System.Linq;
411
using System.Text;

src/Utils/Licensing/ILicenseGenerator.cs

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
using System.IO;
1+
//*********************************************************************
2+
//xToolkit
3+
//Copyright(C) 2023 Xarial Pty Limited
4+
//Product URL: https://xtoolkit.xarial.com
5+
//License: https://xtoolkit.xarial.com/license/
6+
//*********************************************************************
7+
8+
using System.IO;
29

310
namespace Xarial.XToolkit.Licensing
411
{

src/Utils/Licensing/ILicenseValidator.cs

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
using System;
1+
//*********************************************************************
2+
//xToolkit
3+
//Copyright(C) 2023 Xarial Pty Limited
4+
//Product URL: https://xtoolkit.xarial.com
5+
//License: https://xtoolkit.xarial.com/license/
6+
//*********************************************************************
7+
8+
using System;
29
using System.Collections.Generic;
310
using System.IO;
411
using System.Linq;

src/Utils/Licensing/SignedXmlLicenseGenerator.cs

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
using System.IO;
1+
//*********************************************************************
2+
//xToolkit
3+
//Copyright(C) 2023 Xarial Pty Limited
4+
//Product URL: https://xtoolkit.xarial.com
5+
//License: https://xtoolkit.xarial.com/license/
6+
//*********************************************************************
7+
8+
using System.IO;
29
using System.Text;
310
using System.Xml.Serialization;
411
using System.Xml;

src/Utils/Licensing/SignedXmlLicenseValidator.cs

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
using System;
1+
//*********************************************************************
2+
//xToolkit
3+
//Copyright(C) 2023 Xarial Pty Limited
4+
//Product URL: https://xtoolkit.xarial.com
5+
//License: https://xtoolkit.xarial.com/license/
6+
//*********************************************************************
7+
8+
using System;
29
using System.Collections.Generic;
310
using System.IO;
411
using System.Linq;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
//*********************************************************************
2+
//xToolkit
3+
//Copyright(C) 2023 Xarial Pty Limited
4+
//Product URL: https://xtoolkit.xarial.com
5+
//License: https://xtoolkit.xarial.com/license/
6+
//*********************************************************************
7+
8+
using System;
9+
using System.Collections.Generic;
10+
using System.IO;
11+
using System.Linq;
12+
using System.Reflection;
13+
14+
namespace Xarial.XToolkit.Reflection
15+
{
16+
/// <summary>
17+
/// Simple resolve for the assembly references
18+
/// </summary>
19+
/// <remarks>Resolver will consider the following rules to load missing references
20+
/// 1 - Loading local references only (if requesting assembly is in the working directory)
21+
/// 2 - Loading target or newer version
22+
/// 3 - Loading from work directory and sub-directories
23+
/// 4 - Loading matched name, public key and culture
24+
/// 5 - Loading nearest available version</remarks>
25+
public class AssemblyReferenceResolver : IDisposable
26+
{
27+
private class AssemblyNameEqualityComparer : IEqualityComparer<AssemblyName>
28+
{
29+
public bool Equals(AssemblyName x, AssemblyName y)
30+
=> string.Equals(x.FullName, y.FullName);
31+
32+
public int GetHashCode(AssemblyName obj) => 0;
33+
}
34+
35+
private readonly AppDomain m_AppDomain;
36+
37+
private readonly string m_WorkDir;
38+
private readonly string m_LogName;
39+
40+
private readonly Dictionary<AssemblyName, Assembly> m_CachedAssemblies;
41+
42+
private readonly IEqualityComparer<AssemblyName> m_AssmNameEqualityComparer;
43+
44+
/// <summary>
45+
/// Starts monitoring and loading missin assemblies
46+
/// </summary>
47+
/// <param name="appDomain">Application domain</param>
48+
/// <param name="workDir">Working directories to look for the assmebly to load</param>
49+
/// <param name="logName">Name of the trace log</param>
50+
public AssemblyReferenceResolver(AppDomain appDomain, string workDir, string logName)
51+
{
52+
m_WorkDir = workDir;
53+
54+
m_AssmNameEqualityComparer = new AssemblyNameEqualityComparer();
55+
m_CachedAssemblies = new Dictionary<AssemblyName, Assembly>(m_AssmNameEqualityComparer);
56+
57+
m_LogName = logName;
58+
59+
m_AppDomain = appDomain;
60+
61+
m_AppDomain.AssemblyResolve += OnAssemblyResolve;
62+
}
63+
64+
private Assembly OnAssemblyResolve(object sender, ResolveEventArgs args)
65+
{
66+
var assmName = new AssemblyName(args.Name);
67+
68+
var requestingAssm = args.RequestingAssembly;
69+
70+
if (!assmName.Name.EndsWith(".resources")
71+
&& (requestingAssm == null || IsInDirectory(requestingAssm.Location, m_WorkDir)))
72+
{
73+
return Resolve(assmName);
74+
}
75+
else
76+
{
77+
return null;
78+
}
79+
}
80+
81+
private Assembly Resolve(AssemblyName searchAssmName)
82+
{
83+
if (!m_CachedAssemblies.TryGetValue(searchAssmName, out var assm))
84+
{
85+
var matchedAssmNames = new List<AssemblyName>();
86+
87+
foreach (var assmName in EnumerateAssemblyByName(m_WorkDir, true, searchAssmName))
88+
{
89+
if (m_AssmNameEqualityComparer.Equals(assmName, searchAssmName))
90+
{
91+
Trace($"Loading '{searchAssmName}' from '{assmName.CodeBase}' as exact match");
92+
93+
assm = LoadAssembly(assmName);
94+
break;
95+
}
96+
else
97+
{
98+
if (!matchedAssmNames.Contains(assmName, m_AssmNameEqualityComparer))
99+
{
100+
matchedAssmNames.Add(assmName);
101+
}
102+
}
103+
}
104+
105+
if (assm == null)
106+
{
107+
var targAssmName = ResolveAmbiguity(matchedAssmNames, searchAssmName);
108+
109+
if (targAssmName != null)
110+
{
111+
assm = LoadAssembly(targAssmName);
112+
}
113+
else
114+
{
115+
assm = null;
116+
}
117+
}
118+
119+
m_CachedAssemblies.Add(searchAssmName, assm);
120+
121+
return assm;
122+
}
123+
else
124+
{
125+
return assm;
126+
}
127+
}
128+
129+
private Assembly LoadAssembly(AssemblyName assmName)
130+
{
131+
Trace($"Loading assembly '{assmName}'");
132+
133+
return Assembly.Load(assmName);
134+
}
135+
136+
private AssemblyName ResolveAmbiguity(IReadOnlyList<AssemblyName> assmNames, AssemblyName searchAssmName)
137+
{
138+
Trace($"Resolving ambiguity for '{searchAssmName}'");
139+
140+
var targAssmName = assmNames.FirstOrDefault(a => m_AssmNameEqualityComparer.Equals(a, searchAssmName));
141+
142+
if (targAssmName != null)
143+
{
144+
Trace($"Ambiguity for '{searchAssmName}' is resolved by exact match");
145+
}
146+
else
147+
{
148+
targAssmName = assmNames.OrderBy(a => a.Version).FirstOrDefault();
149+
150+
if (targAssmName != null)
151+
{
152+
Trace($"Ambiguity for '{searchAssmName}' is resolved by nearest available version of the assembly");
153+
}
154+
else
155+
{
156+
Trace($"Ambiguity for '{searchAssmName}' is not resolved");
157+
}
158+
}
159+
160+
return targAssmName;
161+
}
162+
163+
private IEnumerable<AssemblyName> EnumerateAssemblyByName(string dir, bool recurse, AssemblyName searchAssmName)
164+
{
165+
var probeAssmFilePath = Path.Combine(dir, searchAssmName.Name + ".dll");
166+
167+
if (File.Exists(probeAssmFilePath))
168+
{
169+
var probeAssmName = AssemblyName.GetAssemblyName(probeAssmFilePath);
170+
171+
if (Matches(probeAssmName, searchAssmName))
172+
{
173+
yield return probeAssmName;
174+
}
175+
}
176+
177+
if (recurse)
178+
{
179+
foreach (var subDir in Directory.EnumerateDirectories(dir, "*.*", SearchOption.TopDirectoryOnly))
180+
{
181+
foreach (var res in EnumerateAssemblyByName(subDir, recurse, searchAssmName))
182+
{
183+
yield return res;
184+
}
185+
}
186+
}
187+
}
188+
189+
private bool Matches(AssemblyName probeAssmName, AssemblyName searchAssmName)
190+
{
191+
return (probeAssmName.Name == searchAssmName.Name)
192+
&& (GetPublicKeyTokenString(probeAssmName) == GetPublicKeyTokenString(searchAssmName))
193+
&& (probeAssmName.CultureName == probeAssmName.CultureName)
194+
&& (probeAssmName.Version >= searchAssmName.Version);
195+
}
196+
197+
private string GetPublicKeyTokenString(AssemblyName assmName)
198+
{
199+
var token = assmName.GetPublicKeyToken();
200+
201+
if (token != null)
202+
{
203+
return string.Join("", token.Select(t => string.Format("{0:x2}", t)));
204+
}
205+
else
206+
{
207+
return "";
208+
}
209+
}
210+
211+
private bool IsInDirectory(string thisPath, string parentDir)
212+
{
213+
if (!parentDir.EndsWith("\\"))
214+
{
215+
parentDir = parentDir + "\\";
216+
}
217+
218+
return thisPath.StartsWith(parentDir, StringComparison.CurrentCultureIgnoreCase);
219+
}
220+
221+
private void Trace(string message)
222+
=> System.Diagnostics.Trace.WriteLine(message, m_LogName);
223+
224+
public void Dispose()
225+
{
226+
m_AppDomain.AssemblyResolve -= OnAssemblyResolve;
227+
}
228+
}
229+
}

0 commit comments

Comments
 (0)