Walkthrough: Enabling Backward Compatibility as Your Host Changes

This walkthrough describes version 2 of the pipeline that is described in Walkthrough: Creating an Extensible Application. Version 2 includes more calculation features by providing a comma-delimited string of arithmetic operations that it supports to the host. The host can then choose an operation and submit an equation for the add-in to calculate.

The pipeline has a new host and a new contract. To enable version 1 of the add-in to work with a new host and contract, the pipeline includes the add-in view used for version 1 and an add-in-side adapter that converts the data from the older add-in view to the new contract. The following illustration shows how both add-ins can work with the same host.

New host, old add-ins

Pipeline scenario: new host, old add-ins.

This pipeline is also described in Add-in Pipeline Scenarios.

This walkthrough describes the following tasks:

  • Creating a Visual Studio solution.

  • Creating the pipeline directory structure.

  • Creating the contract and views.

  • Creating the add-in-side adapter, which includes adapters for the new version of the add-in and for the version 1 add-in.

  • Creating the host-side adapter.

  • Creating the host.

  • Creating the add-in.

  • Deploying the pipeline.

  • Running the host application.

The walkthrough also demonstrates the use of abstract base classes to define views, and shows that such views are compatible with views that are defined by interfaces. Using interfaces is recommended.

Note

Some of the code shown in this walkthrough contains extraneous namespace references. The walkthrough steps accurately reflect the references required in Visual Studio.

You can find additional sample code, and customer technology previews of tools for building add-in pipelines, at the Managed Extensibility and Add-In Framework site on CodePlex.

Prerequisites

You need the following components to complete this walkthrough:

  • Visual Studio.

  • The version 1 pipeline described in Walkthrough: Creating an Extensible Application. Because version 2 uses pipeline segments that were developed in version 1, you must develop and deploy the version 1 pipeline before performing the steps in this topic.

Creating a Visual Studio Solution

Use a solution in Visual Studio to contain the projects of your pipeline segments.

To create the pipeline solution

  1. In Visual Studio, create a new project named Calc2Contract. Base it on the Class Library template.

  2. Name the solution CalculatorV2.

Creating the Pipeline Directory Structure

The add-in model requires the pipeline segment assemblies to be placed in a specified directory structure.

To create the pipeline directory structure

  • If you have not already done so, add the CalcV2 folder to the pipeline folder structure you created in Walkthrough: Creating an Extensible Application. The CalcV2 folder will hold the new version of the add-in.

    Pipeline
      AddIns
        CalcV1
        CalcV2
      AddInSideAdapters
      AddInViews
      Contracts
      HostSideAdapters
    

    It is not necessary to put the pipeline folder structure in your application folder; it is done only for convenience in these walkthroughs. If you put your pipeline folder structure in a different location in the first walkthrough, follow the same pattern for this walkthrough. See the discussion of pipeline directory requirements in Pipeline Development Requirements.

Creating the Contract and Views

The contract segment for this pipeline defines the ICalc2Contract interface, which has the following two methods:

  • The GetAvailableOperations method.

    This method returns a string of the mathematical operations that the add-in supports to the host. Version 2 supports five operations, and this method returns the string "+,-,*,/,**" where "**" represents the Pow operation.

    In the add-in and host views, this method is named Operations instead of GetAvailableOperations.

    You can expose methods on the contract as properties on the views by converting the method call into a property on the adapter.

  • The Operate method.

    The host calls this method to submit an equation for the add-in to calculate and return the result.

To create the contract

  1. In the Visual Studio solution named CalculatorV2, open the Calc2Contract project.

  2. In Solution Explorer, add references to the following assemblies to the Calc2Contract project:

    System.AddIn.Contract.dll

    System.AddIn.dll

  3. In Solution Explorer, exclude the default class that is added to new Class Library projects.

  4. Add a new item to the project, using the Interface template. In the Add New Item dialog box, name the interface ICalc2Contract.

  5. In the interface file, add namespace references to System.AddIn.Contract and System.AddIn.Pipeline.

  6. Use the following code to complete the contract segment. Note that this interface must have the AddInContractAttribute attribute.

    
    Imports Microsoft.VisualBasic
    Imports System
    Imports System.Collections.Generic
    Imports System.Text
    Imports System.AddIn.Contract
    Imports System.AddIn.Pipeline
    
    Namespace CalculatorContracts
        <AddInContract()> _
        Public Interface ICalc2Contract
            Inherits IContract
            Function GetAvailableOperations() As String
            Function Operate(ByVal operation As String, ByVal a As Double, ByVal b As Double) As Double
        End Interface
    End Namespace
    
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.AddIn.Contract;
    using System.AddIn.Pipeline;
    
    namespace CalculatorContracts
    {
        [AddInContract]
        public interface ICalc2Contract : IContract
        {      
            string GetAvailableOperations();
            double Operate(String operation, double a, double b);
        }
    }
    

