6

我有一个带有“核心”功能和子组件的插件,我想在不同的插件或 VBA 项目中引用和使用它们。因为代码重用和单一更新原则。

例如,一个根据条件过滤集合成员并返回子集合的函数。代码本身在这里不是问题。

Public Function listNamesContaining(ByVal NamesInput As Names, ByVal ContainsCriteria As String) As Collection
    Dim NameMember As Name

    Set listNamesContaining = New Collection
    For Each NameMember In NamesInput
        If InStr(1, NameMember.Name, ContainsCriteria, vbTextCompare) Then
            listNamesContaining.Add NameMember
        End If
    Next
End Function

我不想在单元格插入函数中显示此函数,因为它返回一个集合对象,但我想在 VBA 代码中 的多个 VBA 项目中重用它。

当前问题的图片,对象函数显示在单元格插入公式中:

在此处输入图像描述

研究

我使用Option Private Statement为单个项目方法找到了解决方案SO1SO2然而,由于其他应用程序或项目的限制 ,这并不能解决问题。

当模块包含 Option Private Module 时,公共部分,例如在模块级别声明的变量、对象和用户定义的类型,在包含该模块的项目中仍然可用,但对其他应用程序或项目不可用

接下来,我在MRExcel formum 上找到了一个问题 - Hiding VBA functions only。Tom Schreiner 建议,我可以使用自定义类并在 Classes实现功能。这样,它们将无法通过单元格插入功能使用,但仍可用于我的其他项目。

问题

  1. 如何跨项目重用核心 VBA 函数 (UDF),但不在单元格插入函数中显示它们?
  2. 自定义解决方案只有一个吗?
  3. (基于意见)我最初关于通过excel插件(.xlam)在多个项目中共享核心方法的理念是否合理?
4

2 回答 2

2

请向下滚动以更新,因为我发现这本工作簿可以满足 OP 的要求

总之,您需要将您的函数放在插件中的一个类中,但是还有一个额外的步骤可以使跨工作簿脚本运行,您不能New在外部类上使用关键字。所以你需要编写一个可以在外部调用的类工厂函数。

下一个问题是耦合,您可以使用工具参考并参考项目以通过其有用的 Intellisense 进行早期绑定但是由于加载序列,您可能会为您的背部创建一个杆,该插件将由任何调用客户端加载一个参考。另一种选择是 Late Bound 等效项,它消除了引用,但将加载插件的负担放在了开发人员身上。

以下是步骤...

  1. 创建一个项目,我将其命名为 FunctionLibrary.xlsm,我将 Project 属性从“VBAProject”重命名为 FunctionLibrary。

  2. 向您的项目添加一个类,我调用了我的 MyLibrary,我将其设置 Instancing2 - PublicNotCreateable. 我添加了(简单)以下代码


    Option Explicit

    Public Function Add(x, y)
        Add = x + y
    End Function
  1. 添加一个名为“modEarlyBoundClassFactory”的标准模块并添加以下代码

    Option Explicit

    Public Function CreateMyLibraryEarlyBoundEntryPoint(ByVal sLicenceKey As String) As MyLibrary
        'If sLicenceKey = "Yourlicencekey" Then
            Set CreateMyLibraryEarlyBoundEntryPoint = New MyLibrary
        'End If
    End Function
  1. 在 ThisWorkbook 模块中,我添加了以下代码

    Option Explicit

    Public Function CreateMyLibraryLateBoundEntryPoint(ByVal sLicenceKey As String) As Object

        'If sLicenceKey = "Yourlicencekey" Then
            Set CreateMyLibraryLateBoundEntryPoint = New MyLibrary
        'End If

    End Function
  1. 保存工作簿
  2. 创建一个调用工作簿,我调用了我的 FunctionLibraryCallers.xlsm 并在一个新的标准模块中添加了以下代码


Option Explicit

Sub EarlyBoundTest() '* requires Tools->References to addin (this can create load sequence issues and the addin equivalent of dll hell)! Dim o As FunctionLibrary.mylibrary Set o = FunctionLibrary.CreateMyLibraryEarlyBoundEntryPoint("open sesame") Debug.Print o.Add(4, 5)

End Sub

Sub LateBoundTest()

'* you need to write code to ensure the function library is loaded!!!
On Error Resume Next
Dim wbFL As Excel.Workbook
Set wbFL = Application.Workbooks.Item("FunctionLibrary.xlsm")
On Error GoTo 0
Debug.Assert Not wbFL Is Nothing
'* End of 'you need to write code to ensure the function library is loaded!!!'

Dim o As Object 'FunctionLibrary.mylibrary
Set o = wbFL.CreateMyLibraryLateBoundEntryPoint("open sesame")  '* this works because the method is defined in ThisWorkbook module of library
Debug.Print o.Add(4, 5)

End Sub

  1. 要运行 top sub,您需要转到 Tools->References 和 references FunctionLibrary.xlsm。
  2. 要运行底部子程序不需要工具->参考,尽管您必须注释掉顶部子程序以避免编译错误。

更新:折叠评论者的反馈。DLL Hell 是当您将代码移至库时,您必须担心加载它、加载正确的版本和正确的依赖项。

本工作簿

OP询问ThisWorkbook,这个想法源于一个 关于编译错误的不同SO问题。如果将变量定义为 Workbook,编译器将不会强制执行标准 Workbook 接口。可以随意调用标准接口中没有的额外方法,我猜这是因为ThisWorkbook可以用作可扩展机制。

ThisWorkbook 隐藏了“插入函数”对话框中的函数

有趣的是,它ThisWorkbook从“插入函数”对话框中隐藏了一个函数,因此它是实现 OP 要求的一种更简单的方法!

ThisWorkbook 隐藏了 Application.Run 中的函数和子项

实际上,因为Thisworkbook是一个类的单个实例,所以开发人员在其中定义的所有函数和子类都不会添加到全局命名空间中,因此无法调用Application.Run它们。要执行它们,必须获取Excel.Workbook对库工作簿对象的引用并通过该实例调用方法。

是的,这适用于 xlam 和 xlsm。

感谢OP,我今天学到了一些东西。

于 2017-02-08T10:19:01.943 回答
1

Q1&2 - 另一种解决方案是您可以创建一个 COM 插件:

不能从工作表中的单元格公式直接调用 COM 加载项函数。

https://support.microsoft.com/en-au/help/291392/excel-com-add-ins-and-automation-add-ins

Q3 - 是的插件仍然是合理的;创建 COM 加载项需要大量的额外工作。

于 2017-02-08T06:39:49.257 回答