Steps to develop an AddIn using Managed AddIn Framework (MAF)

I had blogged earlier about Managed AddIn Framework and the resources to learn the basics. Making use of System.AddIn framework features can appear complex for beginners. In this example we develop a host application and AddIn using Managed AddIn Framework.

Source Code: You can download the source code for this example below.

 Source Code 

The diagram below demonstrates how we are going to implement each component in the Managed AddIn Framework in our sample application.

clip_image001

When you activate an Addin from host, you can load the Addin in its own application domain or in an existing application domain or even in a new process separate from host’s process.

Step -1 – HostView

Create a solution with the following solution folders. Create a class library project named HostView under the Pipeline solution folder.

It is important to set the project settings. For HostView, change the output path to ..\output\ (Visual Studio default will be \bin\debug or \bin\release)

The HostView is an abstract class with methods corresponding to the methods exposed by the Addin. In this example I go for a simple method which returns the sum of two integer numbers.

namespace Demo.HostView
{
    public abstract class HostView
    {
        public abstract int ComputeSum(int a, int b);
    }
}

Step -2 – Create Host Application

Create a Windows Application project named SampleHost under the HostApplication solution folder.

clip_image003 

  1. Change the output path to ..\output\

  2. Add reference to assemblies System.AddIn and System.AddIn.Contract

  3. Add reference to project HostView

The host application UI is given below. It aims to load our addin and use it for computing the sum of numbers supplied from the host application.

clip_image005

Add the following code in the Host to load the AddIn

Collection<AddInToken> _addinTokens;
Collection<HostView> _addinList = new Collection<HostView>();
HostView calcAddin;

private void btn_Load_Click(object sender, EventArgs e)
{
    LoadAddins();
}

public void LoadAddins()
{
    // Set root directory for finding Add-ins
    string addinRoot = Environment.CurrentDirectory;

    // Rebuild the add-ins and pipeline components 
    AddInStore.Rebuild(addinRoot);

    // Find add-ins of type HostView
    _addinTokens = AddInStore.FindAddIns(typeof(HostView), addinRoot);

    foreach (AddInToken addinToken in _addinTokens)
    {
        // Activate the add-in
        _addinList.Add(
            addinToken.Activate<HostView>(AddInSecurityLevel.Internet));

        // Display Addin-Details
        richText1.AppendText(String.Format("Loaded Add-in {0} Version {1}",
            addinToken.Name, addinToken.Version));
    }

    calcAddin = _addinList[0];
}
And we are ready to use the addin instance. 
private void btn_Add_Click(object sender, EventArgs e)
{
    if (calcAddin != null)
    {
        //Invoking the AddIn method
        int sum = calcAddin.ComputeSum(
            Convert.ToInt32(textBox1.Text), 
            Convert.ToInt32(textBox2.Text));
        lblResult.Text = sum.ToString();
    }
    else
        MessageBox.Show("AddIn is not loaded"); 
}

Step -3 –Create AddIn View

Create a class library project named AddInView under the Pipeline solution folder.

  • Change the output path to ..\output\AddInViews\

  • Add reference to assemblies System.AddIn and System.AddIn.Contract

The AddInView is an abstract class with methods corresponding to the methods exposed by the Addin. Note that this class should have the attribute [AddInBase]

using System.AddIn.Pipeline;
namespace Demo.AddinView
{
    [AddInBase]
    public abstract class AddInView
    {
        public abstract int ComputeSum(int a, int b);
    }
}

Step – 4 – Create Contract

The contract in the middle of the pipeline is loaded into both Host’s app domain and AddIn’s app domain.

Create a class library and;

  • Change the output path to ..\output\Contracts\
  • Add reference to assemblies System.AddIn and System.AddIn.Contract

 

clip_image007

 

The contract interface should have the [AddInContract] attribute as shown below.

using System.AddIn.Contract;
using System.AddIn.Pipeline;

namespace Demo.Contracts
{
    [AddInContract]
    public interface IContracts:IContract
    {
        int ComputeSum(int a, int b);
    }
}

Step -5 –Create Host Side Adapter

The host-side converts the flow of types between Host View and the contract.

Create a class library project named HostSideAdapters under the Pipeline solution folder

 

  • Change the output path to ..\output\HostSideAdapters\

  • Add reference to assemblies System.AddIn and System.AddIn.Contract

  • Add reference to project Contracts, set Copy Local property to False

  • Add reference to project HostView, set Copy Local property to False

 

