COM 互操作程序集加载顺序

发布于 2024-07-09 21:54:02 字数 2741 浏览 7 评论 0原文

我在使用 Outlook 插件时遇到了非常奇怪的程序集引用问题和加载问题。 以下是详细信息(很长的故事:)):

我有一个旧的 Outlook 插件,是使用 .Net 1.1 编写和构建的。 该插件是在其自己的应用程序域中使用非托管填充程序加载的。 即使用户计算机上不存在 .Net 2.0,它也可以正常工作。

该插件使用一个自定义的 Outlook 互操作程序集,该程序集是由 VS 2003 针对 Outlook 2000 创建的,然后重新构建为强命名的(就像我的插件一样)。

在插件项目中,我仅引用此自定义互操作程序集,没有引用官方 MS 互操作程序集。

当此插件在包含 Outlook 2007 和 .Net 2.0 的环境中使用时,其中官方 MS 互操作程序集安装在 GAC 中,出于某种原因,我看到插件加载并使用了它们。

在 Connect 类的代码中,我有一个 using 指令:

using Outlook;

它是我的自定义互操作程序集的命名空间。

在 Connect ctor 中,我有以下代码行(出于测试目的而添加):

Assembly.LoadFrom(PATHTOMYASSEMBLY + "Interop.Outlook.dll");
Type type = typeof(Outlook.ApplicationClass);
logger.Debug("Outlook.Application full type is: {0}", type.AssemblyQualifiedName);

此输出:

Outlook.Application 完整类型为: Outlook.ApplicationClass, Interop.Outlook,版本=9.0.0.0, 文化=中立, PublicKeyToken=4cfbdc5349cf59d8

这正是我所期望的。

问题是,当 OnConnection(对象应用程序,Extensibility.ext_ConnectMode connectMode,对象addInInst,ref System.Array自定义) 被调用,我在日志中看到(我有一个当前域的 AssemblyLoad 事件的钩子),MS 互操作程序集也被加载:

private void app_domain_AssemblyLoad(object sender, AssemblyLoadEventArgs args)
{
    Assembly loadedAssembly = args.LoadedAssembly;
    logger.Debug("Assembly {0} is loaded from: {1}", loadedAssembly.FullName, loadedAssembly.GlobalAssemblyCache ? "GAC" : loadedAssembly.Location);
}

输出:

组装 Microsoft.Office.Interop.Outlook, 版本=12.0.0.0,文化=中立, PublicKeyToken=71e9bce111e9429c 是 加载自:GAC

我的 OnConnection 方法如下所示:

public void OnConnection(object application, Extensibility.ext_ConnectMode connectMode, object addInInst, ref System.Array custom)
{
    Type type = application.GetType();
    logger.Debug("OnConnection application object's full type is: {0}", type.AssemblyQualifiedName);

    Outlook.Application applicationObject = (Outlook.Application)application;

此输出:

OnConnection 应用程序对象已满 类型是: Microsoft.Office.Interop.Outlook.ApplicationClass, Microsoft.Office.Interop.Outlook, 版本=12.0.0.0,文化=中立, PublicKeyToken=71e9bce111e9429c

这真的很奇怪,你可以看到在下一行我可以成功地转换到 Outlook.Application 而没有任何问题。

我已经检查了 Reflector,我的程序集没有以任何方式引用 Microsoft 的互操作程序集。 我的 Interop.Outlook.dll 也是如此。

那么,有人知道发生了什么事吗? 这些问题的答案是什么:

  1. 为什么它加载 Microsoft 程序集?

  2. 如何在不同程序集中定义的不相关的类/接口之间进行转换?

注意:我创建了一个新的插件,非常简单,它什么也不做,只是加载。 我可以重现这个问题,所以真的,有人知道 CLR 如何决定加载什么互操作以及从哪里加载。 除了在 GAC 中之外,是否还有另一个地方(注册表???)在 COM 对象与其所需的互操作之间存在链接?

There is very strange assembly reference problem and loading problem I experience with my Outlook addin. Here are the detail (a long story :) ):

I have an old Outlook addin, written and build using .Net 1.1. The addin is loaded using an unmanaged shim in its own application domain. It works OK, with .Net 2.0, even if 1.1 is not present on the user's machine.

The addin uses a custom Outlook interop assembly, created by VS 2003 against Outlook 2000, and after that rebuild to be strongly-named (as is my addin).

In the addin project I reference only this custom interop assembly, no reference to the official MS interop assemblies.

When this addin is used in an environment with Outlook 2007 and .Net 2.0, where the official MS interop assemblies are installed in GAC, for some reason I see that the addin loads and uses them.

In the code of the Connect class, I have a using directive:

using Outlook;

which is the namespace of my custom interop assembly.

