diff options
Diffstat (limited to 'src/WixToolset.Core/ExtensionManager.cs')
-rw-r--r-- | src/WixToolset.Core/ExtensionManager.cs | 110 |
1 files changed, 110 insertions, 0 deletions
diff --git a/src/WixToolset.Core/ExtensionManager.cs b/src/WixToolset.Core/ExtensionManager.cs new file mode 100644 index 00000000..45cb65ec --- /dev/null +++ b/src/WixToolset.Core/ExtensionManager.cs | |||
@@ -0,0 +1,110 @@ | |||
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 | |||
3 | namespace WixToolset | ||
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 | |||
12 | public class ExtensionManager | ||
13 | { | ||
14 | private List<Assembly> extensionAssemblies = new List<Assembly>(); | ||
15 | |||
16 | /// <summary> | ||
17 | /// Loads an assembly from a type description string. | ||
18 | /// </summary> | ||
19 | /// <param name="extension">The assembly type description string.</param> | ||
20 | /// <returns>The loaded assembly. This assembly can be ignored since the extension manager maintains the list of loaded assemblies internally.</returns> | ||
21 | /// <remarks> | ||
22 | /// <paramref name="extension"/> can be in several different forms: | ||
23 | /// <list type="number"> | ||
24 | /// <item><term>AssemblyName (MyAssembly, Version=1.3.0.0, Culture=neutral, PublicKeyToken=b17a5c561934e089)</term></item> | ||
25 | /// <item><term>Absolute path to an assembly (C:\MyExtensions\ExtensionAssembly.dll)</term></item> | ||
26 | /// <item><term>Filename of an assembly in the application directory (ExtensionAssembly.dll)</term></item> | ||
27 | /// <item><term>Relative path to an assembly (..\..\MyExtensions\ExtensionAssembly.dll)</term></item> | ||
28 | /// </list> | ||
29 | /// </remarks> | ||
30 | public Assembly Load(string extension) | ||
31 | { | ||
32 | string assemblyName = extension; | ||
33 | Assembly assembly; | ||
34 | |||
35 | // Absolute path to an assembly which means only "load from" will work even though we'd prefer to | ||
36 | // use Assembly.Load (see the documentation for Assembly.LoadFrom why). | ||
37 | if (Path.IsPathRooted(assemblyName)) | ||
38 | { | ||
39 | assembly = ExtensionManager.ExtensionLoadFrom(assemblyName); | ||
40 | } | ||
41 | else if (ExtensionManager.TryExtensionLoad(assemblyName, out assembly)) | ||
42 | { | ||
43 | // Loaded the assembly by name from the probing path. | ||
44 | } | ||
45 | else if (ExtensionManager.TryExtensionLoad(Path.GetFileNameWithoutExtension(assemblyName), out assembly)) | ||
46 | { | ||
47 | // Loaded the assembly by filename alone along the probing path. | ||
48 | } | ||
49 | else // relative path to an assembly | ||
50 | { | ||
51 | // We want to use Assembly.Load when we can because it has some benefits over Assembly.LoadFrom | ||
52 | // (see the documentation for Assembly.LoadFrom). However, it may fail when the path is a relative | ||
53 | // path, so we should try Assembly.LoadFrom one last time. We could have detected a directory | ||
54 | // separator character and used Assembly.LoadFrom directly, but dealing with path canonicalization | ||
55 | // issues is something we don't want to deal with if we don't have to. | ||
56 | assembly = ExtensionManager.ExtensionLoadFrom(assemblyName); | ||
57 | } | ||
58 | |||
59 | this.extensionAssemblies.Add(assembly); | ||
60 | return assembly; | ||
61 | } | ||
62 | |||
63 | /// <summary> | ||
64 | /// Creates extension of specified type from assemblies loaded into the extension manager. | ||
65 | /// </summary> | ||
66 | /// <typeparam name="T">Type of extension to create.</typeparam> | ||
67 | /// <returns>Extensions created of the specified type.</returns> | ||
68 | public IEnumerable<T> Create<T>() where T : class | ||
69 | { | ||
70 | var extensionType = typeof(T); | ||
71 | var types = this.extensionAssemblies.SelectMany(a => a.GetTypes().Where(t => !t.IsAbstract && !t.IsInterface && extensionType.IsAssignableFrom(t))); | ||
72 | return types.Select(t => (T)Activator.CreateInstance(t)).ToList(); | ||
73 | } | ||
74 | |||
75 | private static Assembly ExtensionLoadFrom(string assemblyName) | ||
76 | { | ||
77 | try | ||
78 | { | ||
79 | return Assembly.LoadFrom(assemblyName); | ||
80 | } | ||
81 | catch (Exception e) | ||
82 | { | ||
83 | throw new WixException(WixErrors.InvalidExtension(assemblyName, e.Message), e); | ||
84 | } | ||
85 | } | ||
86 | |||
87 | private static bool TryExtensionLoad(string assemblyName, out Assembly assembly) | ||
88 | { | ||
89 | try | ||
90 | { | ||
91 | assembly = Assembly.Load(assemblyName); | ||
92 | return true; | ||
93 | } | ||
94 | catch (IOException innerE) | ||
95 | { | ||
96 | if (innerE is FileLoadException || innerE is FileNotFoundException) | ||
97 | { | ||
98 | assembly = null; | ||
99 | return false; | ||
100 | } | ||
101 | |||
102 | throw new WixException(WixErrors.InvalidExtension(assemblyName, innerE.Message), innerE); | ||
103 | } | ||
104 | catch (Exception e) | ||
105 | { | ||
106 | throw new WixException(WixErrors.InvalidExtension(assemblyName, e.Message), e); | ||
107 | } | ||
108 | } | ||
109 | } | ||
110 | } | ||