Stop Sub Workbook_BeforeClose from rerunning at close - excel

My problem is when closing my Workbook it reruns the Workbook_BeforeClose Macro. When the user clicks No it closes the ActiveWorkbook and another one that it inputs data to. When the active workbook closes it reruns the message box. It does this because it is part of the close sequence. How do i tell excel to run the code and close without running the code again. Please let me know if i can explain anything more clearly.
Private Sub Workbook_BeforeClose(Cancel As Boolean)
MSG1 = MsgBox("Are you ready to email to MFG?", vbYesNo, "EMAIL MFG")
If MSG1 = vbYes Then
'Attaches current worksheet to new message in Outlook
If MSG1 = vbNo Then
' Enter data into separate work and close all Workbooks
INWBK.Close
Wbk.Close
End Sub
I also get an Error 91 on Wbk.Close, This is the Activeworkbook.
INWBK:
INWBK = ThisWorkbook
Wbk:
Const kPath As String = "C:\"
Const kFile As String = "QUOTE REQUEST LOG 2015.xlsm"
Rem Set Wbk in case it's open
On Error Resume Next
Set Wbk = Workbooks(kFile)
On Error GoTo 0
Rem Validate Wbk
If Wbk Is Nothing Then Set Wbk = Workbooks.Open(kPath & kFile)

The Workbook_BeforeClose sub runs only because the workbook is being closed. Hence there is no need to put INWBK.Close again. After the sub finishes, the workbook window will be closed.
For the Error 91 on Wbk.Close, the error message is Object variable not set. Because you calling the Workbook_BeforeClose twice as explained above, thus Wbk.close is also being called twice. The first time should be a success, but the second time, there is no object bind to it anymore, and thus Excel cried Error 91.

Assuming:
INWBK is the workbook that you are closing (i.e. it has the Workbook_BeforeClose event that is running), and
Wbk is the secondary workbook where "data" is copied to.
There is no need to run INWBK.Close within your Workbook_BeforeClose event handler because INWBK is already in the process of closing. In running INWBK.Close in you code, you are starting another Workbook_BeforeClose event hence the message box being displayed again. You can confirm this by putting a break point on your INWBK.Close command then using F8 to step through the code from there.
I recommend deleting INWBK.Close from your code
An alternative would be to tell Excel to ignore the 2nd close event by adding Application.EnableEvents = False before displaying your message box then run Application.EnableEvents = True before your End Sub.

Related

Application.Quit closes all open Excel files

I want to close the active macro workbook inside the Userform_Terminate event. The problem I am facing is that, a ghost window of excel application lingers on even after workbook has been closed.
Have tried most of the suggested ways, I could get my hands on (described in detail in the code snippet) but to no avail. If anybody can help, much grateful.
NOTE: Have released almost all excel related objects from memory by setting it to nothing.
Code :
Private Sub UserForm_Terminate()
' Application.DisplayAlerts = False ' The excel ghost window lingers on
' ThisWorkbook.Close , False
' Application.DisplayAlerts = True
'
' Application.DisplayAlerts = False ' The excel ghost window lingers on
' ThisWorkbook.Saved = True
' ThisWorkbook.Close , False
' Application.DisplayAlerts = True
' Application.DisplayAlerts = False 'The excel ghost window lingers on.
' ThisWorkbook.Close , False
' Application.Quit
' Application.DisplayAlerts = True
Application.DisplayAlerts = False 'Ghost window is closed but also kills all instances of excel currently open
Application.Quit
Application.DisplayAlerts = True
'NOTE:
'Have released all excel related objects from memory by setting it to nothing, post use.
End Sub
Snap:
Well, your "gost" problem has the next explanation:
An Excel session/instance means the same Application handler. If you open a workbook from the Excel existing interface, it is open in the same instance. Pressing Ctrl + F6 will jump to the next workbook open in the same instance...
If there are open workbooks not seen in the Ctrl + F6 sequence, this only means that they are open in a different instance.
Another instance is open, for instance :), in this way:
Din ExApp as Object
Set ExApp = CreateObject("Excel.Application")
ExApp.Workbooks.add 'without this line, the instance is quit by itself...
Set ExApp = Nothing 'this only releases the memory
You can see more open Excel instances (if they exist) looking in TaskManager and seeing more the one such application (Excel.exe)...
When you close a workbook, and this specific workbook is the single one of the instance, the application Window, what you name a "gost" remains!. If there are some other workbooks open, the so named "gost" window disappears, too.
In order to handle both situations, please try the next approach:
Private Sub UserForm_Terminate()
If Workbooks.Count > OpenWb Then
ThisWorkbook.Close , False
Else
Application.Quit
End If
End Sub
Function OpenWb() As Long
Dim count As Long, wb As Workbook, arr
For Each wb In Workbooks
arr = Split(wb.Name, ".")
If UCase(arr(UBound(arr))) = "XLSB" Then count = count + 1
Next
OpenWb = count + 1
End Function
Quitting the application is done here only because you asked for it... When you try programmatically to open and close many workbooks, it is more efficient to keep the application open. To open a new instance takes time. To open a workbook in an existing instance takes less time... But to do that, your code must find that existing instance:
Sub testExcelInstance()
Dim Ex As Object
On Error Resume Next
Set Ex = GetObject(, "Excel.Application")
If Ex Is Nothing Then
Err.Clear: On Error GoTo 0
Set Ex = CreateObject("Excel.Application")
End If
On Error GoTo 0
Ex.Workbooks.Add 'just doing something after having the Excel instance object
End Sub
Releasing the objects from the memory does not do anything, in terms of the object itself existence. If physically disappears only if you quit it.

