In my last entry, Using the Add-In Manager to Load and Unload Add-Ins , I mentioned the Microsoft.VisualStudio.Tools.Applications.AddInManager.dll assembly and that it contains classes that can be used to load add-ins into a hosting application. I also promised that I’d write another entry that explained how to do just that. This time around, we’ll actually get into some of the code.
By default, the classes in the Add-In Manager load add-ins into their own AppDomains, which the Add-In Manager will create automatically. This can be accomplished easily using a piece of code like this:
AddIn addIn = new AddIn("My Arbitrary AddIn Name", @"c:\myaddins\myaddin.dll");
AddInCollection addInCollection = new AddInCollection();
Context addInContext = new Context("My Arbitrary Context Name", myHostItemProvider);
Let’s examine this example in detail. First, we instantiate an AddIn that will be used to load the add-in assembly “c:\myaddins\myaddin.dll”. We give it a name to identify it later, which is also going to be used as the FriendlyName of the AppDomain that will be created for it later on. Note that at this point we haven’t loaded any code or created an AppDomain; all we’ve done is provided some data about the add-in so that we can load it later. Next the AddInCollection is instantiated and the AddIn added to the collection. As I mentioned earlier this step might seem superfluous at the moment, but for the time being it is necessary. Finally, we create the context, again with an arbitrary name for identification.
We’ll also supply our IHostItemProviderContract implementation to the Context, which will be passed into each add-in when it starts up. The IHostItemProviderContract is basically the mechanism through which the add-in bootstraps communicates with the hosting application. Details on implementing IHostItemProviderContract are provided in the VSTA SDK documentation, so I won’t go into that here.
The last method call to AddIn.Load actually does all the work. The Add-In Manager creates a new fully-trusted AppDomain with the ApplicationBase property set to “c:\ mycodebase.” Note that the ApplicationBase is passed to the Load function, the path information passed to the AddIn’s constructor is not used here. Behind the scenes, the Add-In Manager then uses a combination of Assembly.LoadFrom and Assembly.Load to load the add-in passed to the constructor into the new AppDomain. That sounds a little complicated, but it results in the behavior that one would expect: the path information specified to the constructor will be honored if no assembly with the correct name can be found in the GAC or the ApplicationBase.
Once the add-in assembly has been loaded into the target domain, the Add-In Manager invokes its constructor and starts calling various methods on it to start it up. This eventually results in a method called InternalStartup being called, and an OnStartup event being fired. These are the two main entry points to the user’s add-in code. The user’s code will most likely advise on some events of the object model in this method, or do any other necessary startup work. Once the initialization code completes, the add-in is in a state that we call “Loaded,” meaning that the AddIn.IsLoaded property will be true. For the most part, you can think of “Loaded” as meaning “Executing” when it pertains to the Add-In Manager, since there is no concept of a “The assembly is loaded but not yet executing” state.
So, that’s a simple rundown of how the Add-In Manager loads add-ins by default. Of course, we’ve designed the Add-In Manager so that it also supports loading add-ins into specific AppDomains. We’ve also built-in numerous other features to support partially-trusted add-ins. More on that later.
Aaron HareSoftware Design Engineer