연습: 호스트 변경으로 인한 이전 버전과의 호환성 활성화

이 연습에서는 연습: 확장 가능한 응용 프로그램 만들기에서 설명하는 파이프라인의 버전 2에 대해 설명합니다. 버전 2에는 호스트에 제공되는 쉼표로 구분된 산술 연산 문자열을 비롯하여 더 많은 계산 기능이 포함되어 있습니다. 호스트는 연산을 선택하고 추가 기능에서 계산하도록 수식을 전송할 수 있습니다.

파이프라인에는 새 호스트와 새 계약이 있습니다. 추가 기능의 버전 1이 새 호스트와 계약에서 작동할 수 있도록 파이프라인에는 버전 1에 사용된 추가 기능 뷰와 이전 추가 기능 뷰의 데이터를 새 계약으로 변환하는 추가 기능측 어댑터가 포함되어 있습니다. 다음 그림에서는 두 개의 추가 기능이 같은 호스트에서 함께 작동하는 방법을 보여 줍니다.

호스트, 이전 추가 기능

파이프라인 시나리오: 새 호스트, 이전 추가 기능

추가 기능 파이프라인 시나리오에서도 이 파이프라인에 대해 설명합니다.

이 연습에서는 다음 작업에 대해 설명합니다.

  • Visual Studio 솔루션 만들기

  • 파이프라인 디렉터리 구조 만들기

  • 계약 및 뷰 만들기

  • 새 버전의 추가 기능 및 버전 1 추가 기능에 대한 어댑터를 포함하는 추가 기능측 어댑터 만들기

  • 호스트측 어댑터 만들기

  • 호스트 만들기

  • 추가 기능 만들기

  • 파이프라인 배포

  • 호스트 응용 프로그램 실행

이 연습에서는 추상 기본 클래스를 사용하여 뷰를 정의하고 이러한 뷰가 인터페이스에서 정의하는 뷰와 호환되는 방법도 보여 줍니다. 이때 인터페이스를 사용하는 것이 좋습니다.

참고참고

이 연습에 제공된 일부 코드에는 불필요한 네임스페이스 참조가 포함되어 있습니다.연습 단계에는 Visual Studio에 필요한 참조가 정확하게 반영됩니다.

추가 코드 샘플을 얻고 추가 기능 파이프라인 개발용 도구에 대한 고객 기술을 미리 보려면 CodePlex의 Managed Extensibility and Add-In Framework 사이트를 참조하십시오.

사전 요구 사항

이 연습을 완료하려면 다음 구성 요소가 필요합니다.

  • Visual Studio.

  • 연습: 확장 가능한 응용 프로그램 만들기에서 설명하는 버전 1 파이프라인. 버전 2에는 버전 1에서 개발된 파이프라인 세그먼트가 사용되므로 이 항목의 단계를 수행하기 전에 먼저 버전 1 파이프라인을 개발하고 배포해야 합니다.

Visual Studio 솔루션 만들기

Visual Studio의 솔루션을 사용하여 파이프라인 세그먼트의 프로젝트를 포함할 수 있습니다.

파이프라인 솔루션을 만들려면

  1. Visual Studio에서 Calc2Contract라는 새 프로젝트를 만듭니다. 클래스 라이브러리 템플릿을 기반으로 합니다.

  2. 솔루션 이름을 CalculatorV2로 지정합니다.

파이프라인 디렉터리 구조 만들기

추가 기능 모델에서는 파이프라인 세그먼트 어셈블리를 지정한 디렉터리 구조에 넣어야 합니다.

파이프라인 디렉터리 구조를 만들려면

  • 연습: 확장 가능한 응용 프로그램 만들기에서 만든 파이프라인 폴더 구조에 CalcV2 폴더를 아직 추가하지 않았으면 지금 추가합니다. CalcV2 폴더에는 새 버전의 추가 기능이 포함됩니다.

    Pipeline
      AddIns
        CalcV1
        CalcV2
      AddInSideAdapters
      AddInViews
      Contracts
      HostSideAdapters
    

    여기에 제공된 연습에서는 편의를 위해 응용 프로그램 폴더 내에 파이프라인 폴더 구조를 넣었지만 반드시 그렇게 할 필요는 없습니다. 첫 번째 연습에서 파이프라인 폴더 구조를 다른 위치에 넣은 경우에는 이 연습에서도 같은 패턴을 따라야 합니다. 파이프라인 개발 요구 사항의 파이프라인 디렉터리 요구 사항에 대한 설명을 참조하십시오.

