using System;
using System.Collections.Generic;
using System.Reflection;
using System.CodeDom.Compiler;
using System.IO;
namespace ExtensionLoader
{
///
/// Exception thrown when there is a problem with an extension
///
public class ExtensionException : Exception
{
public ExtensionException(string message)
: base(message)
{
}
public ExtensionException(string message, Exception innerException)
: base(message, innerException)
{
}
}
public static class ExtensionLoader
{
/// Currently loaded extensions
public static List Extensions;
///
public static CodeDomProvider CSCompiler;
///
public static CompilerParameters CSCompilerParams;
static ExtensionLoader()
{
Extensions = new List();
CSCompiler = CodeDomProvider.CreateProvider("C#");
CSCompilerParams = new CompilerParameters();
CSCompilerParams.GenerateExecutable = false;
CSCompilerParams.GenerateInMemory = true;
if (System.Diagnostics.Debugger.IsAttached)
CSCompilerParams.IncludeDebugInformation = true;
else
CSCompilerParams.IncludeDebugInformation = false;
}
///
/// Load extensions within the current assembly, from assembly files in
/// a given directory, or from source code files in a given directory
///
/// Main assembly to load extensions from
/// Directory to load assembly and source code
/// extensions from
/// Object that owns the extensions. A reference to
/// this is passed to the constructor of each extension
/// List of assemblies the
/// extensions need references to
/// Search pattern for extension
/// dlls, for example MyApp.Extension.*.dll
/// Search pattern for extension
/// source code files, for example MyApp.Extension.*.cs
/// The object containing the
/// assignable interfaces
/// A list of interface types and
/// interface references to assign extensions to
public static void LoadAllExtensions(Assembly assembly, string path, TOwner owner,
List referencedAssemblies, string assemblySearchPattern, string sourceSearchPattern,
object assignablesParent, Dictionary assignableInterfaces)
{
// Add referenced assemblies to the C# compiler
CSCompilerParams.ReferencedAssemblies.Clear();
if (referencedAssemblies != null)
{
for (int i = 0; i < referencedAssemblies.Count; i++)
CSCompilerParams.ReferencedAssemblies.Add(referencedAssemblies[i]);
}
// Load internal extensions
LoadAssemblyExtensions(assembly, owner);
// Load extensions from external assemblies
List extensionNames = ListExtensionAssemblies(path, assemblySearchPattern);
foreach (string name in extensionNames)
LoadAssemblyExtensions(Assembly.LoadFile(name), owner);
// Load extensions from external code files
extensionNames = ListExtensionSourceFiles(path, sourceSearchPattern);
foreach (string name in extensionNames)
{
CompilerResults results = CSCompiler.CompileAssemblyFromFile(CSCompilerParams, name);
if (results.Errors.Count == 0)
LoadAssemblyExtensions(results.CompiledAssembly, owner);
else
throw new ExtensionException("Error(s) compiling " + name);
}
if (assignableInterfaces != null)
{
// Assign extensions to interfaces
foreach (KeyValuePair kvp in assignableInterfaces)
{
Type type = kvp.Key;
FieldInfo assignable = kvp.Value;
for (int i = 0; i < Extensions.Count; i++)
{
IExtension extension = Extensions[i];
if (extension.GetType().GetInterface(type.Name) != null)
assignable.SetValue(assignablesParent, extension);
}
}
// Check for unassigned interfaces
foreach (KeyValuePair kvp in assignableInterfaces)
{
if (kvp.Value.GetValue(assignablesParent) == null)
throw new ExtensionException("Unassigned interface " + kvp.Key.Name);
}
}
}
public static List ListExtensionAssemblies(string path, string searchPattern)
{
List plugins = new List();
string[] files = Directory.GetFiles(path, searchPattern);
foreach (string f in files)
{
try
{
Assembly a = Assembly.LoadFrom(f);
System.Type[] types = a.GetTypes();
foreach (System.Type type in types)
{
if (type.GetInterface("IExtension") != null)
{
plugins.Add(f);
break;
}
}
}
catch (Exception e)
{
throw new ExtensionException("Unrecognized extension " + f, e);
}
}
return plugins;
}
public static List ListExtensionSourceFiles(string path, string searchPattern)
{
List plugins = new List();
string[] files = Directory.GetFiles(path, searchPattern);
foreach (string f in files)
{
if (File.ReadAllText(f).Contains("IExtension"))
plugins.Add(f);
}
return plugins;
}
public static void LoadAssemblyExtensions(Assembly assembly, TOwner owner)
{
Type[] constructorParams = new Type[] { typeof(TOwner) };
foreach (Type t in assembly.GetTypes())
{
try
{
if (t.GetInterface("IExtension") != null)
{
ConstructorInfo info = t.GetConstructor(constructorParams);
IExtension extension = (IExtension)info.Invoke(new object[] { owner });
Extensions.Add(extension);
}
}
catch (Exception e)
{
throw new ExtensionException(String.Format(
"Failed to load IExtension {0} from assembly {1}", t.FullName, assembly.FullName), e);
}
}
}
}
}