How To: Build an Add-In using System.AddIn

13 באוגוסט 2007

How To: Build an Add-In using System.AddIn


When you want to build an extensible application, that dynamically loads add-ins into it, you face some challenges such as discovering, activating and managing the lifetime of the add-ins. With the new System.AddIn Framework that ships with Visual Studio 2008, this tasks becomes much easier. 


This post is a step by step guide for building a *very* simple add-in using the new System.AddIn Framework. I will guide you through building a Console Application that actives add-ins and runs them. In later posts I will discuss how to version the host and add-ins separately, talk about less trivial scenarios and provide feedback about this process which I hope the team will collect.


Create the Solution


Create a new solution in Visual Studio 2008, and create the following structure using solution folders.


System.AddIn Build


Create the Host Side


Host View


The Host View defines the way the host sees the add-ins.


Create the Host View of the add-in. Create a Class Library under the Host Side Solution Folder.


System.AddIn Build


Set the output directory of the host application project to be ..\output instead of bin\debug. This is because the discovery and pipeline building phases require a certain directory structure that will be discussed later.


Create a class for the simple add-in host view. This class is an abstract class with the operations that the host will call on the add-in.



/// <summary>


/// The host view defines the how the host sees the add-in


/// </summary>


public abstract class SimpleAddInHostView


{


    public abstract string SayHello(string name);


}


Host application


The host application actives and manages the lifetime of the add-ins.


Create the Host application as a Console Application under the Host Side Solution Folder.


System.AddIn Build


Set the output folder of the host view project to be ..\output\


Set this project as start up project.


Add references to System.AddIn and System.AddIn.Contract assemblies. Also add a reference to the Host.View project.


In the Main method of the host application, write the following code:



static void Main(string[] args)


{


    // Set the add-ins discovery root directory to be the current directory


    string addinRoot = Environment.CurrentDirectory;


 


    // Rebuild the add-ins cache and pipeline components cache.


    AddInStore.Rebuild(addinRoot);


 


    // Get registerd add-ins of type SimpleAddInHostView


    Collection<AddInToken> addins = AddInStore.FindAddIns(typeof(SimpleAddInHostView), addinRoot);


 


    foreach (AddInToken addinToken in addins)


    {


        // Activate the add-in


        SimpleAddInHostView addinInstance =


           addinToken.Activate<SimpleAddInHostView>(AddInSecurityLevel.Internet);


 


        // Use the add-in


        Console.WriteLine(String.Format("Add-in {0} Version {1}",


             addinToken.Name, addinToken.Version));


        Console.WriteLine(addinInstance.SayHello("Guy"));


    }


}


This code snippet rebuilds the cache of the add-ins store and the pipeline components. Then, the host is looking for add-ins of type SimpleAddIn and uses them.


Create the Add-In Side


Add-In Views


The add-in views define the base class for the add-ins. It is the way the add-in receives the calls from the host.


Create the AddIn Views project as a Class Library under the AddIn Side Solution Folder.


System.AddIn Build


Set the output folder of the host view project to be ..\output\AddInViews\


Add references to System.AddIn and System.AddIn.Contract assemblies.


Create a class for the simple add-in view. This class is an abstract class with the operations that the host will call on the add-in. This class is decorated with the AddInBase attribute. This attribute is important when building the pipeline.



/// <summary>


/// The add-in view defines the how the add-in sees how it is


/// called by the host


/// </summary>


[AddInBase]


public abstract class SimpleAddInView


{


    public abstract string SayHello(string name);


}


Add-In Implementation


The host can activate many add-ins. Each add-in derives from the add-in view and is deployed in a separate directory.


Create each Add-In Implementation project as a Class Library under the AddIn Side\AddIns Solution Folder. In the samples I created 2 add-ins.


System.AddIn Build


Set the output folder of the host view project to be ..\output\AddIns\<AddInName>. (For example: ..\output\first\ and ..\output\second). 


For each add-in implementation project, add references to System.AddIn and System.AddIn.Contract assemblies. Also add a reference to the AddIn.View project, but make sure you set Copy Local = False.


Implement the add-ins. Create a class that derives from the AddIn View class (SimpleAddInView), and decorated with the AddIn attribute. This attribute specifies the matedata of the add-in.



[AddIn("My First Add-In",


    Version="1.0.0.0",


    Description="Description of My First Add-In",


    Publisher="Guy Burstein")]


public class FirstAddIn : SimpleAddInView


{


    public override string SayHello(string name)


    {


        return "Hi, " + name;


    }


}


