Walkthrough: Creating an Extensible Application

This walkthrough describes how to create a pipeline for an add-in that performs simple calculator functions. It does not demonstrate a real-world scenario; rather, it demonstrates the basic functionality of a pipeline and how an add-in can provide services for a host.

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.

  • Creating the host-side adapter.

  • Creating the host.

  • Creating the add-in.

  • Deploying the pipeline.

  • Running the host application.

This pipeline passes only serializable types (Double and String), between the host and the add-in. For an example that shows how to pass collections of complex data types, see Walkthrough: Passing Collections Between Hosts and Add-Ins.

The contract for this pipeline defines an object model of four arithmetic operations: add, subtract, multiply, and divide. The host provides the add-in with an equation to calculate, such as 2 + 2, and the add-in returns the result to the host.

Version 2 of the calculator add-in provides more calculating possibilities and demonstrates versioning. It is described in Walkthrough: Enabling Backward Compatibility as Your Host Changes.

Note

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 to complete this walkthrough:

  • Visual Studio.

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 Calc1Contract. Base it on the Class Library template.

  2. Name the solution CalculatorV1.

Creating the Pipeline Directory Structure

The add-in model requires the pipeline segment assemblies to be placed in a specified directory structure. For more information about the pipeline structure, see Pipeline Development Requirements.

To create the pipeline directory structure

  1. Create an application folder anywhere on your computer.

  2. In that folder, create the following structure:

    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 here only for convenience. At the appropriate step, the walkthrough explains how to change the code if the pipeline folder structure is in a different location. See the discussion of pipeline directory requirements in Pipeline Development Requirements.

    Note

    The CalcV2 folder is not used in this walkthrough; it is a placeholder for Walkthrough: Enabling Backward Compatibility as Your Host Changes.

Creating the Contract and Views

The contract segment for this pipeline defines the ICalc1Contract interface, which defines four methods: add, subtract, multiply, and divide.

To create the contract

  1. In the Visual Studio solution named CalculatorV1, open the Calc1Contract project.

  2. In Solution Explorer, add references to the following assemblies to the Calc1Contract 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. In Solution Explorer, add a new item to the project, using the Interface template. In the Add New Item dialog box, name the interface ICalc1Contract.

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

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

    Imports System.AddIn.Contract
    Imports System.AddIn.Pipeline
    
    Namespace CalculatorContracts
    
        ' The AddInContractAttribute identifes this pipeline segment as a 
        ' contract.
        <AddInContract()> _
        Public Interface ICalc1Contract
            Inherits IContract
    
            Function Add(ByVal a As Double, ByVal b As Double) As Double 
            Function Subtract(ByVal a As Double, ByVal b As Double) As Double 
            Function Multiply(ByVal a As Double, ByVal b As Double) As Double 
            Function Divide(ByVal a As Double, ByVal b As Double) As Double 
        End Interface 
    
    End Namespace
    
    using System.AddIn.Contract;
    using System.AddIn.Pipeline;
    
    namespace CalculatorContracts
    {
        // The AddInContractAttribute identifes this pipeline segment as a  
        // contract.
        [AddInContract]
        public interface ICalc1Contract : IContract
        {
        double Add(double a, double b);
        double Subtract(double a, double b);
        double Multiply(double a, double b);
        double Divide(double a, double b);
        }
    }
    
  7. Optionally, build the Visual Studio solution. The solution cannot be run until the final procedure, but building it after each procedure ensures that each project is correct.

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

To create the add-in view

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

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

  3. In Solution Explorer, exclude the default class that is added to new Class Library projects, and add a new item to the project, using the Interface template. In the Add New Item dialog box, name the interface ICalculator.

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

  5. Use the following code to complete this add-in view. Note that this interface must have the AddInBaseAttribute attribute.

    Imports System.AddIn.Pipeline
    
    Namespace CalcAddInViews
    
        ' The AddInBaseAttribute identifes this interface as the basis for the 
        ' add-in view pipeline segment.
        <AddInBaseAttribute()> _
        Public Interface ICalculator
    
            Function Add(ByVal a As Double, ByVal b As Double) As Double 
            Function Subtract(ByVal a As Double, ByVal b As Double) As Double 
            Function Multiply(ByVal a As Double, ByVal b As Double) As Double 
            Function Divide(ByVal a As Double, ByVal b As Double) As Double 
        End Interface 
    
    End Namespace
    
    using System.AddIn.Pipeline;
    
    namespace CalcAddInViews 
    {
        // The AddInBaseAttribute identifes this interface as the basis for 
        // the add-in view pipeline segment.
        [AddInBase()]
        public interface ICalculator 
        {
        double Add(double a, double b);
        double Subtract(double a, double b);
        double Multiply(double a, double b);
        double Divide(double a, double b);
        }
    }
    
  6. Optionally, build the Visual Studio solution.

