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..

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;

}

returnnull;

}

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
  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
 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
8
 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

private static AppDomain GetAppDomain(string friendlyName) 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;
}
{
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;
}

9 thoughts on “Cross AppDomain Singleton

  1. Marcin Buciora says:

    COOL!!!!

  2. Thanks. I’ve working on a new version that will work with the compact framework as well. Involves quite a bit magic. 🙂

  3. Tini says:

    Thank you soo much for this! really helpful.

  4. No problem. I’m glad it turned out helpful for you.

  5. Delf says:

    Thanks a lot for this generic singleton class. Now I should be able to get access to my singletons over App domains. This point is really appreciated when applications using plugins/modules that use application singletons.

  6. Glad to be of any assistance. You should also look at the update post for this :

    http://www.dolittle.com/blogs/einar/archive/2007/05/30/crossappdomainsingleton-update.aspx

  7. t9mike says:

    Super old post I know, but via the Internet Archive here is a link to Einar’s follow-up: http://web.archive.org/web/20120427003305/http://www.dolittle.com/blogs/einar/archive/2007/05/30/crossappdomainsingleton-update.aspx

  8. […] I forgot a couple of very essential things in my last post about a singleton that lives across appdomains. […]

Leave a Reply

Your email address will not be published. Required fields are marked *