Excel 关闭不同工作簿将结束当前工作簿的代码执行

Excel 关闭不同工作簿将结束当前工作簿的代码执行,excel,vba,Excel,Vba,我正试图在Book1.xlsm中运行foo,它在Book2.xlsm中运行另一个子项bar。以下是两者的代码: 在第1册中: Sub foo() Dim oldBook As Workbook: Set oldBook = ActiveWorkbook Dim newBook As Workbook: Set newBook = Workbooks("Book2.xlsm") Application.Run "'Book2.xlsm'!Module1.bar", old

我正试图在
Book1.xlsm
中运行
foo
,它在
Book2.xlsm
中运行另一个子项
bar
。以下是两者的代码:

在第1册中:

Sub foo()
    Dim oldBook As Workbook: Set oldBook = ActiveWorkbook
    Dim newBook As Workbook: Set newBook = Workbooks("Book2.xlsm")

    Application.Run "'Book2.xlsm'!Module1.bar", oldBook
End Sub
第二册:

Sub bar(book As Workbook)
    book.Close False
    Debug.Print "Closed Workbook!"
    MsgBox "Closed workbook!"
End Sub
foo
的执行运行良好,控件被传递到
Book2.xlsm
中的
bar
。行
book.Close false
运行正常(关闭
Book1.xlsm
),但代码执行立即停止,没有任何警告(控制台或弹出窗口中没有消息)。既然控件被传递到了
bar
,那么这两行不是都应该在
Book2.xlsm
中执行吗

我通过调用
条形工作簿(1)
工作簿(1)=Book1.xlsm
)尝试了这一点,整个代码运行良好,正如预期的那样。难道不应该发生同样的事情吗


如何在第一个场景中保持代码执行,即运行
foo
运行
bar
,更改工作簿,关闭
Book1.xlsm
并继续执行
bar

您可以使用Application.OnTime完成此任务

Book2.xlsm

Public wb As Workbook

Public Sub bar(book As Workbook)
'set the variable for the workbook we want to close - We do this because we cannot pass a workbook object
Set wb = book
'set it to call 1 second from now
Application.OnTime DateTime.DateAdd("s", 1, DateTime.Now), "CloseIt"
End Sub


Sub CloseIt()
    wb.Close False
    Debug.Print "Closed Workbook!"
    MsgBox "Closed workbook!"
End Sub

重新考虑谁对什么负责。给工作簿一个随机的
workbook
对象,然后继续
关闭它,这是不正常的

当然,您可以绕过这样一个事实,即您实际上是在关闭调用堆栈的所有者,但问题的根源是,当您有
WorkbookA
负责执行某项操作,并且某项操作涉及打开或创建
WorkbookB
时,然后,
WorkbookA
有责任在完成后关闭
WorkbookB

Book2
没有关闭任何内容的业务。假设真正的宏实际上做的不仅仅是关闭它,那么它应该做它自己的事情,然后让调用者决定是关闭工作簿还是保持工作簿打开<代码>第二册!条形码被赋予了一个它不拥有的资源:关闭该资源超出了它的责任范围

也许这可以作为一个简单的例子:

Public Sub DoSomething()
    Set t = New Thing
    UseTheThing t
    t.SomeMethod ' error 91: object reference is gone!
End Sub

Private Sub UseTheThing(ByRef t As Thing)
    t.Foobar 42
    Set t = Nothing ' not your job!
End Sub

围绕事物的自然顺序(调用者->被调用者->返回调用者)工作既不推荐也不有用。从更大的角度来看,有些东西被破坏了-后退一步,修复更高的级别,而不是与整个范例抗争。

因为foo()是堆栈上的调用方,一旦关闭,它的所有子进程都将关闭,这是有意义的。至于我问题的第二部分,如果首先关闭了原始工作簿,我如何继续使用
bar
?为什么不关闭第1册中的第1册?在运行第2册中的代码并将控制权转移回第2册之后,不要像这样断章取义。负责打开资源的代码/工作簿(无论是工作簿、文件、数据库连接、任何东西)都应该是负责关闭该资源/清理的代码。如果需要在应用程序级别上运行代码和控制事项,请考虑制作Excel外接程序。这样,您就得到了一个“总体”组件,可以协调谁可以做什么。