C#Excel插件 – 跨域单例例外

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

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

因为这是一个Excel插件,所以在创build包含单例的AppDomain时,我不得不对其进行修改,以便在search程序集时使用正确的基本目录。 以下是我的修改版本:

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方法(我也试过一个属性,但没有进一步),我系统地得到一个exception:

Nom inconnu。 (从HRESULTexception:0x80020006(DISP_E_UNKNOWNNAME))

或英文:未知名称。 (从HRESULTexception:0x80020006(DISP_E_UNKNOWNNAME))

当我保持内置types时,编组工作正常,但是由于我想访问的对象(Microsoft.Office.Interop.Excel.Application)是一个COM对象,所以我担心这是问题所在。

我对Remoting和编组很陌生。 有任何想法吗? 它是否与COM对象的序列化有关?

提前谢谢了! 肖恩

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

我build议你写一个小帮手,你可以从每个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 。

在C#中,远程对象有两种工作方式。 对象从MarshalByRefObjectinheritance,或者它需要是可Serializable 。 由于你显然不想序列化Excel应用程序对象(即使你可以,这将是巨大的,并不会参考另一边的Excel的实时副本),唯一的select是MarshalByRef。 不幸的是,你不能控制应用程序对象的源代码,所以我认为这个operall方法是一个非启动器。

你应该做的就是从插件的主AppDomain中使用MarshalByRef对象公开一个本地的.NET API,然后在该对象中调用你需要的Excel应用程序对象。

所以你的结果如下所示:

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

特别关注如何使远程处理(我把!! !!)100%的托pipe代码暴露的API,完全没有暴露或依赖于Excel。