Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 96 additions & 9 deletions Samples/Shared/SdkResolverNetCoreApp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@ namespace Genetec.Dap.CodeSamples;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using System.Text.Json;
using Microsoft.Win32;
using System.Collections.Concurrent;

public static class SdkResolver
{
private static string s_probingPath;
private static AssemblyDependencyResolver s_dependencyResolver;
private static Dictionary<string, string> s_packageAssemblyPaths;
private static readonly ConcurrentDictionary<string, Lazy<Assembly>> s_loaders = new(StringComparer.OrdinalIgnoreCase);

public static void Initialize()
Expand All @@ -27,12 +28,87 @@ public static void Initialize()
if (string.IsNullOrEmpty(s_probingPath))
throw new InvalidOperationException("SDK probing path could not be found.");

var sdkDll = Path.Combine(s_probingPath, "Genetec.Sdk.dll");
s_dependencyResolver = File.Exists(sdkDll) ? new AssemblyDependencyResolver(sdkDll) : null;
s_packageAssemblyPaths = BuildPackageAssemblyIndex(s_probingPath);

AssemblyLoadContext.Default.Resolving += OnAssemblyResolve;
}

private static Dictionary<string, string> BuildPackageAssemblyIndex(string probingPath)
{
var index = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

var nugetCache = Environment.GetEnvironmentVariable("NUGET_PACKAGES");
if (string.IsNullOrWhiteSpace(nugetCache))
nugetCache = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget", "packages");

bool hasNugetCache = Directory.Exists(nugetCache);

foreach (var depsFile in Directory.EnumerateFiles(probingPath, "*.deps.json"))
{
try
{
using var stream = File.OpenRead(depsFile);
using var doc = JsonDocument.Parse(stream);

if (!doc.RootElement.TryGetProperty("targets", out var targets))
continue;

foreach (var target in targets.EnumerateObject())
{
foreach (var package in target.Value.EnumerateObject())
{
if (!package.Value.TryGetProperty("runtime", out var runtime))
continue;

foreach (var runtimeEntry in runtime.EnumerateObject())
{
var runtimeName = runtimeEntry.Name;

if (Path.IsPathRooted(runtimeName))
continue;

var dllName = Path.GetFileNameWithoutExtension(Path.GetFileName(runtimeName));
if (index.ContainsKey(dllName))
continue;

// Check if DLL is already in the SDK folder
var localPath = Path.Combine(probingPath, Path.GetFileName(runtimeName));
if (File.Exists(localPath))
{
index[dllName] = localPath;
continue;
}

// Try to find in NuGet cache
if (hasNugetCache)
{
var packageParts = package.Name.Split('/');
if (packageParts.Length == 2)
{
var packagePath = Path.Combine(nugetCache, packageParts[0].ToLowerInvariant(), packageParts[1], runtimeName);
if (File.Exists(packagePath))
{
index[dllName] = packagePath;
}
}
}
}
}
}
}
catch (JsonException)
{
// Skip malformed deps.json files
}
catch (IOException)
{
// Skip files that cannot be read
}
}

return index;
}

private static Assembly OnAssemblyResolve(AssemblyLoadContext context, AssemblyName assemblyName)
{
string key = assemblyName.FullName ?? assemblyName.Name;
Expand All @@ -54,10 +130,21 @@ private static Assembly OnAssemblyResolve(AssemblyLoadContext context, AssemblyN

private static Assembly LoadAssembly(AssemblyLoadContext context, AssemblyName assemblyName)
{
// 1) Try AssemblyDependencyResolver
var path = s_dependencyResolver.ResolveAssemblyToPath(assemblyName);
if (!string.IsNullOrEmpty(path) && File.Exists(path))
return context.LoadFromAssemblyPath(path);
if (s_packageAssemblyPaths == null)
return null;

// 1) Try the package assembly index (built from all .deps.json files)
if (s_packageAssemblyPaths.TryGetValue(assemblyName.Name, out var resolvedPath) && File.Exists(resolvedPath))
{
try
{
return context.LoadFromAssemblyPath(resolvedPath);
}
catch (BadImageFormatException)
{
// Fall through to next resolution strategy
}
}

// 2) Try direct probing folder
if (!string.IsNullOrEmpty(s_probingPath))
Expand All @@ -68,7 +155,7 @@ private static Assembly LoadAssembly(AssemblyLoadContext context, AssemblyName a
{
return context.LoadFromAssemblyPath(candidate);
}
catch (Exception)
catch (BadImageFormatException)
{
// Ignore and try next candidate
}
Expand Down Expand Up @@ -134,4 +221,4 @@ private static string GetProbingPath()
}
}

#endif
#endif