To create the host view of the add-in

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

  2. In Solution Explorer, exclude the default class that is added to new Class Library projects, and add a new item to the project, using the Interface template. In the Add New Item dialog box, name the interface ICalculator.

  3. In the interface file, use the following code to complete the host view of the add-in.

    Namespace CalcHVAs
    
        Public Interface ICalculator
            Function Add(ByVal a As Double, ByVal b As Double) As Double 
            Function Subtract(ByVal a As Double, ByVal b As Double) As Double 
            Function Multiply(ByVal a As Double, ByVal b As Double) As Double 
            Function Divide(ByVal a As Double, ByVal b As Double) As Double 
        End Interface 
    
    End Namespace
    
    namespace CalcHVAs 
    {
        public interface ICalculator 
        {
            double Add(double a, double b);
            double Subtract(double a, double b);
            double Multiply(double a, double b);
            double Divide(double a, double b);
        }
    }
    
  4. Optionally, build the Visual Studio solution.

Creating the Add-in-side Adapter

This add-in-side adapter consists of one view-to-contract adapter. This pipeline segment converts the types from the add-in view to the 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 on the add-in side of this pipeline.

To create the add-in-side adapter

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

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

    System.AddIn.dll

    System.AddIn.Contract.dll

  3. Add project references to the projects for the adjacent pipeline segments:

    Calc1AddInView

    Calc1Contract

  4. Select each project reference, and in Properties set Copy Local to False. 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 namespace references 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 Calc1Contract.CalculatorContracts, unless you have turned off the default namespaces in your Visual Basic projects.)

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

  9. Make the CalculatorViewToContractAddInSideAdapter class inherit ContractBase, which provides a default implementation of the IContract interface, and implement the contract interface for the pipeline, ICalc1Contract.

  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 ICalc1Contract, simply call the corresponding members of the ICalculator instance that is passed to the constructor, and return the results. This adapts the view (ICalculator) to the contract (ICalc1Contract).

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

    Imports System.AddIn.Pipeline
    Imports Calc1AddInView.CalcAddInViews
    Imports Calc1Contract.CalculatorContracts
    
    Namespace CalcAddInSideAdapters
    
        ' The AddInAdapterAttribute identifes this class as the add-in-side  
        ' adapter pipeline segment.
        <AddInAdapter()> _
        Public Class CalculatorViewToContractAddInSideAdapter
            Inherits ContractBase
            Implements ICalc1Contract
    
            Private _view As ICalculator
    
            Public Sub New(ByVal view As ICalculator)
                MyBase.New()
                _view = view
            End Sub 
    
            Public Function Add(ByVal a As Double, ByVal b As Double) As Double Implements ICalc1Contract.Add
                Return _view.Add(a, b)
            End Function 
    
            Public Function Subtract(ByVal a As Double, ByVal b As Double) As Double Implements ICalc1Contract.Subtract
                Return _view.Subtract(a, b)
            End Function 
    
            Public Function Multiply(ByVal a As Double, ByVal b As Double) As Double Implements ICalc1Contract.Multiply
                Return _view.Multiply(a, b)
            End Function 
    
            Public Function Divide(ByVal a As Double, ByVal b As Double) As Double Implements ICalc1Contract.Divide
                Return _view.Divide(a, b)
            End Function 
    
        End Class 
    End Namespace
    
    using System.AddIn.Pipeline;
    using CalcAddInViews;
    using CalculatorContracts;
    
    namespace CalcAddInSideAdapters 
    {
        // The AddInAdapterAttribute identifes this class as the add-in-side adapter 
        // pipeline segment.
        [AddInAdapter()]
        public class CalculatorViewToContractAddInSideAdapter :
            ContractBase, ICalc1Contract 
        {
            private ICalculator _view;
    
            public CalculatorViewToContractAddInSideAdapter(ICalculator view) 
            {
                _view = view;
            }
    
            public virtual double Add(double a, double b) 
            {
                return _view.Add(a, b);
            }
    
            public virtual double Subtract(double a, double b) 
            {
                return _view.Subtract(a, b);
            }
    
            public virtual double Multiply(double a, double b) 
            {
                return _view.Multiply(a, b);
            }
    
            public virtual double Divide(double a, double b) 
            {
                return _view.Divide(a, b);
            }
        }
    }
    
  12. Optionally, build the Visual Studio solution.