계약 및 뷰 만들기

이 파이프라인의 계약 세그먼트는 다음과 같은 두 메서드를 포함하는 ICalc2Contract 인터페이스를 정의합니다.

  • GetAvailableOperations 메서드

    이 메서드는 추가 기능에서 지원하는 수학 연산의 문자열을 호스트에 반환합니다. 버전 2에서는 5개 연산을 지원하며 이 메서드는 "+,-,*,/,**" 문자열을 반환합니다. 여기서 "**"는 Pow 연산을 나타냅니다.

    추가 기능 및 호스트 뷰에서는 이 메서드의 이름이 GetAvailableOperations 대신 Operations로 지정됩니다.

    메서드 호출을 어댑터의 속성으로 변환하여 계약의 메서드를 뷰의 속성으로 노출할 수 있습니다.

  • Operate 메서드

    호스트는 이 메서드를 호출하여 추가 기능에서 계산하도록 수식을 전송하고 결과를 반환합니다.

계약을 만들려면

  1. CalculatorV2라는 Visual Studio 솔루션에서 Calc2Contract 프로젝트를 엽니다.

  2. 솔루션 탐색기에서 다음 어셈블리에 대한 참조를 Calc2Contract 프로젝트에 추가합니다.

    System.AddIn.Contract.dll

    System.AddIn.dll

  3. 솔루션 탐색기에서 새 클래스 라이브러리 프로젝트에 추가되는 기본 클래스를 제외합니다.

  4. 인터페이스 템플릿을 사용하여 프로젝트에 새 항목을 추가합니다. 이때 새 항목 추가 대화 상자가 나타나면 인터페이스 이름을 ICalc2Contract로 지정합니다.

  5. 인터페이스 파일에서 System.AddIn.ContractSystem.AddIn.Pipeline에 대한 네임스페이스 참조를 추가합니다.

  6. 다음 코드를 사용하여 계약 세그먼트를 완료합니다. 이 인터페이스에는 AddInContractAttribute 특성이 있어야 합니다.

    
    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);
        }
    }
    

추가 기능 뷰와 호스트 뷰의 코드가 같으므로 간단한 방법으로 동시에 뷰를 만들 수 있습니다. 단, 추가 기능 뷰에는 AddInBaseAttribute 특성이 필요하고 추가 기능의 호스트 뷰에는 특성이 필요하지 않다는 한 가지 차이점이 있습니다.

버전 2에 대한 추가 기능 뷰를 만들려면

  1. Calc2AddInView라는 새 프로젝트를 CalculatorV2 솔루션에 추가합니다. 클래스 라이브러리 템플릿을 기반으로 합니다.

  2. 솔루션 탐색기에서 System.AddIn.dll에 대한 참조를 Calc2AddInView 프로젝트에 추가합니다.

  3. Calculator2 클래스의 이름을 바꿉니다.

  4. 클래스 파일에서 System.AddIn.Pipeline에 대한 네임스페이스 참조를 추가합니다.

  5. Calculator2를 abstract 클래스(Visual Basic의 경우 MustInherit 클래스)로 만듭니다.

  6. 이 추가 기능 뷰에 대해 다음 코드를 사용합니다. 이 클래스에는 AddInBaseAttribute 특성이 있어야 합니다.

    
    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);
        }
    }
    

추가 기능의 호스트 뷰를 만들려면

  1. Calc2HVA라는 새 프로젝트를 CalculatorV2 솔루션에 추가합니다. 클래스 라이브러리 템플릿을 기반으로 합니다.

  2. Calculator 클래스의 이름을 바꿉니다.

  3. Calculator를 abstract 클래스(Visual Basic의 경우 MustInherit 클래스)로 만듭니다.

  4. 클래스 파일에서 다음 코드를 사용하여 추가 기능의 호스트 뷰를 만듭니다.

    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);
        }
    }
    

