AppDomain CreateDomain
An example of running a assembly in another AppDomain. Here we're using a runner class (it must inherit MarshalByRefObject) which loads itself in the other domain and executes the code. Depenedencies are assumed to be in the same folder, and are resolved using AssemblyResolve handler. Creating a new domain allows you to use an App.config (even for a dll). There's a hack for swapping config files in the same domain too.
using System;
using System.Configuration;
using System.IO;
using System.Reflection;
namespace Library.Reflection
{
/// <summary>
/// Run a type in an assembly - call static <see cref="RunInNewDomain"/>
/// </summary>
/// <remarks>
/// Note this has to inherit from MarshalByRefObject.
/// <para/>
/// Calls a static method named Run.
/// </remarks>
/// <example>
/// <code>
/// string typeName = "TestMe.RunMe";
/// string assemblyPath = @"C:\TestMe\bin\Debug\TestMe.dll";
/// AssemblyRunner.RunInNewDomain(typeName, assemblyPath);
/// //calls TestMe.RunMe.Run();
/// </code>
/// </example>
[Serializable]
public class AssemblyRunner : MarshalByRefObject
{
/// <summary>
/// Runs the specified type name in a new domain.
/// </summary>
/// <param name="assemblyPath">The assembly path.</param>
/// <param name="typeName">Name of the type.</param>
public static void RunInNewDomain(string assemblyPath, string typeName)
{
if (!File.Exists(assemblyPath))
throw new FileNotFoundException("File not found", assemblyPath);
//create a new domain with a config
var setup = new AppDomainSetup();
//directory where the dll and dependencies are
setup.ApplicationBase = Path.GetDirectoryName(assemblyPath);
//try to load a configuration file
if (File.Exists(assemblyPath + ".config"))
setup.ConfigurationFile = assemblyPath + ".config";
setup.ApplicationName = "AssemblyRunner";
//Create the new domain
var domain = AppDomain.CreateDomain("AssemblyRunner", null, setup);
try
{
//load this assembly/ type into the new domain
var runner =
(AssemblyRunner)domain.CreateInstanceFromAndUnwrap(
Assembly.GetExecutingAssembly().Location,
typeof(AssemblyRunner).FullName);
//other instance of this class in new domain loads dll
runner.LoadDll(assemblyPath, typeName);
}
finally
{
//unload domain
AppDomain.Unload(domain);
}
}
private string _oldConfigPath;
/// <summary>
/// Loads the DLL (this should be run in a different domain)
/// </summary>
/// <param name="filePath">The file path.</param>
/// <param name="typeName">Name of the type.</param>
public void LoadDll(string filePath, string typeName)
{
if (!File.Exists(filePath)) return;
CheckForConfig(filePath);
string location = Path.GetDirectoryName(filePath);
//resolve any dependencies
AppDomain.CurrentDomain.AssemblyResolve +=
delegate(object sender, ResolveEventArgs args)
{
string findName = args.Name;
string simpleName = new AssemblyName(findName).Name;
string assemblyPath = Path.Combine(location, simpleName) + ".dll";
if (File.Exists(assemblyPath))
return Assembly.LoadFrom(assemblyPath);
//can't find it
return null;
};
//load the assembly into bytes and load it
byte[] assemblyBytes = File.ReadAllBytes(filePath);
Assembly a = Assembly.Load(assemblyBytes);
//find the type in the assembly
Type t = a.GetType(typeName, true);
//find the method "Run"
var run = t.GetMethod("Run");
try
{
//run it (static method, no arguments)
run.Invoke(null, new object[0]);
}
finally
{
RestoreConfig();
}
}
private void RestoreConfig()
{
//remember to revert to original config file
if (!string.IsNullOrEmpty(_oldConfigPath))
SwapConfigFile(_oldConfigPath);
}
private void CheckForConfig(string filePath)
{
//if this is called in the current domain, the config will be wrong
string configFile = filePath + ".config";
if (File.Exists(configFile))
{
var existingConfig = AppDomain.CurrentDomain.GetData("APP_CONFIG_FILE").ToString();
if (!configFile.Equals(existingConfig, StringComparison.OrdinalIgnoreCase))
{
_oldConfigPath = existingConfig;
SwapConfigFile(configFile);
}
}
}
private static void SwapConfigFile(string configFile)
{
AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", configFile);
//reset the private state flag in ConfigurationManager
FieldInfo fiInit = typeof(ConfigurationManager).GetField(
"s_initState",
BindingFlags.NonPublic | BindingFlags.Static);
if (fiInit != null)
fiInit.SetValue(null, 0);
}
}
}