Because the add-in view and the host view have the same code, you can easily create the views at the same time. They differ by only one factor: the add-in view requires the AddInBaseAttribute attribute; the host view of the add-in does not require any attributes.

To create the add-in view for version 2

  1. Add a new project named Calc2AddInView to the CalculatorV2 solution. Base it on the Class Library template.

  2. In Solution Explorer, add a reference to System.AddIn.dll to the Calc2AddInView project.

  3. Rename the class Calculator2.

  4. In the class file, add a namespace reference to System.AddIn.Pipeline.

  5. Make Calculator2 an abstract class (MustInherit class in Visual Basic).

  6. Use the following code for this add-in view. Note that this class must have the AddInBaseAttribute attribute.

    
    Imports Microsoft.VisualBasic
    Imports System
    Imports System.Collections.Generic
    Imports System.Text
    Imports System.AddIn.Pipeline
    
    Namespace CalcAddInViews
        <AddInBase()> _
        Public MustInherit Class Calculator2
            Public MustOverride ReadOnly Property Operations() As String
    
            Public MustOverride Function Operate(ByVal operation As String, ByVal a As Double, ByVal b As Double) As Double
        End Class
    End Namespace
    
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.AddIn.Pipeline;
    
    namespace CalcAddInViews
    {
        [AddInBase]
        public abstract class Calculator2
        {
            public abstract string Operations
            {
                get;
            }
    
            public abstract double Operate(string operation, double a, double b);
        }
    }
    

To create the host view of the add-in

  1. Add a new project named Calc2HVA to the CalculatorV2 solution. Base it on the Class Library template.

  2. Rename the class Calculator.

  3. Make Calculator an abstract class (MustInherit class in Visual Basic).

  4. In the class file, use the following code to create the host view of the add-in.

    Imports Microsoft.VisualBasic
    Imports System
    Namespace CalcHVAs
    
        Public MustInherit Class Calculator
    
            Public MustOverride ReadOnly Property Operations() As String
    
            Public MustOverride Function Operate(ByVal operation As String, ByVal a As Double, ByVal b As Double) As Double
        End Class
    End Namespace
    
    namespace CalcHVAs {
    
    
        public abstract class Calculator {
    
            public abstract string Operations
            {
                get;
            }
    
            public abstract double Operate(string operation, double a, double b);
        }
    }
    

To demonstrate the use of the version 1 add-in with the new host, the solution must include the add-in view created for version 1 of the calculator add-in.

To add the add-in view project from version 1

  1. In Solution Explorer, right-click the CalculatorV2 solution.

  2. Click Add, and then click Existing Project.

  3. Go to the folders that contain the CalculatorV1 solution and select the project file for the Calc1AddInView project.

Creating the Add-in-side Adapter

This add-in-side adapter consists of two view-to-contract adapters: one to adapt the version 2 add-in view to the version 2 contract, and one to adapt the version 1 add-in view to the version 2 contract.

In this pipeline, the add-in provides a service to the host, and the types flow from the add-in to the host. Because no types flow from the host to the add-in, you do not have to include a contract-to-view adapter.

