1 | using System;
|
---|
2 | using System.Collections.Generic;
|
---|
3 | using System.IO;
|
---|
4 | using System.Linq;
|
---|
5 | using System.Reflection;
|
---|
6 | using System.Runtime.Loader;
|
---|
7 |
|
---|
8 | namespace HeuristicLab.PluginInfrastructure {
|
---|
9 |
|
---|
10 | /// <summary>
|
---|
11 | /// Class to load assemblies.
|
---|
12 | /// </summary>
|
---|
13 | public class AssemblyLoader : IAssemblyLoader {
|
---|
14 | /// <summary>
|
---|
15 | /// Comparer for AssemblyNames.
|
---|
16 | /// </summary>
|
---|
17 | private class AssemblyNameComparer : IEqualityComparer<AssemblyName> {
|
---|
18 | private object concatProps(AssemblyName name) => name.FullName + name.Version.ToString();
|
---|
19 | public bool Equals(AssemblyName lhs, AssemblyName rhs) => concatProps(lhs).Equals(concatProps(rhs));
|
---|
20 | public int GetHashCode(AssemblyName obj) => concatProps(obj).GetHashCode();
|
---|
21 | }
|
---|
22 |
|
---|
23 | #region Vars
|
---|
24 | private readonly IList<Assembly> assemblies = new List<Assembly>();
|
---|
25 | private readonly IList<Type> types = new List<Type>();
|
---|
26 | #endregion
|
---|
27 |
|
---|
28 | #region Properties
|
---|
29 | /// <summary>
|
---|
30 | /// All loaded assemblies. Contains items only after a call of LoadAssemblies.
|
---|
31 | /// </summary>
|
---|
32 | public IEnumerable<Assembly> Assemblies => assemblies;
|
---|
33 |
|
---|
34 | /// <summary>
|
---|
35 | /// All types found in all assemblies.
|
---|
36 | /// </summary>
|
---|
37 | public IEnumerable<Type> Types => types;
|
---|
38 | #endregion
|
---|
39 |
|
---|
40 | #region Constructors
|
---|
41 | public AssemblyLoader() {
|
---|
42 |
|
---|
43 | #region alternative code
|
---|
44 | /*
|
---|
45 | ScanTypesInBasePath();
|
---|
46 | Types = GetTypes();
|
---|
47 | */
|
---|
48 | /*
|
---|
49 | IEnumerable<Assembly> asms = CheckDependencies(GetLoadableAssemblies(GetAssembliesFromBasePath()));
|
---|
50 | LoadAssemblies(asms);
|
---|
51 | LoadTypes(asms);
|
---|
52 | */
|
---|
53 | //IEnumerable<Assembly> asms = GetLoadableAssemblies(GetAssembliesFromBasePath());
|
---|
54 | //LoadAssemblies(asms);
|
---|
55 | //LoadTypes(asms);
|
---|
56 | #endregion
|
---|
57 | }
|
---|
58 | #endregion
|
---|
59 |
|
---|
60 |
|
---|
61 | private IEnumerable<string> GetAssembliesFromBasePath(string basePath) {
|
---|
62 | return Directory.GetFiles(basePath, "*.dll", SearchOption.AllDirectories)
|
---|
63 | .Concat(Directory.GetFiles(basePath, "*.exe", SearchOption.AllDirectories));
|
---|
64 | }
|
---|
65 |
|
---|
66 | #region alternative code
|
---|
67 | /*
|
---|
68 | // 2.
|
---|
69 | private IEnumerable<Assembly> GetLoadableAssemblies(IEnumerable<string> paths) {
|
---|
70 | IList<Assembly> assemblies = new List<Assembly>();
|
---|
71 | foreach (string path in paths) {
|
---|
72 | try {
|
---|
73 | var asm = Assembly.LoadFile(path);
|
---|
74 | if (asm != null)
|
---|
75 | assemblies.Add(asm);
|
---|
76 | else
|
---|
77 | Console.WriteLine($"cannot load assembly with path={path}");
|
---|
78 | } catch (Exception e) {
|
---|
79 | Console.WriteLine($"exception occured for assembly with path={path}, type of exception={e.GetType()}");
|
---|
80 | }
|
---|
81 | }
|
---|
82 |
|
---|
83 | return assemblies;
|
---|
84 | }
|
---|
85 |
|
---|
86 | // 3.
|
---|
87 | private IEnumerable<Assembly> CheckDependencies(IEnumerable<Assembly> assemblies) {
|
---|
88 | IEqualityComparer<AssemblyName> comp = new AssemblyNameComparer();
|
---|
89 | IDictionary<AssemblyName, Assembly> dict = new Dictionary<AssemblyName, Assembly>(comp);
|
---|
90 |
|
---|
91 | // fill dict
|
---|
92 | foreach (Assembly a in assemblies)
|
---|
93 | dict.Add(a.GetName(), a);
|
---|
94 |
|
---|
95 | Parallel.ForEach(new List<Assembly>(dict.Values), asm => {
|
---|
96 | if (CheckDependencyRecursiveHelper(asm.GetName(), asm.GetReferencedAssemblies(), comp, dict))
|
---|
97 | lock(dict)
|
---|
98 | dict.Remove(asm.GetName());
|
---|
99 | });
|
---|
100 | return dict.Values;
|
---|
101 | }
|
---|
102 |
|
---|
103 | private bool CheckDependencyRecursiveHelper(AssemblyName toCheck, IEnumerable<AssemblyName> refs, IEqualityComparer<AssemblyName> comp, IDictionary<AssemblyName, Assembly> dict) {
|
---|
104 | foreach(AssemblyName name in refs) {
|
---|
105 | if (comp.Equals(toCheck, name) || !dict.ContainsKey(name) || CheckDependencyRecursiveHelper(toCheck, dict[name].GetReferencedAssemblies(), comp, dict)) return true;
|
---|
106 | }
|
---|
107 | return false;
|
---|
108 | }
|
---|
109 |
|
---|
110 | // 4.
|
---|
111 | private void LoadAssemblies(IEnumerable<Assembly> assemblies) {
|
---|
112 |
|
---|
113 | foreach (Assembly asm in assemblies) {
|
---|
114 | try {
|
---|
115 | Assemblies.Add(alc.LoadFromAssemblyName(asm.GetName()));
|
---|
116 | } catch(Exception e) { }
|
---|
117 | }
|
---|
118 | }
|
---|
119 | */
|
---|
120 | #endregion
|
---|
121 |
|
---|
122 | private void LoadTypes(IEnumerable<Assembly> assemblies) {
|
---|
123 | foreach (Assembly asm in assemblies) {
|
---|
124 | try {
|
---|
125 | foreach (Type t in asm.GetExportedTypes()) {
|
---|
126 | types.Add(t);
|
---|
127 | }
|
---|
128 | } catch (ReflectionTypeLoadException e) {
|
---|
129 | // ReflectionTypeLoadException gets thrown if any class in a module cannot be loaded.
|
---|
130 | try {
|
---|
131 | foreach (Type t in e.Types) { // fetch the already loaded types, be careful some of them can be null
|
---|
132 | if (t != null) {
|
---|
133 | types.Add(t);
|
---|
134 | }
|
---|
135 | }
|
---|
136 | } catch (BadImageFormatException) { }
|
---|
137 | } catch (Exception) { // to catch every other exception
|
---|
138 | //Tracing.Logger.Error(
|
---|
139 | // $"Exception occured while loading types of assembly {asm.FullName}! \n " +
|
---|
140 | // $"---- Stacktrace ---- \n {e}");
|
---|
141 | }
|
---|
142 | }
|
---|
143 | }
|
---|
144 |
|
---|
145 | public IEnumerable<Assembly> LoadAssemblies(string basePath) {
|
---|
146 | foreach (string path in GetAssembliesFromBasePath(basePath))
|
---|
147 | LoadAssemblyFromPath(path);
|
---|
148 |
|
---|
149 | LoadTypes(this.Assemblies);
|
---|
150 | return this.Assemblies;
|
---|
151 | }
|
---|
152 |
|
---|
153 | public IEnumerable<Assembly> LoadAssemblies(IEnumerable<AssemblyInfo> assemblyInfos) {
|
---|
154 | foreach (var info in assemblyInfos)
|
---|
155 | LoadAssemblyFromPath(info.Path.ToString());
|
---|
156 |
|
---|
157 | LoadTypes(this.Assemblies);
|
---|
158 | return this.Assemblies;
|
---|
159 | }
|
---|
160 |
|
---|
161 | private void LoadAssemblyFromPath(string path) {
|
---|
162 | try {
|
---|
163 | var asm = AssemblyLoadContext.Default.LoadFromAssemblyPath(path); // loads assembly into default context
|
---|
164 | if (asm != null) {
|
---|
165 | assemblies.Add(asm);
|
---|
166 | } // else
|
---|
167 | //Tracing.Logger.Error($"Unnable to load assembly with path {path}!");
|
---|
168 | } catch (Exception) { // to catch every exception occured by assembly loading.
|
---|
169 | //Tracing.Logger.Error(
|
---|
170 | // $"Exception occured while loading assembly from path {path}! \n " +
|
---|
171 | // $"---- Stacktrace ---- \n {e}");
|
---|
172 | }
|
---|
173 | }
|
---|
174 | }
|
---|
175 | } |
---|