You need to execute an assembly in an application domain other than the current one.
Call the
ExecuteAssembly
method of the
AppDomain
object that represents the application domain, and specify the
If you have an executable assembly that you want to load and run
in an application domain, the
ExecuteAssembly
method
provides the
The ExecuteAssembly method loads the specified assembly and executes the method defined in metadata as the assembly's entry point (usually the Main method). If the specified assembly isn't executable, ExecuteAssembly throws a System.Runtime.InteropServices.COMException . The CLR doesn't start execution of the assembly in a new thread, so control won't return from the ExecuteAssembly method until the newly executed assembly exits. Because the ExecuteAssembly method loads an assembly using partial information (only the filename), the CLR won't use the GAC or probing to resolve the assembly. (See recipe 3.5 for more information.)
The following example
using System;
public class ExecuteAssemblyExample {
public static void Main(string[] args) {
// For the purpose of this example, if this assembly is executing
// in an AppDomain with the friendly name "NewAppDomain", do not
// create a new AppDomain. This avoids an infinite loop of
// AppDomain creation.
if (AppDomain.CurrentDomain.FriendlyName != "NewAppDomain") {
// Create a new application domain
AppDomain domain = AppDomain.CreateDomain("NewAppDomain");
// Execute this assembly in the new application domain and
// pass the array of command-line arguments.
domain.ExecuteAssembly("ExecuteAssemblyExample.exe",
null, args);
}
// Display the command-line arguments to the screen prefixed with
// the friendly name of the AppDomain.
foreach (string s in args) {
Console.WriteLine(AppDomain.CurrentDomain.FriendlyName +
" : " + s);
}
}
}
You need to instantiate a type in an application domain other than the current one.
Call the CreateInstance or CreateInstanceFrom method of the AppDomain object that represents the target application domain.
The
ExecuteAssembly
method discussed in recipe 3.6 is straightforward to use, but when you are developing sophisticated applications that make use of application domains, you are likely to want more control over the loading of assemblies, instantiation of types, and the invocation of object
The
CreateInstance
and
CreateInstanceFrom
methods provide a variety of overloads that offer fine-grained control over the process of object instantiation. The simplest overloads assume use of a type's default constructor, but both
The CreateInstance method loads a named assembly into the application domain using the process described for the Assembly.Load method in recipe 3.5. CreateInstance then instantiates a named type and returns a reference to the new object wrapped in an ObjectHandle (described in recipe 3.3). The CreateInstanceFrom method also instantiates a named type and returns an ObjectHandle wrapped object reference; however, CreateInstanceFrom loads the specified assembly into the application domain using the process described in recipe 3.5 for the Assembly.LoadFrom method.
| Tip |
AppDomain
also provides two convenience methods named
CreateInstanceAndUnwrap
and
CreateInstanceFromAndUnwrap
that automatically extract the reference of the
|
Be aware that if you use CreateInstance or CreateInstanceFrom to instantiate MBV types in another application domain, the object will be created but the returned object reference won't refer to that object. Because of the way MBV objects cross application domain boundaries, the reference will refer to a copy of the object created automatically in the local application domain. Only if you create an MBR type will the returned reference refer to the object in the other application domain. (See recipe 3.2 for more details about MBV and MBR types.)
A common technique to simplify the management of application domains is to use a controller class. A controller class is a custom MBR type. You create an application domain and then instantiate your controller class in the application domain using CreateInstance . The controller class implements the functionality required by your application to manipulate the application domain and its contents. This could include the loading of assemblies, creating further application domains, cleaning up prior to the deletion of the application domain, or enumerating program elements (something you can't normally do from outside an application domain).
The following example
using System;
using System.Reflection;
using System.Collections;
using System.Collections.Specialized;
// A common interface that all plug-ins must implement.
public interface IPlugin {
void Start();
void Stop();
}
// A simple IPlugin implementation to demonstrate the PluginManager
// controller class.
public class SimplePlugin : IPlugin {
public void Start() {
Console.WriteLine(AppDomain.CurrentDomain.FriendlyName +
": SimplePlugin starting...");
}
public void Stop() {
Console.WriteLine(AppDomain.CurrentDomain.FriendlyName +
": SimplePlugin stopping...");
}
}
// The controller class, which manages the loading and manipulation
// of plug-ins in its application domain.
public class PluginManager : MarshalByRefObject {
// A ListDictionary to hold keyed references to IPlugin instances.
private ListDictionary plugins = new ListDictionary();
// Default constructor.
public PluginManager() {}
// Constructor that loads a set of specified plug-ins on creation.
public PluginManager(ListDictionary pluginList) {
// Load each of the specified plug-ins.
foreach (string plugin in pluginList.Keys) {
this.LoadPlugin((string)pluginList[plugin], plugin);
}
}
// Load the specified assembly and instantiate the specified
// IPlugin implementation from that assembly.
public bool LoadPlugin(string assemblyName, string pluginName) {
try {
// Load the named private assembly.
Assembly assembly = Assembly.Load(assemblyName);
// Create the IPlugin instance, ignore case.
IPlugin plugin =
(IPlugin)assembly.CreateInstance(pluginName, true);
if (plugin != null) {
// Add new IPlugin to ListDictionary
plugins[pluginName] = plugin;
return true;
} else {
return false;
}
} catch {
return false;
}
}
public void StartPlugin(string plugin) {
// Extract the IPlugin from the ListDictionary and call Start.
((IPlugin)plugins[plugin]).Start();
}
public void StopPlugin(string plugin) {
// Extract the IPLugin from the ListDictionary and call Stop.
((IPlugin)plugins[plugin]).Stop();
}
public ArrayList GetPluginList() {
// Return an enumerable list of plug-in names. Take the keys
// and place them in an ArrayList, which supports marshal-by-value.
return new ArrayList(plugins.Keys);
}
}
public class CreateInstanceExample {
public static void Main() {
// Create a new application domain.
AppDomain domain1 = AppDomain.CreateDomain("NewAppDomain1");
// Create a PluginManager in the new application domain using
// the default constructor.
PluginManager manager1 =
(PluginManager)domain1.CreateInstanceAndUnwrap(
"CreateInstanceExample", "PluginManager");
// Load a new plug-in into NewAppDomain1.
manager1.LoadPlugin("CreateInstanceExample", "SimplePlugin");
// Start and stop the plug-in in NewAppDomain1.
manager1.StartPlugin("SimplePlugin");
manager1.StopPlugin("SimplePlugin");
// Create a new application domain.
AppDomain domain2 = AppDomain.CreateDomain("NewAppDomain2");
// Create a ListDictionary containing a list of plug-ins to create.
ListDictionary pluginList = new ListDictionary();
pluginList["SimplePlugin"] = "CreateInstanceExample";
// Create a PluginManager in the new application domain and
// specify the default list of plug-ins to create.
PluginManager manager2 =
(PluginManager)domain1.CreateInstanceAndUnwrap(
"CreateInstanceExample", "PluginManager", true, 0,
null, new object[] {pluginList}, null, null, null);
// Display the list of plug-ins loaded into NewAppDomain2.
Console.WriteLine("Plugins in NewAppDomain2:");
foreach (string s in manager2.GetPluginList()) {
Console.WriteLine(" - " + s);
}
// Wait to continue
Console.ReadLine();
}
}