새 호스트에서 버전 1 추가 기능을 사용하는 방법을 보여 주려면 계산기 추가 기능의 버전 1에 대해 만든 추가 기능 뷰가 솔루션에 포함되어 있어야 합니다.

버전 1에서 추가 기능 뷰 프로젝트를 추가하려면

  1. 솔루션 탐색기에서 CalculatorV2 솔루션을 마우스 오른쪽 단추로 클릭합니다.

  2. 추가를 클릭한 다음 기존 프로젝트를 클릭합니다.

  3. CalculatorV1 솔루션이 들어 있는 폴더로 이동하여 Calc1AddInView 프로젝트의 프로젝트 파일을 선택합니다.

추가 기능측 어댑터 만들기

이 추가 기능측 어댑터는 뷰에서 계약으로 변환하는 두 개의 어댑터로 구성되는데, 하나는 버전 2 추가 기능 뷰를 버전 2 계약에 맞게 변경하는 어댑터이고 다른 하나는 버전 1 추가 기능 뷰를 버전 2 계약에 맞게 변경하는 어댑터입니다.

이 파이프라인에서 추가 기능은 호스트에 서비스를 제공하고 형식은 추가 기능에서 호스트로 흐릅니다. 형식은 호스트에서 추가 기능으로 흐르지 않으므로 계약에서 뷰로 변환하는 어댑터를 포함할 필요가 없습니다.

추가 기능측 어댑터를 만들려면

  1. Calc2AddInSideAdapter라는 새 프로젝트를 CalculatorV2 솔루션에 추가합니다. 클래스 라이브러리 템플릿을 기반으로 합니다.

  2. 솔루션 탐색기에서 다음 어셈블리에 대한 참조를 Calc2AddInSideAdapter 프로젝트에 추가합니다.

    System.AddIn.dll

    System.AddIn.Contract.dll

  3. 프로젝트 참조를 다음 프로젝트에 추가합니다.

    Calc2AddInView

    Calc2Contract

  4. 참조된 어셈블리가 로컬 빌드 폴더에 복사되지 않도록 하려면 각 프로젝트 참조를 선택한 다음 속성에서 로컬 복사False로 설정합니다. 어셈블리는 이 연습의 뒷부분에 있는 "파이프라인 배포" 절차의 설명과 같이 파이프라인 디렉터리에 배치됩니다. Visual Basic에서는 프로젝트 속성참조 탭을 사용하여 두 프로젝트 참조의 로컬 복사 값을 False로 설정합니다.

  5. 프로젝트의 기본 클래스인 CalculatorViewToContractAddInSideAdapter의 이름을 바꿉니다.

  6. 클래스 파일에서 System.AddIn.Pipeline에 대한 네임스페이스 참조를 추가합니다.

  7. 클래스 파일에서 인접 세그먼트에 대한 네임스페이스 참조 CalcAddInViews 및 CalculatorContracts를 추가합니다. Visual Basic의 경우 Visual Basic 프로젝트에서 기본 네임스페이스를 해제하지 않았으면 이러한 네임스페이스 참조는 Calc2AddInView.CalcAddInViews 및 Calc2Contract.CalculatorContracts입니다.

  8. 이 추가 기능측 어댑터에 대해 다음 코드를 사용합니다. 구현 패턴은 버전 1의 추가 기능측 어댑터와 비슷하지만 계약 인터페이스는 전혀 다릅니다.

    
    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);
            }
    
        }
    }
    

버전 1 추가 기능이 새 호스트와 통신할 수 있도록 하려면 버전 1 추가 기능의 파이프라인에 이전 추가 기능 뷰의 데이터를 새 계약으로 변환하는 추가 기능측 어댑터가 필요합니다.

