Пошаговое руководство. Создание расширяемого приложения

В этом пошаговом руководстве описано, как создать конвейер для надстройки, которая выполняет простые функции калькулятора. Здесь не приводится сценария, пригодного для использования в производственной среде, а просто демонстрируется основные функции конвейера и предоставление обслуживание надстройкой основного приложения.

В этом пошаговом руководстве описаны следующие задачи:

  • Создание решения в Visual Studio.

  • Создание структуры каталогов конвейера.

  • Создание контракта и представлений.

  • Создание адаптера на стороне надстройки.

  • Создание адаптера на стороне основного приложения.

  • Создание основного приложения.

  • Создание надстройки.

  • Развертывание конвейера.

  • Запуск основного приложения.

Этот конвейер передает только сериализованные типы (Double и String) между основным приложением и надстройкой. Пример, в котором показано, как передавать коллекции сложных типов данных, см. в разделе Пошаговое руководство. Передача коллекций между основными приложениями и надстройками.

Контракт для этого конвейера определяет модель объекта из четырех арифметических операций: сложение, вычитание, умножение и деление. Основное приложение предоставляет надстройке формулу, которую надо рассчитать, например 2 + 2, а надстройка возвращает обратно результат.

В версии 2 надстройки калькулятора предоставляются дополнительные возможности вычисления, и демонстрируется управление версиями. Это описано в разделе Пошаговое руководство. Включение обратной совместимости при изменении основного приложения.

ПримечаниеПримечание

Дополнительные примеры кода и CTP-версии средств построения конвейеров надстроек см. на веб-узле CodePlex в разделе, посвященном управлению расширениями и надстройками.

Обязательные компоненты

Для выполнения данного пошагового руководства необходимо следующее:

  • Visual Studio.

Создание решения в Visual Studio.

Используйте решение в Visual Studio, чтобы разместить проекты сегментов конвейера.

Чтобы создать решение конвейера

  1. В Visual Studio создайте новый проект с именем Calc1Contract. Он должен быть основан на шаблоне Библиотека классов.

  2. Назовите решение CalculatorV1.

Создание структуры каталогов конвейера

Для модели надстройки необходимо, чтобы сборки сегментов конвейера были размещены в определенной структуре каталогов. Дополнительные сведения о структуре конвейера см. в разделе Требования к разработке конвейера.

Чтобы создать структуру каталогов конвейера

  1. Создайте папку приложения где-либо на локальном компьютере.

  2. В этой папке создайте следующую структуру:

    Pipeline
      AddIns
        CalcV1
        CalcV2
      AddInSideAdapters
      AddInViews
      Contracts
      HostSideAdapters
    

    Не необходимости размещать структуру папок конвейера в папке приложения; это делается только один раз для удобства. На определенном этапе этого пошагового руководства объясняется, как изменить код, если структура папок конвейера расположена в другом месте. См. описание требований к каталогам конвейера в разделе Требования к разработке конвейера.

    ПримечаниеПримечание

    Папка CalcV2 в этом пошаговом руководстве не используется; это местозаполнитель для Пошаговое руководство. Включение обратной совместимости при изменении основного приложения.

Создание контракта и представлений

Сегмент контракта для этого конвейера определяет интерфейс ICalc1Contract, который, в свою очередь, определяет четыре метода: add, subtract, multiply и divide.

Чтобы создать контракт

  1. В решении Visual Studio с именем CalculatorV1 откройте проект Calc1Contract.

  2. В обозревателе решений в проект Calc1Contract добавьте ссылки на следующие сборки:

    System.AddIn.Contract.dll

    System.AddIn.dll

  3. В обозревателе решений исключите класс по умолчанию, который был добавлен в новые проекты Библиотека классов.

  4. В Обозревателе решений добавьте в проект новый элемент с помощью шаблона Интерфейс. В диалоговом окне Добавление нового элемента назовите этот интерфейс ICalc1Contract.

  5. В файле интерфейса добавьте ссылки на пространства имен к System.AddIn.Contract и System.AddIn.Pipeline.

  6. Используйте следующий код для завершения этого сегмента контракта. Обратите внимание, что этот интерфейс должен иметь атрибут AddInContractAttribute.

    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. При необходимости постройте решение Visual Studio. Это решение не может быть выполнено до процедуры завершения, однако ее построение после каждой процедуры гарантирует правильность каждого проекта.