Creating the Host-side Adapter

This host-side adapter consists of one contract-to-view adapter. This segment adapts the contract to the host view of the add-in.

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 Calc1HostSideAdapter to the CalculatorV1 solution. Base it on the Class Library template.

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

    System.AddIn.dll

    System.AddIn.Contract.dll

  3. Add project references to the projects for the adjacent segments:

    Calc1Contract

    Calc1HVA

  4. Select each project reference, and in Properties set Copy Local to False. 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 Calc1HVA.CalcHVAs and Calc1Contract.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 segment.

  9. Make the CalculatorContractToViewHostSideAdapter class implement the interface that represents the host view of the add-in: Calc1HVAs.ICalculator (Calc1HVA.CalcHVAs.ICalculator in Visual Basic).

  10. Add a public constructor that accepts the pipeline contract type, ICalc1Contract. 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 Note:

    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. To implement the members of ICalculator, simply call the corresponding members of the ICalc1Contract instance that is passed to the constructor, and return the results. This adapts the contract (ICalc1Contract) to the view (ICalculator).

    The following code shows the completed host-side adapter.

    Imports System.AddIn.Pipeline
    Imports Calc1Contract.CalculatorContracts
    Imports Calc1HVA.CalcHVAs
    
    Namespace CalcHostSideAdapters
    
        ' The HostAdapterAttribute identifes this class as the host-side adapter 
        ' pipeline segment.
        <HostAdapterAttribute()> _
        Public Class CalculatorContractToViewHostSideAdapter
            Implements ICalculator
    
            Private _contract As ICalc1Contract
            Private _handle As System.AddIn.Pipeline.ContractHandle
    
            Public Sub New(ByVal contract As ICalc1Contract)
                    MyBase.New()
                _contract = contract
                _handle = New ContractHandle(contract)
            End Sub 
    
            Public Function Add(ByVal a As Double, ByVal b As Double) As Double _
                    Implements ICalculator.Add
                Return _contract.Add(a, b)
            End Function 
    
            Public Function Subtract(ByVal a As Double, ByVal b As Double) As Double _
                    Implements ICalculator.Subtract
                Return _contract.Subtract(a, b)
            End Function 
    
            Public Function Multiply(ByVal a As Double, ByVal b As Double) As Double _
                    Implements ICalculator.Multiply
                Return _contract.Multiply(a, b)
            End Function 
    
            Public Function Divide(ByVal a As Double, ByVal b As Double) As Double _
                    Implements ICalculator.Divide
                Return _contract.Divide(a, b)
            End Function 
    
        End Class 
    
    End Namespace
    
    using System.AddIn.Pipeline;
    using CalcHVAs;
    using CalculatorContracts;
    
    namespace CalcHostSideAdapters 
    {
        // The HostAdapterAttribute identifes this class as the host-side adapter 
        // pipeline segment.
        [HostAdapterAttribute()]
        public class CalculatorContractToViewHostSideAdapter : ICalculator 
        {
            private ICalc1Contract _contract;
            private System.AddIn.Pipeline.ContractHandle _handle;
    
            public CalculatorContractToViewHostSideAdapter(ICalc1Contract contract) 
            {
                _contract = contract;
                _handle = new ContractHandle(contract);
            }
    
            public double Add(double a, double b) 
            {
                return _contract.Add(a, b);
            }
    
            public double Subtract(double a, double b) 
            {
                return _contract.Subtract(a, b);
            }
    
            public double Multiply(double a, double b) 
            {
                return _contract.Multiply(a, b);
            }
    
            public double Divide(double a, double b) 
            {
                return _contract.Divide(a, b);
            }
        }
    }
    
  12. Optionally, build the Visual Studio solution.

Creating the Host

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

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

  • Find add-ins of the host view type, ICalculator, under the specified pipeline root directory.

  • Prompt the user to specify which add-in to use.

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

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

