-
Notifications
You must be signed in to change notification settings - Fork 71
Expand file tree
/
Copy pathAssemblyCache.cs
More file actions
99 lines (85 loc) · 4.34 KB
/
AssemblyCache.cs
File metadata and controls
99 lines (85 loc) · 4.34 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
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
namespace WebViewControl {
internal class AssemblyCache {
private object SyncRoot { get; } = new object();
// We now allow to load multiple versions of the same assembly, which means that resource urls can
// (optionally) specify the version. We don't force the version to be specified to maintain backwards
// compatibility, and thus for each assembly name we two entries in the dictionary: with and without a version.
// Note that no guarantee is provided about which version is resolved if there are multiple loaded assemblies
// with the same name and no specific version is provided.
// This, consumer apps are encouraged to include the version in the resource url
private IDictionary<(string AssemblyName, Version AssemblyVersion), Assembly> assemblies;
private bool newAssembliesLoaded = true;
internal Assembly ResolveResourceAssembly(Uri resourceUrl, bool failOnMissingAssembly) {
if (assemblies == null) {
lock (SyncRoot) {
if (assemblies == null) {
assemblies = new Dictionary<(string, Version), Assembly>();
AppDomain.CurrentDomain.AssemblyLoad += delegate { newAssembliesLoaded = true; };
}
}
}
var (assemblyName, assemblyVersion) = ResourceUrl.GetEmbeddedResourceAssemblyNameAndVersion(resourceUrl);
var assembly = GetAssemblyByNameAndVersion(assemblyName, assemblyVersion);
if (assembly == null) {
if (newAssembliesLoaded) {
lock (SyncRoot) {
if (newAssembliesLoaded) {
// add loaded assemblies to cache
newAssembliesLoaded = false;
foreach (var domainAssembly in AppDomain.CurrentDomain.GetAssemblies()) {
AddOrReplace(domainAssembly);
}
}
}
}
assembly = GetAssemblyByNameAndVersion(assemblyName, assemblyVersion);
if (assembly == null) {
try {
// try loading the assembly from a file named AssemblyName.dll (or AssemblyName-AssemblyVersion.dll if
// a version was provided)
var fileName = $"{assemblyName}{(assemblyVersion == null ? "" : $"-{assemblyVersion}")}.dll";
var assemblyPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName);
assembly = AssemblyLoader.LoadAssembly(assemblyPath);
} catch (IOException) {
// ignore
}
if (assembly != null) {
lock (SyncRoot) {
AddOrReplace(assembly);
}
}
}
}
if (failOnMissingAssembly && assembly == null) {
throw new InvalidOperationException("Could not find assembly for: " + resourceUrl);
}
return assembly;
}
private void AddOrReplace(Assembly assembly) {
var identity = assembly.GetName();
var assemblyName = identity.Name;
if (assemblyName == null) {
return;
}
// add two entries, with and without the version.
// for the null-version entry, keep the assembly with the highest version
var version = identity.Version;
if (!assemblies.TryGetValue((assemblyName, null), out var nullVersionAssembly) ||
(nullVersionAssembly.GetName().Version is { } previousVersion && previousVersion < version)) {
assemblies[(assemblyName, null)] = assembly;
}
assemblies[(assemblyName, version)] = assembly;
}
private Assembly GetAssemblyByNameAndVersion(string assemblyName, Version assemblyVersion) {
lock (SyncRoot) {
assemblies.TryGetValue((assemblyName, assemblyVersion), out var assembly);
return assembly;
}
}
}
}