【问题标题】:C# Excel Addin - Cross-domain singleton ExceptionC# Excel 插件 - 跨域单例异常
【发布时间】:2012-02-24 05:23:35
【问题描述】:

我正在开发一个 excel 插件,在这个插件中有几个 AppDomain。我需要访问每个 AppDomain 中的一些共享数据,所以我决定使用跨 AppDomain 单例。我遵循了该线程中描述的内容:

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

因为这是一个 excel 插件,所以我在创建包含单例的 AppDomain 时必须对其进行一些修改,以便在搜索程序集时使用正确的基本目录。以下是我修改后的版本:

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 对象,我担心这就是问题所在。

我对远程处理和编组非常陌生。有任何想法吗?是不是跟COM对象的序列化有关系?

提前非常感谢! 肖恩

【问题讨论】:

    标签: c# excel exception singleton appdomain


    【解决方案1】:

    你当然不应该传递那个 Application 对象,它会造成无穷无尽的麻烦。

    我建议您编写一个小助手,您可以从每个 AppDomain 调用它来获取正确的 Application 对象。这样做有一个小问题,因为通常的 CreateObject 方法并不总是为您所在的进程获取 Excel 应用程序实例。Andrew Whitechapel 在这里有一个解释和正确的代码:http://blogs.officezealot.com/whitechapel/archive/2005/04/10/4514.aspx

    最后,在其他语言环境中调用 Excel COM 对象时,您应该注意区域设置问题。有时调用需要本地化,或者您需要切换线程的 UI 语言。此处的一些信息:http://msdn.microsoft.com/en-us/library/aa168494(v=office.11).aspx 以及有关他们在 VSTO 中所做的一些信息:http://blogs.msdn.com/b/eric_carter/archive/2005/06/15/429515.aspx

    【讨论】:

    • +1 因为这解决了我的问题,无需在幕后进行重构。 officezealot 的文章真的很有用,而且我们在 excel 中的文化始终是美式英语,所以不用担心。谢谢!!!
    【解决方案2】:

    在 C# 中,远程对象的工作方式有两种。该对象要么继承自 MarshalByRefObject,要么必须是 Serializable。由于您显然不想序列化 Excel 应用程序对象(即使可以,它也会很大,并且不会引用另一侧的 Excel 的实时副本),唯一的选择是 MarshalByRef。不幸的是,您也不控制 Application 对象的源代码,所以我认为这种操作方法是行不通的。

    您可能应该做的是使用 MarshalByRef 对象从插件的主 AppDomain 公开本机 .NET API,然后在该对象内对 Excel 应用程序对象进行所需的调用。

    所以你得到的架构看起来像这样:

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

    特别关注使通过远程处理(我放了!!)公开的 API 100% 托管代码,完全不公开或依赖 Excel。

    【讨论】:

    • 好主意。这将需要大量的重构,因为 Excel 对象在多个项目中存储和使用,但它是最有前途的解决方案,无需编写一些 c++ 和 p/invoke
    猜你喜欢
    • 2011-06-23
    • 1970-01-01
    • 1970-01-01
    • 2018-12-16
    • 1970-01-01
    • 2020-06-15
    • 2012-10-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多