How to detect when a workbook is closing?
This is an evolution of my 1st Answer - it detects the edge case problem by comparing the ActiveWindow.Caption against ThisWorkbook.Name so it can detect that issue and deal with it. It's not the most elegant solution but I believe it works.
All Code in the Workbook most of it in DeActivate
Public ByeBye As String Private Sub Workbook_BeforeClose(Cancel As Boolean) ByeBye = "B4C" End Sub Private Sub Workbook_Deactivate() If ByeBye = "B4C" Then If ActiveWindow.Caption = ThisWorkbook.Name Then If ThisWorkbook.Saved Then MsgBox "No problem - Closing after Saving" Else MsgBox "No problem - Closing without Saving" End If Else If ThisWorkbook.Saved Then MsgBox "No problem - New Workbook Activation" Else MsgBox "Oops Try Again You Cannot Activate '" & ActiveWindow.Caption & "' until '" & ThisWorkbook.Name & "' has completed processing & IT HAS NOW COMPLETED", vbOKOnly, "Hiding" ThisWorkbook.Activate End If End If Else MsgBox "No problem - Just Hiding" End If ByeBye = "Done" End Sub Private Sub Workbook_Open() ByeBye = "OPENED" End Sub
In response to comment about saving I tested this for 7 possible combinations as follows
1) Closing without Edits - No Saving Involved ... MsgBox Prompted with ... No problem - Closing after Saving 2) Not closing - Just Switch Workbook - Whether Edited or Not ... MsgBox Prompted with ... No problem - Just Hiding 3) Not closing - Switch Workbook - After Edit & Cancel ... MsgBox Prompted with ... Oops Try Again … 4) Closing and saving ... MsgBox Prompted with ... No problem - Closing after Saving 5) Closing and Saving after a prior Cancel ... MsgBox Prompted with ... No problem - Closing after Saving 6) Closing but Not Saving ... MsgBox Prompted with ... No problem - Closing without Saving 7) Closing but not Saving after a prior Cancel ... MsgBox Prompted with ... No problem - Closing without Saving
I think trying to cancel the close event is the wrong approach for what you are trying to do. A better approach would be to have a function that is only called when the workbook is actually closing.
Thank you for the comments regarding OnTime not being called while the dialog is open as that pointed me in the right direction. What we need to test is the time between the workbook deactivation and the closing of either the workbook itself or the save dialog. Using the Excel.Application.OnTime function to set this close time means this is possible as it can be delayed until the save dialogue has closed.
Once we have this time, a simple comparison to the deactivation time allows us to decide whether to call the exit function or not.
I initially ran into issues with the workbook reopening to run the .OnTime procedure, so an artificial delay needs to be added into the Deactivation function so the workbook hasn't closed until the close time has been set. Using the code from here - Delay Macro to allow events to finish we can accomplish this.
Option Explicit Private Sub Workbook_BeforeClose(Cancel As Boolean) Excel.Application.OnTime EarliestTime:=Now, Procedure:="SetCloseTime" End Sub Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean) If Timer < CloseTime + 0.2 Then Call CloseProcedure End Sub Private Sub Workbook_Deactivate() Delay (0.3) If Timer < CloseTime + 0.4 Then Call CloseProcedure End Sub
In a module
Option Explicit Public CloseTime As Single Function SetCloseTime() CloseTime = Timer End Function Function Delay(Seconds As Single) Dim StopTime As Single: StopTime = Timer + Seconds Do While Timer < StopTime DoEvents Loop End Function Function CloseProcedure() MsgBox "Excel is closing" End Function
The .OnTime seems to run within one second cycles which dictates the length of the delay and the time difference test has a little leeway added with an additional 1/10th of a second (which I found necessary). These timings could potentially need slight tweaking but have so far worked for me with the different scenarios when closing the workbook.