2022-09-21 22:43:27 +03:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
2023-07-30 18:14:34 +03:00
|
|
|
|
using System.IO;
|
2023-08-06 23:45:49 +03:00
|
|
|
|
using System.Linq;
|
2022-09-21 22:43:27 +03:00
|
|
|
|
using System.Reflection;
|
|
|
|
|
|
|
|
|
|
|
|
namespace PascalABCCompiler.NetHelper
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Will force the application domain to load passed assemblies when requested, while ignoring the assembly
|
|
|
|
|
|
/// versions. Handles <see cref="AppDomain.AssemblyResolve"/> until disposed.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public class AssemblyResolveScope : IDisposable
|
|
|
|
|
|
{
|
|
|
|
|
|
private readonly AppDomain _appDomain;
|
2023-07-30 18:14:34 +03:00
|
|
|
|
private readonly Dictionary<string, Assembly> _assembliesByName = new Dictionary<string, Assembly>();
|
|
|
|
|
|
private readonly Dictionary<string, Assembly> _assembliesByPath = new Dictionary<string, Assembly>();
|
2022-09-21 22:43:27 +03:00
|
|
|
|
|
2023-08-08 23:57:50 +03:00
|
|
|
|
public ICollection<AssemblyName> CalculateBindingRedirects()
|
|
|
|
|
|
{
|
|
|
|
|
|
var redirectedAssemblies = new HashSet<string>();
|
|
|
|
|
|
foreach (var assembly in _assembliesByName.Values)
|
|
|
|
|
|
{
|
|
|
|
|
|
foreach (var referenceName in assembly.GetReferencedAssemblies())
|
|
|
|
|
|
{
|
|
|
|
|
|
var shortName = referenceName.Name;
|
|
|
|
|
|
if (_assembliesByName.TryGetValue(shortName, out var referenceAssembly)
|
|
|
|
|
|
&& referenceAssembly.GetName().Version != referenceName.Version)
|
|
|
|
|
|
{
|
|
|
|
|
|
redirectedAssemblies.Add(shortName);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2023-08-06 23:45:49 +03:00
|
|
|
|
|
2023-08-08 23:57:50 +03:00
|
|
|
|
return redirectedAssemblies.Select(a => _assembliesByName[a].GetName()).ToList();
|
|
|
|
|
|
}
|
2023-08-06 23:45:49 +03:00
|
|
|
|
|
2023-07-30 18:14:34 +03:00
|
|
|
|
public AssemblyResolveScope(AppDomain appDomain)
|
2022-09-21 22:43:27 +03:00
|
|
|
|
{
|
|
|
|
|
|
_appDomain = appDomain;
|
|
|
|
|
|
_appDomain.AssemblyResolve += OnAssemblyResolve;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-07-30 18:14:34 +03:00
|
|
|
|
public Assembly PreloadAssembly(string assemblyFilePath)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Convert to normalized form (slashes, name case, possible relative paths):
|
|
|
|
|
|
assemblyFilePath = Path.GetFullPath(assemblyFilePath);
|
|
|
|
|
|
|
|
|
|
|
|
if (_assembliesByPath.TryGetValue(assemblyFilePath, out var assembly))
|
|
|
|
|
|
return assembly;
|
|
|
|
|
|
|
|
|
|
|
|
assembly = NetHelper.LoadAssembly(assemblyFilePath);
|
|
|
|
|
|
_assembliesByName[assembly.GetName().Name] = assembly;
|
|
|
|
|
|
_assembliesByPath[assemblyFilePath] = assembly;
|
|
|
|
|
|
return assembly;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-09-21 22:43:27 +03:00
|
|
|
|
public void Dispose()
|
|
|
|
|
|
{
|
|
|
|
|
|
_appDomain.AssemblyResolve -= OnAssemblyResolve;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-08-08 23:57:50 +03:00
|
|
|
|
private Assembly OnAssemblyResolve(object obj, ResolveEventArgs args)
|
2022-09-21 22:43:27 +03:00
|
|
|
|
{
|
|
|
|
|
|
var requestedName = new AssemblyName(args.Name);
|
2023-07-30 18:14:34 +03:00
|
|
|
|
if (_assembliesByName.TryGetValue(requestedName.Name, out var assembly))
|
2022-09-21 22:43:27 +03:00
|
|
|
|
return assembly;
|
2023-08-06 23:45:49 +03:00
|
|
|
|
var dir = NetHelper.GetAssemblyDirectory(args.RequestingAssembly);
|
2023-07-30 15:54:49 +03:00
|
|
|
|
if (string.IsNullOrEmpty(dir))
|
|
|
|
|
|
return null;
|
2023-08-06 23:45:49 +03:00
|
|
|
|
string path = Path.Combine(dir, args.Name.Substring(0, args.Name.IndexOf(",")) + ".dll");
|
|
|
|
|
|
if (File.Exists(path))
|
2023-07-30 15:54:49 +03:00
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2023-07-30 18:14:34 +03:00
|
|
|
|
return PreloadAssembly(path);
|
2023-07-30 15:54:49 +03:00
|
|
|
|
}
|
|
|
|
|
|
catch
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
}
|
2022-09-21 22:43:27 +03:00
|
|
|
|
|
2023-07-30 15:54:49 +03:00
|
|
|
|
}
|
2022-09-21 22:43:27 +03:00
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|