Create the Pipeline Components


Usually, we prefer that the add-ins will be activated isolated in a separate AppDomain for better sandboxing and robustness of the host application. This isolation brings the need for inter-appdomain communication that is addressed by building the appropriate pipeline between the host and the add-in.


The Contract


The minimal interface between the host and the add-in is expressed by a contract. This contract cannot be versioned.


Create the Contract project as a Class Library under the Contracts Solution Folder.


System.AddIn Build


Set the output folder of the host view project to be ..\output\Contracts.


Add references to System.AddIn and System.AddIn.Contract assemblies.


Create an interface for the contract. This interface contain the operations that the host will call on the add-in.



/// <summary>


/// This interface defines the contract between the host and the add-in, and


/// therefore cannot be versioned.


/// </summary>


[AddInContract]


public interface ISimpleContract : IContract


{


    string SayHello(string name);


}


Notice that this interface derives from IContract interface and is decorated by the AddInContract attribute.


Host Side Adapter


The host side adapter is responsible for converting from the contract to the host view. This way, the host view has no dependency on the contract and can be versioned independently.


In this sample, the host calls methods on the add-in, and not the opposite. Therefore, we only need an adapter from the Contract to the Host View.


Create the Host Adapters project as a Class Library under the Host Side Solution Folder.


System.AddIn Build


Set the output folder of the host view project to be ..\output\HostSideAdapters.


Add references to System.AddIn and System.AddIn.Contract assemblies. Also add a reference to Host.View and Contracts projects, but make sure you set Copy Local = False.


Implement the adapter. This adapter derives from the host view, and as an implementation calls method on an instance referenced by the contract interface.



/// <summary>


/// This class converts a contract instance to the host view


/// </summary>


[HostAdapter]


public class SimpleContractToHostViewAdapter : SimpleAddInHostView


{


    private ISimpleContract _contract;


    private ContractHandle _handle;


 


    public SimpleContractToHostViewAdapter(ISimpleContract contract)


    {


        this._contract = contract;


        _handle = new ContractHandle(contract);


    }


 


    public override string SayHello(string name)


    {


        return this._contract.SayHello(name);


    }


}


Notice that the constructor initiates an instance of ContractHandle which helps the lifetime management of the add-in.


Add In Adapter


The add-in side adapter is responsible for converting from the add-in view to the contract. This way, the add-in view has no dependency on the contract and can be versioned independently.


In this sample, the host calls methods on the add-in, and not the opposite. Therefore, we only need an adapter from the Add-In View to the contract.


Create the Add-In Adapters project as a Class Library under the AddIn Side Solution Folder.


System.AddIn Build


Set the output folder of the host view project to be ..\output\AddInSideAdapters.


Add references to System.AddIn and System.AddIn.Contract assemblies. Also add a reference to AddIn.View and Contracts projects, but make sure you set Copy Local = False.


Implement the adapter. This adapter derives from the BaseContract, implements the contract, and as an implementation calls method on an instance of the add-in view.



/// <summary>


/// This class converts an add-in view to the contract


/// </summary>


[AddInAdapter]


public class SimpleAddInViewToContractAdapter : ContractBase, ISimpleContract


{


    private SimpleAddInView _view;


 


    public SimpleAddInViewToContractAdapter(SimpleAddInView view)


    {


        this._view = view;


    }


 


    public string SayHello(string name)


    {


        return this._view.SayHello(name);


    }


}


Running the Sample


You can download the sample project that contain the simple add-in created with System.AddIn in this post. Running this sample, you should see this result:


System.AddIn Build


Additional Resources



Enjoy!

Add comment
facebook linkedin twitter email

