I am trying to run
Book1.xlsm that runs another sub,
Book2.xlsm. Following is the code for both:
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
The execution of
foo runs perfectly fine and the control is passed onto
Book2.xlsm. The line
book.Close false runs perfectly fine (closes
Book1.xlsm), but code execution stops immediately without any warning (no messages in console or popup). Shouldn’t both lines be executed in
Book2.xlsm since the control was passed to
I tried this by calling
bar workbooks(1) (
workbooks(1) = Book1.xlsm) and the entirety of the code runs perfectly fine, as expected. Shouldn’t the same happen
How do I preserve code execution in the first scenario, i.e.
bar, change workbooks, close
Book1.xlsm and continue execution of
Reconsider who’s responsible for what. It’s not normal that a workbook is just given any random
Workbook object and proceeds to
Sure you can hack around the fact that you’re essentially closing the owner of the call stack, but the root of the problem is, when you have
WorkbookA responsible for doing something, and that something involves opening or creating
WorkbookB, then it’s
WorkbookA‘s responsibility to close
WorkbookB when it’s done.
Book2 has no business closing anything. Assuming the real macro actually does something more than just closing it, then it should do its thing and then let the caller decide whether to close the workbook or leave it open;
Book2!Bar is given a resource that it doesn’t own: it’s beyond its responsibility to close that resource.
Maybe this can serve as a simple illustration:
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
Working around the natural order of things (caller -> callee -> back to caller) is neither recommended nor useful. Something is broken in the bigger picture – take a step back and fix the higher level instead of fighting the entire paradigm.
You can use Application.OnTime to accomplish this task.
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