Subscribe

RSS Feed (xml)

Instantiate a Type in a Different Application Domain

The ExecuteAssembly method discussed in last post 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 members within the application domain.
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 methods implement overloads that allow you to provide arguments to use any constructor.
The CreateInstance method loads a named assembly into the application domain using the process described for the Assembly.Load method previous post. CreateInstance then instantiates a named type and returns a reference to the new object wrapped in an ObjectHandle (described in this post). 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 here for the Assembly.LoadFrom method.

Tip 
AppDomain also provides two convenience methods named CreateInstanceAndUnwrap and CreateInstanceFromAndUnwrap that automatically extract the reference of the instantiated object from the returned ObjectHandle object; you must cast the returned object to the correct type.
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 here 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 demonstrates the use of a simplified controller class named PluginManager. When instantiated in an application domain, PluginManager allows you to instantiate classes that implement the IPlugin interface, start and stop those plug-ins, and return a list of currently loaded plug-ins.
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();
    }
}

No comments:

Post a Comment

Archives

LocalsAdda.com-Variety In Web World

Fun Mail - Fun in the Mail