using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; namespace PascalABCCompiler.NetHelper { /// /// Will force the application domain to load passed assemblies when requested, while ignoring the assembly /// versions. Handles until disposed. /// public class AssemblyResolveScope : IDisposable { private readonly AppDomain _appDomain; private readonly Dictionary _assembliesByName = new Dictionary(); private readonly Dictionary _assembliesByPath = new Dictionary(); public ICollection CalculateBindingRedirects() { var redirectedAssemblies = new HashSet(); 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); } } } return redirectedAssemblies.Select(a => _assembliesByName[a].GetName()).ToList(); } public AssemblyResolveScope(AppDomain appDomain) { _appDomain = appDomain; _appDomain.AssemblyResolve += OnAssemblyResolve; } 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; } public void Dispose() { _appDomain.AssemblyResolve -= OnAssemblyResolve; } static string standartAssemblyPath = Path.GetDirectoryName(System.Reflection.Assembly.GetAssembly(typeof(string)).ManifestModule.FullyQualifiedName); private string GetStandardAssemblyPath(string name) { name = name.Replace("%GAC%\\", ""); string ttn = System.IO.Path.GetFileNameWithoutExtension(name); string tn = Path.Combine(standartAssemblyPath, name); if (File.Exists(tn)) return tn; if (Environment.OSVersion.Platform != PlatformID.Unix && Environment.OSVersion.Platform != PlatformID.MacOSX) { string windir = Path.Combine(Environment.GetEnvironmentVariable("windir"), "Microsoft.NET"); tn = windir + @"\assembly\GAC_MSIL\"; tn += ttn + "\\"; System.IO.DirectoryInfo di = new System.IO.DirectoryInfo(tn); if (!di.Exists) { tn = windir + @"\assembly\GAC_64\"; tn += ttn + "\\"; di = new System.IO.DirectoryInfo(tn); if (!di.Exists) { tn = windir + @"\assembly\GAC_32\"; tn += ttn + "\\"; di = new System.IO.DirectoryInfo(tn); if (!di.Exists) { tn = windir + @"\assembly\GAC\"; tn += ttn + "\\"; di = new System.IO.DirectoryInfo(tn); if (!di.Exists) { windir = Environment.GetEnvironmentVariable("windir"); tn = windir + @"\assembly\GAC_MSIL\"; tn += ttn + "\\"; di = new System.IO.DirectoryInfo(tn); if (!di.Exists) { tn = windir + @"\assembly\GAC_64\"; tn += ttn + "\\"; di = new System.IO.DirectoryInfo(tn); if (!di.Exists) { tn = windir + @"\assembly\GAC_32\"; tn += ttn + "\\"; di = new System.IO.DirectoryInfo(tn); if (!di.Exists) { tn = windir + @"\assembly\GAC\"; tn += ttn + "\\"; di = new System.IO.DirectoryInfo(tn); if (!di.Exists) { return null; } } } } } } } } System.IO.DirectoryInfo[] diarr = di.GetDirectories(); tn = Path.Combine((diarr[0]).FullName, name); } else { string gac_path = "/usr/lib/mono/4.0/gac"; DirectoryInfo di = new DirectoryInfo(Path.Combine(gac_path, ttn)); if (di.Exists) { System.IO.DirectoryInfo[] diarr = di.GetDirectories(); tn = Path.Combine((diarr[diarr.Length - 1]).FullName, name); } else return null; } return tn; } public List missingAssemblies = new List(); private Assembly OnAssemblyResolve(object obj, ResolveEventArgs args) { if (args.RequestingAssembly == null) //fix Linux/Mono IDE missworking after loading a huge pas source such as GraphABC.pas or GraphABCHelper.pas return null; var requestedName = new AssemblyName(args.Name); if (_assembliesByName.TryGetValue(requestedName.Name, out var assembly)) return assembly; var dir = NetHelper.GetAssemblyDirectory(args.RequestingAssembly); if (string.IsNullOrEmpty(dir)) return null; string fileName = args.Name.Substring(0, args.Name.IndexOf(",")) + ".dll"; string path = Path.Combine(dir, fileName); if (!File.Exists(path)) { string assmPath = GetStandardAssemblyPath(fileName); if (assmPath != null) path = assmPath; } if (File.Exists(path)) { try { return PreloadAssembly(path); } catch { } } if (!missingAssemblies.Contains(fileName)) missingAssemblies.Add(fileName); return null; } } }