Так как представление надстройки и представление основного приложения, относящееся к надстройке, как правило, не отличаются в коде, особенно в первой версии надстройки, можно без труда создать оба этих представления одновременно. Они отличаются только в одном: представление надстройки нуждается в атрибуте AddInBaseAttribute, тогда как представление основного приложения, относящееся к надстройке, не нуждается в атрибутах.

Чтобы создать представление надстройки

  1. Добавьте новый проект с именем Calc1AddInView в решение CalculatorV1. Он должен быть основан на шаблоне Библиотека классов.

  2. В обозревателе решений добавьте ссылку на System.AddIn.dll в проект Calc1AddInView.

  3. В обозревателе решений исключите класс по умолчанию, который был добавлен в новые проекты Библиотека классов, затем добавьте новый элемент в этот проект с помощью шаблона Интерфейс. В диалоговом окне Добавление нового элемента назовите этот интерфейс ICalculator.

  4. В файле интерфейса добавьте ссылку на пространство имен к System.AddIn.Pipeline.

  5. Используйте следующий код для завершения этого представления надстройки. Обратите внимание, что этот интерфейс должен иметь атрибут AddInBaseAttribute.

    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. При необходимости постройте решение Visual Studio.

Чтобы создать представление основного приложения, относящееся к надстройке

  1. Добавьте к решению CalculatorV1 новый проект Calc1HVA. Он должен быть основан на шаблоне Библиотека классов.

  2. В обозревателе решений исключите класс по умолчанию, который был добавлен в новые проекты Библиотека классов, затем добавьте новый элемент в этот проект с помощью шаблона Интерфейс. В диалоговом окне Добавление нового элемента назовите этот интерфейс ICalculator.

  3. В файле интерфейса используйте следующий код для завершения представления основного приложения, относящегося к надстройке.

    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. При необходимости постройте решение Visual Studio.

Создание адаптера на стороне надстройки

Этот адаптер на стороне надстройки состоит из одного адаптера "представление-контракт". В этом сегменте конвейера преобразуются типы из представления надстройки в контракт.

В этом конвейере надстройка обслуживает основное приложение, а типы направляются из надстройки в основное приложение. Так как никакие типы не направляются из основного приложения в надстройку, нет необходимости включать адаптер "контракт-представление" на стороне надстройки в этом конвейере.

