aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Core/ExtensibilityServices/ExtensionManager.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/WixToolset.Core/ExtensibilityServices/ExtensionManager.cs')
-rw-r--r--src/WixToolset.Core/ExtensibilityServices/ExtensionManager.cs113
1 files changed, 113 insertions, 0 deletions
diff --git a/src/WixToolset.Core/ExtensibilityServices/ExtensionManager.cs b/src/WixToolset.Core/ExtensibilityServices/ExtensionManager.cs
new file mode 100644
index 00000000..5714701a
--- /dev/null
+++ b/src/WixToolset.Core/ExtensibilityServices/ExtensionManager.cs
@@ -0,0 +1,113 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Core.ExtensibilityServices
4{
5 using System;
6 using System.Collections.Generic;
7 using System.IO;
8 using System.Linq;
9 using System.Reflection;
10 using WixToolset.Data;
11 using WixToolset.Extensibility;
12 using WixToolset.Extensibility.Services;
13
14 internal class ExtensionManager : IExtensionManager
15 {
16 private List<IExtensionFactory> extensionFactories = new List<IExtensionFactory>();
17 private Dictionary<Type, List<object>> loadedExtensionsByType = new Dictionary<Type, List<object>>();
18
19 public void Add(Assembly extensionAssembly)
20 {
21 var types = extensionAssembly.GetTypes().Where(t => !t.IsAbstract && !t.IsInterface && typeof(IExtensionFactory).IsAssignableFrom(t));
22 var factories = types.Select(t => (IExtensionFactory)Activator.CreateInstance(t)).ToList();
23
24 this.extensionFactories.AddRange(factories);
25 }
26
27 public void Load(string extensionPath)
28 {
29 Assembly assembly;
30
31 // Absolute path to an assembly which means only "load from" will work even though we'd prefer to
32 // use Assembly.Load (see the documentation for Assembly.LoadFrom why).
33 if (Path.IsPathRooted(extensionPath))
34 {
35 assembly = ExtensionManager.ExtensionLoadFrom(extensionPath);
36 }
37 else if (ExtensionManager.TryExtensionLoad(extensionPath, out assembly))
38 {
39 // Loaded the assembly by name from the probing path.
40 }
41 else if (ExtensionManager.TryExtensionLoad(Path.GetFileNameWithoutExtension(extensionPath), out assembly))
42 {
43 // Loaded the assembly by filename alone along the probing path.
44 }
45 else // relative path to an assembly
46 {
47 // We want to use Assembly.Load when we can because it has some benefits over Assembly.LoadFrom
48 // (see the documentation for Assembly.LoadFrom). However, it may fail when the path is a relative
49 // path, so we should try Assembly.LoadFrom one last time. We could have detected a directory
50 // separator character and used Assembly.LoadFrom directly, but dealing with path canonicalization
51 // issues is something we don't want to deal with if we don't have to.
52 assembly = ExtensionManager.ExtensionLoadFrom(extensionPath);
53 }
54
55 this.Add(assembly);
56 }
57
58 public IEnumerable<T> Create<T>() where T : class
59 {
60 if (!this.loadedExtensionsByType.TryGetValue(typeof(T), out var extensions))
61 {
62 extensions = new List<object>();
63
64 foreach (var factory in this.extensionFactories)
65 {
66 if (factory.TryCreateExtension(typeof(T), out var obj) && obj is T extension)
67 {
68 extensions.Add(extension);
69 }
70 }
71
72 this.loadedExtensionsByType.Add(typeof(T), extensions);
73 }
74
75 return extensions.Cast<T>().ToList();
76 }
77
78 private static Assembly ExtensionLoadFrom(string assemblyName)
79 {
80 try
81 {
82 return Assembly.LoadFrom(assemblyName);
83 }
84 catch (Exception e)
85 {
86 throw new WixException(ErrorMessages.InvalidExtension(assemblyName, e.Message), e);
87 }
88 }
89
90 private static bool TryExtensionLoad(string assemblyName, out Assembly assembly)
91 {
92 try
93 {
94 assembly = Assembly.Load(assemblyName);
95 return true;
96 }
97 catch (IOException innerE)
98 {
99 if (innerE is FileLoadException || innerE is FileNotFoundException)
100 {
101 assembly = null;
102 return false;
103 }
104
105 throw new WixException(ErrorMessages.InvalidExtension(assemblyName, innerE.Message), innerE);
106 }
107 catch (Exception e)
108 {
109 throw new WixException(ErrorMessages.InvalidExtension(assemblyName, e.Message), e);
110 }
111 }
112 }
113}