VBA关闭工作簿,未能关闭引用的加载项工作簿。

3

版本

Excel 2016桌面版

背景

一个VBA项目(“客户端” .xlsb文件)引用了另一个工作簿(工具菜单 > 引用)。所引用的工作簿不是插件,而是一个普通的 .xlsb 文件。

这导致如果客户端文档仍然打开,则无法关闭被引用的文档:

"This workbook is currently referenced by another workbook and cannot be closed."

我希望实现的行为是当使用VBA通过ThisWorkbook.Close关闭客户端工作簿时,被引用的工作簿保留在内存中而不自动关闭。如何在使用VBA关闭客户端工作簿时使被引用的工作簿自动关闭呢?理想情况下,解决方案应该可以关闭被引用的工作簿,即使打开了其他工作簿。不能使用需要编程从客户端中删除引用的解决方案,因为无法保证用户是否拥有VBA的受信任访问权限和宏安全性。

假设有多个打开的文档可能引用了被引用的文档,因此必须在最后一个客户端关闭之前才能关闭引用。已尝试的解决方案包括:1.使用AfterClose事件来让被引用的文档在没有其他打开的客户端时关闭自身,但挑战在于需要等到所有客户端关闭后才能关闭被引用的文档,并且没有适合的Workbook_AfterClose事件; 2.使用OnTime事件在最后一个客户端关闭5秒后模拟AfterClose事件,但如果在由VBA启动的ClientWorkbook.Close中注册了OnTime事件,则似乎不会触发; 3.模拟关闭按钮点击,但操作鼠标似乎是个坏主意,而且如果有其他无关的工作簿打开,此方法无法关闭被引用的文档; 4.在关闭客户端之前以编程方式删除引用,但这需要VBA的受信任访问权限。


1
你能分享一下使所描述的行为成为可能的过程的位置和代码吗? - VBasic2008
@VBasic2008 完成。 - johny why
1
“第三个效果”对我不起作用,即所引用的工作簿无论如何都保持打开状态。为什么(或者如何)所引用的工作簿会在手动关闭时关闭?请分享一下。 - VBasic2008
似乎只有在没有其他文件打开的情况下(不相关的文件,而不是指向同一参考书的客户端),我才能获得第三个效果。@VBasic2008 - johny why
@VBasic2008 我从问题中删除了关于.IsAddin的信息。它与问题无关。 - johny why
2个回答

2

感谢Naresh的帮助,我看到如何在程序化工作簿关闭事件中执行OnTime。我对该方法进行了改进,将详细信息放入外部工作簿。

此解决方案要求客户端工作簿调用自定义CloseBook过程,而不是ThisWorkbook.Close,但这并不可怕。

客户端工作簿

模块1

Sub Test()
          CloseBook ThisWorkbook
End Sub

  • 参考:如果事件监听器被加载到引用中,则可以使用上述语法--将CloseBook作为客户端书的内部调用。
  • Addin:如果事件监听器是一个addin,或者只是另一个工作簿,则需要使用Run语法:Application.Run "CloseBook", ThisWorkbook

事件监听器书

模块1

Sub CloseBook(oBk As Workbook)
          ' This is the secret sauce. 
          ' The OnTime procedure won't execute until this sub finishes, 
          ' which won't happen until oBk.Close finishes. 
          ' That's what makes this an AfterClose event. 
          Application.OnTime Now, "Workbook_AfterClose"
          oBk.Close
End Sub


Sub Workbook_AfterClose()
          ' This is the AfterClose event. Put anything you want here.
          If NoClients Then ThisWorkbook.Close
End Sub

(注意,此问题范围之外的 NoClients 过程)

1
如果它按预期工作,您可以接受自己的答案。这将让其他人知道它是否有效 :) .. 另外,不确定此页面和此YouTube视频(从11:30帧开始)是否有助于解决此类问题。 - Naresh
1
谢谢提供的链接,我会查看它们。我还开发了一种方法,在用户手动关闭工作簿时触发AfterClose事件。它适用于任意数量的打开书籍(与默认行为不同,如果客户端是唯一打开的书,则仅关闭引用书)。将其包含在此答案中可能与主题无关,因此我正在考虑修改我的问题,以包括程序化和用户书籍关闭事件的AfterClose代码。不确定修改我的问题是否可以。这个问题变成了“如何生成Workbook_AfterClose事件”。 - johny why
1
@Naresh 我的解决方案消除了删除引用的需要。以编程方式删除引用需要在Excel中选择 文件>选项>信任中心>信任中心设置>宏设置>信任对VBA项目对象模型的访问。这不能通过编程方式完成。开发人员可以使用后期绑定而不是引用,但这是不可取的。https://answers.microsoft.com/en-us/msoffice/forum/all/how-do-i-get-microsoft-vba-extensibility-53... https://social.msdn.microsoft.com/Forums/security/en-US/57813453-9a21-4080-9d4a-e548e715d7ca/add-visual-basic-extensibility-library-through-code - johny why

2
这是你面临的问题: enter image description here 你是否尝试过这个?
Option Explicit

'~~> This is the name of the VBA project from the referenced workbook
Private Const ReferenceWbModule As String = "MyVBAProject"
Private Const ReferenceWb As String = "Test.xlsb"

Sub Sample()       
    '~~> Save the workbook (IMPORTANT)
    ThisWorkbook.Save
    
    '~~> Remove the reference
    Dim RefName As String: RefName = ReferenceWbModule
    Dim oRefS As Object, oRef As Object
    Set oRefS = ThisWorkbook.VBProject.References
    For Each oRef In oRefS
        If oRef.Name = RefName Then
            oRefS.Remove oRef
            Exit For
        End If
    Next
    
    '~~> Close the workbook
    Dim wb As Workbook
    Set wb = Workbooks(ReferenceWb)
    wb.Close (False)
    
    '~~> Close without saving so that next time the reference is still there
    ThisWorkbook.Close (False)
End Sub

现在的情况是这样的:

enter image description here

注意:

您需要给予Office VBA项目程序化访问权限

enter image description here

否则,当您运行代码时,将会出现以下错误:

enter image description here


谢谢你!我可能会在你的设计中找到解决方案。但是,我不能直接使用你的解决方案,因为我无法保证对VBA进行编程访问。 - johny why
顺便提一下,只需要删除引用的书籍,而不是所有引用。 - johny why

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接