Excel从两个不同的AppDomain调用.NET自动化服务器?

我有一个Excel插件(用C#编写),它是一个静态variables,位于单例数据caching的核心:

static DataCache _instance; 

这是通过三种不同的代码path访问的:

  1. VSTO ribbon-bar上的事件处理程序将初始化实例,并将其读取以在辅助对话框中显示
  2. RTD服务器(声明为[ComVisible]并实现IRtdServer接口的类)使用RTD公式的数据
  3. 一组自动化调用(在声明为[ComVisible]的另一个类中实现)也可以对数据进行操作。 这些是通过在Excel工作表上的button被点击时调用的VBA代码调用的。

编辑(#3):

根据这些代码path被首先调用的顺序,我发现我的代码在两个独立的AppDomain中运行。

来自function区事件处理程序的所有访问都发生在名为“MyPlugIn.vsto”的AppDomain中。 如果这是对我的COM对象的第一次访问,那么所有的后续调用(包括RTD调用)都发生在同一个AppDomain中。

但是,如果第一次访问是通过RTD接口进行的,则该调用和所有后续的RTD调用都发生在名为“DefaultDomain”的AppDomain中。 (使用embedded式RTD公式加载保存的文档时会发生这种情况。)通过工具栏初始化和操作DataCache的后续调用仍然出现在“MyPlugIn.vsto”AppDomain中。 这意味着RTD公式总是像DataCache未初始化一样运行(因为在一个AppDomain中设置的静态variables在其他AppDomain中保持未初始化)。

看来,VSTO初始化时,Excel或VSTO正在创build一个AppDomain。 在此初始化之前通过COM互操作创build的对象位于默认AppDomain中,而之后创build的对象位于VSTO AppDomain中。

我如何确保使用相同的DataCache实例,而不pipe在哪个AppDomain中创build了我的RTD服务器对象?

你的静态variables当然不是在AppDomain之间共享的,所以你所看到的和预期的一样,因为AppDomain是不同的。

我认为它是这样工作的:

VSTO加载项在其自己的AppDomain中运行。 如果您的caching对象(或RTD服务器)的COM类工厂是从该AppDomain中创build的,则它将被加载到调用的AppDomain中。 随后访问该COM类将会发现它已经加载到进程中,并使用现有的实例。

但是,如果第一次激活是由Excel本身触发的,例如通过RTD调用,.NET实现的COM对象将被加载到进程的默认AppDomain中。 你不能控制加载过程的这一部分,除非你做了一个非托pipe的填充,因为当你的加载发生时你的代码没有运行。

我的头顶有些build议:

  1. 为从您的.NET加载项公开的RTD调用创build一些包装函数。 这样,您可以确保在调用Excel的Application.RTD进行真正的RTD设置之前加载RTD类。

  2. 通过用户定义的函数从RTD服务器访问实际的caching – 这样,即使Excel不是RTD服务器所在的当前AppDomain,Excel也会调用具有真实caching的AppDomain。

  3. 尝试通过Application.AddIns获取加载项对象….有一种方法来获得实际的加载项COM对象,并使用它的一些接口到达caching…

  4. 为您的RTD服务器制作一个不受pipe理的垫片(在网上search“COM shim wizard”)。 以某种方式找出如何让您的VSTO AppDomain加载,然后将RTD服务器加载到该AppDomain中。

  5. 查看是否有方法将VSTO加载项加载到默认AppDomain中。 我不知道,但也许有一个标志或开关,告诉“Microsoft Office系统加载程序”(或任何部分被称为现在)不创build孤立的AppDomain。

  6. 正确的答案:使用Excel-Dna (免责声明:我是开发人员)。 它支持托pipe加载项中的Ribbons,RTD和UDF,无需注册,所有内容都将放入您的加载项AppDomain中。 它是免费的,但是需要一些时间和精力来移植你的东西 – RTD是微不足道的,但是如果你使用了许多VSTO助手对象来实现function区和表单访问(表等),你需要考虑这一点。

我希望这给你一些想法。

–Govert–

首先,您可能需要将您的实例声明为:

 static DataCache _instance = new DataCache(); 

这样(不是唯一确定的),你知道_instance是线程安全的。 有关线程安全单例的主题有很多报道,但这似乎是最简单的解决scheme之一。

第二件事可能是你想尝试使用的是一个结构

 Lock (_lockObject) { ... } 

用于读取和写入。 这将使您的读取和写入安全从不同的线程。

最后,但是这是纯粹的猜测,你可以通过为驻留在STA中的COM调用创build一个单独的对象来访问你的库。

祝你好运!