Behind the Scenes: Activation [Pete Sheill]
Let’s discuss the specifics of the construction of the add-in, the isolation boundary and the pipeline in the add-in model – i.e. activation. I’ll be referring to the discovery and activation as discussed here and the segments of the add-in pipeline as discussed here. Let’s say you have discovered a single token for an add-in, and you have decided to activate it. To isolate the add-in from the host you chose to run it in its own new appdomain. What happens next when you call one of the Activate() methods on the add-in token? Let’s enumerate all of the steps that the system takes:
1. The appdomain is created with the permissions that you specified in the call to Activate().
2. The add-in assembly is loaded into that appdomain through a call to Assembly.LoadFrom(), passing in the location of the assembly. The add-in type will not be loaded in the host’s appdomain. In other uses this call can cause loader context issues if not used carefully, but we’ve taken steps to avoid them here. In fact, this method was created specifically to support add-in scenarios.
3. We use reflection to call the no-argument constructor for the add-in to create an instance in that appdomain. Naturally, since the add-in derives from the AddInBase class that is deployed in the AddInView assembly, this action causes the loader to load that assembly. At this point we have an instance of the add-in running in its own appdomain. Now we need to connect it to the host.
4. An instance of the AddInSideAdapter is constructed, passing in the add-in (typed as the add-in base) as the only argument to the constructor. (This adapter implements a Contract interface and derives from MarshalByRefObject through the intermediate ContractBase class. Therefore it can appear to live in both the host’s and the add-in’s appdomain—any calls to it from the host’s domain are proxied and executed in the add-in’s appdomain.)
5. The activation code passes the add-in adapter back to the host’s appdomain typed as the Contract that it implements.
6. An instance of the HostSideAdapter is constructed in the host’s appdomain, with the AddInSideAdapter being passed in as its only argument. The exact HostSideAdapter type is determined by examining the add-in token. This adapter derives from (or implements) the HostViewOfAddin that the host compiled against.
7. The activation code returns the host-side adapter to the host, typed as the HostViewOfAddin.
The host will sometimes do further initialization of the add-in through a separate call, passing in any needed arguments. Then it is up to the host and add-in to communicate as needed. Activation has completed.
Do the adapters need to be public? Actually, no. Though the normal rules of visibility would suggest that they would need to be public in order to be created and used by the activation code, the rules don’t apply in this case. That’s because we use reflection to invoke the constructor, and given enough permission, reflection can invoke non-public constructors. We didn’t want the adapters to need to be public because they exist as an implementation detail, not things to be documented and supported in themselves.
When the HostSideAdapter is constructed with a reference to the AddInSideAdapter, it does something right away that we wouldn’t need to do if all the objects were in the same appdomain. It needs to call the AcquireLifetimeToken() member, specified in the IContract interface, of that adapter. That is part of another big topic, lifetime management. We’ll discuss that in another post.
This is one way to activate an addin in this model. There are also some other different ways you could activate the same addin. You could choose to activate it in the same appdomain as the host. You could also choose to activate it in an entirely separate process, if isolation matters above all else. We’ll explore those options more in later posts.
Comments
Anonymous
July 03, 2007
Hi,I just tried multiprocess activation and it works fine.I was wondering if you are going to support Async calls to Addins, in order to avoid to block the calling application.ThanksClaudioAnonymous
July 13, 2007
with in-process addins, will this architecture support addins, plugging in or providing UI elements? what are the mechanics? i know with the current framework there's issues with serializing/marshalling controls.Anonymous
July 16, 2007
The comment has been removedAnonymous
July 17, 2007
I spoke to Jesse [Kaplan] on multiple occasions and thought it would be better to post this topic here instead of emailing directly. Jesse mentioned that there would be provisions this time around for developers to load an executable (plugin) in a new app domain along with its respective app.config file! I was wondering if any one of you folsk can throw some more light on this issue (an example would give me immense pleasure!). Also, if this feature is provided, what would be the consequences when someone loads multiple plugins - specifically, will the plugins depend on the host (not good) for config info, or each plugin will be allowed its plugin file (very good!). Also, if each plugin can have its own config file, I am sure there will be ways to detect that plugin's config file? Thanks!Anonymous
July 18, 2007
Does this Add-In API set allow EXEs to also be loaded and communicted with or does it work only with DLLs.. essentially the question is can the AddIn have its own UI that it will show in the AppDomain?Anonymous
July 19, 2007
Atul, I don't believe we have any issues with Add-in's as exe's. We won't call Main() however. Add-in's may have UI. - JackAnonymous
July 19, 2007
Dhaval, A quick look at the code looks like we support Add-In config files. Examples of config files may be found on here (http://blogs.msdn.com/suzcook/archive/2003/06/02/57160.aspx). - JackGAnonymous
July 19, 2007
Just some comments on JJB's rant: While the current add-in model is particular about the placement of separate dll’s for adapter and contract layers, you can have a common interface that both the add-in and host view use. And after the initial add-in pipeline has been established, you have a lot more flexibility as to where you can store subsequent adapters. I’m currently using a single adapter dll that both the host and add-in adapter dll’s reference. I find everything needs a contract in the contract dll, mostly because things need to be neutered to either base CLR types or other contracts. The pay-off is not in today’s code, but in tomorrow’s code. Also, being beta we have to hand code everything, whereas I expect we may see some code gen tools sooner or later.Anonymous
July 19, 2007
The comment has been removedAnonymous
July 20, 2007
Claudio,We do support invoking addin methods asynchronously. For the methods that need to be invoked that way, create a delegate in the HostViewOfAddIn and call BeginInvoke on that delegate. Note that care should be taken to properly handle errors, including exceptions, since you will no longer be on the main thread's call stack. Here are more details on this:http://msdn.microsoft.com/msdnmag/issues/04/01/BasicInstincts/-PeteAnonymous
July 27, 2007
Regarding support for UI elements - Here you go! http://blogs.msdn.com/clraddins/archive/2007/07/27/by-popular-demand-jack-gudenkauf.aspxJackGAnonymous
August 07, 2007
The comment has been removedAnonymous
August 07, 2007
Hello David,Please take a look at our recent post on new features in beta2 and I think you’ll find something that fits your need: http://blogs.msdn.com/clraddins/archive/2007/07/27/by-popular-demand-jack-gudenkauf.aspx.There is a new feature called DirectConnect which ensures that when the feature is enabled we avoid activating the pipeline in the cases where the host and add-in use the same interface and are in the same AppDomain. This allows you to avoid the extra overhead of the pipeline when you don’t need it.We still encourage you to take a look at AppDomain isolation and verify that the costs are too expensive before you rule it out as you may find that it is acceptable. But there are definitely cases where the need for performance trumps the needs for isolation which is why we added the above feature.Anonymous
August 07, 2007
Thanks for the answer. I should add that my framework will provide a number of generic interfaces and classes, as well as other things such as IEnumerable<> or IList<> of other interfaces and non-serializable classes, which go against the "Restrictions on Contracts". Can the Add-in framework disregard all these restrictions when staying within one AppDomain? After all, all I really need is to keep track of what add-in assemblies and classes are available, and to load the assemblies and instantiate the classes on-demand from tokens. Since it is a compiler project, the process itself is short-lived and the inability to unload DLLs is not a problem.I see that isolation is good sometimes, but I envision millions of inter-addin calls happening each time the program is run, because the interfaces tend to provide fine-grained functionality. For example, a character stream interface has an indexer that gets called for each character. It wouldn't be good for the CPU to spend most of its time marshalling.I would love to see an example that doesn't use isolation, which shows how to optimize calls between addins for speed.Anonymous
August 29, 2007
System.AddIn in Framework V3.5 is about building hosts that load plug-in Add-Ins with functionality around...Anonymous
October 02, 2007
Is it possible to have multiple hosts sharing the same addin? If yes, how the activation scheme works for the second host?Anonymous
October 24, 2007
The comment has been removed