COM注册:位置是否重要?

时间:2015-06-05 01:38:19

标签: .net com com-interop

我在解决方案中有两个项目 - 比如ProjectAProjectB。这两个项目都注册了COM互操作,ProjectA引用ProjectB - 当ProjectA构建时,它将ProjectB.dll复制到其输出文件夹中。

现在,COM客户端需要访问ProjectB.tlb。我尝试使用xcopyProjectB的输出文件夹中的类型库ProjectA添加到ProjectB中;一目了然,这样做 - COM客户端可以“看到”.tlb中的类型。

问题在于,只要COM客户端尝试调用.tlb中的方法,就会出现自动化错误

我的直觉是.tlb需要在其使用的位置创建/注册,所以我取消选中xcopy中的“注册COM互操作”框,删除了{{1}来自后期构建事件,并在regasm.exe /tlb的构建后事件中添加了ProjectA调用,以便在{{1}的输出文件夹中创建并注册ProjectB.tlb } ...然后我清理并重建了解决方案。

同样的事情:COM客户端看到类型,但在尝试实际使用公开的API时会抛出自动化错误

这两个项目原本是同一个。我们决定拆分它们,以便在我们发送ProjectA的更新时,我们可以这样做,而不会引起引用ProjectA中代码的COM客户端的兼容性问题。这个想法似乎非常好,但是......它甚至可能吗?如果是这样,怎么样?

或者将ProjectB折回ProjectB我唯一的希望是让COM客户端使用ProjectA公开的COM API吗?

2 个答案:

答案 0 :(得分:3)

TLB位置并不重要;它只是编译器带来的元数据的好处。它没有在运行时使用。但是,.NET必须能够在运行时找到程序集DLL,这可能很微妙。

.NET将尝试使用通常的Fusion探测规则来定位DLL,这些规则基于客户端进程EXE 的位置,而不是DLL的位置,或TLB的位置物。这基本上给你三个选择:

  • 您可以将 ProjectA.DLLProjectB.DLL复制到与客户端EXE相同的文件夹中。

我不这样做,因为我发现它非常...... 类似于COM的。令人惊讶的是。此外,客户端通常位于不方便的位置(例如IIS主机进程)。

这给你留下了另外两个选择:

  • 您可以将两个DLL放在GAC中。 (请注意,这是运行regasm.com以注册COM互操作程序集的单独步骤。)

  • 您可以在COM中注册程序集时添加/Codebase regasm.exe参数,该参数指定.NET应记住正在注册的DLL的位置,并在运行时查找它同一地点。

请记住,无论如何都必须使用两个程序集运行regasm.exe,以便在将要运行它们的计算机上注册COM interop。

删除"注册COM互操作"选项只是意味着COM互操作可见的唯一类是明确具有[ComVisible]属性的类。选中该选项后,每个与COM兼容的类都将可见。

答案 1 :(得分:3)

  

但会引发自动化错误

自动化客户端中的错误报告通常很糟糕。这是"这不是我的问题,打电话给其他人"错误报告。 Java吃微软午餐的一个重要原因。通过诊断潜在原因以及需要从.NET项目开始,首先解决这个问题非常重要。在这种特殊情况下非常可能会导致此错误。

选择ProjectA作为启动项目,然后选择Project>属性>调试选项卡。选择"启动外部程序"并将自动化客户端主机或测试程序的名称放在"命令行参数"框。使用Debug>例外并勾选CLR异常的Thrown复选框。按F5启动客户端,调试器在抛出异常时进入,并显示出错的地方。

  

ProjectA引用ProjectB - 当ProjectA构建时,它会复制ProjectB.dll

这不好并且保证会导致此问题。如果A实际上对B具有依赖性,它将仅复制ProjectB。换句话说,您未能真正使COM服务器独立运行。您必须删除引用,现在您将收到一个编译错误,显示此依赖关系是如何进入的。

此依赖项将在运行时触发FileNotFoundException,CLR不知道如何查找ProjectB.dll。它不在探测路径中。它可以找到ProjectA.dll的事实没有帮助,它从注册表中得到了提示。

如果你不能打破对B的依赖,但你的任务是使B独立而不是A,那么使用以下技术之一:

  • 使用ILMerge将ProjectB合并到ProjectA.dll中,因此只有一个文件
  • 在GAC中安装ProjectB,以便始终可以找到它。一般来说,COM服务器的正确方法,DLL地狱的一个很好的解决方案,虽然你不喜欢在你自己的机器上
  • 将ProjectB.dll复制到与自动化客户端的EXE相同的目录中。现在它处于探测路径中并且始终可以找到。在您的开发机器上避免使用GAC的正确解决方法
  • 让ProjectA实现AppDomain.CurrentDomain.AssemblyResolve事件。您需要在A中使用一个好的触发器来订阅事件,即保证由客户创建的类的构造函数。