To create the add-in-side adapter

  1. Add a new project named Calc2AddInSideAdapter to the CalculatorV2 solution. Base it on the Class Library template.

  2. In Solution Explorer, add references to the following assemblies to the Calc2AddInSideAdapter project:

    System.AddIn.dll

    System.AddIn.Contract.dll

  3. Add project references to the following projects:

    Calc2AddInView

    Calc2Contract

  4. Select each project reference, and in Properties set Copy Local to False, to prevent the referenced assemblies from being copied to the local build folder. The assemblies will be located in the pipeline directory, as described in the "Deploying the Pipeline" procedure later in this walkthrough. In Visual Basic, use the References tab of Project Properties to set Copy Local to False for the two project references.

  5. Rename the project's default class CalculatorViewToContractAddInSideAdapter.

  6. In the class file, add a namespace reference to System.AddIn.Pipeline.

  7. In the class file, add namespace references for the adjacent segments: CalcAddInViews and CalculatorContracts. (In Visual Basic, these namespace references are Calc2AddInView.CalcAddInViews and Calc2Contract.CalculatorContracts, unless you have turned off the default namespaces in your Visual Basic projects.)

  8. Use the following code for this add-in-side adapter. The implementation pattern is similar to the add-in-side adapter for version 1, although the contract interface is very different.

    
    Imports Microsoft.VisualBasic
    Imports System
    Imports System.Collections.Generic
    Imports System.Text
    Imports System.AddIn.Pipeline
    Imports System.AddIn.Contract
    Imports Calc2Contract.CalculatorContracts
    Imports Calc2AddInView.CalcAddInViews
    
    Namespace CalculatorContractsAddInAdapters
    
        <AddInAdapterAttribute()> _
        Public Class CalculatorViewToContractAddInAdapter
            Inherits ContractBase
            Implements ICalc2Contract
    
            Private _view As Calculator2
    
            Public Sub New(ByVal calculator As Calculator2)
                _view = calculator
            End Sub
    
            Public Function GetAvailableOperations() As String Implements ICalc2Contract.GetAvailableOperations
                Return _view.Operations
            End Function
    
            Public Function Operate(ByVal operation As String, ByVal a As Double, ByVal b As Double) As Double Implements ICalc2Contract.Operate
                Return _view.Operate(operation, a, b)
            End Function
        End Class
    End Namespace
    
    using System.AddIn.Pipeline;
    using CalcAddInViews;
    using CalculatorContracts;
    
    
    namespace CalcAddInSideAdapters {
    
    
        [AddInAdapterAttribute]
        public class CalculatorViewToContractAddInAdapter : ContractBase, ICalc2Contract {
    
            private Calculator2 _view;
    
            public CalculatorViewToContractAddInAdapter(Calculator2 calculator)
            {
                _view = calculator;
            }
    
            public string GetAvailableOperations()
            {
                return _view.Operations;
            }
    
            public double Operate(string operation, double a, double b)
            {
                return _view.Operate(operation, a, b);
            }
    
        }
    }
    

To enable version 1 of the add-in to communicate with the new host, the pipeline for the version 1 add-in requires an add-in-side adapter that converts data from the old add-in view to the new contract.

To create the version 1 to version 2 add-in-side adapter

  1. Add a new project named Calc2V1toV2AddInSideAdapter to the CalculatorV2 solution. Base it on the Class Library template.

  2. In Solution Explorer, add references to the following assemblies to the Calc2V1toV2AddInSideAdapter project:

    System.AddIn.dll

    System.AddIn.Contract.dll

  3. Add project references to the following projects:

    Calc1AddInView

    Calc2Contract

  4. Select each project reference, and in Properties set Copy Local to False, to prevent the referenced assemblies from being copied to the local build folder. In Visual Basic, use the References tab of Project Properties to set Copy Local to False for the two project references.

  5. Rename the project's default class Calc2V1ViewToV2ContractAddInSideAdapter.

  6. In the class file, add a namespace reference to System.AddIn.Pipeline.

  7. In the class file, add namespace references for the adjacent segments: CalcAddInViews and CalculatorContracts. (In Visual Basic these namespace references are Calc1AddInView.CalcAddInViews and Calc2Contract.CalculatorContracts, unless you have turned off the default namespaces in your Visual Basic projects.) Note that the view namespace is from version 1 and the contract is from version 2.

  8. Apply the AddInAdapterAttribute attribute to the Calc2V1ViewToV2ContractAddInSideAdapter class, to identify it as the add-in-side adapter.

  9. Make the Calc2V1ViewToV2ContractAddInSideAdapter class inherit ContractBase, which provides a default implementation of the IContract interface, and implement the version 2 contract interface for the pipeline, ICalc2Contract.

  10. Add a public constructor that accepts an ICalculator, caches it in a private field, and calls the base class constructor.

  11. To implement the members of ICalc2Contract, you must call the appropriate members of the ICalculator instance that is passed to the constructor, and return the results. Because of the differences between the version 1 and version 2 contract interfaces, you must have a switch statement (Select Case statement in Visual Basic) to adapt the view (ICalculator) to the contract (ICalc2Contract).

    The following code shows the completed add-in-side adapter.

    Imports System
    Imports System.Collections.Generic
    Imports System.Text
    Imports System.AddIn.Pipeline
    Imports Calc1AddInView.CalcAddInViews
    Imports Calc2Contract.CalculatorContracts
    
    Namespace AddInSideV1toV2Adapter
    
    
        <AddInAdapter()> _
        Public Class Calc2V1ViewToV2ContractAddInSideAdapter
            Inherits ContractBase
            Implements ICalc2Contract
    
            Private _view As ICalculator
    
            Public Sub New(ByVal calc As ICalculator)
                MyBase.New()
                _view = calc
            End Sub
    
            Public Function GetAvailableOperations() As String Implements ICalc2Contract.GetAvailableOperations
                Return "+, -, *, /"
            End Function
    
            Public Function Operate(ByVal operation As String, ByVal a As Double, ByVal b As Double) _
             As Double Implements ICalc2Contract.Operate
                Select Case (operation)
                    Case "+"
                        Return _view.Add(a, b)
                    Case "-"
                        Return _view.Subtract(a, b)
                    Case "*"
                        Return _view.Multiply(a, b)
                    Case "/"
                        Return _view.Divide(a, b)
                    Case Else
                        Throw New InvalidOperationException(("This add-in does not support: " + operation))
                End Select
            End Function
        End Class
    End Namespace
    
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.AddIn.Pipeline;
    using CalcAddInViews;
    using CalculatorContracts;
    
    
    namespace AddInSideV1toV2Adapter
    {
        [AddInAdapter]
        public class Calc2V1ViewToV2ContractAddInSideAdapter : ContractBase, ICalc2Contract
        {
            ICalculator _view;
    
            public Calc2V1ViewToV2ContractAddInSideAdapter(ICalculator calc)
            {
                _view = calc;
            }
    
            public string GetAvailableOperations()
            {
                return  "+, -, *, /" ;
            }
    
            public double Operate(string operation, double a, double b)
            {
                switch (operation)
                {
                    case "+":
                        return _view.Add(a, b);
                    case "-":
                        return _view.Subtract(a, b);
                    case "*":
                        return _view.Multiply(a, b);
                    case "/":
                        return _view.Divide(a, b);
                    default:
                        throw new InvalidOperationException("This add-in does not support: " + operation);
                }
            }
    
        }
    }
    

