-
-
Notifications
You must be signed in to change notification settings - Fork 11
Expand file tree
/
Copy pathdbatools.library.CoreRedirector.cs
More file actions
204 lines (181 loc) · 6.14 KB
/
Copy pathdbatools.library.CoreRedirector.cs
File metadata and controls
204 lines (181 loc) · 6.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
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;
}
}