如何跨项目重用核心 VBA 函数 (UDF),但不在单元格插入函数中显示它们

How to reuse Core VBA functions (UDFs) across projects, but not show them in cell insert function

我有一个带有 "core" 函数和子程序的插件,我想在不同的插件或 VBA 项目中引用和使用它们。因为代码重用和单一更新原则。

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

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

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

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

研究

我找到了解决方案 SO1,SO2 for a single project methods, using Option Private Statement
然而,这并不能解决问题,因为 其他应用程序或项目 限制。

When a module contains Option Private Module, the public parts, for example, variables, objects, and user-defined types declared at module level, are still available within the project containing the module, but they are not available to other applications or projects.

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

问题

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

Q1&2 - 另一个解决方案是您可以创建一个 COM Add-in:

COM Add-in functions cannot be directly called from cell formulas in worksheets.

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

Q3 - 是 add-ins 仍然合理;创建 COM Add-in 需要大量的额外工作。

请向下滚动以进行更新,因为我发现这本手册可以满足 OP 的要求

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

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

这是步骤...

  1. 创建一个项目,我叫我的FunctionLibrary.xlsm,我改名为 项目 属性 从 'VBAProject' 到 FunctionLibrary。

  2. 给你的项目添加一个class,我叫我的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</p>

<p>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)</p>

<p>End Sub</p>

<p>Sub LateBoundTest()</p>

<pre><code>'* 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. 到 运行 顶部子目录,您需要转到工具->参考和参考 FunctionLibrary.xlsm。
  2. 至 运行 底部子不需要工具->参考,但您必须注释掉顶部子以避免编译错误。

更新:折叠评论者的反馈。 DLL 地狱是当您将代码移动到库中,然后您必须担心加载它,加载正确的版本和正确的依赖项。

本工作簿

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

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

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

ThisWorkbook 隐藏了 Application.Run

的函数和子函数

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

是的,这适用于 xlam 和 xlsm。

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