Creating the Host-side Adapter

This host-side adapter consists of one contract-to-view adapter. One contract-to-view adapter is sufficient to support both versions of the add-in, because each add-in-side adapter converts from its respective view to the version 2 contract.

In this pipeline, the add-in provides a service to the host and the types flow from the add-in to the host. Because no types flow from the host to the add-in, you do not have to include a view-to-contract adapter.

To implement lifetime management, use a ContractHandle object to attach a lifetime token to the contract. You must keep a reference to this handle in order for lifetime management to work. After the token is applied, no additional programming is required because the add-in system can dispose of objects when they are no longer being used and make them available for garbage collection. For more information, see Lifetime Management.

To create the host-side adapter

  1. Add a new project named Calc2HostSideAdapter to the CalculatorV2 solution. Base it on the Class Library template.

  2. In Solution Explorer, add references to the following assemblies to the Calc2HostSideAdapter project:

    System.AddIn.dll

    System.AddIn.Contract.dll

  3. Add project references to the following projects:

    Calc2Contract

    Calc2HVA

  4. Select each project reference, and in Properties set Copy Local to False, to prevent the referenced assemblies from being copied to the local build folder. In Visual Basic, use the References tab of Project Properties to set Copy Local to False for the two project references.

  5. Rename the project's default class CalculatorContractToViewHostSideAdapter.

  6. In the class file, add namespace references to System.AddIn.Pipeline.

  7. In the class file, add namespace references for the adjacent segments: CalcHVAs and CalculatorContracts. (In Visual Basic, these namespace references are Calc2HVA.CalcHVAs and Calc2Contract.CalculatorContracts, unless you have turned off the default namespaces in your Visual Basic projects.)

  8. Apply the HostAdapterAttribute attribute to the CalculatorContractToViewHostSideAdapter class, to identify it as the host-side adapter.

  9. Make the CalculatorContractToViewHostSideAdapter class inherit the abstract base class that represents the host view of the add-in: CalcHVAs.Calculator (Calc2HVA.CalcHVAs.Calculator in Visual Basic). Note the difference from version 1, where the host view of the add-in is an interface.

  10. Add a public constructor that accepts the pipeline contract type, ICalc2Contract. The constructor must cache the reference to the contract. It must also create and cache a new ContractHandle for the contract, to manage the lifetime of the add-in.

    Important noteImportant

    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.

  11. When you override the members of Calculator, simply call the corresponding members of the ICalc2Contract instance that is passed to the constructor, and return the results. This adapts the contract (ICalc2Contract) to the view (Calculator).

    The following code shows the completed host-side adapter segment.

    
    Imports Microsoft.VisualBasic
    Imports System
    Imports System.Collections.Generic
    Imports System.Text
    Imports System.AddIn.Pipeline
    Imports Calc2HVA.CalcHVAs
    Imports Calc2Contract.CalculatorContracts
    
    Namespace CalculatorContractsHostAdapers
        <HostAdapter()> _
        Public Class CalculatorContractToViewHostAdapter
            Inherits Calculator
    
        Private _contract As ICalc2Contract
        Private _handle As ContractHandle
    
        Public Sub New(ByVal contract As ICalc2Contract)
            _contract = contract
            _handle = New ContractHandle(contract)
        End Sub
    
        Public Overrides ReadOnly Property Operations() As String
            Get
                Return _contract.GetAvailableOperations()
            End Get
        End Property
    
        Public Overrides Function Operate(ByVal operation As String, ByVal a As Double, ByVal b As Double) As Double
            Return _contract.Operate(operation, a, b)
        End Function
    End Class
    End Namespace
    
    using System.AddIn.Pipeline;
    using CalcHVAs;
    using CalculatorContracts;
    
    namespace CalcHostSideAdapters {
    
    
    [HostAdapter]
    public class CalculatorContractToViewHostAdapter : Calculator {
    
        private CalculatorContracts.ICalc2Contract _contract;
    
        private System.AddIn.Pipeline.ContractHandle _handle;
    
        public CalculatorContractToViewHostAdapter(ICalc2Contract contract) {
            _contract = contract;
            _handle = new System.AddIn.Pipeline.ContractHandle(contract);
        }
    
    
        public override string Operations
        {
            get 
            { 
                return _contract.GetAvailableOperations(); 
            }
        }
    
        public override double Operate(string operation, double a, double b)
        {
            return _contract.Operate(operation, a, b);
        }
     }
    }
    

