C # Excel Addin - междоменное исключение Singleton - PullRequest
0 голосов
/ 23 февраля 2012

Я занимаюсь разработкой надстройки для Excel, и в этом надстройке есть несколько доменов приложений. Мне нужно иметь доступ к некоторым общим данным по каждому домену приложений, поэтому я решил использовать кросс-домен синглтона. Я следовал тому, что было описано в этой теме:

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

Поскольку это надстройка Excel, мне пришлось немного изменить ее при создании домена приложений, содержащего синглтон, чтобы при поиске сборок использовался правильный базовый каталог. Ниже моя модифицированная версия:

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;
        }
    }
}

Вот моя реализация 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;
    }
}

Как только я пытаюсь использовать метод get или set (я тоже пробовал свойство, но не получил ничего), я систематически получаю исключение:

Ном инконну. (Исключение из HRESULT: 0x80020006 (DISP_E_UNKNOWNNAME))

или на английском: Неизвестное имя (Исключение из HRESULT: 0x80020006 (DISP_E_UNKNOWNNAME))

Маршаллинг отлично работает, когда я сохраняю встроенные типы, но, учитывая, что объект, к которому я хочу получить доступ (Microsoft.Office.Interop.Excel.Application), является COM-объектом, я боюсь, что это проблема.

Я очень плохо знаком с Remoting и Marshalling. Есть идеи? Это как-то связано с сериализацией COM-объекта?

Большое спасибо заранее! Шон

Ответы [ 2 ]

2 голосов
/ 24 февраля 2012

Вы, конечно, не должны передавать этот объект Application, это вызовет бесконечные проблемы.

Я предлагаю вам написать небольшого помощника, которого вы можете вызывать из каждого AppDomain, чтобы получить правильный объект Application.В этом есть небольшая загвоздка, поскольку обычный подход CreateObject не всегда получает экземпляр приложения Excel для процесса, в котором вы находитесь. Эндрю Уайтчепел имеет объяснение и правильный код здесь: http://blogs.officezealot.com/whitechapel/archive/2005/04/10/4514.aspx.

НаконецВы должны позаботиться о проблемах локали при вызове COM-объекта Excel в других языковых средах.Иногда вызовы должны быть локализованы, или вам нужно переключить язык интерфейса потока.Некоторая информация здесь: http://msdn.microsoft.com/en-us/library/aa168494(v=office.11).aspx и некоторая информация о том, что они делают во ВСТО здесь: http://blogs.msdn.com/b/eric_carter/archive/2005/06/15/429515.aspx.

0 голосов
/ 23 февраля 2012

В C # удаленные объекты работают одним из двух способов.Либо объект наследуется от MarshalByRefObject, либо он должен быть Serializable.Поскольку вы явно не хотите сериализовать объект приложения Excel (даже если бы вы могли, он был бы огромным и не ссылался бы на оперативную копию Excel с другой стороны), единственным вариантом является MarshalByRef.К сожалению, вы также не управляете исходным кодом объекта Application, поэтому я думаю, что этот операционный подход не является начальным.

Что вам, вероятно, следует сделать, это предоставить собственный .NET API из надстройкиmain AppDomain, используя объект MarshalByRef, а затем внутри этого объекта делает необходимые вам вызовы к объекту приложения Excel.

Таким образом, ваша результирующая архитектура выглядит так:

С особым упором на то, чтобы сделать API, работающий с удаленным доступом (куда я положил !!), на 100% управляемым кодом, без какой-либо подверженности или зависимости от Excel.

...