버전 1과 버전 2 간의 추가 기능측 어댑터를 만들려면

  1. Calc2V1toV2AddInSideAdapter라는 새 프로젝트를 CalculatorV2 솔루션에 추가합니다. 클래스 라이브러리 템플릿을 기반으로 합니다.

  2. 솔루션 탐색기에서 다음 어셈블리에 대한 참조를 Calc2V1toV2AddInSideAdapter 프로젝트에 추가합니다.

    System.AddIn.dll

    System.AddIn.Contract.dll

  3. 프로젝트 참조를 다음 프로젝트에 추가합니다.

    Calc1AddInView

    Calc2Contract

  4. 참조된 어셈블리가 로컬 빌드 폴더에 복사되지 않도록 하려면 각 프로젝트 참조를 선택한 다음 속성에서 로컬 복사False로 설정합니다. Visual Basic에서는 프로젝트 속성참조 탭을 사용하여 두 프로젝트 참조의 로컬 복사 값을 False로 설정합니다.

  5. 프로젝트의 기본 클래스인 Calc2V1ViewToV2ContractAddInSideAdapter의 이름을 바꿉니다.

  6. 클래스 파일에서 System.AddIn.Pipeline에 대한 네임스페이스 참조를 추가합니다.

  7. 클래스 파일에서 인접 세그먼트에 대한 네임스페이스 참조 CalcAddInViews 및 CalculatorContracts를 추가합니다. Visual Basic의 경우 Visual Basic 프로젝트에서 기본 네임스페이스를 해제하지 않았으면 이러한 네임스페이스 참조는 Calc1AddInView.CalcAddInViews 및 Calc2Contract.CalculatorContracts입니다. 뷰 네임스페이스는 버전 1에서 가져오고 계약은 버전 2에서 가져옵니다.

  8. Calc2V1ViewToV2ContractAddInSideAdapter 클래스에 AddInAdapterAttribute 특성을 적용하여 이 클래스를 추가 기능측 어댑터로 식별합니다.

  9. IContract 인터페이스의 기본 구현을 제공하는 ContractBase를 상속하도록 Calc2V1ViewToV2ContractAddInSideAdapter 클래스를 설정하고 파이프라인의 버전 2 계약 인터페이스 ICalc2Contract를 구현합니다.

  10. ICalculator를 사용하는 공용 생성자를 추가하여 전용 필드에 캐시하고 기본 클래스 생성자를 호출합니다.

  11. ICalc2Contract의 멤버를 구현하려면 생성자에 전달되는 ICalculator 인스턴스의 해당 멤버를 호출하고 결과를 반환해야 합니다. 버전 1과 버전 2 간의 계약 인터페이스 차이로 인해 switch 문(Visual Basic의 경우 Select Case 문)을 사용하여 뷰(ICalculator)를 계약(ICalc2Contract)에 맞게 변경해야 합니다.

    다음 코드에서는 완성된 추가 기능측 어댑터를 보여 줍니다.

    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);
                }
            }
    
        }
    }
    

호스트측 어댑터 만들기

이 호스트측 어댑터는 계약에서 뷰로 변환하는 하나의 어댑터로 구성되어 있습니다. 추가 기능측 어댑터는 각 뷰를 버전 2 계약으로 변환하므로 계약에서 뷰로 변환하는 어댑터만 있으면 두 버전의 추가 기능을 모두 지원할 수 있습니다.

이 파이프라인에서 추가 기능은 호스트에 서비스를 제공하고 형식은 추가 기능에서 호스트로 흐릅니다. 형식은 호스트에서 추가 기능으로 흐르지 않으므로 뷰에서 계약으로 변환하는 어댑터를 포함할 필요가 없습니다.

수명 관리를 구현하려면 ContractHandle 개체를 사용하여 계약에 수명 토큰을 연결합니다. 이 핸들에 대한 참조를 유지해야 수명 관리가 정상적으로 작동합니다. 추가 기능 시스템에서 더 이상 사용되지 않는 개체를 삭제하고 가비지 수집에 사용 가능하도록 설정할 수 있으므로 토큰이 적용된 후 추가 프로그래밍이 필요하지 않습니다. 자세한 내용은 수명 관리를 참조하십시오.

