Question

We are testing the MAF addin to use as our addin framework. But we get stuck at a basic issue. Can we use serializable types as IContract parameters?

Both the contract and the parameter type is defined in the same assembly:

    public interface IHostContract : IContract
    {
        void SetCurrent(TheValue tagValue);   // does not work
        void SetCurrentSimple(double value);  // works fine
    }

    [Serializable]
    public sealed class TheValue
    {
       public int Id { get; set; }

       public double Value { get; set; }
    }

We are able to get everything up and running. Calling the SetCurrent results in an exception: AppDomainUnloadedException :

The application domain in which the thread was running has been unloaded.

Server stack trace: 
   at System.Threading.Thread.InternalCrossContextCallback(Context ctx, IntPtr ctxID, Int32 appDomainID, InternalCrossContextDelegate ftnToCall, Object[] args)
   at System.Runtime.Remoting.Channels.CrossAppDomainSink.DoTransitionDispatch(Byte[] reqStmBuff, SmuggledMethodCallMessage smuggledMcm, SmuggledMethodReturnMessage& smuggledMrm)
   at System.Runtime.Remoting.Channels.CrossAppDomainSink.SyncProcessMessage(IMessage reqMsg)

Exception rethrown at [0]: 

Loading and running of plugins:

public void Run(string PluginFolder)
{
    AddInStore.Rebuild(PluginFolder);
    Collection<AddInToken> tokens = AddInStore.FindAddIns(typeof(Plugins.IPlugin), PluginFolder);

    foreach (var token in tokens)
    {
        Console.WriteLine("Found addin: " + token.Name + " v" + token.Version);
        try
        {
            var plugin = token.Activate<Plugins.IPlugin>(AddInSecurityLevel.FullTrust);
            plugin.PluginHost = this;
            plugin.Start();
            plugin.Stop();
        }
        catch (Exception exception)
        {
            Console.WriteLine("Error starting plugin: " + exception.Message);
        }
    }
}

Plugin:

[System.AddIn.AddIn("Plugin1", Version = "1.0.0")]
public class Plugin1 : IPlugin
{
    private int started;

    public Plugin1()
    {
        Console.WriteLine("Plugin 1 created");
    }

    public void Start()
    {
        Console.WriteLine("Plugin 1 started: {0}", started);
        started++;

        var tagValue = new TheValue { Id = 1, Value = 4.32 };
        PluginHost.SetCurrent(tagValue);
    }

    public void Stop()
    {
        Console.WriteLine("Plugin 1 stopped");
    }

    public IPluginHost PluginHost { get; set; }
}
Was it helpful?

Solution

You need to follow the guidelines for lifetime management. In each contract-to-view adapter you need to store a ContractHandle. This is necessary for the lifetime management of the proxies that System.AddIn implicitly creates (remember that System.AddIn is based on .NET Remoting).

Taken from MSDN:

The ContractHandle is critical to lifetime management. If you fail to keep a reference to the ContractHandle object, garbage collection will reclaim it, and the pipeline will shut down when your program does not expect it. This can lead to errors that are difficult to diagnose, such as AppDomainUnloadedException. Shutdown is a normal stage in the life of a pipeline, so there is no way for the lifetime management code to detect that this condition is an error.

If you decide to use System.AddIn in your application then you need the PipelineBuilder. In the discussion board you will find help on how to make it work with VS2010 (it is quite simple). I guess it will not be hard to make it work with VS2012 as well. This tool will take care all the System.AddIn intricacies for you. All you will need to do is create the contracts and PipelineBuilder will create the rest of the pipeline for you. It will also make sure that you follow the guidelines on how to build your contracts which is the most important thing with System.AddIn.

Before you decide on an add-in framework, don't forget to check out MEF. MEF can be used with Autofac and provide versioning through adapters. IMHO, the only reason that anyone should choose System.AddIn is for the isolation feature. But note that 100% isolation is only when the add-ins are loaded in a different process from the host.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top