【问题标题】:Why aren't TypeLib enums exposed as enums in Visual Basic 6.0?为什么 TypeLib 枚举在 Visual Basic 6.0 中不作为枚举公开?
【发布时间】:2011-04-15 09:57:20
【问题描述】:

我有一个引用 COMSVCSLib 的 VB6 项目,其中一种方法调用 COMSVCSLib 的 SharedPropertyGroupManager.CreatePropertyGroup,并传递 LockMethodProcess 作为参数。

清理了 VB6 代码:

Dim groupName       As String
Dim spmMgr          As COMSVCSLib.SharedPropertyGroupManager
Dim spmGroup        As COMSVCSLib.SharedPropertyGroup

Dim bGroupExists    As Boolean

Set spmMgr = New COMSVCSLib.SharedPropertyGroupManager

With spmMgr
    Set spmGroup = .CreatePropertyGroup(groupName, LockMethod, Process, bGroupExists)

End With

几年没用VB6了,起初我以为LockMethod和Process是在项目的其他地方定义的变量或常量。

在对对象浏览器进行一些研究后,我发现它们都在 COMSVCSLib 中作为常量公开。

但是查看它们在 OLE/COM 对象查看器中的定义,它们似乎被定义为枚举的值:

typedef enum {
    LockSetGet = 0,
    LockMethod = 1
} __MIDL___MIDL_itf_autosvcs_0469_0002;

为什么 COMSVCSLib 中的 IDL/TypeLib 枚举不作为枚举暴露给 Visual Basic 6.0?

