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

Eri*_*ric 6 .net com excel vsto appdomain

我有一个Excel插件(用C#编写),带有一个静态变量,它是单例数据缓存的核心:

static DataCache _instance;
Run Code Online (Sandbox Code Playgroud)

这可以通过三种不同的代码路径访问:

  1. VSTO功能区上的事件处理程序初始化实例,并读取它以在帮助程序对话框中显示
  2. RTD服务器(声明为[ComVisible]并实现IRtdServer接口的类)利用RTD公式的数据
  3. 一组自动化调用(在另一个声明为[ComVisible]的类中实现)也对数据进行操作.这些是通过单击Excel工作表上的按钮时调用的VBA代码调用的.

编辑(#3):

根据首次调用这些代码路径的顺序,我发现我的代码在两个单独的AppDomain中运行.

功能区事件处理程序的所有访问都发生在名为"MyPlugIn.vsto"的AppDomain中.如果这是对我的COM对象的第一次访问,则所有后续调用(包括RTD调用)都发生在同一AppDomain中.

但是,如果FIRST访问是通过RTD接口进行的,则该调用和所有后续RTD调用都发生在名为"DefaultDomain"的AppDomain中.(当加载带有嵌入式RTD公式的已保存文档时会发生这种情况.)通过工具栏初始化和操作DataCache的后续调用仍会出现在"MyPlugIn.vsto"AppDomain中.这意味着RTD公式总是像DataCache未初始化一样运行(因为在一个AppDomain中设置的静态变量在另一个AppDomain中保持未初始化).

当VSTO初始化时,Excel或VSTO似乎正在创建AppDomain.在此初始化之前通过COM interop创建的对象在默认AppDomain中着陆,而之后创建的对象则在VSTO AppDomain中着陆.

无论在哪个AppDomain中创建我的RTD服务器对象,我如何确保使用相同的DataCache实例?

Gov*_*ert 1

您的静态变量肯定不会在 AppDomain 之间共享,因此考虑到不同的 AppDomain,您所看到的内容符合预期。

我认为它的工作原理是这样的:

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

然而,如果第一次激活是由 Excel 本身触发的,例如通过 RTD 调用,则 .NET 实现的 COM 对象将被加载到进程的默认 AppDomain 中。除非您创建非托管填充程序,否则您无法控制加载过程的这一部分,因为加载发生时“您的代码”并未运行。

我脑海中浮现出一些建议:

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

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

  3. 尝试通过 Application.AddIns 获取外接程序对象...有一种方法可以获取实际的外接程序 COM 对象,并使用其上的某些接口来访问缓存...

  4. 为您的 RTD 服务器创建一个非托管填充程序(在网络上搜索“COM 填充程序向导”)。以某种方式弄清楚如何加载 VSTO AppDomain,然后将 RTD 服务器加载到该 AppDomain 中。

  5. 查看是否有办法将 VSTO 加载项加载到默认 AppDomain 中。我不知道,但也许有一个标志或开关告诉“Microsoft Office Systems 加载程序”(或现在该部分的名称)不要创建隔离的 AppDomain。

  6. 正确答案:使用Excel-Dna(免责声明:我是开发人员)。它支持托管加载项中的功能区、RTD 和 UDF,无需注册,所有内容都会放入您的加载项 AppDomain 中。它是免费的,但需要一些时间和精力来移植您的东西 - RTD 很简单,但如果您使用大量 VSTO 辅助对象来访问功能区和工作表(表格等),您将需要考虑有点。

我希望这能给你一些想法。

——戈弗特——