private Assembly LoadAssemblyFromPathInternal([NotNull] string assemblyFullPath) { if (assemblyFullPath == null) { throw new ArgumentNullException(nameof(assemblyFullPath)); } assemblyFullPath = Path.GetFullPath(assemblyFullPath); try { lock (loadedAssemblies) { LoadedAssembly loadedAssembly; if (loadedAssembliesByName.TryGetValue(assemblyFullPath, out loadedAssembly)) { return(loadedAssembly.Assembly); } if (!File.Exists(assemblyFullPath)) { return(null); } // Find pdb (if it exists) var pdbFullPath = Path.ChangeExtension(assemblyFullPath, ".pdb"); if (!File.Exists(pdbFullPath)) { pdbFullPath = null; } // PreLoad the assembly into memory without locking it var assemblyBytes = File.ReadAllBytes(assemblyFullPath); var pdbBytes = pdbFullPath != null?File.ReadAllBytes(pdbFullPath) : null; // Load the assembly into the current AppDomain Assembly assembly; if (new UDirectory(AppDomain.CurrentDomain.BaseDirectory) == new UFile(assemblyFullPath).GetFullDirectory()) { // If loading from base directory, don't even try to load through byte array, as Assembly.Load will notice there is a "safer" version to load // This happens usually when opening Xenko assemblies themselves assembly = Assembly.LoadFrom(assemblyFullPath); } else { // Load .deps.json file (if any) var depsFile = Path.ChangeExtension(assemblyFullPath, ".deps.json"); Dictionary <string, string> dependenciesMapping = null; if (File.Exists(depsFile)) { // Read file var dependenciesReader = new DependencyContextJsonReader(); DependencyContext dependencies = null; using (var dependenciesStream = File.OpenRead(depsFile)) { dependencies = dependenciesReader.Read(dependenciesStream); } // Locate NuGet package folder var settings = NuGet.Configuration.Settings.LoadDefaultSettings(Path.GetDirectoryName(assemblyFullPath)); var globalPackagesFolder = NuGet.Configuration.SettingsUtility.GetGlobalPackagesFolder(settings); // Build list of assemblies listed in .deps.json file dependenciesMapping = new Dictionary <string, string>(); foreach (var library in dependencies.RuntimeLibraries) { if (library.Path == null) { continue; } foreach (var runtimeAssemblyGroup in library.RuntimeAssemblyGroups) { foreach (var runtimeFile in runtimeAssemblyGroup.RuntimeFiles) { var fullPath = Path.Combine(globalPackagesFolder, library.Path, runtimeFile.Path); if (File.Exists(fullPath)) { var assemblyName = Path.GetFileNameWithoutExtension(runtimeFile.Path); // TODO: Properly deal with file duplicates (same file in multiple package, or RID conflicts) if (!dependenciesMapping.ContainsKey(assemblyName)) { dependenciesMapping.Add(assemblyName, fullPath); } } } } // It seems .deps.json files don't contain library info if referencer is non-RID specific and dependency is RID specific // (i.e. compiling Game project without runtime identifier and a dependency needs runtime identifier) // TODO: Look for a proper way to query that properly, maybe using NuGet API? however some constraints are: // - limited info (only .deps.json available from path, no project info; we might need to reconstruct a proper NuGet restore request from .deps.json) // - need to be fast (make sure to use NuGet caching mechanism) if (library.RuntimeAssemblyGroups.Count == 0) { var runtimeFolder = new[] { "win", "any" } .Select(runtime => Path.Combine(globalPackagesFolder, library.Path, "runtimes", runtime)) .Where(Directory.Exists) .SelectMany(folder => Directory.EnumerateDirectories(Path.Combine(folder, "lib"))) .FirstOrDefault(file => Path.GetFileName(file).StartsWith("net")); // Only consider framework netXX and netstandardX.X if (runtimeFolder != null) { foreach (var runtimeFile in Directory.EnumerateFiles(runtimeFolder, "*.dll")) { var assemblyName = Path.GetFileNameWithoutExtension(runtimeFile); // TODO: Properly deal with file duplicates (same file in multiple package, or RID conflicts) if (!dependenciesMapping.ContainsKey(assemblyName)) { dependenciesMapping.Add(assemblyName, runtimeFile); } } } } } } // TODO: Is using AppDomain would provide more opportunities for unloading? assembly = pdbBytes != null?Assembly.Load(assemblyBytes, pdbBytes) : Assembly.Load(assemblyBytes); loadedAssembly = new LoadedAssembly(this, assemblyFullPath, assembly, dependenciesMapping); loadedAssemblies.Add(loadedAssembly); loadedAssembliesByName.Add(assemblyFullPath, loadedAssembly); // Force assembly resolve with proper name (with proper context) var previousSearchDirectory = currentSearchDirectory; var previousContainer = currentContainer; try { currentContainer = this; currentSearchDirectory = Path.GetDirectoryName(assemblyFullPath); Assembly.Load(assembly.FullName); } finally { currentContainer = previousContainer; currentSearchDirectory = previousSearchDirectory; } } // Make sure there is no duplicate Debug.Assert(!assemblyToContainers.TryGetValue(assembly, out var _)); // Add to mapping assemblyToContainers.GetValue(assembly, _ => loadedAssembly); // Make sure that Module initializer are called foreach (var module in assembly.Modules) { RuntimeHelpers.RunModuleConstructor(module.ModuleHandle); } return(assembly); } } catch (Exception exception) { log.Error($"Error while loading assembly reference [{assemblyFullPath}]", exception); var loaderException = exception as ReflectionTypeLoadException; if (loaderException != null) { foreach (var exceptionForType in loaderException.LoaderExceptions) { log.Error("Unable to load type. See exception.", exceptionForType); } } } return(null); }
private Assembly LoadAssemblyFromPathInternal([NotNull] string assemblyFullPath) { if (assemblyFullPath == null) { throw new ArgumentNullException(nameof(assemblyFullPath)); } assemblyFullPath = Path.GetFullPath(assemblyFullPath); try { lock (loadedAssemblies) { LoadedAssembly loadedAssembly; if (loadedAssembliesByName.TryGetValue(assemblyFullPath, out loadedAssembly)) { return(loadedAssembly.Assembly); } if (!File.Exists(assemblyFullPath)) { return(null); } // Find pdb (if it exists) var pdbFullPath = Path.ChangeExtension(assemblyFullPath, ".pdb"); if (!File.Exists(pdbFullPath)) { pdbFullPath = null; } // PreLoad the assembly into memory without locking it var assemblyBytes = File.ReadAllBytes(assemblyFullPath); var pdbBytes = pdbFullPath != null?File.ReadAllBytes(pdbFullPath) : null; // Load the assembly into the current AppDomain Assembly assembly; if (new UDirectory(AppDomain.CurrentDomain.BaseDirectory) == new UFile(assemblyFullPath).GetFullDirectory()) { // If loading from base directory, don't even try to load through byte array, as Assembly.Load will notice there is a "safer" version to load // This happens usually when opening Xenko assemblies themselves assembly = Assembly.LoadFrom(assemblyFullPath); } else { // Load .deps.json file (if any) var depsFile = Path.ChangeExtension(assemblyFullPath, ".deps.json"); Dictionary <string, string> dependenciesMapping = null; if (File.Exists(depsFile)) { // Read file var dependenciesReader = new DependencyContextJsonReader(); DependencyContext dependencies = null; using (var dependenciesStream = File.OpenRead(depsFile)) { dependencies = dependenciesReader.Read(dependenciesStream); } // Locate NuGet package folder var settings = NuGet.Configuration.Settings.LoadDefaultSettings(Path.GetDirectoryName(assemblyFullPath)); var globalPackagesFolder = NuGet.Configuration.SettingsUtility.GetGlobalPackagesFolder(settings); // Build list of assemblies listed in .deps.json file dependenciesMapping = new Dictionary <string, string>(); foreach (var library in dependencies.RuntimeLibraries) { if (library.Path == null) { continue; } foreach (var runtimeAssemblyGroup in library.RuntimeAssemblyGroups) { foreach (var runtimeFile in runtimeAssemblyGroup.RuntimeFiles) { var fullPath = Path.Combine(globalPackagesFolder, library.Path, runtimeFile.Path); if (File.Exists(fullPath)) { var assemblyName = Path.GetFileNameWithoutExtension(runtimeFile.Path); // TODO: Properly deal with file duplicates (same file in multiple package, or RID conflicts) if (!dependenciesMapping.ContainsKey(assemblyName)) { dependenciesMapping.Add(assemblyName, fullPath); } } } } } } // TODO: Is using AppDomain would provide more opportunities for unloading? assembly = pdbBytes != null?Assembly.Load(assemblyBytes, pdbBytes) : Assembly.Load(assemblyBytes); loadedAssembly = new LoadedAssembly(this, assemblyFullPath, assembly, dependenciesMapping); loadedAssemblies.Add(loadedAssembly); loadedAssembliesByName.Add(assemblyFullPath, loadedAssembly); // Force assembly resolve with proper name (with proper context) var previousSearchDirectory = currentSearchDirectory; var previousContainer = currentContainer; try { currentContainer = this; currentSearchDirectory = Path.GetDirectoryName(assemblyFullPath); Assembly.Load(assembly.FullName); } finally { currentContainer = previousContainer; currentSearchDirectory = previousSearchDirectory; } } // Make sure there is no duplicate Debug.Assert(!assemblyToContainers.TryGetValue(assembly, out var _)); // Add to mapping assemblyToContainers.GetValue(assembly, _ => loadedAssembly); // Make sure that Module initializer are called foreach (var module in assembly.Modules) { RuntimeHelpers.RunModuleConstructor(module.ModuleHandle); } return(assembly); } } catch (Exception exception) { log.Error($"Error while loading assembly reference [{assemblyFullPath}]", exception); var loaderException = exception as ReflectionTypeLoadException; if (loaderException != null) { foreach (var exceptionForType in loaderException.LoaderExceptions) { log.Error("Unable to load type. See exception.", exceptionForType); } } } return(null); }
private Assembly LoadAssemblyFromPathInternal([NotNull] string assemblyFullPath) { if (assemblyFullPath == null) { throw new ArgumentNullException(nameof(assemblyFullPath)); } assemblyFullPath = Path.GetFullPath(assemblyFullPath); try { lock (loadedAssemblies) { LoadedAssembly loadedAssembly; if (loadedAssembliesByName.TryGetValue(assemblyFullPath, out loadedAssembly)) { return(loadedAssembly.Assembly); } if (!File.Exists(assemblyFullPath)) { return(null); } // Find pdb (if it exists) var pdbFullPath = Path.ChangeExtension(assemblyFullPath, ".pdb"); if (!File.Exists(pdbFullPath)) { pdbFullPath = null; } // PreLoad the assembly into memory without locking it var assemblyBytes = File.ReadAllBytes(assemblyFullPath); var pdbBytes = pdbFullPath != null?File.ReadAllBytes(pdbFullPath) : null; // Load the assembly into the current AppDomain Assembly assembly; if (new UDirectory(AppDomain.CurrentDomain.BaseDirectory) == new UFile(assemblyFullPath).GetFullDirectory()) { // If loading from base directory, don't even try to load through byte array, as Assembly.Load will notice there is a "safer" version to load // This happens usually when opening Xenko assemblies themselves assembly = Assembly.LoadFrom(assemblyFullPath); } else { // TODO: Is using AppDomain would provide more opportunities for unloading? assembly = pdbBytes != null?Assembly.Load(assemblyBytes, pdbBytes) : Assembly.Load(assemblyBytes); loadedAssembly = new LoadedAssembly(assemblyFullPath, assembly); loadedAssemblies.Add(loadedAssembly); loadedAssembliesByName.Add(assemblyFullPath, loadedAssembly); // Force assembly resolve with proper name // (doing it here, because if done later, loadingInstance will be set to null and it won't work) Assembly.Load(assembly.FullName); } // Make sure that all referenced assemblies are loaded here foreach (var assemblyRef in assembly.GetReferencedAssemblies()) { Assembly.Load(assemblyRef); } // Make sure that Module initializer are called if (assembly.GetTypes().Length > 0) { foreach (var module in assembly.Modules) { RuntimeHelpers.RunModuleConstructor(module.ModuleHandle); } } return(assembly); } } catch (Exception exception) { log.Error($"Error while loading assembly reference [{assemblyFullPath}]", exception); var loaderException = exception as ReflectionTypeLoadException; if (loaderException != null) { foreach (var exceptionForType in loaderException.LoaderExceptions) { log.Error("Unable to load type. See exception.", exceptionForType); } } } return(null); }
private Assembly LoadAssemblyFromPathInternal([NotNull] string assemblyFullPath) { if (assemblyFullPath == null) { throw new ArgumentNullException(nameof(assemblyFullPath)); } assemblyFullPath = Path.GetFullPath(assemblyFullPath); try { lock (loadedAssemblies) { LoadedAssembly loadedAssembly; if (loadedAssembliesByName.TryGetValue(assemblyFullPath, out loadedAssembly)) { return(loadedAssembly.Assembly); } if (!File.Exists(assemblyFullPath)) { return(null); } // Find pdb (if it exists) var pdbFullPath = Path.ChangeExtension(assemblyFullPath, ".pdb"); if (!File.Exists(pdbFullPath)) { pdbFullPath = null; } // PreLoad the assembly into memory without locking it var assemblyBytes = File.ReadAllBytes(assemblyFullPath); var pdbBytes = pdbFullPath != null?File.ReadAllBytes(pdbFullPath) : null; // Load the assembly into the current AppDomain Assembly assembly; if (new UDirectory(AppDomain.CurrentDomain.BaseDirectory) == new UFile(assemblyFullPath).GetFullDirectory()) { // If loading from base directory, don't even try to load through byte array, as Assembly.Load will notice there is a "safer" version to load // This happens usually when opening Xenko assemblies themselves assembly = Assembly.LoadFrom(assemblyFullPath); } else { // TODO: Is using AppDomain would provide more opportunities for unloading? assembly = pdbBytes != null?Assembly.Load(assemblyBytes, pdbBytes) : Assembly.Load(assemblyBytes); loadedAssembly = new LoadedAssembly(this, assemblyFullPath, assembly); loadedAssemblies.Add(loadedAssembly); loadedAssembliesByName.Add(assemblyFullPath, loadedAssembly); // Force assembly resolve with proper name (with proper context) var previousSearchDirectory = currentSearchDirectory; var previousContainer = currentContainer; try { currentContainer = this; currentSearchDirectory = Path.GetDirectoryName(assemblyFullPath); Assembly.Load(assembly.FullName); } finally { currentContainer = previousContainer; currentSearchDirectory = previousSearchDirectory; } } // Make sure there is no duplicate Debug.Assert(!assemblyToContainers.TryGetValue(assembly, out var _)); // Add to mapping assemblyToContainers.GetValue(assembly, _ => loadedAssembly); // Make sure that Module initializer are called foreach (var module in assembly.Modules) { RuntimeHelpers.RunModuleConstructor(module.ModuleHandle); } return(assembly); } } catch (Exception exception) { log.Error($"Error while loading assembly reference [{assemblyFullPath}]", exception); var loaderException = exception as ReflectionTypeLoadException; if (loaderException != null) { foreach (var exceptionForType in loaderException.LoaderExceptions) { log.Error("Unable to load type. See exception.", exceptionForType); } } } return(null); }