39 comments

  1. Eran Kampf14 באוגוסט 2007 ב 9:48

    Why exactly do I need both AddinView, HostView base classes and the ISimpleContract interface?

    Why not simply have an ISimpleContract implemented by both addin classes and the adapter? Shouldn't this save all the conversions etc.?

  2. Guy Burstein14 באוגוסט 2007 ב 17:15

    Hi,

    To be short, the reason is to allow both the add-in and the host to be versioned independently. I really encourage you to take a look at the article in MSDN magazine for better explanation. http://msdn.microsoft.com/msdnmag/issues/07/03/CLRInsideOut/default.aspx

    Guy

  3. Amit Caspi15 באוגוסט 2007 ב 9:21

    Hi Guy,
    A good one.
    Can I ask for Chapter-II? A host and add-in demo where the add-in calls host methods (services). i.e demonstrate the opposite direction of calls.

    Amit

  4. Jesse Kaplan21 באוגוסט 2007 ב 23:24

    Amit, take a look at this post on our team blog: http://blogs.msdn.com/clraddins/archive/2007/04/13/automation-add-ins-jesse-kaplan.aspx

    It includes a sample that demonstrates how the host can provide services to the add-in.

    –Jesse

  5. Alex Hoffman22 באוגוסט 2007 ב 2:01

    Using a separate AppDomain is understandable for certain circumstances, especially when add-ins can be implemented by untrusted 3rd parties, but does the architecture support primary AppDomain add-ins? Ones that are internally produced, and therefore completely trusted and which don't need to be sandboxed? If so, is a simpler pipeline supported for those?

    One more question… is Remoting used for inter AppDomain communication?

  6. JackG5 בספטמבר 2007 ב 4:26

    Alex,
    You can (if appropriate) use the Add-In model for in AppDomain fully trusted "Add-in's". We have an optimization for this type of scenerio:
    http://blogs.msdn.com/clraddins/archive/2007/07/27/by-popular-demand-jack-gudenkauf.aspx
    - JackG

  7. Boris Yurovsky10 באוקטובר 2007 ב 17:22

    Hi Guy,
    Thanks a lot for detailed instructions on building the pipeline. Some details would have taken me a lot of time to discover on my own.
    Do you happen to have any samples demonstrating how to pass a UI object, say, Window.Forms.Control, from Add-in to Host side, so the host can place the control populated by Add-in wherever it (host) likes? There is a good sample of the same scenario in CLR Add-In Team Blog, but their example deals with WPF and not with simple Window Forms.
    Boris.

  8. John Kattenhorn25 באוקטובר 2007 ב 11:20

    Hi,

    Thanks a lot for this, it was exactly the walkthrough i was looking for.

    John

  9. Walter Senekal2 בדצמבר 2007 ב 20:44

    Hi
    Do you have this sample in VB as well?
    I tried to convert it but I get some problems
    There is no replacement for the "abstract"
    public abstract class SimpleAddInView
    {
    public abstract string SayHello(string name);
    }
    }
    The closest I can get to this in VB is:
    Public MustInherit Class SimpleAddInView
    Public Function SayHello(ByVal name As String) As String
    End Function
    End Class
    Best regards
    Walter

  10. Guy Burstein [MVP]2 בדצמבר 2007 ב 20:49

    Sorry, I don't have any VB sample :(

  11. Craig Wilson16 בדצמבר 2007 ב 1:01

    vb ->

    Public MustInherit Class SimpleAddInView

    Public MustOverride Function SayHello(ByVal name As String) As String

    End Class

  12. limor18 במאי 2008 ב 22:15

    How can the add in display a window form?

    Thanks in advance

  13. ThisIzNutz22 במאי 2008 ב 17:35

    Um, this is crazy amounts of code to do something you can do with simple Reflection, isn't it? Security aside….

  14. Consider...22 במאי 2008 ב 17:46

    Define an interface that the assembly implementers must use and…

    FileInfo[] files = SomeFolder.GetFiles("*.dll");
    foreach(FileInfo f in files)
    {
    try
    {
    Assembly a = Assembly.LoadFrom(f.FullName);
    Type[] types = a.GetTypes();

    foreach (Type t in types)
    {
    Type tpdType = t.GetInterface("ISomeInterface");
    if (tpdType != null)
    {
    ISomeInterface ipd =
    (ISomeInterface)Activator.CreateInstance(t);

    // save in a list to be used somewhere….
    someList.Add(ipd);
    }
    }

    }
    catch (Exception e)
    {
    // log it somewhere…..
    }
    }

  15. Roy Hodges23 ביוני 2008 ב 21:49

    I think this is absolutely crazy, this takes development and multiplies the development time by a factor of four to five times. I come from a C/Assembly background and the reason I program in C# is to get away from all this stuff, and now with security, it's gone off the deep end. So basically I have to write the same idea of the class, four times. One for the add in, one for the host view, one for the plugin view, and the one for the contract?

    I just spent eight hours+ trying to figure out how these different projects work, how this is handled, why you called it a HostView, when it's not really a view with regards to any UI – very confusing for a UI programmer. All for what? To write "Hello" to the screen? And now this adapter thing is just silly, I quit for 20 minutes after I had to write an adapter.

    And how am I supposed to build an addin that pulls from a pluggable datatier into a pluggable UI? You've got to be kiddding! Security aside, this is not what I think of as a usable API. It's going to cost us thousands, if not tens of thousands in programming time to develop addins/snapins/plug###. We've got to have people take the time to learn it, take the time to apply it then figure out how to fit a single plugin into all these views, adapters, contracts and such.

    Please try to think of something a bit more easy on the wallet.

  16. Roy Hodges23 ביוני 2008 ב 22:16

    As a suggestion, why not expand CAS to be handle security of relection loaded assemblies with respect to addins?

  17. Ramaraju23 ביולי 2008 ב 10:59

    In C#
    I have one doubt reg Passing Collection Between Add-In and Host.
    When I Pass one class object to Add-In from Host which is derieved from ISerialazable, it have behave like separe instance…how come…I want to use same instance in both Host and Add-In Side.

    Can you suggest me any body??

  18. Ramaraju23 ביולי 2008 ב 11:27

    In C#

    I have one doubt reg Passing Collection Between Add-In and Host.

    When I Pass one class object to Add-In from Host which is derieved from ISerialazable, it have behave like separe instance…how come…I want to use same instance in both Host and Add-In Side.

    Can you suggest me any body??

  19. Anthony Kilhoffer7 באוגוסט 2008 ב 13:07

    I've seen examples which use both interfaces and abstract classes for the host view and add in view. Right now, I have a working demo that uses interfaces for the host views and add in views. I would like to migrate to an abstract class so I can place common implementation in the base class. My question is, do I have to use an abstract class in both the host and add in views, or can I just use the base class in the add in view and leave the interface in place for the host view?

  20. сайдинг18 באוגוסט 2008 ב 6:18

    6rI'll thingk about it.4p I compleatly disagree with last post .
    дома сайдинг 6v
    стальной сайдинг 1b

  21. Mark20 באוגוסט 2008 ב 4:17

    This API is a classic example of over-engineering. I hope version 2 is easier to use. Until then I'll stick to using reflection.

  22. Arthur Pham12 בספטמבר 2008 ב 2:55

    Great tutorial!

    However, when I tried to use this with a ASP.NET application it said:

    calculators.Add(addInToken.Activate(AddInSecurityLevel.Internet));
    Unable to cast transparent proxy to type 'Contracts.ICalculatorContract'.

    Or in detail:
    Unable to cast transparent proxy to type 'Contracts.ICalculatorContract'.
    Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

    Exception Details: System.InvalidCastException: Unable to cast transparent proxy to type 'Contracts.ICalculatorContract'.

    Source Error:

    Line 41: foreach (AddInToken addInToken in calculatorAddIns)
    Line 42: {
    Line 43: calculators.Add(addInToken.Activate(AddInSecurityLevel.Internet));
    Line 44: }
    Line 45: return calculators;

    Source File: …\MyWebCalculatorAddInArchitecture\Host.CalculatorProvider\CalculatorProvider.cs Line: 43

    Stack Trace:

    [InvalidCastException: Unable to cast transparent proxy to type 'Contracts.ICalculatorContract'.]
    CalculatorContractToHostAdapter_ConstructorInvoker(Object ) +41
    System.AddIn.Hosting.AddInActivator.AdaptToHost(AddInToken pipeline, IContract addInContract) +266
    System.AddIn.Hosting.AddInActivator.ActivateInAppDomain(AddInToken pipeline, AppDomain domain, AddInControllerImpl controller, Boolean weOwn) +680
    System.AddIn.Hosting.AddInActivator.Activate(AddInToken token, PermissionSet permissionSet, String appDomainName) +193
    System.AddIn.Hosting.AddInActivator.Activate(AddInToken token, AddInSecurityLevel level, String appDomainName) +152
    System.AddIn.Hosting.AddInActivator.Activate(AddInToken token, AddInSecurityLevel level) +111
    System.AddIn.Hosting.AddInToken.Activate(AddInSecurityLevel trustLevel) +46
    Host.CalculatorProvider.CalculatorProvider.LoadCalculatorAddIns() in D:\My Documents\OMNIABIT\AddIns\MyWebCalculatorAddInArchitecture\Host.CalculatorProvider\CalculatorProvider.cs:43
    Host.CalculatorProvider.CalculatorProvider.get_Calculators() in D:\My Documents\OMNIABIT\AddIns\MyWebCalculatorAddInArchitecture\Host.CalculatorProvider\CalculatorProvider.cs:20
    Host.WebCalculator._Default.Page_Load(Object sender, EventArgs e) in D:\My Documents\OMNIABIT\AddIns\MyWebCalculatorAddInArchitecture\Host.WebCalculator\Default.aspx.cs:26
    System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr fp, Object o, Object t, EventArgs e) +15
    System.Web.Util.CalliEventHandlerDelegateProxy.Callback(Object sender, EventArgs e) +33
    System.Web.UI.Control.OnLoad(EventArgs e) +99
    System.Web.UI.Control.LoadRecursive() +47
    System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +1436

    Can you tell me how to get out of this. Or can you extend a Web App to your current tutorial.

    Thx.

    Regards,

    Arthur Pham

  23. Arthur Pham12 בספטמבר 2008 ב 9:10

    Nevermind my previous post. I have been able to handle it also bundled with UltiDev Cassini project. I now have a complete framework from Addins -> Host -> Web Server -> Setup -> and running like a charm! Thx for your tutorial about AddIns.

  24. Ramaraju22 באוקטובר 2008 ב 8:15

    I have problem.
    i Implemented one Add-In and added ref addinviews from pipeline path.
    Host succefully identify the addin.

    now if i copy addinviews dll to somewhere and referecing that dll into addin.

    now host not able to identify addin.

    Can you tell me any body how to fix this problem.(i need to ref addinviews from different location not from addin pipeline path).

  25. Christian11 בנובמבר 2008 ב 6:29

    Hello,

    first its a great tutorial. But now, i implement it into a bigger project and i cant get it work.

    My problem is , that the AddInStore could not find the AddInAdapters. Is there any way to debug this problem?

    I call:
    Collection addins = AddInStore.FindAddIns(typeof(SimpleAddInHostView), addinRoot);

    (there is NO exception, telling me that the pathes are incorrect, so i think thats fine).

    When i debug the AddInStore, i see, that it finds the AddIns, but the AddInSideAdapter Count is 0. They are located in ../output/AddInSideAdapters

    cheers,

    Christian

  26. dotnetnoob19 בנובמבר 2008 ב 12:15

    I'm curious

    Do people think this is overkill for web applications? We are working on an enterprise web application which will need to be extended by other business units and possibly customers. This would *seem* like a good fit.

  27. Peter Kulek24 בנובמבר 2008 ב 15:26

    I agree with Roy Hodges that this is absolutely crazy way of doing Dynamic programing.
    I have been developing enterprise wide business applications for over 20 years using a variety of static and dynamic languages including but not limited to C, C++, Java, .NET, Delphi, Smalltalk, Clipper, Python and Lua.
    I have found that using a dynamically typed language is far more productive less error prone and extending applications at runtime was trivial.
    The .NET, JAVA platforms have one main problem in that they are statically typed and as such have added tons of work a-rounds to make them act like a dynamic language such as Reflection, Function Overloading, Generics, Interfaces, Lambda expressions amongst others. Both have now developed a poorly implemented dynamic language interface, how sad.
    In practice I have found that using a Dynamically typed language produces as robust an application as statically typed with the same performance, using far less code, are much easier to extend and need less learning.

  28. Gosh22 בינואר 2009 ב 10:43

    developing enterprise wide business applications with Lua?!? Hum…

  29. sammy4 באפריל 2009 ב 5:33

    4kX46K vkoo7wvY5Xkfak7bf1Th

  30. Nevin9 באפריל 2009 ב 23:12

    Good god, there has to be an easier way than this. Doing it (with what I thought was the hard way) with reflection and manually loading and unloading appdomains is easier than this. That is now the easy way…System.AddIn is now the hard way.

  31. Jarek16 באפריל 2009 ב 16:18

    I was wondering about people getting this to work in a asp.net application. What steps do I need to take to use MAF in that type of setting. I have tried to incorporate the calculator example by adding a new webapp project to the solution. But as of yet to no avail.

    I was also unable to find any code on the internet that used a ASP.NET project in conjunction with MAF.

    Any help would be much appreciated.

  32. states fourth agriculture5 ביוני 2009 ב 16:22

    community chemical projected high led amount

  33. yahoo fuel studies december21 ביוני 2009 ב 1:43

    warms states lapse prepared adaptation

  34. climate levels inside decline24 ביוני 2009 ב 8:40

    issues state time africa comparable percent likewise

  35. videos new29 ביוני 2009 ב 0:40

    measurements india reducing alternative 100 physical

  36. future basis 20053 ביולי 2009 ב 7:17

    bush 2007 web economists warmest available countries service

  37. Chris Pietschmann30 ביולי 2009 ב 5:04

    I know it's been awhile since you originally posted this, but do you know of any examples/articles that show how to use System.AddIn within an ASP.NET Application for creating UI addins? Thanks.

Comments are closed.