【问题标题】:How to reuse Core VBA functions (UDFs) across projects, but not show them in cell insert function如何跨项目重用核心 VBA 函数 (UDF),但不在单元格插入函数中显示它们
【发布时间】:2017-06-22 07:52:50
【问题描述】:

我有一个带有“核心”功能和子功能的插件,我想在不同的插件或 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 代码中的项目

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

研究

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

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

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

问题

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

【问题讨论】:

  • 3) 是的。是的。我认为您根本不应该担心这些功能会出现。
  • @GSerg,我还有通过CreateObject 方法实例化应用程序的函数。例如。 CreateObject("InternetExplorer.Application")。这些方法不能简单地忽略。我正在考虑重构这些。对于其他 VBA 对象,可以按照您的建议忽略它们在工作表上的使用。感谢您的意见。
  • 你无法真正阻止坚定的用户成为白痴。如果你想尝试,你可以在你的函数中检查Application.Caller,以确保它们在TypeOf Application.Caller is Range时不起作用。

标签: vba excel code-reuse excel-addins excel-udf


【解决方案1】:

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

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

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

步骤如下……

  1. 创建一个项目,我叫我的FunctionLibrary.xlsm,我重命名了 从“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 and references FunctionLibrary.xlsm。
  2. 运行底部子程序不需要工具->参考,但您必须注释掉顶部子程序以避免编译错误。

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

本工作簿

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

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

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

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

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

是的,这适用于 xlam 和 xlsm。

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

【讨论】:

  • 感谢您的回复。相当的回答。我需要查看您提到的关键字,例如加载序列和 dll 地狱。我在创建自己的自定义类方面几乎没有经验。代码看起来很合理。只需 3 个问题:1) 最终用户是否可以通过插入功能看到 CreateMyLibraryEarlyBoundEntryPoint 功能?不是问题,只是想弄清楚这一点。 2) 为什么将CreateMyLibraryLateBoundEntryPoint 放到ThisWorkbook 模块中? 3) xlam(插件)和xlsm(宏书)是否也存在 dll 地狱问题?
  • 您好,您的反问引发了一个新发现(对我而言),这是一种更好的方法来满足您的要求。我已经编辑了答案,我很想删除原始答案。请阅读新部分。
  • 我对您的发现感到非常兴奋!对我来说也有新的东西。为了进一步推动它,我还可以使用 Sheet 对象从插入函数中“隐藏”我的函数,但仍然可以在代码中使用它们,使用它们的完整路径。我什至可以使用工作表 CodeNames!通过这种方式,我还可以以与模块类似的方式将我的代码组织成逻辑组(适当调用的工作表)。唯一的问题是核心插件文件是否不会增长太多。
  • 优秀。关于大小,如果您指的是 ThisWorkbook 模块大小,您仍然可以使用类来组织您的代码并允许通过 ThisWorkbook 进入它们。如果您的意思是工作簿大小,那么您可以随时给自己另一个工作簿。
  • 实际上在其他情况下建议将函数挂在 ThisWorkbook 上,这里是 MSDN interop article with VBA calls C# code which hangs off ThisWorkbook。 blogs.msdn.microsoft.com/bethmassi/2009/11/05/…
【解决方案2】:

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

无法从工作表中的单元格公式直接调用 COM 插件函数。

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

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

【讨论】:

  • 感谢您回复迈克尔。这绝对是一种方式。我会更多地研究它。我听说他们是相当多的工作。
猜你喜欢
  • 1970-01-01
  • 2020-11-18
  • 1970-01-01
  • 1970-01-01
  • 2019-11-27
  • 1970-01-01
  • 2015-05-24
  • 2010-10-25
  • 1970-01-01
相关资源
最近更新 更多