gpt4 book ai didi

excel - 如何后期绑定(bind)项目中的类模块?

转载 作者:行者123 更新时间:2023-12-04 20:23:39 26 4
gpt4 key购买 nike

我的项目中有两个类模块,“ClassAlfa”和“ClassBeta”。我已经将两个类之间的代码分开,这样如果最终用户的项目中没有包含 ClassBeta,那么 ClassAlfa 对它的任何后期绑定(bind)引用都会优雅地失败。如果最终用户确实有可用的 ClassBeta,我遇到的问题是使脚本工作。
在 ClassAlfa 中,我有以下代码:

Option Explicit

Public myClass as Object

Private Sub Class_Initialize()
On Error Resume Next
Set myClass = Application.Run("ClassBeta")
If Err = 0 Then 'End user has this class available, go ahead and use it.
'Do code with ClassBeta
Else
'Do code without ClassBeta
End If
On Error GoTo 0
End Sub
这会在 Set 上引发以下错误线:

Run-time error '1004':

Cannot run the macro 'ClassBeta'. The macro may not be available in this workbook or all macros may be disabled.


我也试过替换 Set与此一致:
Set myClass = CreateObject("ClassBeta")
而是引发错误

Run-time error '429':

ActiveX component can't create object


也不起作用:
Set myClass = CreateObject("'" & ThisWorkbook.Name & "'!" & "ClassBeta")
从我自己的项目中后期绑定(bind)自定义类的正确方法是什么?

最佳答案

VBA 中没有任何机制可以让您通过名称检查一个类是否存在,如果存在则创建一个新实例。
不过,是有可能实现的。让我们剖析这些问题,看看我们如何解决这些问题。
问题1
您需要创建一个您不确定它是否存在于当前项目中的类的实例。如何?
正如您已经尝试过的,Application.RunCreateObject不工作。Application.Run只能在标准 .bas 模块(不是类模块)中运行方法。它不创建类的实例。CreateObject在幕后做一些事情。首先,它调用 CLSIDFromProgIDEx使用您传递的 ProgID(字符串)。然后,它调用 CoCreateInstance .问题是 VBA 类在注册表中没有它们的 ProgID,因此 CreateObject 根本不起作用。您需要将您的类(class)放在 ActiveX.exe 的已注册 .dll 文件中才能完成这项工作。
这给我们留下了New关键字实例化一个新的ClassBeta .这显然在类存在时有效,但在不存在时会给出“用户定义的类型未定义”编译器错误。只有两种方法可以抑制此编译器错误:

  • 有一个编译器指令
  • 没有New ClassBeta完全
  • ClassAlfa 中的编译器指令如下所示:
    Option Explicit

    #Const BETA_EXISTS = False

    Sub Test()
    Dim myBeta As Object
    #If BETA_EXISTS Then
    Set myBeta = New ClassAlpha
    #End If
    Debug.Print "Type of 'myBeta' is: " & TypeName(myBeta)
    End Sub
    没有 ClassBeta 就可以正常编译在项目中,但您永远无法制作 BETA_EXISTS conditional compiler constantTrue因为:

    Only conditional compiler constants and literals can be used in expression


    所以,最后的选择是不要有 New ClassBeta项目中的任何地方。除了,我们这样做。我们可以把它放在 ClassBeta本身作为工厂:
    Option Explicit

    Public Function Factory() As ClassBeta
    Set Factory = New ClassBeta
    End Function
    当类丢失时,工厂丢失和 New ClassBeta不会抛出编译器错误。
    问题2
    我们如何调用 .Factory ClassBeta 上的方法?
    好吧,我们显然不能创建 ClassBeta 的新实例。因为这是我们的第一个问题。
    我们可以做的是确保 ClassBeta总是有一个默认的全局实例(就像用户表单一样)。为此,我们需要将类导出到 .cls 文本文件并使用文本编辑器(如记事本)对其进行编辑。文本应如下所示:
    VERSION 1.0 CLASS
    BEGIN
    MultiUse = -1 'True
    END
    Attribute VB_Name = "ClassBeta"
    Attribute VB_GlobalNameSpace = False
    Attribute VB_Creatable = False
    Attribute VB_PredeclaredId = True
    Attribute VB_Exposed = False
    Option Explicit

    Public Function Factory() As ClassBeta
    Set Factory = New ClassBeta
    End Function
    请注意,我已将 VB_PredeclaredId 属性更改为 True (手动,在文本编辑器中)。现在我们可以将类导入回来。我们可以通过输入 ?Typename(ClassBeta.Factory) 来检查它是否有效。在“立即”窗口中,然后按 Enter。我们应该看到:
    enter image description here ClassAlfa中的代码现在可以写成:
    Option Explicit

    Sub Test()
    Dim myBeta As Object

    On Error Resume Next
    Set myBeta = ClassBeta.Factory
    On Error GoTo 0

    Debug.Print "Type of 'myBeta' is: " & TypeName(myBeta)
    End Sub
    问题3
    如果我们删除 ClassBeta从项目中,以下行无法编译:
    Set myBeta = ClassBeta.Factory
    但是,与问题 1 相比,这次编译器错误是“ undefined variable ”。
    为了摆脱这个新的编译器错误,我能想到的唯一方法是关闭 Option Explicit .是的!那么不好! ClassAlfa :
    'Option Explicit 'needs to be off to be able to compile without the ClassBeta class

    Sub Test()
    Dim myBeta As Object

    On Error Resume Next
    Set myBeta = ClassBeta.Factory
    On Error GoTo 0

    Debug.Print "Type of 'myBeta' is: " & TypeName(myBeta)
    End Sub
    最后的想法
    您可以在 Option Explicit 时进行开发为您的用户打开并关闭它。
    如果您的用户将使用 ClassAlfaOption Explicit关闭,代码应该可以正常工作。但是如果他们想要 ClassBeta同样,他们获得它的唯一方法是通过导入 .cls 文件。复制粘贴代码不起作用,因为它们会丢失 ClassBeta 的全局实例(我们在 VB_PredeclaredId 隐藏属性中设置的那个)。
    我必须说我不建议删除 Option Explicit因为这可能会导致其他问题。我认为没有办法以“干净”的方式实现您想要的。

    关于excel - 如何后期绑定(bind)项目中的类模块?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66198343/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com