Creating the Host

A host application interacts with the add-in through the host view. It uses add-in discovery and activation methods provided by the AddInStore and AddInToken classes to do the following:

  • Rebuild the cache of pipeline and add-in information.

  • Find add-ins of type Calculator under the specified pipeline root directory.

  • Prompt the user to specify which add-in to use. In this example, you will see two available add-ins.

  • Activate the selected add-in in a new application domain with a specified security trust level.

  • Run the RunCalculator method, which calls the add-in's methods as provided by the host view of the add-in.

To create the host

  1. Add a new project named MathHost2 to the CalculatorV2 solution. Base it on the Console Application template.

  2. In Solution Explorer, add a reference to the System.AddIn.dll assembly to the MathHost2 project.

  3. Add a project reference to the Calc2HVA project. Select the project reference, and in Properties set Copy Local to False, to prevent the referenced assembly from being copied to the local build folder. In Visual Basic, use the References tab of Project Properties to set Copy Local to False.

  4. Rename the class file (module in Visual Basic) MathHost2.

  5. In Visual Basic, use the Application tab of the Project Properties dialog box to set Startup object to Sub Main.

  6. In the class or module file, add a namespace reference to System.AddIn.Hosting.

  7. In the class or module file, add a namespace reference for the host-view of the add-in: CalcHVAs. (In Visual Basic, this namespace reference is Calc2HVA.CalcHVAs, unless you have turned off the default namespaces in your Visual Basic projects.)

  8. In Solution Explorer, select the solution and from the Project menu choose Properties. In the Solution Property Pages dialog box, set the Single Startup Project to be this host application project.

  9. Use the following code to create the host application.

    
    Imports Microsoft.VisualBasic
    Imports System
    Imports System.Collections.Generic
    Imports System.Collections.ObjectModel
    Imports System.Text
    Imports System.AddIn.Hosting
    Imports Calc2HVA.CalcHVAs
    
    Namespace Mathhost
    
        Module MathHost2
    
            Sub Main()
                ' Assume that the current directory is the application folder, 
                ' and that it contains the pipeline folder structure. 
                Dim pipeRoot As String = Environment.CurrentDirectory & "\Pipeline"
    
                ' Rebuild the cache of pipline and add-in information.
                AddInStore.Rebuild(pipeRoot)
    
                ' Find add-ins of type Calculator under the specified pipeline root directory.
                Dim tokens As Collection(Of AddInToken) = AddInStore.FindAddIns(GetType(Calculator), pipeRoot)
    
                ' Determine which add-in to use.
                Dim calcToken As AddInToken = ChooseCalculator(tokens)
    
                ' Activate the selected AddInToken in a new  
                ' application domain with a specified security trust level.
                Dim calculator As Calculator = calcToken.Activate(Of Calculator)(AddInSecurityLevel.Internet)
    
                ' Run the calculator.
                RunCalculator(calculator)
            End Sub
    
            Private Function ChooseCalculator(ByVal tokens As Collection(Of AddInToken)) As AddInToken
                If tokens.Count = 0 Then
                    Console.WriteLine("No calculators are available")
                    Return Nothing
                End If
                Console.WriteLine("Available Calculators: ")
                ' Show the token properties for each token 
                ' in the AddInToken collection (tokens),
                ' preceded by the add-in number in [] brackets.
    
                Dim tokNumber As Integer = 1
                For Each tok As AddInToken In tokens
                    Console.WriteLine(vbTab & "[{0}]: {1} - {2}" & _
                            vbLf & vbTab & "{3}" & _
                            vbLf & vbTab & "{4}" & _
                            vbLf & vbTab & "{5} - {6}", _
                            tokNumber.ToString, tok.Name, _
                            tok.AddInFullName, tok.AssemblyName, _
                            tok.Description, tok.Version, tok.Publisher)
                    tokNumber = tokNumber + 1
                Next
                Console.WriteLine("Which calculator do you want to use?")
                Dim line As String = Console.ReadLine()
                Dim selection As Integer
                If Int32.TryParse(line, selection) Then
                    If selection <= tokens.Count Then
                        Return tokens(selection - 1)
                    End If
                End If
                Console.WriteLine("Invalid selection: {0}. Please choose again.", line)
                Return ChooseCalculator(tokens)
            End Function
    
            Private Sub RunCalculator(ByVal calc As Calculator)
    
                If calc Is Nothing Then
                    'No calculators were found, read a line and exit
                    Console.ReadLine()
                End If
                Console.WriteLine("Available operations: " & calc.Operations)
                Console.WriteLine("Request a calculation , such as: 2 + 2")
                Console.WriteLine("Type ""exit"" to exit")
                Dim line As String = Console.ReadLine()
                Do While Not line.Equals("exit")
                    ' Parser  
                    Try
                        Dim c As Parser = New Parser(line)
                        Console.WriteLine(calc.Operate(c.action, c.A, c.B))
                    Catch
                        Console.WriteLine("Invalid command: {0}. Commands must be formated: [number] [operation] [number]", line)
                        Console.WriteLine("Available operations: " & calc.Operations)
                    End Try
    
                    line = Console.ReadLine()
                Loop
            End Sub
        End Module
    
    
        Friend Class Parser
    
            Public partA As Double
    
            Public partB As Double
    
            Public action As String
    
            Friend Sub New(ByVal line As String)
                MyBase.New()
                Dim parts() As String = line.Split(" ")
                partA = Double.Parse(parts(0))
                action = parts(1)
                partB = Double.Parse(parts(2))
            End Sub
    
            Public ReadOnly Property A() As Double
                Get
                    Return partA
                End Get
            End Property
    
            Public ReadOnly Property B() As Double
                Get
                    Return partB
                End Get
            End Property
    
            Public ReadOnly Property CalcAction() As String
                Get
                    Return action
                End Get
            End Property
        End Class
    End Namespace
    
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Text;
    using System.AddIn.Hosting;
    using CalcHVAs;
    
    namespace MathHost
    {
        class Program
        {
            static void Main()
            {
                // Assume that the current directory is the application folder, 
                // and that it contains the pipeline folder structure. 
                String addInRoot = Environment.CurrentDirectory + "\\Pipeline";
    
                //Check to see if new add-ins have been installed.
                AddInStore.Rebuild(addInRoot);
    
                //Search for Calculator add-ins.
                Collection<AddInToken> tokens = AddInStore.FindAddIns(typeof(Calculator), addInRoot);
    
                //Ask the user which add-in they would like to use.
                AddInToken calcToken = ChooseCalculator(tokens);
    
                //Activate the selected AddInToken in a new
                //application domain with the Internet trust level.
                Calculator calculator = calcToken.Activate<Calculator>(AddInSecurityLevel.Internet);
    
                //Run the add-in.
                RunCalculator(calculator);
            }
    
            private static AddInToken ChooseCalculator(Collection<AddInToken> tokens)
            {
                if (tokens.Count == 0)
                {
                    Console.WriteLine("No calculators are available");
                    return null;
                }
                Console.WriteLine("Available Calculators: ");
                // Show the token properties for each token 
                // in the AddInToken collection (tokens),
                // preceded by the add-in number in [] brackets.
                int tokNumber = 1;
                foreach (AddInToken tok in tokens)
                {
                    Console.WriteLine(String.Format("\t[{0}]: {1} - {2}\n\t{3}\n\t\t {4}\n\t\t {5} - {6}",
                        tokNumber.ToString(), 
                        tok.Name,
                        tok.AddInFullName,
                        tok.AssemblyName,
                        tok.Description,
                        tok.Version,
                        tok.Publisher));
                    tokNumber++;
                }
                Console.WriteLine("Which calculator do you want to use?");
                String line = Console.ReadLine();
                int selection;
                if (Int32.TryParse(line, out selection))
                {
                    if (selection <= tokens.Count)
                    {
                        return tokens[selection - 1];
                    }
                }
                Console.WriteLine("Invalid selection: {0}. Please choose again.", line);
                return ChooseCalculator(tokens);
            }
    
            private static void RunCalculator(Calculator calc)
            {
    
                if (calc == null)
                {
                    //No calculators were found, read a line and exit.
                    Console.ReadLine();
                }
                Console.WriteLine("Available operations: " + calc.Operations);
                Console.WriteLine("Type \"exit\" to exit");
                String line = Console.ReadLine();
                while (!line.Equals("exit"))
                {
                    // The Parser class parses the user's input.
                    try
                    {
                        Parser c = new Parser(line);
                        Console.WriteLine(calc.Operate(c.Action, c.A, c.B));
                    }
                    catch
                    {
                        Console.WriteLine("Invalid command: {0}. Commands must be formated: [number] [operation] [number]", line);
                        Console.WriteLine("Available operations: " + calc.Operations);
                    }
    
                    line = Console.ReadLine();
                }
            }
        }
    
    
        internal class Parser
        {
            internal Parser(String line)
            {
                String[] parts = line.Trim().Split(' ');
                a = Double.Parse(parts[0]);
                action = parts[1];
                b = Double.Parse(parts[2]);
            }
    
            double a;
    
            public double A
            {
                get { return a; }
            }
            double b;
    
            public double B
            {
                get { return b; }
            }
            String action;
    
            public String Action
            {
                get { return action; }
            }
        }
    }
    

    Note

    This code assumes that the pipeline folder structure is located in the application folder. If you located it elsewhere, change the line of code that sets the addInRoot variable, so that the variable contains the path to your pipeline directory structure.