In Connect ctor I have these lines of code (added for testing purposes):

Assembly.LoadFrom(PATHTOMYASSEMBLY + "Interop.Outlook.dll");
Type type = typeof(Outlook.ApplicationClass);
logger.Debug("Outlook.Application full type is: {0}", type.AssemblyQualifiedName);

This outputs:

Outlook.Application full type is:
Outlook.ApplicationClass,
Interop.Outlook, Version=9.0.0.0,
Culture=neutral,
PublicKeyToken=4cfbdc5349cf59d8

Which is exactly what I would expected.

The problem is, that when the
OnConnection(object application, Extensibility.ext_ConnectMode connectMode, object addInInst, ref System.Array custom)
is called, I see in the log (I have a hook to AssemblyLoad event of the current domain) that MS interop assembly is loaded as well:

private void app_domain_AssemblyLoad(object sender, AssemblyLoadEventArgs args)
{
    Assembly loadedAssembly = args.LoadedAssembly;
    logger.Debug("Assembly {0} is loaded from: {1}", loadedAssembly.FullName, loadedAssembly.GlobalAssemblyCache ? "GAC" : loadedAssembly.Location);
}

Output:

Assembly
Microsoft.Office.Interop.Outlook,
Version=12.0.0.0, Culture=neutral,
PublicKeyToken=71e9bce111e9429c is
loaded from: GAC

My OnConnection method starts like this:

public void OnConnection(object application, Extensibility.ext_ConnectMode connectMode, object addInInst, ref System.Array custom)
{
    Type type = application.GetType();
    logger.Debug("OnConnection application object's full type is: {0}", type.AssemblyQualifiedName);

    Outlook.Application applicationObject = (Outlook.Application)application;

This outputs:

OnConnection application object's full
type is:
Microsoft.Office.Interop.Outlook.ApplicationClass,
Microsoft.Office.Interop.Outlook,
Version=12.0.0.0, Culture=neutral,
PublicKeyToken=71e9bce111e9429c

This is really weird, as you can see that on the next line I can successfully cast to Outlook.Application without any problems.

I have checked with Reflector, and my assembly DOES NOT reference Microsoft's interop assemblies in any way. Same for my Interop.Outlook.dll.

So, does someone knows what's going on? What is the answer for these questions:

  1. Why it loads the Microsoft assemblies at all?

  2. How it is possible to cast between unrelated classes/interfaces, defined in different assemblies?

NOTE: I have created a new addin, very simple one, which does nothing, just loads. I could reproduce the problem, so really, does anyone knows how CLR decides what interop to load and from where. Besides in GAC, is there another place (registry???) where there is a link between a COM object and the interop it requires?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

何处潇湘 2024-07-16 21:54:02

我想我在 Microsoft 的 主要互操作程序集 底漆。 Visual Studio 中的 PIA 处理方式有所不同:

当用户尝试添加对已注册 PIA 的类型库的引用时,Visual Studio 将静默使用已注册的 PIA,而不是使用 Tlbimp 重新导入类型库。 这可确保尽可能使用 PIA。

这意味着当你向项目添加对自己的IA的引用时,Visual Studio将检查是否有为此COM对象注册的PIA。 Outlook PIA 是在以下密钥下注册的:

HKEY_CLASSES_ROOT\CLSID\{0006F023-0000-0000-C000-000000000046}\InprocServer32\12.0.0.0
    Assembly="Microsoft.Office.Interop.Outlook, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71E9BCE111E9429C"

据我所知,使用 regasm 工具取消注册 PIA 应该删除该密钥,然后重新添加对您自己的 IA 的引用应该会产生预期的结果。

但是,如果有可用的 PIA,Microsoft 不建议使用自定义 IA。 我不明白其确切原因,但我认为这可能与封送优化和具有独特的类型定义有关。

I think I found the answer to your problem in Microsoft's Primary Interop Assemblies primer. PIAs are handled differently in Visual Studio:

When a user attempts to add a reference to a type library that has a registered PIA, Visual Studio will silently use the registered PIA instead of reimporting the type library with Tlbimp. This ensures that the PIA is used whenever possible.

This means that when you add a reference to your own IA to the project, Visual Studio will check whether there is a PIA registered for this COM object. The Outlook PIAs are registered under the following key:

HKEY_CLASSES_ROOT\CLSID\{0006F023-0000-0000-C000-000000000046}\InprocServer32\12.0.0.0
    Assembly="Microsoft.Office.Interop.Outlook, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71E9BCE111E9429C"

From what I understand unregistering the PIA using the regasm tool should remove the key and re-adding the reference to your own IA should give the expected result.

However, Microsoft does not recommend to use custom IAs if there is a PIA available. I don't understand the exact reason for this, but I assume this might be related to marshaling optimizations and having unique type definitions.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文