Post

Cross AppDomain Singleton

PS: There is an addition to this post that you can find here

One thing that I find a bit inconvient from time to time is the lack of shared memory between appdomains. Today I figured I had to find a way of doing this, so I created a CrossAppDomainSingleton class that has it’s implementation and data shared between AppDomains.

The solution we want is that the singleton class is created in a given AppDomain and in all other AppDomains we get a transparent proxy to that singleton.

In order to do this we need to have the ability to enumerate through existing AppDomains in order to create an instance in the correct AppDomain (at least I found this to be cool way of doing it). I came across a thread on microsoft.public.dotnet.framework.clr that gave me a good solution (http://groups.google.com/group/microsoft.public.dotnet.framework.clr/browse_frm/thread/dba9c445ad8d5c3/9df14bf0af393c28?lnk=st&q=enumerate+appdomain+group%3Amicrosoft.public.dot%20net.*&rnum=5#9df14bf0af393c28)

You need to add a reference to the mscoree.tlb which is situated in the .net directory (c:windowsmicrosoft.netframeworkv2.0.50727). When you add a reference to it you’ll get an Interop.mscoree.dll added to your output directory. You will have to have this alongside your deployment if you’re going to use this in a solution.

With my modifications, we get a method that finds a AppDomain based upon the friendly name. If it doesn’t find it, it returns null..

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
private static AppDomain GetAppDomain(string friendlyName)
{
  IntPtr enumHandle = IntPtr.Zero;
  mscoree.CorRuntimeHostClass host = new mscoree.CorRuntimeHostClass();

  try
  {
    host.EnumDomains(out enumHandle);

    object domain = null;

    while (true)
    {
      host.NextDomain(enumHandle, out domain);

      if (domain == null)
        break;

      AppDomain appDomain = (AppDomain)domain;

      if( appDomain.FriendlyName.Equals(friendlyName) )
        return appDomain;
    }
  }
  finally
  {
    host.CloseEnum(enumHandle);
    Marshal.ReleaseComObject(host);
    host = null;
  }

  return null;
}

The full implementation of the class is then :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
public class CrossAppDomainSingleton<T> : MarshalByRefObject where T:new()
{
  private static readonly string AppDomainName = "Singleton AppDomain";
  private static T _instance;

  private static AppDomain GetAppDomain(string friendlyName) 
  { 
    IntPtr enumHandle = IntPtr.Zero; 
    mscoree.CorRuntimeHostClass host = new mscoree.CorRuntimeHostClass(); 
    
    try 
    { 
      host.EnumDomains(out enumHandle);

      object domain = null;
      
      while (true) 
      { 
        host.NextDomain(enumHandle, out domain); 
        
        if (domain == null) 
        { 
          break; 
        } 
        
        AppDomain appDomain = (AppDomain)domain; 
        
        if( appDomain.FriendlyName.Equals(friendlyName) ) 
        { 
          return appDomain; 
        } 
      } 
    } 
    finally 
    { 
      host.CloseEnum(enumHandle); 
      Marshal.ReleaseComObject(host); 
      host = null; 
    } 

    return null; 
  }

  public static T Instance 
  { 
    get 
    { 
      if (null == _instance) 
      { 
        AppDomain appDomain = GetAppDomain(AppDomainName); 
        if (null == appDomain) 
        { 
          appDomain = AppDomain.CreateDomain(AppDomainName);
        } 
        
        Type type = typeof(T); 

        T instance = (T)appDomain.GetData(type.FullName);
        if (null == instance) 
        { 
          instance = (T)appDomain.CreateInstanceAndUnwrap(type.Assembly.FullN ame, type.FullName);
          appDomain.SetData(type.FullName, instance); 
        } 
        
        _instance = instance; 
      }

      return _instance; 
    } 
  } 
}

This class is not thread safe, so that bit needs to be added..

To use the class you do the following :

1
2
3
4
5
6
7
public class MySingleton : CrossAppDomainSingleton<MySingleton>
{
  public void HelloWorld()
  {
    Console.WriteLine("Hello world from '" + AppDomain.CurrentDomain.FriendlyName + " (" + AppDomain.CurrentDomain.Id + ")'"); 
  } 
}

Look at the entire C# sample file from here

This post is licensed under CC BY 4.0 by the author.