호스트측 어댑터를 만들려면

  1. Calc2HostSideAdapter라는 새 프로젝트를 CalculatorV2 솔루션에 추가합니다. 클래스 라이브러리 템플릿을 기반으로 합니다.

  2. 솔루션 탐색기에서 다음 어셈블리에 대한 참조를 Calc2HostSideAdapter 프로젝트에 추가합니다.

    System.AddIn.dll

    System.AddIn.Contract.dll

  3. 프로젝트 참조를 다음 프로젝트에 추가합니다.

    Calc2Contract

    Calc2HVA

  4. 참조된 어셈블리가 로컬 빌드 폴더에 복사되지 않도록 하려면 각 프로젝트 참조를 선택한 다음 속성에서 로컬 복사False로 설정합니다. Visual Basic에서는 프로젝트 속성참조 탭을 사용하여 두 프로젝트 참조의 로컬 복사 값을 False로 설정합니다.

  5. 프로젝트의 기본 클래스인 CalculatorContractToViewHostSideAdapter의 이름을 바꿉니다.

  6. 클래스 파일에서 System.AddIn.Pipeline에 대한 네임스페이스 참조를 추가합니다.

  7. 클래스 파일에서 인접 세그먼트에 대한 네임스페이스 참조 CalcHVAs 및 CalculatorContracts를 추가합니다. Visual Basic의 경우 Visual Basic 프로젝트에서 기본 네임스페이스를 해제하지 않았으면 이러한 네임스페이스 참조는 Calc2HVA.CalcHVAs 및 Calc2Contract.CalculatorContracts입니다.

  8. CalculatorContractToViewHostSideAdapter 클래스에 HostAdapterAttribute 특성을 적용하여 이 클래스를 호스트측 어댑터로 식별합니다.

  9. 추가 기능의 호스트 뷰를 나타내는 추상 기본 클래스인 CalcHVAs.Calculator(Visual Basic의 경우 Calc2HVA.CalcHVAs.Calculator)를 상속하도록 CalculatorContractToViewHostSideAdapter 클래스를 설정합니다. 버전 1과 비교하면 추가 기능의 호스트 뷰가 인터페이스라는 차이가 있습니다.

  10. 파이프라인 계약 형식(ICalc2Contract)을 사용하는 공용 생성자를 추가합니다. 이 생성자는 계약에 대한 참조를 캐시해야 합니다. 또한 계약에 대해 새 ContractHandle을 만들고 캐시하여 추가 기능의 수명을 관리해야 합니다.

    중요중요

    ContractHandle은 수명 관리에 필수적입니다.ContractHandle 개체에 대한 참조를 유지하지 못하는 경우 가비지 수집을 통해 이를 회수하게 되며 프로그램에서 더 이상 필요로 하지 않는 파이프라인은 종료됩니다.이로 인해 AppDomainUnloadedException 같이 진단하기 어려운 오류가 발생할 수 있습니다.종료는 파이프라인 수명의 정상적인 단계이므로 수명 관리 코드에서 이 상태가 오류인지 여부를 감지할 수는 없습니다.

  11. Calculator의 멤버를 재정의하려면 생성자에 전달되는 ICalc2Contract 인스턴스의 해당 멤버를 호출하고 결과를 반환하면 됩니다. 이렇게 하면 뷰(Calculator)에 맞게 계약(ICalc2Contract)이 변경됩니다.

    다음 코드에서는 완성된 호스트측 어댑터 세그먼트를 보여 줍니다.

    
    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);
        }
     }
    }
    

호스트 만들기

호스트 응용 프로그램은 호스트 뷰를 통해 추가 기능과 상호 작용합니다. AddInStoreAddInToken 클래스에서 제공하는 추가 기능 검색 및 활성화 메서드를 사용하여 다음을 수행합니다.

  • 파이프라인 및 추가 기능 정보의 캐시를 다시 빌드합니다.

  • 지정한 파이프라인 루트 디렉터리에서 Calculator 형식의 추가 기능을 찾습니다.

  • 사용할 추가 기능을 지정하라는 메시지를 사용자에게 표시합니다. 이 예제에서는 두 개의 사용 가능한 추가 기능이 표시됩니다.

  • 새 응용 프로그램 도메인에서 지정된 보안 신뢰 수준으로 선택한 추가 기능을 활성화합니다.

  • 추가 기능의 호스트 뷰에서 제공한 대로 추가 기능의 메서드를 호출하는 RunCalculator 메서드를 실행합니다.

