PDC05: Addins in .NET

   I noted in an earlier post that I was involved in a committee to establish an Addin Model for Windows Vista. As will most large committees, progress was slow. But it wasn't fruitless. I will be speaking at PDC tomorrow with my colleague Jim Miller on the subject of Addins (FUN309: Designing Managed AddIns for Reliability, Security, and Versioning). The fact that the committee existed, and the subject of our talk, should tell you something: there is not as yet an easy to use addin model in .NET. Of course, we're working on one; the talk tomorrow will reveal some of the results of that committee, as well as the start of the solution. But this article is about the problem.

   So, it is possible to do Addins in .NET today. The issue is that everyone must roll their own system. And when they do they run into the same issues and problems around versioning, isolation and security.

   To illustrate the issue let me draw and analogy: In the old days of Win32 programming you would create dlls which contained exported functions which you wanted to expose to consumers. The consumer would go through a few steps to access your function: LoadLibrary, GetProcAddress, and then invoke the function. It was possible to create an addin model this way, but consumers and suppliers would have to agree on a set of entry point function signatures. Then came COM. COM provided a system to agree on this set of functions by defining COM interfaces. It also wrapped up a lot of the complication of discovery and loading functionality by creating helper functions like CoCreateInstance.

   Well, .NET is basically still at this first level. It has the analog of the LoadLibrary\GetProcAddress functionality – but it is actually more complicated because of its object oriented nature. The analog is Assembly.Load, GetType, GetMethod, Invoke – in other words System.Reflection. It is possible to create addin models using reflection. It is even possible for the consumers and suppliers to agree upon types to use. But then you start getting into versioning issues. In .NET two different versions of a Type are technically different types. If I write an addin to your version 1 types, it won’t work with the same but newer versioned v2 types – the system regards them as different types. There are hoops you can jump through to work around this dilemma using config files and binding redirects, but that adds a level of complexity you don’t want to deal with.

   The next issue is unloadability. What if an addin misbehaves and you want to get rid of it? Win32 had UnloadLibrary, but in .NET you can’t directly unload an assembly. This has to do with the garbage collection system. The closest you can get is to create a separate AppDomain, load the addins in separate domains, and when they misbehave – or you just want to unload it for any reason – you can unload the appdomain, which unloads anything in it. BUT, and this is a big but, if you want to communicate with that addin – or want it to communicate with you, everything that goes through the communication layer must be marshallable to cross the domain boundary. This means – using .NET remoting – that the transitive closure of all of the types used to communicate – which can include even internal and private types if objects are copied – must be either serializable or derived from System.MarshalByRefObject. Whoa.

   OK, while we’re on the subject of AppDomains, let’s consider security. .NET provides a rich security model. There is no better application of security than addins – addins can come from anywhere and you have no idea whether to trust them necessarily. But in order to load an addin in a different security profile than you, you need to isolate it – either in another AppDomain or even another process. That gets you back to the marshalling restrictions.

   And we haven't even talked about discoverability. Now, as in the early times of Win32, it is up to you to roll your own system of finding the addins, and then getting them loaded and hooking up the functions.

 So you see, addins in .NET have some issues the way things are today. But wait until tomorrow. After our talk I’ll start going into the solutions.