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

kol*_*inx 6 code-reuse vba excel-vba excel-addins excel-udf

我有一个带有"核心"函数和子函数的Addin,我想在不同的Addins或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
Run Code Online (Sandbox Code Playgroud)

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

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

在此输入图像描述

研究

我使用Option Private Statement为单个项目方法找到了解决方案SO1,SO2. 然而,这并没有解决问题,因为其他应用或项目限制.

当模块包含选项专用模块时,在模块级别声明的公共部分(例如,变量,对象和用户定义类型)在包含该模块的项目中仍然可用,但它们不可用于其他应用程序或项目.

接下来,我在MRExcel formum上找到了一个问题- 仅隐藏VBA函数.汤姆·施赖纳建议,我可以使用自定义的类并实现内部的功能.这样,它们将无法通过单元格插入功能获得,但仍可用于我的其他项目.

问题

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

S M*_*den 3

请向下滚动以更新,因为我发现这本练习册符合 OP 的要求

总之,您需要将函数放在外接程序中的类中,但是需要一个额外的步骤才能使跨工作簿脚本可操作,您不能New在外部类上使用关键字。所以需要编写一个可以被外部调用的类工厂函数。

下一个问题是耦合,您可以使用工具参考并参考项目来获得与其有用的智能感知的早期绑定,但是由于加载序列,您可能会为您的背部创建一个杆,插件将由任何具有以下功能的调用客户端加载一个参考。另一种替代方案是 Late Bound 等效项,它消除了引用,但将加载插件的负担交给了开发人员。

以下是步骤...

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

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


    Option Explicit

    Public Function Add(x, y)
        Add = x + y
    End Function
Run Code Online (Sandbox Code Playgroud)
  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
Run Code Online (Sandbox Code Playgroud)
  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
Run Code Online (Sandbox Code Playgroud)
  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)
Run Code Online (Sandbox Code Playgroud)

End Sub

Run Code Online (Sandbox Code Playgroud)

  1. 要运行顶部子程序,您需要转到“工具”->“引用”并引用 FunctionLibrary.xlsm。
  2. 要运行底部子程序不需要“工具”->“参考”,但您必须注释掉顶部子程序以避免编译错误。

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

本练习册

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

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

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

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

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

是的,这适用于 xlam 和 xlsm。

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