// 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.Core.Extensibility
{
using System;
using System.IO;
using System.Reflection;
using WixToolset.Data;
using WixToolset.Tools;
///
/// A command line option.
///
public struct HeatCommandLineOption
{
public string Option;
public string Description;
///
/// Instantiates a new CommandLineOption.
///
/// The option name.
/// The description of the option.
public HeatCommandLineOption(string option, string description)
{
this.Option = option;
this.Description = description;
}
}
///
/// An extension for the WiX Toolset Harvester application.
///
public abstract class HeatExtension
{
///
/// Gets or sets the heat core for the extension.
///
/// The heat core for the extension.
public IHeatCore Core { get; set; }
///
/// Gets the supported command line types for this extension.
///
/// The supported command line types for this extension.
public virtual HeatCommandLineOption[] CommandLineTypes
{
get { return null; }
}
///
/// Loads a HeatExtension from a type description string.
///
/// The extension type description string.
/// The loaded HeatExtension.
///
/// can be in several different forms:
///
/// - AssemblyQualifiedName (TopNamespace.SubNameSpace.ContainingClass+NestedClass, MyAssembly, Version=1.3.0.0, Culture=neutral, PublicKeyToken=b17a5c561934e089)
/// - 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)
///
/// To specify a particular class to use, prefix the fully qualified class name to the assembly and separate them with a comma.
/// For example: "TopNamespace.SubNameSpace.ContainingClass+NestedClass, C:\MyExtensions\ExtensionAssembly.dll"
///
public static HeatExtension Load(string extension)
{
Type extensionType = null;
int commaIndex = extension.IndexOf(',');
string className = String.Empty;
string assemblyName = extension;
if (0 <= commaIndex)
{
className = extension.Substring(0, commaIndex);
assemblyName = (extension.Length <= commaIndex + 1 ? String.Empty : extension.Substring(commaIndex + 1));
}
className = className.Trim();
assemblyName = assemblyName.Trim();
if (null == extensionType && 0 < assemblyName.Length)
{
Assembly extensionAssembly;
// case 3: Absolute path to an assembly
if (Path.IsPathRooted(assemblyName))
{
extensionAssembly = ExtensionLoadFrom(assemblyName);
}
else
{
try
{
// case 2: AssemblyName
extensionAssembly = Assembly.Load(assemblyName);
}
catch (IOException e)
{
if (e is FileLoadException || e is FileNotFoundException)
{
try
{
// case 4: Filename of an assembly in the application directory
extensionAssembly = Assembly.Load(Path.GetFileNameWithoutExtension(assemblyName));
}
catch (IOException innerE)
{
if (innerE is FileLoadException || innerE is FileNotFoundException)
{
// case 5: 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.
extensionAssembly = ExtensionLoadFrom(assemblyName);
}
else
{
throw new WixException(ErrorMessages.InvalidExtension(assemblyName, innerE.Message));
}
}
}
else
{
throw new WixException(ErrorMessages.InvalidExtension(assemblyName, e.Message));
}
}
}
if (0 < className.Length)
{
try
{
// case 1: AssemblyQualifiedName
extensionType = extensionAssembly.GetType(className, true /* throwOnError */, true /* ignoreCase */);
}
catch (Exception e)
{
throw new WixException(ErrorMessages.InvalidExtensionType(assemblyName, className, e.GetType().ToString(), e.Message));
}
}
else
{
// if no class name was specified, then let's hope the assembly defined a default WixExtension
AssemblyDefaultHeatExtensionAttribute extensionAttribute = (AssemblyDefaultHeatExtensionAttribute)Attribute.GetCustomAttribute(extensionAssembly, typeof(AssemblyDefaultHeatExtensionAttribute));
if (null != extensionAttribute)
{
extensionType = extensionAttribute.ExtensionType;
}
else
{
throw new WixException(ErrorMessages.InvalidExtensionType(assemblyName, typeof(AssemblyDefaultHeatExtensionAttribute).ToString()));
}
}
}
if (extensionType.IsSubclassOf(typeof(HeatExtension)))
{
return Activator.CreateInstance(extensionType) as HeatExtension;
}
else
{
throw new WixException(ErrorMessages.InvalidExtensionType(extension, extensionType.ToString(), typeof(HeatExtension).ToString()));
}
}
///
/// Parse the command line options for this extension.
///
/// The active harvester type.
/// The option arguments.
public virtual void ParseOptions(string type, string[] args)
{
}
private static Assembly ExtensionLoadFrom(string assemblyName)
{
Assembly extensionAssembly = null;
try
{
extensionAssembly = Assembly.LoadFrom(assemblyName);
}
catch (Exception e)
{
throw new WixException(ErrorMessages.InvalidExtension(assemblyName, e.Message));
}
return extensionAssembly;
}
}
}