Creating the Add-In

An add-in implements the methods specified by the add-in view. In this add-in, the Operations method returns a string that lists the mathematical operations that the add-in supports. The Operate method provides the code to calculate the result based on the host's selection of an operation and two numbers.

To create the add-in

  1. Add a new project named AddInCalcV2 to the CalculatorV2 solution. Base it on the Class Library template.

  2. In Solution Explorer, add references to the following assemblies to the AddInCalcV2 project:

    System.AddIn.dll

    System.AddIn.Contract.dll

  3. Add a project reference to the Calc2AddInView project. Select the project reference, and in Properties set Copy Local to False, to prevent the referenced assembly from being copied to the local build folder. In Visual Basic, use the References tab of Project Properties to set Copy Local to False.

  4. Rename the class file SampleV2AddIn.

  5. In the class file, add namespace references to System.AddIn and System.AddIn.Pipeline. System.AddIn.Pipeline is required only because the code includes an example of the QualificationDataAttribute attribute.

  6. In the class file, add a namespace reference for the version 2 add-in view segment: CalcAddInViews (Calc2AddInView.CalcAddInViews in Visual Basic).

  7. Apply the AddInAttribute attribute to the SampleV2AddIn class, to identify the class as an add-in.

  8. Apply the QualificationDataAttribute attribute to the SampleV2AddIn class, and specify information that the host can retrieve from the AddInToken. In this case, the information suggests that the add-in should be loaded into its own application domain. See How to: Use Qualification Data.

  9. Make the SampleV2AddIn class inherit the abstract base class that represents the add-in view: Calculator2.

  10. Override the members of Calculator2 and return the results of the appropriate calculations.

    The following code shows the completed add-in.

    
    Imports Microsoft.VisualBasic
    Imports System
    Imports System.Collections.Generic
    Imports System.Text
    Imports System.AddIn
    Imports System.AddIn.Pipeline
    Imports Calc2AddInView.CalcAddInViews
    
    Namespace CalculatorAddIns
    ' This pipeline segment has
    ' two attributes:
    ' 1 - An AddInAttribute to identify
    '     this segment as an add-in.
    '
    ' 2 - A QualificationDataAttribute to
    '     indicate that the add-in should
    '     be loaded into a new application domain.
    
    <AddIn("Calculator Add-in", Version:="2.0.0.0")> _
    <QualificationData("Isolation", "NewAppDomain")> _
        Public Class SampleV2AddIn
        Inherits Calculator2
    Public Overrides ReadOnly Property Operations() As String
        Get
            Return "+, -, *, /, **"
        End Get
    End Property
    
    Public Overrides Function Operate(ByVal operation As String, _
            ByVal a As Double, ByVal b As Double) As Double
        Select Case operation
            Case "+"
                Return a + b
            Case "-"
                Return a - b
            Case "*"
                Return a * b
            Case "/"
                Return a / b
            Case "**"
                Return Math.Pow(a, b)
            Case Else
                Throw New InvalidOperationException("This add-in does not support: " & operation)
        End Select
    End Function
    
    End Class
    End Namespace
    
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.AddIn;
    using System.AddIn.Pipeline;
    using CalcAddInViews;
    namespace CalcAddIns
    {
    // This pipeline segment has
    // two attributes:
    // 1 - An AddInAttribute to identify
    //     this segment as an add-in.
    //
    // 2 - A QualificationDataAttribute to
    //     indicate that the add-in should
    //     be loaded into a new application domain.
    
        [AddIn("Calculator Add-in",Version="2.0.0.0")]
        [QualificationData("Isolation", "NewAppDomain")]
        public class SampleV2AddIn : Calculator2
        {
            public override string Operations
            {
                get
                {
                    return "+, -, *, /, **";
                }
            }
    
            public override double Operate(string operation, double a, double b)
            {
                switch (operation)
                {
                    case "+":
                        return a + b;
                    case "-":
                        return a - b;
                    case "*":
                        return a * b;
                    case "/":
                        return a / b;
                    case "**":
                        return Math.Pow(a, b);
                    default:
                        throw new InvalidOperationException("This add-in does not support: " + operation);
                }
            }
    
        }
    }
    