【问题讨论】:

    标签: vb6 enums constants typelib


    【解决方案1】:

    免责声明我不是 IDL(接口定义语言,用于定义 COM 类型的语言)或 Microsoft IDL 编译器 (MIDL) 方面的专家,但我来了在玩弄了 scrrun.dll 的类型库之后得出以下结论,这与 enum 有类似的问题。其中一些信息是通过快速浏览这篇关于 IDL 和 VB6 的 DevX 文章收集的:IDL for VB Tutorial

    VB6 期望实际的枚举有一个名称,而不仅仅是一个名称为typedef'd 的枚举。 __MIDL___MIDL_itf_autosvcs_0469_0002 名称是一个占位符,因为原始类型库没有在定义枚举常量的同一 typedef 中定义枚举名称。

    当您在 OLE 查看器中查看类型库时,enum 可能如下所示:

    typedef [public] __MIDL___MIDL_itf_autosvcs_0469_0002 LockModes;
    
    typedef enum {
        LockSetGet = 0,
        LockMethod = 1
    } __MIDL___MIDL_itf_autosvcs_0469_0002;
    

    第一个 typedef 创建公共名称 LockModes 作为自动生成的 MIDL___MIDL_itf_autosvcs_0469_0002 名称的别名,该名称已分配给 enum。编译原始类型库时,midl 编译器为原始 enum 生成了长 __MIDL 名称,并自动创建了一个指向它的 typedef 别名。

    原来的 IDL 大概是这样定义枚举的:

    typedef enum {
         LockSetGet = 0,
         LockMethod = 1
    } LockModes;
    

    midl 编译器处理以这种方式编写的enum 定义时,它会自动为enum 生成一个名称(因为它缺失 - 它应该出现在enum 关键字之后)。这就是您在 OLE 查看器中查看类型库时看到的 __MIDL 名称。 midl 编译器还会自动生成第二个 typedef,它将 typedef 名称别名为自动生成的 enum 名称。

    问题在于 VB6 无法理解以这种方式创建的枚举。它希望所有内容都在一个 typedef 中(即您给 enum 一个名称,以及命名 typedef):

    typedef enum LocksMode {
        LockSetGet = 0,
        LockMethod = 1
    } LocksMode;
    

    IDL 对待 typedef 的方式与 C 或 C++ 相同:您不必为枚举本身命名,因为 typedef 已经有了名称,但您可以 em> 如果您选择,请给枚举命名。换句话说,typedefenum 实际上是两个独立的实体。 VB6 碰巧将typedefenum 识别为两个不同但相关性模糊的事物,因此在您的情况下,它看到一个名为__MIDL___MIDL_itf_autosvcs_0469_0002typedef,它认为这是一个未命名的别名枚举,它还看到LockModestypedef,这是另一个typedef 的公共别名。

    由于第一个typedef 是公共的,您将在对象浏览器中看到LockModes 的条目,并且因为它是枚举的别名,您也会在对象浏览器中看到枚举常量。但是,实际枚举本身没有名称(因此它会在浏览器中获得分配给它的时髦的自动生成名称),并且 VB6 不能使用枚举,因为自动生成的名称恰好在 VB6 中是非法的(带有双下划线的名称在 VB6 中会自动隐藏)。

    为了证明最后一点,如果您在 VB6 代码中键入此内容,Intellisense 将工作并编译,但显然,它不是很理想:

    MsgBox COMSVCSLib.[__MIDL___MIDL_itf_autosvcs_0469_0002].LockMethod
    

    此代码有效的原因是您可以将通常会导致语法错误的名称(例如以下划线开头的名称)放在括号中,以允许 VB6 接受通常非法的名称。此外,使用自动生成的名称为常量添加前缀适用于 Intellisense,因为它是 VB6 与 enum 关联的实际名称(请记住,另一个 typedef 只是这个“真实”的别名,但自动生成的名称,而 VB6 显然无法将所有部分放在一起以实现两个名称引用相同的 enum)。

    除了像上面那样输入长得可笑的名称之外,您还可以通过在库名称前面加上 enum 常量来访问它们,例如,COMSVCSLib.LockMethod 应该可以工作。我不太清楚为什么这实际上有效,而且我不确定如果两个不同的 enum 定义具有相同名称的常量会发生什么。

    最后,您可以通过使用 OLE 查看器中的 IDL 创建自定义 IDL 文件以不同的方式解决此问题,在该文件中,您将现有的 enum typedefs 替换为单个 typedef 用于每个 enum只需给enumtypedef 赋予相同的名称(即typedef enum LockModes { ... } LockModes;),但由于OLE 查看器不一定生成有效的IDL,您可能需要对其进行更多调整才能使其实际编译.如果您可以让它工作,那么您可以从您的 VB6 项目(而不是 COMSVCSLib 库)中引用您的自定义 .tlb,并且 enum 将像您期望的那样工作。

    如果你想走这条路,你还需要另外两个工具,它们应该已经安装在你的开发机器上(但你可能需要搜索它们):

    • midl.exe:此工具可以从.idl 文件生成类型库文件 (*.tlb)。因此,您可以将 OLE 查看器中的 IDL 复制到记事本中,如上所述修改枚举定义,将其保存为 .idl 文件,并将其传递给 midl.exe 以创建新的类型库:

      midl my-custom-typelib.idl

    • regtlib.exe:这个工具可以注册一个 .tlb 文件,如果你希望能够将它作为引用添加到你的 VB6 项目中,这是必需的:

      regtlib.exe my-custom-typelib.tlb

    但是,为此创建自定义类型库可能有点过头了,而且如前所述,可能很难根据 OLE 查看器的输出获得可编译的 IDL 文件,因为它显示类型库的反向工程 IDL ,而不是原来的 IDL。

    【讨论】:

    • @wqw:我不确定所有的技术细节——这个答案是基于经验测试的。例如,如果您获取 OLE 查看器生成的 scrrun.dll 的 IDL,并将其编译为带有 midl.exe 的新类型库,则 VB6 只有在给出枚举的时才能正确看到枚举typedef 是 IDL 中的一个名称。否则,您将在对象浏览器中看到 enum 名称,但 IntelliSense 将无法正常工作,并且如果您查看对象浏览器中的枚举常量,它会说该枚举是具有 auto 的枚举的成员- 生成的名称。
    • 您甚至不必创建新的类型库即可看到:只需将scrrun.dll 的引用添加到新的VB6 项目中,然后打开对象浏览器并查看SpecialFolderConst枚举。是的,它显示在对象浏览器中,是的,它列出了常量,但是如果您单击SystemFolder 常量,例如,对象浏览器会说它是Scripting.__MIDL___MIDL_itf_scrrun_0001_0000_0002 的成员。它应该说常量是Scripting.SpecialFolderConst的成员,但是类型库的定义方式并不是100%兼容VB6。
    • 另外,我想说的是我的答案是完美的(这就是我在此处放置免责声明的原因),但据我所知,这种解释符合实际测试。我为 sccrun.dll 创建了一个新类型库,并更改了枚举 typedef 以包含除 typedef 名称之外的枚举名称,当我将该类型库添加到 VB6 时,枚举在对象浏览器中正确显示,枚举常量是列为正确枚举的成员,并且 IntelliSense 可以正常工作。但是,如果我的实际技术细节有误,您能否提供正确信息的答案?
    • 我对这个理论进行了更多测试。如果您使用tlbinf32.dll(允许您以编程方式读取类型库中的数据的库)遍历scrrun.dll 中的所有常量,它会列出SpecialFolderConst 枚举的2 个条目。一个是ALIAS(即typedef),另一个是实际的ENUM。真正的枚举具有自动生成的 __MIDL 名称。 _MIDL 名称由 midl 编译器自动生成,当您不给枚举提供自己的名称时。 IntelliSense 仅适用于类型库中的 ENUM 类型,它无法识别 ALIAS 的类型。
    • COMSVCSLib 类型库也有同样的问题。 IDL 为LockModes 枚举定义了一个typedef(枚举本身有一个自动生成的名称),以及第二个 typedef 将该 typedef 别名为名称LockModes。我也对此进行了测试:midl 编译器会自动创建 ALIAS 并为 ENUM 定义生成一个随机名称,如果您不在 IDL 中为枚举提供自己的名称:如果您编写 typedef enum {...} MyEnum; 然后编译它,midl 将创建一个名为MyEnumALIAS,它指向实际的ENUM,它有一个自动生成的名称。
    【解决方案2】:

    它以枚举的形式公开。在类列表中选择 LockModes 并查看下方信息部分。你会看到它是一个枚举。或者你可以在你的代码中输入LockModes.,你会得到两个选项。

    在对象查看器中,枚举中的每个项目都被标识为一个常量值,但它不是一个独立的常量。当您在类列表中选择 <globals> 项时,独立的 const 会单独列出。

    【讨论】:

    • 是的......你说得对,它是一个枚举。在看到您的回复之前,我最终在对象浏览器中看到了一点。我还尝试使用 LockModes 甚至 __MIDL___MIDL_itf_autosvcs_0469_0002 作为前缀,但没有成功。代码照原样遵守。如果我给它加上前缀,它就不会编译。
    猜你喜欢
    • 2018-10-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多