호스트를 만들려면

  1. MathHost2라는 새 프로젝트를 CalculatorV2 솔루션에 추가합니다. 콘솔 응용 프로그램 템플릿을 기반으로 합니다.

  2. 솔루션 탐색기에서 System.AddIn.dll 어셈블리에 대한 참조를 MathHost2 프로젝트에 추가합니다.

  3. 프로젝트 참조를 Calc2HVA 프로젝트에 추가합니다. 프로젝트 참조를 선택하고 속성에서 로컬 복사False로 설정하여 참조된 어셈블리가 로컬 빌드 폴더에 복사되지 않게 합니다. Visual Basic에서는 프로젝트 속성참조 탭을 사용하여 로컬 복사 값을 False로 설정합니다.

  4. 클래스 파일(Visual Basic의 경우 모듈) MathHost2의 이름을 바꿉니다.

  5. Visual Basic에서 프로젝트 속성 대화 상자의 응용 프로그램 탭을 사용하여 시작 개체Sub Main으로 설정합니다.

  6. 클래스 또는 모듈 파일에서 System.AddIn.Hosting에 대한 네임스페이스 참조를 추가합니다.

  7. 클래스 또는 모듈 파일에서 추가 기능의 호스트 뷰에 대한 네임스페이스 참조인 CalcHVAs를 추가합니다. Visual Basic의 경우 Visual Basic 프로젝트에서 기본 네임스페이스를 해제하지 않았으면 이 네임스페이스 참조는 Calc2HVA.CalcHVAs입니다.

  8. 솔루션 탐색기에서 솔루션을 선택하고 프로젝트 메뉴에서 속성을 선택합니다. 솔루션 속성 페이지 대화 상자에서 한 개의 시작 프로젝트를 이 호스트 응용 프로그램 프로젝트로 설정합니다.

  9. 다음 코드를 사용하여 호스트 응용 프로그램을 만듭니다.

    
    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; }
            }
        }
    }
    
    참고참고

    이 코드에서는 파이프라인 폴더 구조가 응용 프로그램 폴더에 있다고 가정합니다.그러나 파이프라인 폴더 구조가 다른 위치에 있는 경우에는 addInRoot 변수에 파이프라인 디렉터리 구조의 경로가 포함되도록 이 변수를 설정하는 코드 줄을 변경해야 합니다.

추가 기능 만들기

추가 기능은 추가 기능 뷰에서 지정한 메서드를 구현합니다. 이 추가 기능에서 Operations 메서드는 추가 기능이 지원하는 수학 연산을 나열하는 문자열을 호스트에 반환합니다. Operate 메서드는 호스트에서 선택한 연산과 두 개의 숫자를 기반으로 결과를 계산하는 코드를 제공합니다.