To create the host

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

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

  3. Add a project reference to the Calc1HVA project. Select the project reference, and in Properties set Copy Local to False. 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) MathHost1.

  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 Calc1HVA.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. In the class or module file, use the AddInStore.Update method to update the cache. Use the AddInStore.FindAddIn method to get a collection of tokens, and use the AddInToken.Activate method to activate an add-in.

    The following code shows the completed host application.

    Imports System.Collections.Generic
    Imports System.Collections.ObjectModel
    Imports System.AddIn.Hosting
    Imports Calc1HVA.CalcHVAs
    
    Namespace MathHost
    
        Module MathHost1
    
            Sub Main()
                ' Assume that the current directory is the application folder,  
                ' and that it contains the pipeline folder structure. 
                Dim addInRoot As String = Environment.CurrentDirectory & "\Pipeline" 
    
                ' Update the cache files of the pipeline segments and add-ins. 
                Dim warnings() As String = AddInStore.Update(addInRoot)
                For Each warning As String In warnings
                    Console.WriteLine(warning)
                Next 
    
                ' Search for add-ins of type ICalculator (the host view of the add-in). 
                Dim tokens As System.Collections.ObjectModel.Collection(Of AddInToken) = _
                    AddInStore.FindAddIns(GetType(ICalculator), addinRoot)
    
                ' Ask the user which add-in they would like to use. 
                Dim calcToken As AddInToken = ChooseCalculator(tokens)
    
                ' Activate the selected AddInToken in a new application domain  
                ' with the Internet trust level. 
                Dim calc As ICalculator = _
                    calcToken.Activate(Of ICalculator)(AddInSecurityLevel.Internet)
    
                ' Run the add-in.
                RunCalculator(calc)
            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 ICalculator)
                If IsNothing(calc) Then 
                    'No calculators were found, read a line and exit.
                    Console.ReadLine()
                End If
                Console.WriteLine("Available operations: +, -, *, /")
                Console.WriteLine("Request a calculation , such as: 2 + 2")
                Console.WriteLine("Type 'exit' to exit")
                Dim line As String = Console.ReadLine
    
                While Not line.Equals("exit")
                    ' The Parser class parses the user's input. 
                    Try 
                        Dim c As Parser = New Parser(line)
                        Select Case (c.action)
                            Case "+"
                                Console.WriteLine(calc.Add(c.a, c.b))
                            Case "-"
                                Console.WriteLine(calc.Subtract(c.a, c.b))
                            Case "*"
                                Console.WriteLine(calc.Multiply(c.a, c.b))
                            Case "/"
                                Console.WriteLine(calc.Divide(c.a, c.b))
                            Case Else
                                Console.WriteLine("{0} is an invalid command. Valid commands are +,-,*,/", c.action)
                        End Select 
                    Catch Ex As System.Exception
                        Console.WriteLine("Invalid command: {0}. Commands must be formated: [number] [operation] [number]", line)
                    End Try
                    line = Console.ReadLine
    
                End While 
            End Sub 
        End Module 
    
        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.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";
    
                // Update the cache files of the pipeline segments and add-ins. 
                string[] warnings = AddInStore.Update(addInRoot);
                foreach (string warning in warnings)
                {
                    Console.WriteLine(warning);
                }
    
                // Search for add-ins of type ICalculator (the host view of the add-in).
                Collection<AddInToken> tokens = 
                    AddInStore.FindAddIns(typeof(ICalculator), 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.
                ICalculator calc = 
                    calcToken.Activate<ICalculator>(AddInSecurityLevel.Internet);
    
                // Run the add-in.
                RunCalculator(calc);
            }
    
            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(ICalculator calc)
            {
    
                if (calc == null)
                {
                    //No calculators were found; read a line and exit.
                    Console.ReadLine();
                }
                Console.WriteLine("Available operations: +, -, *, /");
                Console.WriteLine("Request a calculation , such as: 2 + 2");
                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);
                        switch (c.Action)
                        {
                            case "+":
                                Console.WriteLine(calc.Add(c.A, c.B));
                                break;
                            case "-":
                                Console.WriteLine(calc.Subtract(c.A, c.B));
                                break;
                            case "*":
                                Console.WriteLine(calc.Multiply(c.A, c.B));
                                break;
                            case "/":
                                Console.WriteLine(calc.Divide(c.A, c.B));
                                break;
                            default:
                                Console.WriteLine("{0} is an invalid command. Valid commands are +,-,*,/", c.Action);
                                break;
                        }
                    }
                    catch
                    {
                        Console.WriteLine("Invalid command: {0}. Commands must be formated: [number] [operation] [number]", line);
                    }
    
                    line = Console.ReadLine();
                }
            }
        }
    
        internal class Parser
        {
            double a;
            double b;
            string action;
    
            internal Parser(string line)
            {
                string[] parts = line.Split(' ');
                a = double.Parse(parts[0]);
                action = parts[1];
                b = double.Parse(parts[2]);
            }
    
            public double A
            {
                get { return a; }
            }
    
            public double B
            {
                get { return b; }
            }
    
            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.

    The code uses a ChooseCalculator method to list the tokens and to prompt the user to choose an add-in. The RunCalculator method prompts the user for simple math expressions, parses the expressions using the Parser class, and displays the results returned by the add-in.

  10. Optionally, build the Visual Studio solution.

Creating the Add-In

An add-in implements the methods specified by the add-in view. This add-in implements the Add, Subtract, Multiply, and Divide operations and returns the results to the host.

To create the add-in

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

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

  3. Add a project reference to the Calc1AddInView project. Select the project reference, and in Properties, set Copy Local to False. In Visual Basic, use the References tab of Project Properties to set Copy Local to False for the project reference.

  4. Rename the class AddInCalcV1.

  5. In the class file, add a namespace reference to System.AddIn and the add-in view segment: CalcAddInViews (Calc1AddInView.CalcAddInViews in Visual Basic).

  6. Apply the AddInAttribute attribute to the AddInCalcV1 class, to identify the class as an add-in.

  7. Make the AddInCalcV1 class implement the interface that represents the add-in view: CalcAddInViews.ICalculator (Calc1AddInView.CalcAddInViews.ICalculator in Visual Basic).

  8. Implement the members of ICalculator by returning the results of the appropriate calculations.

    The following code shows the completed add-in.

    Imports System.AddIn
    Imports Calc1AddInView.CalcAddInViews
    
    Namespace CalcAddIns
    
        ' The AddInAttribute identifies this pipeline segment as an add-in.
        <AddIn("Calculator AddIn", Version:="1.0.0.0")> _
        Public Class AddInCalcV1
            Implements ICalculator
    
            Public Function Add(ByVal a As Double, ByVal b As Double) As Double _
                    Implements ICalculator.Add
                Return (a + b)
            End Function 
    
            Public Function Subtract(ByVal a As Double, ByVal b As Double) As Double _
                    Implements ICalculator.Subtract
                Return (a - b)
            End Function 
    
            Public Function Multiply(ByVal a As Double, ByVal b As Double) As Double _
                    Implements ICalculator.Multiply
                Return (a * b)
            End Function 
    
            Public Function Divide(ByVal a As Double, ByVal b As Double) As Double _
                    Implements ICalculator.Divide
                Return (a / b)
            End Function 
        End Class 
    
    End Namespace
    
    using System.Collections.Generic;
    using System.AddIn;
    using CalcAddInViews;
    
    namespace CalcAddIns
    {
        // The AddInAttribute identifies this pipeline segment as an add-in.
        [AddIn("Calculator AddIn",Version="1.0.0.0")]
        public class AddInCalcV1 : ICalculator
        {
            public double Add(double a, double b)
            {
                return a + b;
            }
    
            public double Subtract(double a, double b)
            {
                return a - b;
            }
    
            public double Multiply(double a, double b)
            {
                return a * b;
            }
    
            public double Divide(double a, double b)
            {
                return a / b;
            }
        }
    }
    
  9. Optionally, build the Visual Studio solution.

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

    AddInCalcV1

    MyApp\Pipeline\AddIns\CalcV1

    Calc1AddInSideAdapter

    MyApp\Pipeline\AddInSideAdapters

    Calc1AddInView

    MyApp\Pipeline\AddInViews

    Calc1Contract

    MyApp\Pipeline\Contracts

    Calc1Host

    MyApp

    Calc1HostSideAdapter

    MyApp\Pipeline\HostSideAdapters

    Calc1HVA

    MyApp

    Note

    If you decided to put your pipeline folder structure in a location other than your application folder, you must modify the paths shown in the table accordingly. See the discussion of pipeline directory requirements in Pipeline Development Requirements.

  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 Calc1AddInView project reference in the AddInCalcV1 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-in.

To run the host application

  1. At the command prompt, go to the application directory and run the host application, Calc1Host.exe.

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

  3. Enter an equation for the calculator, such as 2 + 2. There must be spaces between the numbers and the operator.

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

See Also

Tasks

Walkthrough: Enabling Backward Compatibility as Your Host Changes

Walkthrough: Passing Collections Between Hosts and Add-Ins

Concepts

Pipeline Development Requirements

Contracts, Views, and Adapters

Pipeline Development

Change History

Date

History

Reason

July 2008

Fixed errors in the text. Added a note about keeping a reference to the contract.

Customer feedback.