dbatools.library.CoreRedirector.cs
|
using System;
using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.InteropServices; using System.Runtime.Loader; // This source lives beside the module because dbatools.library.psm1 reads and compiles it at import time. public class CoreRedirector { private static string _libPath; private static bool _registered = false; private static readonly object _syncRoot = new object(); private static readonly string _platformRid = ComputePlatformRid(); private static readonly string _architectureRid = ComputeArchitectureRid(); public static void Register(string libPath) { lock (_syncRoot) { if (_registered) return; _libPath = libPath; AssemblyLoadContext.Default.Resolving += OnResolving; AssemblyLoadContext.Default.ResolvingUnmanagedDll += OnResolvingUnmanagedDll; _registered = true; } } private static Assembly OnResolving(AssemblyLoadContext context, AssemblyName assemblyName) { string name = assemblyName.Name; // First, check if any version of this assembly is already loaded // This handles version mismatches (e.g., dbatools.dll requesting ConnectionInfo 17.100.0.0 when 17.200.0.0 is loaded) foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) { try { if (assembly.GetName().Name == name) { return assembly; } } catch { // Some assemblies may throw when accessing GetName() } } foreach (string dllPath in GetManagedAssemblyPaths(name)) { if (File.Exists(dllPath)) { try { return AssemblyLoadContext.Default.LoadFromAssemblyPath(dllPath); } catch (Exception ex) { Trace.TraceWarning("Failed to load managed assembly from '{0}': {1}", dllPath, ex.Message); // Failed to load, try the next candidate } } } return null; } // This resolver is global to the load context, but it only returns module-owned native assets from _libPath. private static IntPtr OnResolvingUnmanagedDll(Assembly assembly, string libraryName) { if (String.IsNullOrEmpty(_architectureRid)) { return IntPtr.Zero; } foreach (string fileName in GetNativeLibraryNames(libraryName)) { string nativePath = Path.Combine(_libPath, "runtimes", _architectureRid, "native", fileName); if (File.Exists(nativePath)) { try { return NativeLibrary.Load(nativePath); } catch (Exception ex) { Trace.TraceWarning("Failed to load native library from '{0}': {1}", nativePath, ex.Message); // Failed to load, try the next candidate } } } return IntPtr.Zero; } private static string[] GetManagedAssemblyPaths(string name) { string fileName = name + ".dll"; var paths = new List<string>(); if (!String.IsNullOrEmpty(_platformRid)) { paths.Add(Path.Combine(_libPath, "runtimes", _platformRid, "lib", "net8.0", fileName)); } if (!String.IsNullOrEmpty(_architectureRid)) { paths.Add(Path.Combine(_libPath, "runtimes", _architectureRid, "lib", "net8.0", fileName)); } paths.Add(Path.Combine(_libPath, fileName)); return paths.ToArray(); } private static string[] GetNativeLibraryNames(string libraryName) { var names = new List<string>(); names.Add(libraryName); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { if (!libraryName.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)) { names.Add(libraryName + ".dll"); } } else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { if (!libraryName.EndsWith(".dylib", StringComparison.OrdinalIgnoreCase)) { names.Add(libraryName + ".dylib"); names.Add("lib" + libraryName + ".dylib"); } } else { if (!libraryName.EndsWith(".so", StringComparison.OrdinalIgnoreCase)) { names.Add(libraryName + ".so"); names.Add("lib" + libraryName + ".so"); } } return names.ToArray(); } private static string ComputePlatformRid() { // Managed runtime assets used by the module, especially SqlClient, ship // win/unix folders. OS-specific native assets are handled by ComputeArchitectureRid. if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { return "win"; } return "unix"; } private static string ComputeArchitectureRid() { string osPart; if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { osPart = "win"; } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { osPart = "linux"; } else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { osPart = "osx"; } else { return null; } string architecture; switch (RuntimeInformation.ProcessArchitecture) { case Architecture.X86: architecture = "x86"; break; case Architecture.X64: architecture = "x64"; break; case Architecture.Arm: architecture = "arm"; break; case Architecture.Arm64: architecture = "arm64"; break; default: return null; } return osPart + "-" + architecture; } } |