.net COM注册:位置重要吗?
我在一个解决方案中有两个项目-例如,.net COM注册:位置重要吗?,.net,com,com-interop,.net,Com,Com Interop,我在一个解决方案中有两个项目-例如,ProjectA和ProjectB。这两个项目都是为COM互操作注册的,ProjectAreferencesProjectB——当ProjectA生成时,它将ProjectB.dll复制到其输出文件夹中 现在,COM客户端需要访问ProjectB.tlb。我尝试使用后期生成事件将类型库从ProjectB的输出文件夹复制到ProjectA;乍一看,这是可行的——COM客户端可以“看到”.tlb中的类型 问题是,每当COM客户端尝试调用.tlb中的方法时,就会出现
ProjectA
和ProjectB
。这两个项目都是为COM互操作注册的,ProjectA
referencesProjectB
——当ProjectA
生成时,它将ProjectB.dll
复制到其输出文件夹中
现在,COM客户端需要访问ProjectB.tlb
。我尝试使用后期生成事件将类型库从ProjectB
的输出文件夹复制到ProjectA
;乍一看,这是可行的——COM客户端可以“看到”.tlb中的类型
问题是,每当COM客户端尝试调用.tlb中的方法时,就会出现一个自动化错误
我的直觉是.tlb需要在使用它的位置创建/注册,因此我取消选中了ProjectB
中的“注册COM互操作”框,从生成后事件中删除了xcopy
,并在ProjectA
的生成后事件中添加了regasm.exe/tlb
调用,以便在ProjectA
的输出文件夹中创建并注册ProjectB.tlb
。。。然后我清理并重建了解决方案
同样的事情:COM客户端可以看到这些类型,但是当它试图实际使用公开的API时会抛出一个自动化错误
这两个项目原本是同一个项目。我们决定将它们分开,这样当我们发布ProjectA
的更新时,我们就可以这样做,而不会导致COM客户端引用ProjectB
中的代码时出现兼容性问题。这个主意似乎很好,但是……这可能吗?如果是,怎么做
还是将
ProjectB
折叠回ProjectA
我唯一的希望就是让COM客户端使用ProjectB
公开的COM API?TLB的位置无关紧要;对于编译器来说,这只是元数据。它不在运行时使用。但是,.NET必须能够在运行时定位程序集DLL,这可能很微妙
.NET将尝试使用常用的Fusion探测规则定位DLL,这些规则基于客户端进程EXE的位置,而不是DLL或TLB的位置。这基本上给了你三个选择:
- 您可以将项目A.DLL和
复制到与客户端的EXE相同的文件夹中项目B.DLL
- 您可以将两个DLL都放在GAC中。(请注意,这是运行
为com互操作注册程序集的一个单独步骤。)regasm.com
- 在COM中注册程序集时,可以添加
的regasm.exe
参数,该参数指定.NET应该记住要注册的DLL的位置,并在运行时在同一位置查找它/Codebase
regasm.exe
,以便在将运行它们的计算机上为COM互操作注册它们
删除“注册COM互操作”选项只意味着COM互操作中唯一可见的类是那些显式具有[ComVisible]属性的类。选中该选项后,COM兼容的每个类都将可见
但是抛出了一个自动化错误
自动化客户端中的错误报告通常非常糟糕。这是“这不是我的问题,给别人打电话”错误报告。Java吃微软午餐的一个重要原因。非常重要的是,首先要诊断根本原因来解决这个问题,这需要从.NET项目开始。在这种特殊情况下,很可能会导致此错误
选择ProjectA作为启动项目,然后选择项目>属性>调试选项卡。选择“启动外部程序”,并在“命令行参数”框中输入自动化客户端主机或测试程序的名称。使用调试>异常并勾选CLR异常的抛出复选框。按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事件。您需要在订阅事件时使用一个好的触发器,该事件是保证由客户端创建的类的构造函数