C# Excel Addin - Cross-domain singleton Exception

0

I am developing an excel addin and in this addin there are several AppDomains. I need to have access to some shared data across each AppDomain so I decided to use a cross-AppDomain singleton. I followed what was described in this thread :

http://www.dolittle.com/blogs/einar/archive/2007/05/18/cross-appdomain-singleton.aspx

Because this is an excel addin, I had to modify it a little when creating the AppDomain that contains the singleton so that the correct base directory is used when searching for assemblies. Below is my modified version:

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)
                {
                    string baseDir = AppDomain.CurrentDomain.BaseDirectory;
                    appDomain = AppDomain.CreateDomain(AppDomainName, null, baseDir, null, false);
                }
                Type type = typeof(T);
                T instance = (T)appDomain.GetData(type.FullName);
                if (null == instance)
                {
                    instance = (T)appDomain.CreateInstanceAndUnwrap(type.Assembly.FullName, type.FullName);
                    appDomain.SetData(type.FullName, instance);
                }
                _instance = instance;
            }

            return _instance;
        }
    }
}

Here is my implementation of the CrossAppDomainSingleton :

public class RealGlobal : CrossAppDomainSingleton<RealGlobal>
{
    //ExcelApp Value Shared
    private Microsoft.Office.Interop.Excel.Application s_excelApp = null;

    public Microsoft.Office.Interop.Excel.Application GetExcelApp()
    {
        return s_excelApp;
    }

    public void SetExcelApp(Microsoft.Office.Interop.Excel.Application app)
    {
        s_excelApp = app;
    }
}

Once I try to use either the get or set method (I tried a property also but got no further), I systematically get an exception:

Nom inconnu. (Exception from HRESULT: 0x80020006 (DISP_E_UNKNOWNNAME))

or in English: Unknown Name. (Exception from HRESULT: 0x80020006 (DISP_E_UNKNOWNNAME))

Marshalling works fine when I keep built-in types, but given that the object I want to access (Microsoft.Office.Interop.Excel.Application) is a COM object, I fear that this is the problem.

I'm very new to Remoting and Marshalling. Any ideas? Does it have something to do with the serialization of a COM object?

Many thanks in advance! Sean

c#
excel
exception
singleton
appdomain
asked on Stack Overflow Feb 23, 2012 by sean.net

2 Answers

2

You certainly should not be passing that Application object around, it will cause endless trouble.

I suggest you write a small helper that you can call from each AppDomain to get the right Application object. There is a small snag in doing this, since the usual CreateObject approach will not always get the Excel Application instance for the process you are in. Andrew Whitechapel has an explanation and the right code here: http://blogs.officezealot.com/whitechapel/archive/2005/04/10/4514.aspx.

Finally, you should take some care with locale issues when calling the Excel COM object in other language environments. Sometimes calls need to be localised, or you need to swith the thread's UI language. Some info here: http://msdn.microsoft.com/en-us/library/aa168494(v=office.11).aspx and some info on what they do in VSTO here: http://blogs.msdn.com/b/eric_carter/archive/2005/06/15/429515.aspx.

answered on Stack Overflow Feb 23, 2012 by Govert
0

In C# remoting objects work one of two ways. Either the object inherits from MarshalByRefObject or it needs to be Serializable. Since you clearly don't want to serialize the Excel Application object (even if you could, it would be huge and wouldn't refer to the live copy of Excel on the other side), the only option is MarshalByRef. Unfortunately, you don't control the source code of the Application object either, so I think that this operall approach is a non-starter.

What you should probably do is expose a native .NET API from the addin's main AppDomain, using a MarshalByRef object, and then within that object make the calls you need to the Excel Application object.

So your resulting architecture looks like this:

[Excel Application] <--> [.NET Object : MarshalByRef] !! <-- Remoting Boundary --> [Other AppDomains]

With particular focus on making the API that is exposed over remoting (where I put !!) 100% managed code, with no exposure of or dependency on Excel at all.

answered on Stack Overflow Feb 23, 2012 by Chris Shain

User contributions licensed under CC BY-SA 3.0