About Me

I was born in Mobile, AL and graduated from Theodore High School in 1990. I received a BS in Pure Mathematics from the University of Alabama in 1995. I now reside in Hoover, AL.

Friday, October 26, 2007

Cecil, or How I Learned to Stop Logging and Love Mono

A friend of mine, Petar Vucetin, has often expressed interest in the ability to create a flight recorder for software that could record method entries, exits, and exceptions. We always knew it could be done with context-bound objects, as we could itercept calls to/from methods as they crossed the context boundary, but this solution was far from complete. Not only were you only able to intercept calls to/from context-bound objects, but the call actually had to cross a context boundary to be intercepted (i.e. calls from the context-bound object to itself were not intercepted). I played around with using Profiler to intercept calls at runtime, but I found the interfaces to be far too complex. I also looked into a couple of Aspect Oriented Programming (AOP) frameworks, but one relied on the objects to be context-bound and the other replaced the Microsoft compiler with their own. I had already discarded the context-bound solution and I was not about to rely on some third party compiler. In the end, I deemed the flight recorder project not worth the effort to attempt, and it remained Petar's White Whale.

And then came Mono. I was never too interested in the Mono project, as I never envisioned writing .NET programs for any platform other than Windows, but anything to shut those damn "write-once run-anywhere" java geeks up was good for the .NET community. While looking over AOP frameworks a couple of weeks ago, I ran across a link to the Mono.Cecil project. After reading about Mono.Cecil, I discovered that this was exactly that for which I had been searching. This framework allows you to inject CIL (the new MSIL) directly into compiled assemblies. I wrote a framework using Mono.Cecil called Dynamic Aspect Linking Engine (dale for short) that injects the code like the following examples:

Assume we have the following code:

 public static void PublicMethodTest (string testParam1, int testParam2)
 {
    // do something interesting here
 }

After running the injecter, the code becomes:

 public static void PublicMethodTest (string testParam1, int testParam2)
 {
    System.Reflection.MethodBase _dale_method = (new System.Diagnostics.StackFrame(0)).GetMethod();
    dalelib.AspectManager.EnterMethod(method, new object[] { testParam1, testParam2 });
 
    try
    {
       // do something interesting here
    }
    catch (Exception exception)
    {
       dalelib.AspectManager.MethodError(_dale_method, exception);
       throw;
    }
    finally
    {
       dalelib.AspectManager.ExitMethod(_dale_method);
    }
 }




The AspectManager class creates any defined aspects (defined by attributes) and notifies them of the entering, exiting, and/or exception.

The only caveat with injecting code with Cecil is that you must know CIL. The MSDN documentation on CIL is fairly extensive, but if you are more comfortable dragging and dropping components than you are writing them from scratch, then you may want to wait for a commercial product that does the work for you. I am seriously considering packaging the Dynamic Aspect Linking Engine up and either selling it or posting the source code on the internet, so check back if you are interested.

Tuesday, October 9, 2007

Getting a list of loaded AppDomains

Oftentimes, I have a need to view the currently loaded appdomains in my application. An example of this is when I am creating a private application domain to load memory intensive objects that may be static or prone to memory leaks. The benefit of having these objects in a different appdomain than my code should be obvious: I can unload the private appdomain to cleanup my memory space. Now, I want to use this application domain throughout my application, from within the default application domains and any child domains. Creating a static reference to this private domain is insufficient, as statics are scoped to the application domain in which they are created. So what I need is a way to iterate through the application domains in my application and create the private application domain if it does not exists, or reference the private domain if it does exist.

When I first started development of this solution, I referenced mscoree for the ICorRuntimeHost interface and the CorRuntimeHost class, but as this added another dll to my deployment, I decided to hand-code the signatures for these objects in my code.

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
 
/// <summary>
/// Provides a helper class for appdomains.
/// </summary>
public static class DomainHelper
{
   /// <summary>
   /// Gets all of the application domains that are 
   /// currently loaded in the application process.
   /// </summary>
   public static AppDomain[] LoadedDomains
   {
      get
      {
         List<AppDomain> loadedDomains = new List<AppDomain>();
         ICorRuntimeHost runtimeHost = (ICorRuntimeHost)(new CorRuntimeHost());
 
         try
         {
            IntPtr enumeration = IntPtr.Zero;
            runtimeHost.EnumDomains(out enumeration);
 
            try
            {
               object nextDomain = null;
               runtimeHost.NextDomain(enumeration, ref nextDomain);
 
               while (nextDomain != null)
               {
                  loadedDomains.Add((AppDomain)nextDomain);
                  nextDomain = null;
                  runtimeHost.NextDomain(enumeration, ref nextDomain);
               }
            }
            finally
            {
               runtimeHost.CloseEnum(enumeration);
            }
         }
         finally
         {
            Marshal.ReleaseComObject(runtimeHost);
         }
 
         return loadedDomains.ToArray();
      }
   }
 
   [ComImport]
   [Guid("CB2F6723-AB3A-11d2-9C40-00C04FA30A3E")]
   private class CorRuntimeHost// : ICorRuntimeHost
   {
   }
 
   [Guid("CB2F6722-AB3A-11D2-9C40-00C04FA30A3E")]
   [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
   private interface ICorRuntimeHost
   {
      void CreateLogicalThreadState ();
      void DeleteLogicalThreadState ();
      void SwitchInLogicalThreadState ();
      void SwitchOutLogicalThreadState ();
      void LocksHeldByLogicalThread ();
      void MapFile ();
      void GetConfiguration ();
      void Start ();
      void Stop ();
      void CreateDomain ();
      void GetDefaultDomain ();
      void EnumDomains (out IntPtr enumHandle);
      void NextDomain (IntPtr enumHandle, [MarshalAs(UnmanagedType.IUnknown)]ref object appDomain);
      void CloseEnum (IntPtr enumHandle);
      void CreateDomainEx ();
      void CreateDomainSetup ();
      void CreateEvidence ();
      void UnloadDomain ();
      void CurrentDomain ();
   }
}

One point of interest here is that the definition of the ICorRuntimeHost interface contains parameterized definitions for only the methods of interest to the LoadedDomains property.