Deploying the Pipeline

You are now ready to build and deploy the add-in segments to the required pipeline directory structure.

To deploy the segments to the pipeline

  1. For each project in the solution, use the Build tab of Project Properties (the Compile tab in Visual Basic) to set the value of the Output path (the Build output path in Visual Basic). If you named your application folder MyApp, for example, your projects would build into the following folders:

    Project

    Path

    AddInCalcV2

    MyApp\Pipeline\AddIns\CalcV2

    Calc2AddInSideAdapter

    MyApp\Pipeline\AddInSideAdapters

    Calc2V1toV2AddInSideAdapter

    MyApp\Pipeline\AddInSideAdapters

    Calc1AddInView

    MyApp\Pipeline\AddInViews

    Calc2AddInView

    MyApp\Pipeline\AddInViews

    Calc2Contract

    MyApp\Pipeline\Contracts

    MathHost2

    MyApp

    Calc2HostSideAdapter

    MyApp\Pipeline\HostSideAdapters

    Calc2HVA

    MyApp

    Note

    If you put your pipeline folder structure in a location other than your application folder, you must modify the paths shown in the table accordingly.

  2. Build the Visual Studio solution.

  3. Check the application and pipeline directories, to ensure that the assemblies were copied to the correct directories, and that no extra copies of assemblies were installed in the wrong folders.

    Note

    If you did not change Copy Local to False for the Calc2AddInView project reference in the AddInCalcV2 project, loader context problems will prevent the add-in from being located.

    For information about deploying to the pipeline, see Pipeline Development Requirements.

Running the Host Application

You are now ready to run the host and interact with the add-ins.

To run the host application

  1. Make sure that both versions of the add-in are deployed.

  2. At the command prompt, go to the application directory and run the host application. In this example, the host application is MathHost2.exe.

  3. The host finds all available add-ins of its type and prompts you to select an add-in. Enter 1 or 2.

  4. Enter an equation for the calculator, such as 2 + 2.

  5. Type exit and press the Enter key to close the application.

  6. Repeat steps 2-5 to run the other add-in.

See Also

Tasks

Walkthrough: Creating an Extensible Application

Walkthrough: Passing Collections Between Hosts and Add-Ins

Concepts

Pipeline Development Requirements

Contracts, Views, and Adapters

Pipeline Development