Чтобы создать адаптер на стороне надстройки

  1. Добавьте в решение CalculatorV1 новый проект Calc1AddInSideAdapter. Он должен быть основан на шаблоне Библиотека классов.

  2. В обозревателе решений добавьте ссылки на следующие сборки в проект Calc1AddInSideAdapter.

    System.AddIn.dll

    System.AddIn.Contract.dll

  3. Добавьте ссылки проекта на проекты прилегающих сегментов конвейера:

    Calc1AddInView

    Calc1Contract

  4. Выберите ссылку каждого проекта и в поле Свойства задайте значение False для параметра Копировать локально. В Visual Basic используйте вкладку Ссылки в разделе Свойства проекта для задания значения параметра Копировать локально равным False для двух ссылок проекта.

  5. Переименуйте класс проекта по умолчанию CalculatorViewToContractAddInSideAdapter.

  6. В файле класса добавьте ссылки на пространство имен к System.AddIn.Pipeline.

  7. В файле класса добавьте ссылки на пространства имен для прилегающих сегментов CalcAddInViews и CalculatorContracts. (В Visual Basic этими ссылками на пространства имен являются Calc1AddInView.CalcAddInViews и Calc1Contract.CalculatorContracts, если только не были отключены пространства имен по умолчанию в проектах Visual Basic).

  8. Примените атрибут AddInAdapterAttribute к классу CalculatorViewToContractAddInSideAdapter, чтобы указать его в качестве адаптера на стороне надстройки.

  9. Класс CalculatorViewToContractAddInSideAdapter должен наследовать ContractBase, что предоставляет реализацию интерфейса IContract по умолчанию и реализует интерфейс контракта для конвейера ICalc1Contract.

  10. Добавьте открытый конструктор, который принимает ICalculator, кэширует его в закрытом поле и вызывает базовый класс конструктора.

  11. Чтобы реализовать элементы ICalc1Contract, просто вызовите соответствующие элементы экземпляра ICalculator, которые передаются в конструктор, и возвратите результат. Это адаптирует представление (ICalculator) к контракту (ICalc1Contract).

    В следующем коде показан завершенный адаптер на стороне надстройки.

    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. При необходимости постройте решение Visual Studio.

Создание адаптера на стороне основного приложения

Этот адаптер на стороне основного приложения состоит из одного адаптера "контракт-представление". Этот сегмент адаптирует контракт к представлению основного приложения, относящемуся к надстройке.

В этом конвейере надстройка обслуживает основное приложение, а типы направляются из надстройки в основное приложение. Так как никакие типы не направляются от основного приложения в надстройку, нет необходимости включать адаптер "представление-контракт".

Для управления временем существования необходимо использовать объект ContractHandle, чтобы связать с контрактом маркер времени существования. Чтобы механизм управления временем существования работал, необходимо хранить ссылку на данный дескриптор. После применения маркера пропадает необходимость в дополнительном программировании, так как система надстройки может удалить объекты, если они уже не используются, и сделать их доступными для сборщика мусора. Дополнительные сведения см. в разделе Управление жизненным циклом объекта.

Чтобы создать адаптер на стороне основного приложения

  1. Добавьте новый проект с именем Calc1HostSideAdapter в решение CalculatorV1. Он должен быть основан на шаблоне Библиотека классов.

  2. В обозревателе решений добавьте ссылки на следующие сборки в проект Calc1HostSideAdapter:

    System.AddIn.dll

    System.AddIn.Contract.dll

  3. Добавление ссылок проекта в проекты прилегающих сегментов:

    Calc1Contract

    Calc1HVA

  4. Выберите ссылку каждого проекта и в поле Свойства задайте значение False для параметра Копировать локально. В Visual Basic используйте вкладку Ссылки в разделе Свойства проекта для задания значения параметра Копировать локально равным False для двух ссылок проекта.

  5. Переименуйте класс проекта по умолчанию CalculatorContractToViewHostSideAdapter.

  6. В файле класса добавьте ссылки на пространство имен к System.AddIn.Pipeline.

  7. В файле класса добавьте ссылки на пространства имен для прилегающих сегментов: CalcHVAs и CalculatorContracts. (В Visual Basic этими ссылками на пространства имен являются Calc1HVA.CalcHVAs и Calc1Contract.CalculatorContracts, если только пространства имен по умолчанию не были отключены в проектах Visual Basic.)

  8. Примените атрибут HostAdapterAttribute к классу CalculatorContractToViewHostSideAdapter, чтобы определить его как сегмент адаптера на стороне основного приложения.

  9. Класс CalculatorContractToViewHostSideAdapter должен реализовывать интерфейс, который представляет представление основного приложения, относящееся к надстройке: Calc1HVAs.ICalculator (Calc1HVA.CalcHVAs.ICalculator в Visual Basic).

  10. Добавьте открытый конструктор, который принимает тип контракта конвейера ICalc1Contract. Конструктор должен кэшировать ссылки на контракт. Также он должен создавать и кэшировать новый дескриптор ContractHandle для контракта, чтобы управлять жизненным циклом надстройки.

    Важное примечаниеВажно

    Дескриптор ContractHandle играет ключевую роль в управлении временем существования.Если не сохранить ссылку на объект ContractHandle, он будет удален при сборке мусора, и конвейер закроется неожиданно для программы.Это может вызвать ошибки, которые сложно диагностировать, например AppDomainUnloadedException.Завершение работы является неотъемлемым этапом жизненного цикла конвейера, поэтому с помощью кода управления временем существования невозможно определить такое состояние как ошибку.

  11. Чтобы реализовать элементы ICalculator, просто вызовите соответствующие элементы экземпляра ICalc1Contract, которые передаются в конструктор, и возвратите результаты. Это адаптирует контракт (ICalc1Contract) к представлению (ICalculator).

    В следующем коде показан завершенный адаптер на стороне основного приложения.

    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. При необходимости постройте решение Visual Studio.

