213 lines
6.7 KiB
C#
213 lines
6.7 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Reflection;
|
|
using JD.Kernel.Classes;
|
|
using JD.Kernel.StaticHelpers;
|
|
using JD.LogService;
|
|
using JD.TeaBoxAddinSystem.Bootstrap;
|
|
|
|
namespace JD.TeaBoxAddinSystem.Addins.Description
|
|
{
|
|
/// <summary>
|
|
/// A runtime wraps a lazy-loaded assembly, referenced by one or more addin(s).
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Assemblies can be referenced in a number of different ways:
|
|
/// ":AssemblyName" will load AssemblyName with Assembly.Load.
|
|
/// "AssemblyName" will load AssemblyName with Assembly.LoadFrom (using an hint path).
|
|
/// </remarks>
|
|
public sealed class RuntimeAssembly
|
|
{
|
|
#region Variables
|
|
|
|
private Reference<Assembly> m_assembly;
|
|
private readonly string m_assemblyName;
|
|
|
|
#endregion
|
|
|
|
#region Constructors
|
|
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
/// <param name="assemblyName">Assembly Name</param>
|
|
/// <param name="hintPath">A hint path for the Assembly location, if needed.</param>
|
|
public RuntimeAssembly(string assemblyName, string hintPath)
|
|
{
|
|
Throw.IfEmpty(assemblyName, "assemblyName");
|
|
|
|
m_assemblyName = assemblyName;
|
|
m_assembly = ThreadSafeReference<Assembly>.FromDelegate(() => LoadAssembly(assemblyName, hintPath));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Alternative Constructor taking an already loaded assembly.
|
|
/// </summary>
|
|
/// <param name="assembly">The assembly to wrap</param>
|
|
public RuntimeAssembly(Assembly assembly)
|
|
{
|
|
Throw.IfNull(assembly, "assembly");
|
|
|
|
m_assembly = Reference<Assembly>.FromValue(assembly);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Properties
|
|
|
|
/// <summary>
|
|
/// Returns whether the underlying assembly has been loaded yet.
|
|
/// </summary>
|
|
public bool IsLoaded
|
|
{
|
|
get
|
|
{
|
|
return m_assembly.IsInitialized;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the name of the assembly to be loaded.
|
|
/// </summary>
|
|
public string AssemblyName
|
|
{
|
|
get
|
|
{
|
|
if (IsLoaded)
|
|
{
|
|
return m_assembly.Value.FullName;
|
|
}
|
|
|
|
return m_assemblyName;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the assembly referenced by this Runtime, loading it first if needed.
|
|
/// </summary>
|
|
public Assembly Assembly
|
|
{
|
|
get
|
|
{
|
|
return m_assembly.Value;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Methods
|
|
|
|
private Assembly LoadAssembly(string assemblyName, string hintPath)
|
|
{
|
|
AddinManager.Log(new Message("Loading Addin Assembly {0}.", assemblyName) { Importance = MessageImportance.Low });
|
|
|
|
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
|
|
{
|
|
if (IsCurrentAssembly(assembly))
|
|
{
|
|
return assembly;
|
|
}
|
|
}
|
|
|
|
try
|
|
{
|
|
if (assemblyName[0] == ':')
|
|
{
|
|
m_assembly = Assembly.Load(assemblyName.Substring(1));
|
|
}
|
|
else
|
|
{
|
|
if (hintPath == null)
|
|
{
|
|
m_assembly = Assembly.LoadFrom(assemblyName);
|
|
}
|
|
else
|
|
{
|
|
string file = FindExistingPath(Path.Combine(hintPath, assemblyName),
|
|
Path.Combine(Path.GetDirectoryName(hintPath), assemblyName));
|
|
return Assembly.LoadFrom(file);
|
|
}
|
|
}
|
|
}
|
|
catch (FileNotFoundException ex)
|
|
{
|
|
AddinManager.Log(new Error(LogCategory.Error, "The Addin Assembly {0} could not be loaded: {1}", assemblyName, ex.Message));
|
|
throw;
|
|
}
|
|
catch (FileLoadException ex)
|
|
{
|
|
AddinManager.Log(new Error(LogCategory.Error, "The Addin Assembly {0} could not be loaded: {1}", assemblyName, ex.Message));
|
|
throw;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private bool IsCurrentAssembly(Assembly assembly)
|
|
{
|
|
// Compares without the extension first since assembly.Location may fail if assembly is a generated assembly.
|
|
return assembly.GetName().Name == Path.GetFileNameWithoutExtension(AssemblyName) &&
|
|
Path.GetFileName(assembly.Location) == AssemblyName;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Takes the alternative path if the first one does not exists.
|
|
/// </summary>
|
|
/// <param name="path"></param>
|
|
/// <param name="alternative"></param>
|
|
/// <returns></returns>
|
|
private static string FindExistingPath(string path, string alternative)
|
|
{
|
|
try
|
|
{
|
|
if (!File.Exists(path) && File.Exists(alternative))
|
|
return alternative;
|
|
}
|
|
catch { }
|
|
|
|
return path;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates an instance of an object whose type is defined in this <see cref="Runtime"/>.
|
|
/// </summary>
|
|
/// <param name="typeName">The full name of the type to create.</param>
|
|
/// <param name="args">Arguments to be passed to the constructor of the object.</param>
|
|
/// <returns>The created object, or null if it could not be created.</returns>
|
|
public object CreateInstance(string typeName, params object[] args)
|
|
{
|
|
if (Assembly == null || string.IsNullOrEmpty(typeName))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return Assembly.CreateInstance(typeName, true, BindingFlags.Default, null, args, System.Globalization.CultureInfo.CurrentCulture, null);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a specific type within this <see cref="Runtime"/>.
|
|
/// </summary>
|
|
/// <param name="typeName">Full name of the type.</param>
|
|
/// <returns>A <see cref="Type"/> corresponding to the given type, or null if not found.</returns>
|
|
public Type GetType(string typeName)
|
|
{
|
|
if (Assembly == null || string.IsNullOrEmpty(typeName))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return Assembly.GetType(typeName, false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Overriden.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public override string ToString()
|
|
{
|
|
return $"[Runtime: Assembly={AssemblyName}]";
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|