추가 기능을 만들려면

  1. AddInCalcV2라는 새 프로젝트를 CalculatorV2 솔루션에 추가합니다. 클래스 라이브러리 템플릿을 기반으로 합니다.

  2. 솔루션 탐색기에서 다음 어셈블리에 대한 참조를 AddInCalcV2 프로젝트에 추가합니다.

    System.AddIn.dll

    System.AddIn.Contract.dll

  3. 프로젝트 참조를 Calc2AddInView 프로젝트에 추가합니다. 프로젝트 참조를 선택하고 속성에서 로컬 복사False로 설정하여 참조된 어셈블리가 로컬 빌드 폴더에 복사되지 않게 합니다. Visual Basic에서는 프로젝트 속성참조 탭을 사용하여 로컬 복사 값을 False로 설정합니다.

  4. 클래스 파일 SampleV2AddIn의 이름을 바꿉니다.

  5. 클래스 파일에서 System.AddInSystem.AddIn.Pipeline에 네임스페이스 참조를 추가합니다. 코드에 QualificationDataAttribute 특성 예제가 포함되어 있으므로 System.AddIn.Pipeline이 필요합니다.

  6. 클래스 파일에서 버전 2 추가 기능 뷰 세그먼트에 대한 네임스페이스 참조인 CalcAddInViews(Visual Basic의 경우 Calc2AddInView.CalcAddInViews)를 추가합니다.

  7. SampleV2AddIn 클래스에 AddInAttribute 특성을 적용하여 이 클래스를 추가 기능으로 식별합니다.

  8. SampleV2AddIn 클래스에 QualificationDataAttribute 특성을 적용하고, 호스트가 AddInToken에서 가져올 수 있는 정보를 지정합니다. 이 경우에는 추가 기능을 별도의 응용 프로그램 도메인에 로드해야 함을 나타내는 정보입니다. 방법: 정규화 데이터 사용를 참조하십시오.

  9. 추가 기능 뷰를 나타내는 추상 기본 클래스인 Calculator2를 상속하도록 SampleV2AddIn 클래스를 설정합니다.

  10. Calculator2의 멤버를 재정의하고 해당 계산 결과를 반환합니다.

    다음 코드에서는 완성된 추가 기능을 보여 줍니다.

    
    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);
                }
            }
    
        }
    }
    

파이프라인 배포

이제 추가 기능 세그먼트를 빌드하고 필요한 파이프라인 디렉터리 구조에 배포할 준비가 되었습니다.

세그먼트를 파이프라인에 배포하려면

  1. 솔루션의 각 프로젝트에 대해 프로젝트 속성빌드 탭(Visual Basic의 경우 컴파일 탭)을 사용하여 출력 경로(Visual Basic의 경우 빌드 출력 경로) 값을 설정합니다. 예를 들어, 응용 프로그램 폴더의 이름이 MyApp인 경우에는 다음 폴더에 프로젝트가 빌드됩니다.

    프로젝트

    경로

    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

    참고참고

    파이프라인 폴더 구조를 응용 프로그램 폴더 이외의 다른 위치에 넣은 경우에는 이 표에 제공된 경로를 적절하게 수정해야 합니다.

  2. Visual Studio 솔루션을 빌드합니다.

  3. 응용 프로그램 디렉터리 및 파이프라인 디렉터리를 검사하여 어셈블리가 올바른 디렉터리에 복사되었는지 그리고 잘못된 폴더에 추가 어셈블리 복사본이 설치되어 있지 않은지 확인합니다.

    참고참고

    AddInCalcV2 프로젝트에서 Calc2AddInView 프로젝트 참조에 대해 로컬 복사False로 변경하지 않은 경우에는 로더 컨텍스트 문제가 발생하여 추가 기능을 찾을 수 없게 됩니다.

    파이프라인에 배포하는 방법에 대한 자세한 내용은 파이프라인 개발 요구 사항을 참조하십시오.

호스트 응용 프로그램 실행

이제 호스트를 실행하고 추가 기능과 상호 작용할 수 있습니다.

호스트 응용 프로그램을 실행하려면

  1. 추가 기능의 두 버전이 모두 배포되었는지 확인합니다.

  2. 명령 프롬프트에서 응용 프로그램 디렉터리로 이동하여 호스트 응용 프로그램을 실행합니다. 이 예제에서 호스트 응용 프로그램은 MathHost2.exe입니다.

  3. 호스트는 해당 형식의 사용 가능한 모든 추가 기능을 찾고 추가 기능을 선택하라는 메시지를 표시합니다. 이때 1 또는 2를 입력합니다.

  4. 계산기의 수식을 입력합니다(예: 2 + 2).

  5. exit을 입력하고 Enter 키를 눌러 응용 프로그램을 닫습니다.

  6. 2-5단계를 반복하여 다른 추가 기능을 실행합니다.

참고 항목

작업

연습: 확장 가능한 응용 프로그램 만들기

연습: 호스트와 추가 기능 간의 컬렉션 전달

개념

파이프라인 개발 요구 사항

계약, 뷰 및 어댑터

파이프라인 개발