Thursday, January 31, 2008

Job Opening

We have a job opening for a C# developer on our MG-ALFA development team. If you're interested, contact me soon.

Executing code in another app domain.

Recently, we found out that the Infragistics controls we're using have some bugs in them that cause them to never be garbage collected once you've used them in your code. There doesn't seem to be any resolution short of asking them to fix the bugs, as the bugs are related to their code adding event handlers for receiving notification of changes in the office 2007 themes container (their internal class).

Anyway, we figured out that there isn't any workaround for this short of modifying and recompiling their code or waiting for them to fix their bugs. Since we don't have time for that, and it represents a HUGE memory leak in our application (each time our form is opened and closed, the UI sucks up 30-50MB of additional memory!)

So, our solution, you ask? We first built an out of process approach to executing our UI via IDispatch implemented in a out-of-process .EXE COM server. This was a fair amount of work and was a really cool project in and of itself. If you're interested in it, let me know and I'll tell you how it works. Unfortunately, due to changes in windows rules for window activation and some plumbing issues, it turns out that the out-of-process approach won't activate the window when it starts up, and short of doing some really nasty hack, there isn't an easy fix for that.

When we figured the out-of-process solution wasn't really the best answer for our problems, we decided to investigate further. Once we determined that the leak was on our .NET side of the fence (our .NET UI is called by a Dyalog APL-based user interface via their .NET interop support), we decided to investigate the .NET side a bit more. That's when we found that it was a leak caused by the Infragistics controls.

I did a proof of concept that created a separate AppDomain, called our NUI, and then shutdown the app domain. This seems to fix our memory leak problem as all memory associated with the app domain is freed (at least it seems to be). It turned out to be much harder to get this working than I had expected. In theory it's quite easy, but we have several deployment issues that make it very difficult (we can't put our DLLs in the GAC, and our application DLLs are not (and can't be) in the same folder as the 'application base' (because the base application that creates the main app domain is Dyalog.exe or DyalogRT.exe and we don't want this in our application folder).

The process goes something like this:
1) create an app domain
2) load our assembly into the domain
3) create a serializable object with our arguments for the call to the assembly
4) call to the assembly (serializing the arguments) (blocking call)
5) the assembly returns from the call, returning the results in a serializable object
6) the calling code deserializes the object and continues as it normally would.

The problems in this are: (2) finding the assembly; (3) making the serialized object's assembly available to the target assembly; (4) getting a reference to a MarshalByRefObject to make the call against; (5) loading the results objects assembly in the source app domain.

The main issues are loading the assemblies on the caller's side, rather than the target side, amazingly! It seems like this should be the easy part, but for some reason the remoting infrastructure isn't smart enough to realize that the assemblies are ALREADY LOADED!

Anyway, the basics are like this:

  1. create an object that derives from MarshalByRefObject. Put this in an assembly that you don't mind having loaded in both app domains.
  2. define a method on this object that does the work you want done.
  3. make sure that the objects you pass to and from the method are marked Serializable (and can be serialized and deserialized - you can test this by using the BinaryFormatter to write and read the file to/from a stream).
  4. create a method somewhere in your main app domain that does the following:
    1. packages the parameters into the serializable objects
    2. creates an app domain using AppDomain.CreateDomain. We also pass a different appbase that is based on our assemblies' locations.
    3. sets up an event handler on the current app domain's AssemblyResolve event (this is only necessary if you can't get the app domain to properly resolve your assembly and instead your object comes back only as MarshalByRefObject, instead of what you expected).
    4. call CreateInstanceAndUnwrap on the target app domain, asking it to create an instance of your MarshalByRefObject derived object.
    5. cast the object from CreateInstanceAndUnwrap to your target object type.
    6. Call your method.
  5. Put code in the method handler (if you need it) for your AssemblyResolve event that iterates through the loaded assemblies and returns the one that was requested, if it matches by name. ResolveEventArgs.Name should be matched against Assembly.FullName. My code looks like:
    static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
    {
    Debug.WriteLine("Attempting to resolve assembly: "
    + args.Name);
    // search the loaded assemblies for this one.
    foreach (Assembly assy
    in AppDomain.CurrentDomain.GetAssemblies())
    if (assy.FullName == args.Name)
    return assy;
    return Assembly.GetExecutingAssembly().FullName
    == args.Name
    ? Assembly.GetExecutingAssembly() : null;
    }
  6. Write code to unload the AppDomain (call AppDomain.Unload(...)) (I do this in a 'finally' that follows a 'try' started immediately after the app domain is created)