Macro Only Finishes If Run Though VBA Application or Macro List

I have a 3 master sheets that I frequently want open (May, June, and July). I have multiple other macros that grab data from these master sheets.
I've created a macro (OpenFiles) that opens them. I want to be able to call OpenFiles at the start of a macro if I know I will be referencing one of these master sheets.
Sub OpenFiles calls another sub(zzGetIt) that checks if the workbook is already open, otherwise it opens it. It works perfectly if I run it from the VBA application, or by choosing it in the macro list in Excel. But if I use a hotkey to call it, or if it is called through another macro, it exits out of all subs after opening a single file (and ends on that workbook instead of activating my original active workbook).
It will open a workbook if one of them is missing, but it will never open more than one (if, say I have only 1/3 workbooks open- 2 should open). And the only scenario where the macro will continue to the msgbox at the end is if all three files are already open.
Please help- I think this must be super obvious since the macro runs fine if I run it from VBA.
I've tried the following:
Removed any error handling that could be hiding a problem with the
sub/function
Set Tools>Options>"Break on all Errors" and I still don't receive any
errors when the sub ends early.
Went through the whole sub with F8- it runs perfectly when I use that.
Call OpenFiles multiple times in a macro but the sub ends after the sub is called the first time so the rest never even run.
Sub zzGetIt(sfullname As String)
Dim ZGetIt As Workbook
Dim wb As Workbook
Dim ReadOnly As Boolean
Dim o As Boolean
Dim sFile As String
sFile = Dir(sfullname)
MsgBox ("Trying to fetch")
For Each wb In Application.Workbooks
If wb.Name = sFile Then
o = True
Exit For
End If
Next wb
If o = False Then
Set zGetIt = Workbooks.Open(sfullname, ReadOnly:=ReadOnly)
End If
'reset o
o = False
MsgBox ("Finished fetching " & sFile)
End Sub
Sub OpenFiles()
Dim Current As Worksheet
Set Current = ActiveSheet
Dim May As String
Dim Jun As String
Dim Jul As String
May = "A:\Files\My Stuff\05 May 2019 - Master.xlsx"
Jun = "A:\Files\My Stuff\06 June 2019 - Master.xlsx"
Jul = "A:\Files\My Stuff\07 July 2019 - Master.xlsx"
Call zzGetIt(May)
Call zzGetIt(Jun)
Call zzGetIt(Jul)
Current.Activate
Set Current = Nothing
Msgbox("I can only get this msgbox if I run from macro list or
VBA application OR if all 3 workbooks were already open before I ran the
macro")
End Sub
If May needs to be opened it will stop at May so I do not receive the msgbox after the sub is called for the first time.
I want the macro to open any of the three workbooks that are not already open and I need it to continue until the msgbox at the very end pops up
I don't see anything obviously wrong with your code that might cause the observed behavior. But I would still do it differently. Perhaps this will help. I've revised your procedures that check for the file already open/open the file if not already open, but apart from that the main difference is that I'm calling this procedure in a loop from OpenFiles.
Option Explicit
Sub OpenFiles()
Dim Current As Worksheet
Set Current = ActiveSheet
Dim files As New Collection
Dim file
files.Add "A:\Files\My Stuff\05 May 2019 - Master.xlsx"
files.Add "A:\Files\My Stuff\06 June 2019 - Master.xlsx"
files.Add "A:\Files\My Stuff\07 July 2019 - Master.xlsx"
For Each file In files
Debug.Print "Fetching file " & file
If isFileOpen(CStr(file)) Then
Debug.Print file & " is already open :) "
Else
Call GetFile(CStr(file), False)
End If
Next
Current.Activate
Set Current = Nothing
MsgBox ("Finished!")
End Sub
Private Function isFileOpen(fullPath$) As Boolean
Dim wb As Workbook
On Error Resume Next
Set wb = Workbooks(Dir(fullPath))
If Err.Number = 0 Then isFileOpen = True
End Function
Private Sub GetFile(fullPath$, readOnly As Boolean)
' No error handling here, this SHOULD raise an error if the file can't
' be opened for any reason (invalid path, locked/in-use unless readOnly=True, etc.
Debug.Print "Attempting to open " & fullPath
Workbooks.Open fullPath, readOnly:=readOnly
End Sub
I've solved the issue... found this article. The issue is using a hotkey with SHIFT. Hotkey used must be lower-case or use a button to call the macro
Ctrl+Shift+a
'won't work
Ctrl+a
'will work

Close Excel Application if Word crashes while reading from Excel file

I am trying to ensure that an Excel file is not left open if a Word application that is gathering data from it via a macro suddenly crashes.
The macro handles most errors using the code below, but I want to ensure that the Excel file isn't left hanging open in the face of an unhandled event. Such an event might be the user closing the word document from the task manager while the macro is running.
Public Function GatherDataFromExcel(x) As Collection
On Error GoTo CloseExcel
Dim path_file_excel As String
path_file_excel = "\path to file\file.xlsx"
Dim objXL As Object ' Excel.Application
Dim objBook As Object ' Excel.Workbook
Set objXL = CreateObject("Excel.Application")
On Error Resume Next
Set objBook = objXL.Workbooks.Open(path_file_excel)
On Error Resume Next
'Code that gathers data and returns that data from the function
CloseExcel:
If Not objBook Is Nothing Then
objBook.ActiveWorkbook.Close
objBook.Close
Set objBook = Nothing
If Not objXL Is Nothing Then
Set objXL = Nothing
End If
End If
End Function
I have closed the word document mid execution to check, and indeed the excel file is left hanging open. Is there any way to prevent this from happening?
If Word crashes you are going to need a different process to close Excel.
A few options:
Show Excel to the user so that the user can close Excel objXL.visible = true
If your VBA error handler does run you probably want to change:
objBook.ActiveWorkbook.Close
objBook.Close
To
objBook.Close False ' Close workbook without saving changes
objXL.Quit ' Exit Excel
As Freeflow suggested, have another process (e.g. timer event in Excel or a separate VBScript process) that closes Excel if it can't find the Word document

Excel VBA: On Close Save Prompt after ActiveX Resize Despite Thisworkbook.Saved=True

I've run into an interesting situation with Excel, and I was wondering if anyone of you knew an answer / solution.
Setup:
I have an empty excel worksheet with a ActiveX ToggleButton on it.
In a VBA code, I change the width of the button to 0, and then change the width to 100. (Why I do that is a different question, its part of a larger project, however, this causes the problem).
Then I set the save-status of the workbook to true, without actually saving the workbook.
Normally, if I now closed the workbook, it would just close, without asking me wether I want to save.
Now, due to the resizing, and even though the .Saved-Status is True, it asks me if I want to save when I close the workbook, and by clicking onto the close Icon of excel, the .Saved-Status is set to "False"
If you want to try for yourself, try the below steps.
Alternatively, I uploaded for convenience the same file here: (https://filebin.ca/3aLbbRxMTdUs/SavePromptUponResize.xlsm)
1) create a new workbook and add a new ACTIVEX toggle button.
2) in the VBA code of the workbook, add the below code
3) save the workbook, close it, and reopen it.
4) You should see a messagebox and after clicking, as second one
5) Now the status of the workbook is "saved"
6) Try to close the workbook -> you will be prompted if you want to save
7) If you check the .saved status now, it would say "false"
Looking forward to your insights!
Private Sub Workbook_Open()
MsgBox "Now a macro will run and resize twice the button you see." & vbCrLf & "Afterwards, the status of the workbook will be set to 'saved'." & vbCrLf & "However, upon closing, excel will still prompt to save."
Dim wb As Workbook: Set wb = ThisWorkbook
Dim ws As Worksheet: Set ws = wb.Sheets(1)
ws.Shapes("ToggleButton1").Width = 0
ws.Shapes("ToggleButton1").Width = 100
wb.Saved = True
MsgBox "Macro finished, save status is: " & wb.Saved
End Sub
Private Sub CheckSaveStatus()
MsgBox "Save status is: " & ActiveWorkbook.Saved
End Sub
The solution is as simple as this:
Private Sub Workbook_Open()
MsgBox "Now a macro will run and resize twice the button you see." & vbCrLf & "Afterwards, the status of the workbook will be set to 'saved'." & vbCrLf & "However, upon closing, excel will still prompt to save."
Dim wb As Workbook: Set wb = ThisWorkbook
Dim ws As Worksheet: Set ws = wb.Sheets(1)
Dim boolSaved As Boolean
boolSaved = wb.Saved
ws.Shapes("ToggleButton1").Width = 0
ws.Shapes("ToggleButton1").Width = 100
If boolSaved Then wb.Save
MsgBox "Macro finished, save status is: " & wb.Saved
End Sub
This technique can be used anywhere in your code where you need to do something that 'dirties' the workbook. The beauty of it is that it preserves the save prompt if the user has modified the workbook.
As to why it's happening, best guess is - a(nother) ActiveX bug in Excel.
Please try the below:
Option Explicit
Dim wb As Workbook
Dim ws As Worksheet
Dim i As Integer
Private Sub Workbook_BeforeClose(Cancel As Boolean)
If i = 1 Then
wb.Saved = False
Else
wb.Saved = True
End If
End Sub
Private Sub Workbook_Open()
MsgBox "Now a macro will run and resize twice the button you see." & vbCrLf & "Afterwards, the status of the workbook will be set to 'saved'." & vbCrLf & "However, upon closing, excel will still prompt to save."
Set wb = ThisWorkbook
Set ws = wb.Sheets(1)
ws.Shapes("ToggleButton1").Width = 0
ws.Shapes("ToggleButton1").Width = 100
End Sub
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
i = 1
End Sub
My experience has been that this Excel bug is triggered as a result of using VBA code to change the value of some properties of some ActiveX/OLE controls. I don't have a complete list of such properties but here's a few:
CommandButton:
Enabled property
Height property
TextBox:
ForeColor property
BackColor property
I use a generalized system (similar to Nikolaos Polygenis's solution), including extensive explanation due to this bug's subtlety, as follows:
In a standard module, define a project-global variable to indicate the no-save-needed condition:
'*************************************************************************************************************
'EXCEL OLE/ACTIVE-X SAVE-BUG WORKAROUND - BUG WORKAROUND - BUG WORKAROUND - BUG WORKAROUND - BUG WORKAROUND
'
'(See the Workbook_BeforeClose event handler, in the ThisWorkbook module, for a complete explanation.)
'
Public SuppressWkBkSaveMsg As Boolean
'
'END SAVE-BUG WORKAROUND - END WORKAROUND - END WORKAROUND - END WORKAROUND - END WORKAROUND - END WORKAROUND
'*************************************************************************************************************
In the ThisWorkbook module, place the following Workbook_BeforeClose event handler, with full explanation:
Private Sub Workbook_BeforeClose(Cancel As Boolean)
'*************************************************************************************************************
'EXCEL OLE/ACTIVE-X SAVE-BUG WORKAROUND - BUG WORKAROUND - BUG WORKAROUND - BUG WORKAROUND - BUG WORKAROUND
'
'Excel has a very subtle bug in which, if you change the value of some properties of some ActiveX/OLE
'controls, the parent workbook's Saved property will not function correctly until the next actual workbook
'save event. That is, you can subsequently set the workbook's Saved property to True but Excel will still
'prompt the user about whether to save the workbook when closing it. In fact, if you check the value of the
'Saved property in a Workbook_BeforeClose event handler, it will be defined as False even though no changes
'have been made to the workbook after explicitly setting it to True!
'
'The most effective workaround is to defer the override of the workbook's Saved property until the Close event
'has been actually initiated: declare the project-global SuppressWkBkSaveMsg variable in a standard module,
'add this Workbook_BeforeClose event handler to the ThisWorkbook module, and then set SuppressWkBkSaveMsg to
'True whenever it's necessary to suppress Excel's user-inquiry about whether to save changes to the workbook
'before closing it.
'
If SuppressWkBkSaveMsg Then Me.Saved = True
'
'END SAVE-BUG WORKAROUND - END WORKAROUND - END WORKAROUND - END WORKAROUND - END WORKAROUND - END WORKAROUND
'*************************************************************************************************************
End Sub
In any location in the code, in any module, whenever it's necessary to suppress Excel's user-inquiry about whether to save changes to the workbook before closing it, set SuppressWkBkSaveMsg to True:
'Do stuff that doesn't need to be saved, including modifications to OLE/ActiveX control-objects...
'
'*************************************************************************************************************
'EXCEL OLE/ACTIVE-X SAVE-BUG WORKAROUND - BUG WORKAROUND - BUG WORKAROUND - BUG WORKAROUND - BUG WORKAROUND
'
'(See the Workbook_BeforeClose event handler, in the ThisWorkbook module, for a complete explanation.)
'
SuppressWkBkSaveMsg = True
'
'END SAVE-BUG WORKAROUND - END WORKAROUND - END WORKAROUND - END WORKAROUND - END WORKAROUND - END WORKAROUND
'*************************************************************************************************************
'
'...

stop task from running when file is open

I am trying to schedule a task in Window's Task Scheduler that opens an excel file and auto run the macro in it.
The macro part is all done, but one problem I have is when the file is already open and the scheduled task runs. Whenever this happens, it will ask you if you want to reopen the file, if i accidentally click yes, which actually happened, then the data I was working on will all disappear.
So what I want is for the task to not run when file is open. Do I need to run a script? create a batch file with the code? If possible can you provide me a code that can do this as I am not familiar with another language aside from VBA
Note.
Just to clarify, I have a file that constantly pull data form Live Bloomberg. What I want to do is to save it as another workbook for reference everyday at 4:30pm. There is a few problem.
Here is how what I wrote in ThisWorkbook for easier understanding what I want to accomplish
Private Sub Workbook_Open()
Application.OnTime TimeValue("16:30:00"), "MyMacro"
End Sub
1)If I didn't open that excel that day, Workbook_Open() simply won't run.
2)Even if I did open the excel, if I close Excel before 4:30pm the macro won't run
So what I want is to have windows task scheduler constantly open the excel file for me that will always run on time regardless of what happens.
There are other problems too. I always write comments and stuff to the Live Data File, so if Task Scheduler tries to open the data file which in turn activating the Workbook_Open() while I am using it, a popup will come up asking if I want to reopen file. Of course I simply need to press "no" then the task will stop and everything is fine. But what if I wasn't there to press the button? Also on a few occasion I accidentally press "yes", which wiped out all the comments I wrote.
Therefore what I wanted to ask is that if The Live Data File is already open, simply make it so that the task in Task Scheduler won't run.
Have you considered using the Workbook.ReadOnly Property? This won't exactly tell you if the file is open but it will tell you if you have write-access. Try:
Public Sub OnlyRunWithWriteAccess()
Dim oBook As Workbook
Set oBook = Application.ActiveWorkbook
If Not oBook.ReadOnly Then
'Your processing code goes here...
End If
Set oBook = Nothing
End Sub
First of all thanks Steve S for your input.
I already did a workaround. Instead of having the LiveData file to run the macro, I put the macro on a clean excel template and have task scheduler open that file instead. This way is much more reliable and skips all the "Scripting in task scheduler" thing.
Here is my updated code:
'In ThisWorkbook
Public Sub Workbook_Open()
Run "AutoSaveAs"
End Sub
'In Module1
Public Sub AutoSaveAs()
Application.ScreenUpdating = False
Dim LDSP As String
Dim IsOTF
Dim LDS, TWB As Workbook
'Set LiveDealSheet file path
'Check if LiveDealSheet is already open
LDSP = "I:\LiveDealSheet\LiveDealSheet.xlsm"
IsOTF = IsWorkBookOpen(LDSP)
'Set quick workbook shortcut
Set TWB = ThisWorkbook
If IsOTF = False Then
Set LDS = Workbooks.Open(LDSP)
Else
Workbooks("LiveDealSheet.xlsm").Activate
Set LDS = ActiveWorkbook
End If
'Copy all data from LIVE Deals to TWB
LDS.Sheets("LIVE Deals").Cells.Select
Selection.Copy
TWB.Sheets(1).Cells.PasteSpecial (xlValues)
TWB.Sheets(1).Cells.PasteSpecial (xlFormats)
TWB.SaveAs FileName:="I:\LiveDealSheet\" & Format(Date, "yyyymmdd") & " LiveDealSheet", FileFormat:=52
If IsOTF = False Then
LDS.Close False
TWB.Close False
Else
TWB.Close False
End If
Application.ScreenUpdating = True
End Sub
Function IsWorkBookOpen(FileName As String)
Dim ff As Long, ErrNo As Long
On Error Resume Next
ff = FreeFile()
Open FileName For Input Lock Read As #ff
Close ff
ErrNo = Err
On Error GoTo 0
Select Case ErrNo
Case 0: IsWorkBookOpen = False
Case 70: IsWorkBookOpen = True
Case Else: Error ErrNo
End Select
End Function

Resources