Cool, okay, so, this is my talk: HOWTO: Using WPF 4 (and .NET 4 in general) from a .NET 3.5 application.
So, here at Red Gate we write an awful lot of plugins to this application, which is Microsoft SQL Server Management Studio: Prompt, Source Control, Test, Tab Magic, SIP, Doc, Dependency Tracker, Data Generator, and now the new thing that I’m working on, which is a plugin for Deployment Manager to create packages from your database.
So, the problem is that SSMS 2008 is running on the .NET 2 runtime, which you can see here from the debugger: mscorlib v2, and SSMS 2012 is running on the .NET 4 runtime: mscorlib v4.
So, the question when we’re writing an app is which version of the runtime do we target?
So, when I started off writing the plugin for SSMS I targetted .NET 4, and this worked perfectly fine on my machine using SSMS 2012, but when you start loading it on SSMS 2008, you get this error message, which is: “This assembly is built by a runtime newer than the currently loaded runtime and cannot be loaded.”
So this means that to load my application on both SSMS 2008 and SSMS 2012, I have to target the .NET 2 runtime, which is framework version 3.5.
And that’s exactly what SQL Source Control does, and it’s fine, you can write an application like that, until you try to reference NuGet.
When you reference NuGet you get this error message here, which is from NuGet installing a reference to NuGet, which is quite funny, saying that NuGet doesn’t have a DLL that targets 3.5, so you can’t reference this, unless you target .NET 4.
So, we’re now in a quandry. If we target 3.5 we can’t reference NuGet, and if we target 4 then we can’t load into SSMS 2008, but our product manager wants both of these: he wants our application to run in all versions of SSMS, because that’s where the customers are, and he wants our application to create NuGet packages, therefore, it’s going to have to call the NuGet library to do that.
So, what solution do we have to this?
So, the solution which we came up with, thanks to Mike who pointed out that this was possible, is that we have SSMS at the top, which will either be running on the .NET 2 or the .NET 4 runtime, depending on whether it’s 2008 or 2012. And then we have a child process, RedGate.SQLCI.UI.exe, and that’s running on .NET 4.
So, now any operation that we want to do on .NET 4, say for example if we want to call the NuGet library, or if we want to use the Task class in the framework, or if we want to use any of the new shiny features that are in WPF 4, we do it in the child process. And anything that has to do with SSMS, like right-click menus and so forth we can do in the top one.
The problem is that our UX designer wants us to be ingeniously simple; he doesn’t want us to just launch off a child process and have that start displaying windows and everything.
So this is obviously still in development, which is why it looks utterly hideous, but what we’ve got here is SSMS and this is a modal dialog, where the control in this modal dialog is running in the child process and the modal dialog itself is running in the parent process inside SSMS.
So to the users this behaves just like a normal modal dialog and you won’t know what’s going on, and in fact, you’ve seen all of this happen if you’re running Chrome, because Chrome spawns off a child process for each tab, as does IE nowadays, but from a user you can’t tell, because they all seemlessly integrate into one application, and this is because in Windows you can have controls running in seperate programs, and as long as they’re all running under the same user account, the security system doesn’t mind all displaying them together.
So that’s what we’re going to do.
So, how do we actually do this without falling back to P/Invoke, because obviously P/Invoke is bad and wrong? We want to use as much stuff in the framework as we can.
So, the framework defines an interface called INativeHandleContract, which has got a method on it called GetHandle, which basically gives it the window handle, and irrespective of how it’s implemented under the covers, the important thing are these two methods. The first one takes a framework element, which is some kind of WPF control and coverts it to the interface, and the second method takes the interface and converts it back to a framework element.
And the awesome thing here is that framework elements we can’t pass across a .NET remoting channel between the SSMS process and the child process, but implementations of the interface we can pass across just fine, because what happens is: in the child process we call ViewToContractAdapter, which converts our framework element into an implementation of the interface. The interface still can’t go across the boundary because it’s not serializable or MarshallByRef, but it’s an interface, so we can just create a class, which is MarshallByRef, implement the interface, and we can just proxy the method call onto the actual implementation that the .NET framework has gaven us. That MarshallByRef class can now go across the .NET remoting boundary, and on the SSMS side of things, we just call ContractToViewAdapter, which converts the interface back into a framework element, and now the SSMS process can just put it as a child control in the form.
And so that’s absolutely all you have to do; there’s some code on GitHub that does all of this, and we’re now using this.
There are some slight problems, which is…
So this is, yes, so this was on Yammer this morning: this is the number of crash reports coming into the public facing webservice every hour, and you can see that at 9:30 my tester finds a bug on his machine, and it turns out that WPF has some awesome retry logic, so if anything goes wrong WPF just retries automatically, and Ali left the thing running, and that graph happened.
The reason why this hit us was because in the SSMS process all the exceptions get displayed in nice little crash dialogs and don’t send in automatically, but, for debugging, in the child process I had set them to just send in exception reports automatically without popping up consent dialogs, which will change before release, because this sort of thing happens.
So, yeh, that’s the end of the talk. Apart from this, it just works wonderfully. Cool, any questions?