clip_image009

Note the [HostAdapter] attribute of the ContractToHostViewAdapter Class below.

using System.AddIn.Pipeline;
using Demo.Contracts;
namespace Demo.HostSideAdapters
{
    [HostAdapter]
    public class ContractToHostViewAdapter : HostView.HostView
    {
        private IContracts _calcContract;
        private ContractHandle _handle;
        public ContractToHostViewAdapter(IContracts contract)
        {
            _calcContract = contract;
            _handle = new ContractHandle(_calcContract);
        }
        public override int ComputeSum(int a, int b)
        {
            return this._calcContract.ComputeSum(a,b);
        }
    }
}

 

Step -6 – Create AddIn Side Adapter

Create a class library project named AddInSideAdapters under the Pipeline solution folder

 

  • Change the output path to ..\output\AddInSideAdapters\

  • Add reference to assemblies System.AddIn and System.AddIn.Contract

  • Add reference to project Contracts, set Copy Local property to False

  • Add reference to project AddInView, set Copy Local property to False

 

clip_image011

Note the [AddInAdapter] attribute of the AddInViewToContractAdapter Class below. The adapter inherits from System.AddIn.Pipeline.ContractBase class and implements  IContracts interface.

using System.AddIn.Pipeline;
using Demo.AddinView;
using Demo.Contracts;
namespace Demo.AddInSideAdapters
{
    [AddInAdapter]
    public class AddInViewToContractAdapter: ContractBase, IContracts
    {
        private AddInView _view;
        public AddInViewToContractAdapter(AddInView view)
        {
            this._view = view;
        }
        public int ComputeSum(int a, int b)
        {
            return this._view.ComputeSum(a,b);
        }
     }
}

Step -7 –Implement AddIn

Create a class library project named CalcAdd under the AddIn solution folder

Change the output path to ..\output\AddIns\CalcAddin\  (If you have multiple Addin, each AddIn would be deployed into separate directories)

  • Add reference to assemblies System.AddIn and System.AddInx.Contract

  • Add reference to project AddInView, set Copy Local property to False

clip_image013

namespace Demo.CalcAddIn
{
    [System.AddIn.AddIn("Demo Add-In", 
     Version = "1.0.0.0",
     Description = "Description of Demo AddIn", 
     Publisher = "XYZ Company")]
    public class CalcAddIn : Demo.AddinView.AddInView
    {
        public override int ComputeSum(int a, int b)
        {
            return a+b;
        }
    }
}

Step -8 – Run the host application

Build the solution and run the host application. You should see the following result.

clip_image015

Alternately you can download the source code from the link below and run it.

Source Code

Advertisements

Pipeline Builder Tool – Making the AddIin development easy

The CLR Add-in team came up with a very useful tool to save the coding effort and complexity associated with add-in development. The tool is available in CodePlex.

I installed the pipeline builder add-in to Visual Studio 2008 and it works great!  Matt Hidinger in his blog describes the steps to use pipeline builder with a sample.

The links that may be of interest to add-in developers are :

Managed AddIn Framework (MAF)

This week, I have started exploring the add-in programming model for plug-in development.  Developers can use it to develop add-ins and activate them in their host application. The model is based on construction of a communication pipeline between the host and the add-in.
Managed Add-in Framework
Managed Add-in Framework
The assemblies for these segments are not required to be in the same application domain. You can load an add-in into its own new application domain, into an existing application domain, or even into the host’s application domain. You can load multiple add-ins into the same application domain, which enables the add-ins to share resources and security contexts.

The add-in model supports, and recommends, an optional boundary between the host and the add-in, which is called the isolation boundary (also known as a remoting boundary). This boundary can be an application domain or process boundary.

The contract segment in the middle of the pipeline is loaded into both the host’s application domain and the add-in’s application domain. The contract defines the virtual methods that the host and the add-in use to exchange types with each other.

To pass through the isolation boundary, types must be either contracts or serializable types. Types that are not contracts or serializable types must be converted to contracts by the adapter segments in the pipeline.

The view segments of the pipeline are abstract base classes or interfaces that provide the host and the add-in with a view of the methods that they share, as defined by the contract. Source : MSDN