// 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. namespace WixToolset { using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using WixToolset.Data; using WixToolset.Extensibility; public class ExtensionManager : IExtensionManager { private List extensionAssemblies = new List(); /// /// Loads an assembly from a type description string. /// /// The assembly type description string. /// The loaded assembly. This assembly can be ignored since the extension manager maintains the list of loaded assemblies internally. /// /// can be in several different forms: /// /// AssemblyName (MyAssembly, Version=1.3.0.0, Culture=neutral, PublicKeyToken=b17a5c561934e089) /// Absolute path to an assembly (C:\MyExtensions\ExtensionAssembly.dll) /// Filename of an assembly in the application directory (ExtensionAssembly.dll) /// Relative path to an assembly (..\..\MyExtensions\ExtensionAssembly.dll) /// /// public Assembly Load(string extension) { string assemblyName = extension; Assembly assembly; // Absolute path to an assembly which means only "load from" will work even though we'd prefer to // use Assembly.Load (see the documentation for Assembly.LoadFrom why). if (Path.IsPathRooted(assemblyName)) { assembly = ExtensionManager.ExtensionLoadFrom(assemblyName); } else if (ExtensionManager.TryExtensionLoad(assemblyName, out assembly)) { // Loaded the assembly by name from the probing path. } else if (ExtensionManager.TryExtensionLoad(Path.GetFileNameWithoutExtension(assemblyName), out assembly)) { // Loaded the assembly by filename alone along the probing path. } else // relative path to an assembly { // We want to use Assembly.Load when we can because it has some benefits over Assembly.LoadFrom // (see the documentation for Assembly.LoadFrom). However, it may fail when the path is a relative // path, so we should try Assembly.LoadFrom one last time. We could have detected a directory // separator character and used Assembly.LoadFrom directly, but dealing with path canonicalization // issues is something we don't want to deal with if we don't have to. assembly = ExtensionManager.ExtensionLoadFrom(assemblyName); } this.extensionAssemblies.Add(assembly); return assembly; } /// /// Creates extension of specified type from assemblies loaded into the extension manager. /// /// Type of extension to create. /// Extensions created of the specified type. public IEnumerable Create() where T : class { var types = this.extensionAssemblies.SelectMany(a => a.GetTypes().Where(t => !t.IsAbstract && !t.IsInterface && typeof(T).IsAssignableFrom(t))); return types.Select(t => (T)Activator.CreateInstance(t)).ToList(); } private static Assembly ExtensionLoadFrom(string assemblyName) { try { return Assembly.LoadFrom(assemblyName); } catch (Exception e) { throw new WixException(WixErrors.InvalidExtension(assemblyName, e.Message), e); } } private static bool TryExtensionLoad(string assemblyName, out Assembly assembly) { try { assembly = Assembly.Load(assemblyName); return true; } catch (IOException innerE) { if (innerE is FileLoadException || innerE is FileNotFoundException) { assembly = null; return false; } throw new WixException(WixErrors.InvalidExtension(assemblyName, innerE.Message), innerE); } catch (Exception e) { throw new WixException(WixErrors.InvalidExtension(assemblyName, e.Message), e); } } } }