Help Needed! PropertyGrid and Remoted Objects

Alright, I am having a really hard time trying to solve an issue in CodeSmith and I am hoping that you guys can help me out.

The basic problem is that currently every time you compile a template in CodeSmith it creates a dynamic assembly which is then loaded into the current AppDomain and the template class inside of that assembly is instantiated and attached to the property grid control so that the template properties can be set before execution of the template. As you might imagine, over time all of these dynamic assemblies being loaded into the AppDomain and never unloaded (can't unload an assembly) is causing a memory leak in CodeSmith and locks all of the referenced assemblies until CodeSmith is shut down.

The obvious answer to this problem is to move load and execution of templates into a separate AppDomain that can be unloaded, right? Well, I've run into some issues with that approach that I am so far unable to resolve. A remoted object can't be used in the property grid control. I've tried implementing ICustomTypeDescriptor thinking that would be the answer that I was looking for, but I was unsuccessful. There are a couple other ideas that I am tossing around, but I'm not happy with any of them.

If anyone out there knows how to solve this issue, I would be extremely grateful for a little nudge in the right direction. I will be offering some free CodeSmith Professional licenses to the person(s) that help me solve this problem.

Thanks,Eric J. Smith

8 Comments

Eric,

I do not fully appreciate the problem you are having. However, it seems like you need some type of execution proxy that takes the values from the properties collection and then starts the template in another AppDomain and passes the values to it.

If you can't attach the property grid to a proxied object and you can only attach it to a local (i.e. same appdomain) object, then the solution is to add a layer of indirection by putting the state of your templates (such as the properties) in a local class that has a reference to the remoted template, and then have the property grid attach to the local class itself. If the property grid references the local class instead of the remoted template, then it will behave as if the remoted template were a local object. Everything can be solved by adding a layer of indirection, Eric. :)

p.s. I'm currently working on a code generator very similar to CodeSmith and in it I've already solved such issues such as subtemplates, and batch templates, so if you want to swap a few notes, let me know. :)

One more thing. I just realized that there is one slight problem to that previous approach I mentioned--it doesn't scale very well because the you would have to create a local 'proxy' state class for each template that you need to attach to the property grid--so, in short, it doesn't scale very well. However, another approach to get around this is to dynamically create these proxies at runtime using either Reflection.Emit, or your own code generation engine. The slight trade-off, of course, is that instead of having multiple assemblies loaded into the same app domain, you'll have a bunch of value-typed state classes that will be floating around in memory. The memory footprint of these state classes, however, (which I'd assume to be simple structs) would be orders of magnitude less than having a full-blown assembly in the same appdomain, so that slight tradeoff (IMHO) is well worth it. :)

I thought of (yet another, heh) approach to this problem: How about remoting both the property grid and the template into the same (yet separate from the default) appdomain? Templates will still work the same way since they're not being remoted to the property grid (because they're in the same appdomain), and you won't have to worry about memory issues anymore because you can just simply unload the appdomain that held both the property grid and the template once you're done. That way, you won't even have to worry about having to write any type descriptors, etc. What do you think?

Well, if you can't remote a UI control, then just remote the entire form that it's on. :)

For example, I noticed that in the freeware version of CodeSmith you have two forms: The template explorer to display the list of templates, and another form that holds the property grid and runs/compiles the actual template. How about loading the second form into another appdomain? All you have to do is create the form in a remote appdomain and unload that appdomain once you're done and you can instantly reclaim the memory it used. It's simple, effective, and (IMHO) the changes you'd have to make to your existing code base are minimal. :)

Oh, and one more thing. You can remote *any* windows form control because all UI controls in .NET are derived from System.Windows.Forms.Control--which, in turn, is derived from MarshalByRefObject. Happy coding. :)