This seems to do the trick. Let me know if you have any problems.

Here's where I got some of my information:

http://blogs.msdn.com/suzcook/archive/2003/06/12/57169.aspx
http://blogs.msdn.com/suzcook/archive/2003/05/29/57120.aspx
http://www.codeguru.com/forum/showthread.php?t=398030
http://blogs.msdn.com/suzcook/archive/2003/05/29/57143.aspx
http://blogs.msdn.com/suzcook/archive/2003/06/13/57180.aspx

By the way, if you've never come across it, Suzanne's blog is a GREAT source of information.

Thursday, January 24, 2008

WPF Data Binding in code

Recently, I was working on a WPF prototype for our application. Unfortunately, we need some features in a grid control and some other controls that won't allow us to use the WPF grids available on the market. For this reason, we need to use the Infragistics grid (the same one we've used in the past), the WinForms version.

Unfortunately, we also need to have docking (VS 2005 style) and tabbed MDI. Since docking has flyout controls, and WPF z-order doesn't work right with WinForms, we need to host our application in Windows Forms framework.

When you have a windows forms control in a WPF layout, the windows forms control (in the WindowsFormsHost control of WPF) doesn't participate in the z-order the way you might expect. Unfortunately, windows HWNDs don't support layering like WPF (DirectX) does, instead each HWND owns its part of the screen (except for areas overlapped by overlapping windows). Transparency in HWNDs is done by 'hacks', not builtin support.

Due to this structure, winforms controls must be rendered 'on top' of WPF content, when you host winforms in WPF windows. This means my flyout windows will show up "under" the winforms window, rather than on top of it like you'd expect. Anyway, the only way to get around this is to host WPF controls in WinForms rather than the other way around.



One of the real benefits of using WPF is the XAML markup and the ability to do data binding so easily in your layouts. Unfortunately, at first glance, this seems to be lost when you switch to WPF controls on top of a WinForms layout. Don't fear, all is not lost.

Fortunately, even though you can't use XAML markup to define your data binding, you can programmatically add bindings to your winforms layout. For instance, we have a infragistics grid (a winforms control) that lists the 'items' in our browser that people can choose to look at. On the other hand, we have WPF controls that react to which item is selected in the browser grid. It'd be nice if we could bind the WPF controls to a property on the other control so that they are automatically updated when necessary.

The way I did this was to create a DependencyObject that contains my properties for the grid that I'd like to bind to - in particular one of them was CurrentItem. The properties are DependencyProperty-based. Now, I needed one-way binding to update my WPF controls when the current item changed in the grid, but just in case, I set up my code to support two way binding, so that if I chose to set the value of the property from other parts of my code, I could do so safely and have everything update properly.

So, now I have a DO with a DP. When I register the DP, I created a update method so that I could trigger code when the property was changed - this is done by adding a delegate to the UIPropertyMetadata constructor passed in the DependencyProperty.Register call. To expose this DO to my clients (I've called it BindingInterfaceObject), I make a property on my UserControl (that contains the Infragistics Grid) called BindingInterface and create an instance of the BindingInterfaceObject in the constructor of the UserControl. I give it access to the UserControl's private members by making it a nested class and passing the 'this' pointer to its constructor.

In order to bind to it, I need to set bindings on my WPF controls. I do this by creating a Binding object (System.Windows.Data.Binding) with the name of the property in BindingInterfaceObject that I care about (in my case CurrentItem) and pass this binding object to the SetBinding method on the WPF controls.

Voila! I now have WPF controls binding to changes in a non-WPF control. The only thing I have to do now is set the CurrentItem property whenever the appropriate event occurs on the grid. I do this by handling those events in my UserControl and setting the BindingInterface.CurrentItem as appropriate.

I can provide sample code if you're interested. Just ask.