Создание основного приложения

Основное приложение взаимодействует с надстройкой посредством представления основного приложения, относящегося к надстройке. При этом используются методы обнаружения и активации надстройки, которые предоставляются классами AddInStore и AddInToken для выполнения следующих действий:

  • Обновление кэша конвейера и сведений надстройки.

  • Обнаружение надстроек типа представления основного приложения ICalculator в указанном корневом каталоге конвейера.

  • Запрос пользователя на указание надстройки, которая должна быть использована.

  • Активация выбранной надстройки в новом домене приложения с выбранным уровнем доверия.

  • Выполнение пользовательского метода RunCalculator, который вызывает методы надстройки, как указано в представлении основного приложения, относящемся к надстройке.

Чтобы создать основное приложение

  1. Добавление нового проекта с именем Calc1Host в решение CalculatorV1. Он должен быть основан на шаблоне Консольное приложение.

  2. В обозревателе решений добавьте ссылку на сборку System.AddIn.dll в проект Calc1Host.

  3. Добавьте ссылку проекта в проект Calc1HVA. Выберите ссылку проекта, затем в разделе Свойства задайте значение False для параметра Копировать локально. В Visual Basic используйте пункт Свойства проекта на вкладке Ссылки, чтобы задать для параметра Копировать локально значение False.

  4. Переименуйте файл класса (модуль в Visual Basic) MathHost1.

  5. В Visual Basic используйте вкладку Приложение диалогового окна Свойства проекта для задания значения Sub Main для параметра Автоматически запускаемый объект.

  6. В классе или файле модуля добавьте ссылку на пространство имен к System.AddIn.Hosting.

  7. В файле класса или модуля добавьте ссылку на пространства имен для представления основного приложения, относящегося к надстройке: CalcHVAs. (В Visual Basic этой ссылкой на пространство имен является Calc1HVA.CalcHVAs, если только не были отключены пространства имен по умолчанию в проектах Visual Basic.)

  8. В Обозревателе решений выберите решение, затем в меню Проект выберите Свойства. В диалоговом окне Страницы свойств решения задайте Один запускаемый объект как этот проект основного приложения.

  9. В файле класса или модуля используйте метод AddInStore.Update для обновления кэша. Используйте метод AddInStore.FindAddIn для получения коллекции маркеров, а также используйте метод AddInToken.Activate для активации надстройки.

    В следующем коде показано завершение основного приложения.

    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; }
            }
        }
    }
    
    ПримечаниеПримечание

    В этом коде предполагается, что структура папок конвейера расположена в папке приложения.Если эта структура расположена где-либо еще, измените строку кода, в которой задается переменная addInRoot, чтобы эта переменная содержала путь к структуре каталогов конвейера.

    В коде используется метод ChooseCalculator для отображения списка маркеров и для запроса пользователя о выборе надстройки. В методе RunCalculator пользователь должен ввести простые математические выражения, затем они обрабатываются с помощью класса Parser, после чего надстройка возвращает результаты.

  10. При необходимости постройте решение Visual Studio.

