在VBA中对C#COM库进行早期绑定
虽然这是一个很长的问题,但编码和测试部分应该很容易重现。
我在C#
创建了两个单独的Class Libraries
,我认为我遇到了由我以前的项目和试验中的现有注册表项引起的名称冲突问题。
这是我的两个class级:
using System; using System.Runtime.InteropServices; namespace Test { [InterfaceType(ComInterfaceType.InterfaceIsDual), Guid("ED5D264B-1D80-4A5D-9C14-8297D90B7037")] public interface ITest { // body } [ClassInterface(ClassInterfaceType.None)] [Guid("8B261B92-8EC5-4CDC-A551-67DEB42137FF")] [ProgId("Test.TestClass")] public class TestClass : ITest { // body } }
和
using System; using System.Runtime.InteropServices; using ADODB; namespace Test { [InterfaceType(ComInterfaceType.InterfaceIsDual), Guid("ED5D264B-1D80-4A5D-9C14-8297D90B7037")] public interface IConnection { // body } [ClassInterface(ClassInterfaceType.None)] [Guid("8B261B92-8EC5-4CDC-A551-67DEB42137FF")] [ProgId("Test.Connection")] public class Connection : IConnection { // body } }
我有像这样暴露的.Net组件到COM :
为了从Excel访问程序集,我已将ADODB引用添加到程序集,勾选make程序集COM可见并注册com interop 。 此外,我添加了对每个*.tlb
文件的引用( 两个项目的2个文件 ),因此我可以使用早期绑定访问它们并使用VBA Intellisense 。
我在另一台机器上执行了相同的过程,我可以使用Connection
as class进行早期绑定。
我在想我的原始机器上没有删除一些旧的注册表项,这将不允许我在VBE中使用Connection
作为类名。 我手动扫描了我的注册表并删除了我能想到的与我的项目相关的所有内容。
我还完全删除了该项目,并使用第三方软件扫描注册表中缺少的dll
但这没有帮助:/
删除所有以前注册的GUID并在每次创建新项目时应用新的GUID( 以防万一 )
使用不同的命名空间和类名创建新项目( using ADODB;
)我还没有能够使用早期绑定,就像这个Test.Connection
因此我假设我有一个名称冲突问题。 虽然我不是100%肯定,但我怀疑名称类Connection
会导致它。
VBA中的Test.TestClass
命名空间:
我可以使用早期绑定以两种方式声明和使用TestClass
类型的实例:
Dim x as Test.TestClass Dim x as TestClass
现在进入VBE对象资源管理器F2 ,与其他库和使用COM的一般想法相比, TestClass
正确显示。
但是,当我想使用Test.Connection
库时,我无法使用与TestClass
相同的模式进行早期绑定,因为生成的*.tlb
文件会自动更改( 重命名 ) ProgId's
。 所以,我必须像这样绑定它
Dim x As Test.Test_Connection Dim x As Test_Connection
Object Explorer
使用_
(下划线)显示名称,而不是.
(点),这很容易解释为什么会发生这种情况 – 继续阅读:)
目前我确信不是VBE环境改变名称以避免冲突。 它是VS’ *.tlb
生成器。
我去了assembly文件夹并在Notepad++
打开了两个*.tlb
文件。 我可以清楚地看到, Test.Connection
库的*.tlb
已经包含了带有Test.Connection
的名称,而不像Test.TestClass
那样.
小号
我试图手动编辑*.tlb
文件,但作为一个混合二进制文件它需要一些效果,但也导致Excel以一些奇怪的方式停止响应,所以我必须避免这种方法。
我想我已经很好地解释了问题是什么以及它来自何处。 现在我的问题是:
在C#代码中是否有任何属性可以告诉*.tlb
生成器不要覆盖我的ProdId
?
有没有其他方法可以操作*.tlb
文件?
此问题是name collision
,是否可以避免更改Connection
类的名称?
我很抱歉这么久的问题,但我现在已经挖掘和挖掘了近一个星期,我仍然无法解决这个问题。
注意:在使用IntelliSense ctrl + space的 VBA(或VBE对象资源管理器)中,似乎没有使用Connection
或Recordset
。 由于它们尚未在VBE环境中保留,因此我将重新考虑它与我的库本身有关。
作为此处提出此问题的原因的参考,请参阅VBA等效于C#using或VB.NET导入创建别名
非常感谢您的宝贵时间!
避免专注于ProgId。 你实际上并没有使用它,你制作截图的对话框显示的是实际的类名,而不是ProgId。
将类名重命名为“Test_Connection”是类型库导出器的正常行为。 只要它检测到与具有相同名称的另一个接口或类名冲突,它就会这样做。 你肯定通过依赖ADODB来增加发生这种情况的可能性,它也有一个Connection类。 一个非常简单的解决方案是简单地重命名您自己的类型。
您的代码段无法重现此问题。 但当然它不完整,我们无法看到你在代码中真正做了什么。 如果您的任何公共方法使用此类型库中的类型,您将引入对ADODB的依赖。 另请注意,偶然发生这种情况的概率非零。 您可能编写了一个旨在使用您自己的Connection类型的方法,但编译器将其解析为ADODB类型。
调试它的一个重要工具是Oleview.exe,从Visual Studio命令提示符运行它。 首先使用Tlbexp.exe为C#程序集创建类型库。 然后使用File + View Typelib,您将看到以IDL语法表示的类型库的内容。 您可以轻松识别C#类型到IDL声明的映射。
注意文件顶部的importlib
指令。 他们应该是这样的:
// TLib : // TLib : mscorlib.dll : {BED7F4EA-1A96-11D2-8F08-00A0C9A6186D} importlib("mscorlib.tlb"); // TLib : OLE Automation : {00020430-0000-0000-C000-000000000046} importlib("stdole2.tlb");
应该只有那两个。 第一个导入.NET类型,定义_Object。 第二个导入标准COM类型,如IDispatch。 如果您在此处看到其他内容,则会增加名称冲突的几率。
此IDL还为您提供了一种解决问题的方法,如果它无法解决,您可以编辑它以按照您希望的方式命名类型。 将其保存为.idl文件。 并使用midl.exe / tlb编译它以生成带有您首选名称的类型库。 请注意,这不是您想要经常做的事情。