【问题标题】:VB.NET - Module Interpreted as Class in VBEVB.NET - 模块在 VBE 中解释为类
【发布时间】:2017-02-23 11:01:30
【问题描述】:

我正在构建一个用于 VBA 项目的 VB.NET 类库。在我的库中,我有一个名为“Globals”的模块,我希望将一些全局(库级别)属性和方法存储在:

Imports System.Runtime.InteropServices

<ComVisible(True)>
Public Module Globals
    <ComVisible(True)>
    Public Const TestGlobal As Integer = 0
End Module

但是,当我在 Excel VBE 中引用我的库时,我的模块显示为一个类,我在对象浏览器中看不到我的常量变量:

我遗漏了什么或做错了什么导致我的模块显示为一个类?

【问题讨论】:

  • A Module 只是一个静态类。它作为接口暴露给 COM,VBE OB 将其显示为Class,因为它可以通过IDispatch 调用。有关更深入的解释,请参阅 this answer。 IIR 如果您将常量声明为与 public static readonly int 等效的任何 VB,则该常量将是 COM 可见的。
  • 现在它显示为一个类是有道理的。您知道在 VB.NET 中等效的 VBA 模块是什么吗?
  • 一个 VB.NET 模块。一个很好的例子是你可以给一个 VBA 模块属性。 VBA 模块只是静态类的 COM 等价物。
  • 明白了。这是否意味着我无法在我的代码中做任何事情来让 VBE 将我的 Globals 模块视为一个模块而不是一个类?如果有办法做到这一点,我需要在我的代码中进行哪些更改?
  • 如果它显示为ClassModule,真的重要吗? FWIW,VBA.Global 也显示为 Class

标签: vb.net vba dll


【解决方案1】:

VB.NET 和 COM 之间存在某种“阻抗不匹配”。 COM 主要与接口一起工作,而 .NET 从 VB 项目构建 .tlb 文件的方式使它们更容易被 COM 访问。 VB.NET 中的Module 基本上只是托管世界中的一个静态类。当从您的代码构建互操作程序集时,它会看到一个类,将其包装在一个接口中,并将该接口附加到该类:

请注意,您不能Module 提供自己的接口实现,因为.NET 中的静态类无法实现接口。


在 .NET 中不能做的另一件事是创建未包装在类中的成员过程。这基本上就是Module 在 COM 世界中的含义。例如,看一下 VBA 字符串模块的 IDL:

module Strings {
    [entry(516), helpcontext(0x000f665f)]
    short _stdcall Asc([in] BSTR String);
    [entry(537), helpcontext(0x000f6e9f)]
    BSTR _stdcall _B_str_Chr([in] long CharCode);
    [entry(608), helpcontext(0x000f64e2)]
    VARIANT _stdcall _B_var_Chr([in] long CharCode);
    [entry(517), helpcontext(0x000f6ea0)]

请注意,所有条目都指定了一个 .dll 入口点。这基本上是因为您正在进行您在 C 世界中发现的各种原始函数调用。如果在 .NET 世界中存在类似物,它将是在 namespace 级别而不是类(或模块)级别声明的 SubFunction。 ..

<ComVisible(True)>
Namespace SomeClass.Functions  

    <ComVisible(True)>
    Public Function Foo() As String
        Return "Foo"
    End Function    

End Namespace

...这显然不是有效的 .NET 代码。


在相关说明中,请记住,您在 VBA 中看不到整个图片。 VBA 基本上建立在 COM 之上,但它非常努力地抽象掉其中的大部分内容。 VBA 模块 可以出于所有意图和目的被视为静态类。例如,您可以为它们提供属性、公共成员等。归根结底,VBA 对象浏览器是否将某些东西视为类、模块或其他东西并不重要——重要的是您可以使用非托管上下文中的托管代码。只需解决这 2 个基础架构中的差异即可获得可用的东西并收工:

Imports System.Runtime.InteropServices

<ComVisible(True)>
Public Enum Constants
    GlobalOne = 0
    GlobalTwo = 1
    GlobalEtc = 2
End Enum

<ComVisible(True)>
Public Class FunctionProvider
    <ComVisible(True)>
    Public Function Foo() As String
        'Note that the class doesn't need to keep track of "state" to be usable.
        Return "Foo"
    End Function
End Class

【讨论】:

  • 感谢共产国际提供了这么好的文档。我从不反对在我的项目中只使用类(静态或非静态)。这只是让我感到困惑,为什么 VBE 没有看到我的模块的本来面目,但是,它实际上正在正确地解释它,正如你所指出的那样。由于 VB.NET 静态类没有接口,这是否与为什么在 VBE 中,当我在库名称后面加上句点时没有智能感知:MyLibrary.?为了让我获得智能感知,我发现我需要首先对我的库的一个对象进行标注。或者这是一个完全不同的主题?
  • @SamSlaughterbeck - 是的。您需要先声明它并创建一个实例。相当于“默认对象”的粗略 COM 是一个 appobject,但在 .NET 中确实没有类似的东西。把它想象成为你的服务器创建一个Application 对象。
  • 明白了。没有 .NET 等价物……我想的差不多。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-10-27
  • 1970-01-01
  • 1970-01-01
  • 2012-08-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多