Создание надстройки

Надстройка реализует методы, указанные в представлении надстройки. Эта надстройка реализует операции Add, Subtract, Multiply и Divide, а затем возвращает результаты основному приложению.

Чтобы создать надстройку

  1. Добавьте новый проект с названием AddInCalcV1 в решение CalculatorV1. Он должен быть основан на шаблоне Библиотека классов.

  2. В Обозревателе решений добавьте в проект ссылку на сборку System.AddIn.dll.

  3. Добавьте ссылку проекта к проекту Calc1AddInView. Выберите ссылку проекта, затем в разделе Свойства задайте значение False для параметра Копировать локально. В Visual Basic используйте вкладку Ссылки Свойств проекта для задания для параметра Копировать локально значение False для ссылки проекта.

  4. Переименуйте класс AddInCalcV1.

  5. В файле класса добавьте ссылку на пространство имен к System.AddIn, затем добавьте сегмент надстройки CalcAddInViews (Calc1AddInView.CalcAddInViews в Visual Basic).

  6. Примените атрибут AddInAttribute к классу AddInCalcV1 для идентификации класса в качестве надстройки.

  7. Класс AddInCalcV1 должен реализовывать интерфейс, который представляет представление надстройки: CalcAddInViews.ICalculator (Calc1AddInView.CalcAddInViews.ICalculator в Visual Basic).

  8. Реализует элементы ICalculator посредством возвращения результатов соответствующих вычислений.

    В следующем коде показано, как завершить надстройку.

    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. При необходимости постройте решение Visual Studio.

Развертывание конвейера

Теперь можно создать и развернуть сегменты надстройки в требуемой структуре каталогов конвейера.

Чтобы развернуть сегменты в конвейере

  1. Для каждого проекта в этом решении используйте вкладку Построение свойств проекта (вкладка Compile в Visual Basic) для установки значения Путь к выходным данным (Путь к выходным данным построения в Visual Basic). Если назвать папку приложения, например, MyApp, построение проектов будет осуществляться в следующих папках:

    Проект

    Путь

    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

    ПримечаниеПримечание

    Если следует разместить структуру папок конвейера в месте, отличном от папки приложения, необходимо изменить соответствующим образом пути, приведенные в таблице.См. описание требований к каталогам конвейера в разделе Требования к разработке конвейера.

  2. Создайте решение Visual Studio.

  3. Проверьте каталоги приложения и конвейера, чтобы удостовериться в том, что сборки были скопированы в правильные каталоги и что в других папках не осталось дополнительных копий сборок.

    ПримечаниеПримечание

    Если для параметра Копировать локально не было задано значение False для ссылки проекта Calc1AddInView в проекте AddInCalcV1, контекстные проблемы загрузчика помешают нахождению надстройки.

    Сведения о развертывании в конвейере см. в разделе Требования к разработке конвейера.

Запуск основного приложения

Теперь все готово к запуску основного приложения и взаимодействию с надстройкой.

Чтобы выполнить основное приложение

  1. В командной строке перейдите в каталог приложения и запустите основное приложение Calc1Host.exe.

  2. Основное приложение находит все доступные надстройки этого типа и запрашивает пользователя о выборе надстройки. Введите 1 для единственной доступной надстройки.

  3. Введите формулу для калькулятора, например 2 + 2. Между цифрами и знаком должны быть пробелы.

  4. Введите команду exit и нажмите клавишу ВВОД, чтобы закрыть приложение.

См. также

Задачи

Пошаговое руководство. Включение обратной совместимости при изменении основного приложения

Пошаговое руководство. Передача коллекций между основными приложениями и надстройками

Основные понятия

Требования к разработке конвейера

Контракты, представления и адаптеры

Разработка конвейера