4

虽然这是一个很长的问题,但编码和测试部分应该很容易重现。

我已经创建了两个单独Class LibrariesC#,我认为我遇到了由我以前的项目和试验中的现有注册表项引起的名称冲突问题。

这是我的两门课:

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
    }
}

我已经向 COM 公开了 .Net 组件,如下所示:
为了从 Excel 访问程序集,我已将ADODB 引用添加到程序集,勾选使程序集 COM 可见注册 com interop。此外,我添加了对每个*.tlb文件的引用(两个项目的 2 个文件),因此我可以使用早期绑定访问它们并使用 VBA Intellisense

我在另一台机器上遵循了相同的过程,我可以使用Connectionas 类使用早期绑定。

我在想有一些旧的注册表项我没有在我的原始机器上删除,它们不允许我Connection用作 VBE 中的类名。我已经手动扫描了我的注册表并删除了我能想到的与我的项目相关的所有内容。

我还完全删除了该项目并使用第 3 方软件扫描注册表以查找缺失dll的 s,但这并没有帮助:/

每次我创建一个新项目时,删除所有以前注册的 GUID 并应用新的(以防万一

使用不同的命名空间和类名 () 创建了新项目using ADODB;我还不能像这样使用早期绑定,Test.Connection因此我假设我有一个名称冲突问题。我怀疑是名称类Connection导致了它,尽管我不是 100% 确定。

Test.TestClassVBA 中的命名空间:

TestClass我可以使用早期绑定以两种方式声明和使用该类型的实例:

Dim x as Test.TestClass
Dim x as TestClass

现在进入 VBE 对象资源管理器F2TestClass与其他库和使用 COM 的一般想法相比,它已正确显示。

TestClass 对象资源管理器

但是,当我想使用该Test.Connection库时,我无法按照相同的模式使用早期绑定,TestClass因为生成的*.tlb文件会自动更改(重命名ProgId's. 所以,我必须像这样绑定它

Dim x As Test.Test_Connection
Dim x As Test_Connection

并且Object Explorer使用_(下划线)而不是.(点)显示名称,这很容易解释为什么会发生这种情况 - 继续阅读:)

无法使用早期绑定

就目前而言,我确信更改名称以避免冲突的不是 VBE 环境。它是 VS 的*.tlb生成器。

我去了程序集文件夹并*.tlbNotepad++. 我可以清楚地看到*.tlbforTest.Connection库已经包含带有_s 的名称,不像Test.TestClasswhich 有.s

我试图手动编辑该*.tlb文件,但由于它是一个混合二进制文件,它会产生一些效果,但也会导致 Excel 以一些奇怪的方式停止响应,所以我必须避免这种方法。

我想我已经很好地解释了问题是什么以及它来自哪里。现在我的问题是:
在 C# 代码中是否有任何属性可以用来告诉*.tlb生成器不要覆盖我ProdId的 s?
有没有其他操作*.tlb文件的方法?
这个问题是一个问题吗?name collision在不更改Connection类名的情况下是否可以避免?

对于这么长的问题,我很抱歉,但我已经挖了将近一个星期了,我仍然无法解决这个问题。

ctrl注意:在使用 IntelliSense +的VBA(或 VBE 对象资源管理器)中space,似乎既没有Connection使用也Recordset没有使用。由于它们尚未在 VBE 环境中保留,因此我认为这与我的库本身有关。

作为此处提出此问题的参考,请参阅VBA 等效于 C# using 或 VB.NET 导入创建别名
非常感谢您的宝贵时间!

4

1 回答 1

4

请避免关注 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 编译它以生成具有您首选名称的类型库。请注意,这不是您想要经常做的事情。

于 2013-07-23T13:09:29.533 回答