This question has been edited due to lengthy comments and updates from proposed answers.
As requested here is module 13;
Sub SaveInFormat()
Application.DisplayAlerts = False
Workbooks.Application.ActiveWorkbook.SaveAs Filename:="C:\Documents and Settings\jammil\Desktop\AutoFinance\ProjectControl\Data\" & Format(Date, "yyyymm") & "DB" & ".xlsx", leFormat:=51
Application.DisplayAlerts = True
End Sub
Also there are issues with the errorhandling, I know I've gone wrong with it but I'm more interested in fixing the close function at the moment before I get into it. Here is the error handling code that needs some work
Sub test()
Dim wk As String, yr As String, fname As String, fpath As String
Dim owb As Workbook
wk = ComboBox1.Value
yr = ComboBox2.Value
fname = yr & "W" & wk
fpath = "C:\Documents and Settings\jammil\Desktop\AutoFinance\ProjectControl\Data"
owb = Application.Workbooks.Open(fpath & "\" & fname)
On Error GoTo ErrorHandler:
ErrorHandler:
If MsgBox("This File Does Not Exist!", vbRetryCancel) = vbCancel Then Exit Sub Else Call Clear
'Do Some Stuff
Call Module13.SaveInFormat
owb.Close
this is your test code plus my changing of the file path and name
After discussion posting updated answer:
Option Explicit
Sub test()
Dim wk As String, yr As String
Dim fname As String, fpath As String
Dim owb As Workbook
With Application
.DisplayAlerts = False
.ScreenUpdating = False
.EnableEvents = False
End With
wk = ComboBox1.Value
yr = ComboBox2.Value
fname = yr & "W" & wk
fpath = "C:\Documents and Settings\jammil\Desktop\AutoFinance\ProjectControl\Data"
On Error GoTo ErrorHandler
Set owb = Application.Workbooks.Open(fpath & "\" & fname)
'Do Some Stuff
With owb
.SaveAs fpath & Format(Date, "yyyymm") & "DB" & ".xlsx", 51
.Close
End With
With Application
.DisplayAlerts = True
.ScreenUpdating = True
.EnableEvents = True
End With
Exit Sub
ErrorHandler: If MsgBox("This File Does Not Exist!", vbRetryCancel) = vbCancel Then
Else: Call Clear
End Sub
Error Handling:
You could try something like this to catch a specific error:
On Error Resume Next
Set owb = Application.Workbooks.Open(fpath & "\" & fname)
If Err.Number = 1004 Then
GoTo FileNotFound
Else
End If
...
Exit Sub
FileNotFound: If MsgBox("This File Does Not Exist!", vbRetryCancel) = vbCancel Then
Else: Call Clear
I'll try and answer several different things, however my contribution may not cover all of your questions. Maybe several of us can take different chunks out of this. However, this info should be helpful for you. Here we go..
Opening A Seperate File:
ChDir "[Path here]" 'get into the right folder here
Workbooks.Open Filename:= "[Path here]" 'include the filename in this path
'copy data into current workbook or whatever you want here
ActiveWindow.Close 'closes out the file
Opening A File With Specified Date If It Exists:
I'm not sure how to search your directory to see if a file exists, but in my case I wouldn't bother to search for it, I'd just try to open it and put in some error checking so that if it doesn't exist then display this message or do xyz.
Some common error checking statements:
On Error Resume Next 'if error occurs continues on to the next line (ignores it)
ChDir "[Path here]"
Workbooks.Open Filename:= "[Path here]" 'try to open file here
Or (better option):
if one doesn't exist then bring up either a message box or dialogue
box to say "the file does not exist, would you like to create a new
one?
you would most likely want to use the GoTo ErrorHandler shown below to achieve this
On Error GoTo ErrorHandler:
ChDir "[Path here]"
Workbooks.Open Filename:= "[Path here]" 'try to open file here
ErrorHandler:
'Display error message or any code you want to run on error here
Much more info on Error handling here: http://www.cpearson.com/excel/errorhandling.htm
Also if you want to learn more or need to know more generally in VBA I would recommend Siddharth Rout's site, he has lots of tutorials and example code here:
http://www.siddharthrout.com/vb-dot-net-and-excel/
Hope this helps!
Example on how to ensure error code doesn't run EVERYtime:
if you debug through the code without the Exit Sub BEFORE the error handler you'll soon realize the error handler will be run everytime regarldess of if there is an error or not. The link below the code example shows a previous answer to this question.
Sub Macro
On Error GoTo ErrorHandler:
ChDir "[Path here]"
Workbooks.Open Filename:= "[Path here]" 'try to open file here
Exit Sub 'Code will exit BEFORE ErrorHandler if everything goes smoothly
'Otherwise, on error, ErrorHandler will be run
ErrorHandler:
'Display error message or any code you want to run on error here
End Sub
Also, look at this other question in you need more reference to how this works:
goto block not working VBA
Related
I'm using Excel 2010. I have an Excel macro-enabled template that has a data connection to a text file that is set to automatically refresh when a new document is created using this template.
The following macro is within the "ThisWorkbook" object to remove the data connection before saving the new document:
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
Do While ActiveWorkbook.Connections.Count > 0
ActiveWorkbook.Connections.Item(ActiveWorkbook.Connections.Count).Delete
Loop
End Sub
When a user clicks the save icon / hits ctrl+S, inputs a filename and then clicks save to save as a macro-free Excel workbook (as is the default and required filetype) they are prompted with a message stating:
The following features cannot be saved in macro-free workbooks:
• VB project
To save a file with these features, click No, and then choose a
macro-enabled file type in the File Type list.
To continue saving as a macro-free workbook, click Yes.
Is it possible to prevent this message from appearing and have Excel assume that the user wants to continue with a macro-free workbook?
I've searched all over and understand that I may be able to add code to the workbook object that removes itself so that Excel has no VB project to cause this message but this would require each user to change Trust Center Settings (Trust access to the VBA project object model) which I want to avoid.
I've also seen suggestions of using:
Application.DisplayAlerts = False
but can't get this to work. Every example of it's use seems to be within a sub that is also handling the saving of the document whereas in my situation the BeforeSave sub ends before the document is saved in the default, non-vba way which is perhaps why it does not work?
Does this property reset to a default True after the sub has ended / before the save actually occurs?
Apologies for any nonsense I may have dispensed, my experience with VBA is very limited.
I cannot test on Excel 2010, but at least for 2016, it's working fine:
Sub SaveAsRegularWorkbook()
Dim wb As Workbook
Dim Path As String
Set wb = ThisWorkbook
Path = "T:\he\Path\you\prefer\"
Application.DisplayAlerts = False
Application.EnableEvents = False
wb.SaveAs Filename:=Path & "Test.xlsx", FileFormat:=51
Application.DisplayAlerts = True
Application.EnableEvents = True
End Sub
Give it a try.
Different approach... when the template is loaded, require the user to save as (I have a workbook/template with a similar situation...). This should open them up to the user's Documents folder, though you can adjust to save to whatever location.
Inside of the ThisWorkbook module, put:
Option Explicit
Private Sub Workbook_Open()
Dim loc As Variant
Application.DisplayAlerts = False
loc = Application.GetSaveAsFilename(FileFilter:="Excel Files (*.xlsx), *.xlsx", Title:="Save As...", InitialFileName:="%USERPROFILE%\Documents\NAME_OF_FILE")
If loc <> False Then
ActiveWorkbook.SaveAs Filename:=loc, FileFormat:=51
Exit Sub
End If
Application.DisplayAlerts = True
End Sub
Edit1: Adding the if statement using a base-template name, so subsequent saves do not prompt the save-as:
Option Explicit
Private Sub Workbook_Open()
If ActiveWorkbook.Name = "_NAME_OF_FILE.xlsb" Then
Dim loc As Variant
Application.DisplayAlerts = False
loc = Application.GetSaveAsFilename(FileFilter:="Excel Files (*.xlsx), *.xlsx", Title:="Save As...", InitialFileName:="%USERPROFILE%\Documents\_NAME_OF_FILE")
If loc <> False Then
ActiveWorkbook.SaveAs Filename:=loc, FileFormat:=51
Exit Sub
End If
Application.DisplayAlerts = True
End If
End Sub
For this answer, I'm assuming that by Excel macro-enabled template, you mean a xltm file. I also guess that what you mean by "new document" is the document that is generated when a user double-clicks on the xtlm file (hence this new file has no location on since it hasn't been saved yet).
To solve your issue, you could use a custom SaveAs window (Application.GetSaveAsFilename) to have more control on how the user saves the file when the Workbook_BeforeSave event macro gets called.
Here is how to implement it:
1 - Copy this code into a new module.
Option Explicit
Sub SaveAsCustomWindow()
Const C_PROC_NAME As String = "SaveAsCustomWindow"
Dim strFullFileName As String, strPreferedFolder As String, strDefaultName As String
Dim UserInput1 As Variant, UserInput2 As Variant
Dim isValidName As Boolean, isFileClosed As Boolean, isWorkbookClosed As Boolean
Dim strFilename As String, strFilePath As String
'To avoid Warning when overwriting
Application.DisplayAlerts = False
'Disable events (mostly for the BeforeSave event) to avoid creating infinite loop
Application.EnableEvents = False
On Error GoTo ErrHandler
'Customizable section
strDefaultName = ThisWorkbook.Name
strPreferedFolder = Environ("USERPROFILE")
Do While isWorkbookClosed = False
Do While isFileClosed = False
Do While isValidName = False
UserInput1 = Application.GetSaveAsFilename(InitialFileName:=strPreferedFolder & "\" & strDefaultName, FileFilter:="Excel Workbook (*.xlsx),*.xlsx")
If UserInput1 = False Then
GoTo ClosingStatements 'This is important to take care of the case when the user presses cancel
Else
strFullFileName = UserInput1
End If
strFilename = Right(strFullFileName, Len(strFullFileName) - InStrRev(strFullFileName, "\"))
strDefaultName = strFilename
strFilePath = Left(strFullFileName, InStrRev(strFullFileName, "\") - 1)
strPreferedFolder = strFilePath
'If the file exist, ask for overwrite permission
If Dir(strFullFileName) <> "" Then
UserInput2 = MsgBox(strFilename & " already exists." & vbNewLine & "Do you want to overwrite?", vbYesNoCancel Or vbExclamation)
If UserInput2 = vbNo Then
isValidName = False
ElseIf UserInput2 = vbYes Then
isValidName = True
ElseIf UserInput2 = vbCancel Then
GoTo ClosingStatements
Else
GoTo ClosingStatements
End If
Else
isValidName = True
End If
Loop
'Check if file is actually open
If isFileOpen(strFullFileName) Then
MsgBox "The workbook you want to overwrite is currently open. Choose a different name, or close the workbook before saving.", vbExclamation
isValidName = False
isFileClosed = False
Else
isFileClosed = True
End If
Loop
'Check if an opened workbook has the same name
If isWorkbookOpen(strFilename) Then
MsgBox "You cannot save this workbook with the same name as another open workbook or add-in. Choose a different name, or close the other workbook or add-in before saving.", vbExclamation
isValidName = False
isFileClosed = False
isWorkbookClosed = False
Else
isWorkbookClosed = True
End If
Loop
ThisWorkbook.SaveAs Filename:=strFullFileName, FileFormat:=xlOpenXMLWorkbook
ClosingStatements:
Application.EnableEvents = True
Application.DisplayAlerts = True
Exit Sub
ErrHandler:
Call MsgBox("Run-time error '" & Err.Number & "': " & Err.Description & vbNewLine & _
"While running: " & C_PROC_NAME & IIf(Erl <> 0, vbNewLine & "Error Line: " & Erl, "")
GoTo ClosingStatements
End Sub
Function isFileOpen(ByVal Filename As String) As Boolean
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: isFileOpen = False
Case 70: isFileOpen = True
End Select
End Function
Function isWorkbookOpen(ByVal Filename As String) As Boolean
Dim wb As Workbook, ErrNo As Long
On Error Resume Next
Set wb = Workbooks(Filename)
ErrNo = Err
On Error GoTo 0
Select Case ErrNo
Case 0: isWorkbookOpen = True
Case Else: isWorkbookOpen = False
End Select
End Function
Explanation of part 1: This whole thing might seem a bit overkill, but all the error handling is important here to take into account potential errors and make sure that the setting for Application.EnableEvents is turned back to TRUE even if an error occurs. Otherwise, all event macros will be disabled in your Excel application.
2 - Call the SaveAsCustomWindow procedure inside the Workbook_BeforeSave event procedure like this:
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
'Your code
If ThisWorkbook.Path = "" Then
SaveAsCustomWindow
Cancel = True
End If
End Sub
Note that we need to set the variable Cancel = True in order to prevent the default SaveAs window to show up. Also, the if statement is there to make sure that the custom SaveAs window will only be used if the file has never been saved.
To answer your questions:
Is it possible to prevent this message from appearing?
Yes, using the Application.DisplayAlerts property
Is it possible to have Excel assume that the user wants to continue with a macro-free workbook?
No, you have to write the procedure to save the workbook and bypass the SaveAs excel event and save the workbook using the user input (Path & Filename) with the required format.
The following procedure uses a FileDialog to capture the Path and Filename from the user then saves the file without displaying the warning message.
I have added some explanatory comments nevertheless, let me know of any questions you might have.
Copy these procedures in the ThisWorkbook module:
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
Cancel = True 'Prevents repetitive Save
Call Workbook_BeforeSave_ApplySettings_And_Save
End Sub
Private Sub Workbook_BeforeSave_ApplySettings_And_Save()
Dim fd As FileDialog, sFilename As String
Rem Sets FileDialog to capture user input
Set fd = Application.FileDialog(msoFileDialogSaveAs)
With fd
.InitialView = msoFileDialogViewDetails
.Title = vbNullString 'Resets default value in case it was changed
.ButtonName = vbNullString 'Resets default value in case it was changed
.AllowMultiSelect = False
If .Show = 0 Then Exit Sub 'User pressed the Cancel Button
sFilename = .SelectedItems(1)
End With
With ThisWorkbook
Do While .Connections.Count > 0
.Connections.Item(.Connections.Count).Delete
Loop
Application.EnableEvents = False 'Prevents repetition of the Workbook_BeforeSave event
Application.DisplayAlerts = False 'Prevents Display of the warning message
On Error Resume Next 'Prevents Events and Display staying disable in case of error
.SaveAs Filename:=sFilename, FileFormat:=xlOpenXMLWorkbook 'Saves Template as standard excel using user input
If Err.Number <> 0 Then
MsgBox "Run-time error " & Err.Number & String(2, vbLf) _
& Err.Description & String(2, vbLf) _
& vbTab & "Process will be cancelled.", _
vbOKOnly, "Microsoft Visual Basic"
End If
On Error GoTo 0
Application.DisplayAlerts = True
Application.EnableEvents = True
End With
End Sub
I can't find where the error is. A similar code to pull dynamic file name worked in another tab.
Sub MonthlyBCRCPL()
Dim filePath As String
Dim CardsRCPLWb As Workbook
Set CardsRCPLWb = ActiveWorkbook
filePath = CardsRCPLWb.Sheets("BCRCPL").Range("A1").Value
'Optimize Code
Call OptimizeCode_Begin
Const FlashFolder As String = "\\apacdfs\SG\GCGR\GROUPS\ASEAN\Dashboard\Cards\Flash\"
Flashname = Format(CardsRCPLWb.Sheets("ASEAN - CARDS, RCPL").Range("C2").Value, "YYYYMMDD")
Flashname = "ASEAN SD Regional Dashboard - " & Flashname & ".xlsx"
Flashpath = FlashFolder & Flashname
Dim FlashWb As Workbook
Set FlashWb = Workbooks.Open(Flashpath)
If FlashWb Is Nothing Then MsgBox "SD Flash File does not exist": Exit Sub
Consider handling the error in the subroutine and have it raise a message. Then, properly continues/skips/exits rest of code even releasing objects from memory caught during the exception. This is a best practice in VBA (and generally in programming).
I suspect the path cannot be found which looks to be a network UNC, file naming is not valid such as use of special characters, or workbook does not exist when trying to open:
Sub MonthlyBCRCPL()
On Error Goto ErrHandle:
...code...
ExitSubBlock:
Set CardsRCPLWb = Nothing
Set FlashWb = Nothing
Exit Sub
ErrHandle:
Msgbox Err.Number & " - " & Err.Description, vbCritical, "RUNTIME ERROR"
Goto ExitSubBlock
' Resume Next
End Sub
First of all, thanks for all the answers I have gotten on my previous questions, you really helped me out. The excel has evolved and now I'm ready to open different excel sheets in the background and print out different sheets on different printers. However, I'm working on a network that changes it's settings (which appear to change randomly).
Sub Client_Overzetten()
Application.ScreenUpdating = False
'
Workbooks.Open ("G:\Moe\WD\Planning&Control\Client.xlsm")
....etc...
However, if my colleague would try to open this file, he will get an error, as the same document has a different link (due to access restrictions).
His link is
G:\WD\Planning&Control\Client.xlsm")
Is there a formula to go to another location the moment it hits an error? Something like:
Sub Kids_II_Overzetten()
'
Application.ScreenUpdating = False
'
Workbooks.Open ("G:\Moe\WD\Planning&Control\Client.xlsm")
If error, then
Workbooks.Open ("G:\WD\Planning&Control\Client.xlsm")
I have the same problem with the serverports of the printer, these ports change randomly
ActivePrinter = "\\w8vvmprint01\Moecombi07 op Ne07:"
However, the next day it can be the same, or can be a different port
ActivePrinter = "\\w8vvmprint01\Moecombi07 op Ne03:"
With the solving of the problem of my first question, can I answer my second question as well (on error, go to the next line)?
Thanks in advance :)
For the network locations you'll need to use the UNC path which will not change rather than the mapped path which can change on different computers.
To find your UNC paths open a command prompt (Run - cmd.exe) and type in net use.
The resulting table will give the local and remote names of the drives- just replace your mapped (local) connection with the remote one.
For example,
G:\Moe\WD\Planning&Control\Client.xlsm
may become
\\MyServerName\Moe\WD\Planning&Control\Client.xlsm
Edit - the server name can also be found on the file explorer - windows key + E to open.
It will appear in the folder name as Moe on 'MyServerName' (G:)
To only use the mapped locations you could try:
Sub Test()
Dim wrkBk As Workbook
Dim sFileLocation As String
On Error GoTo ERROR_HANDLER
sFileLocation = "S:\Bartrup-CookD_SomeLocation\New Microsoft Excel Worksheet.xlsx"
Set wrkBk = Workbooks.Open(sFileLocation)
On Error GoTo 0
Exit Sub
ERROR_HANDLER:
Select Case Err.Number
Case 1004 'Microsoft Excel cannot access the file
sFileLocation = "S:\Bartrup-CookD\New Microsoft Excel Worksheet.xlsx"
Resume
Case Else
MsgBox "Error " & Err.Number & vbCr & _
" (" & Err.Description & ") in procedure Test."
Err.Clear
Application.EnableEvents = True
End Select
End Sub
or ask the user to select the correct file:
Public Sub AskForFile()
Dim vFile As Variant
Dim wrkBk As Workbook
vFile = GetFile("S:\Bartrup-CookD\")
If vFile <> "" Then
Set wrkBk = Workbooks.Open(vFile)
End If
End Sub
Public Function GetFile(Optional startFolder As Variant = -1) As Variant
Dim fle As FileDialog
Dim vItem As Variant
Set fle = Application.FileDialog(msoFileDialogFilePicker)
With fle
.Title = "Select a File"
.AllowMultiSelect = False
.Filters.Add "Excel Files", "*.xls*", 1
If startFolder = -1 Then
.InitialFileName = Application.DefaultFilePath
Else
If Right(startFolder, 1) <> "\" Then
.InitialFileName = startFolder & "\"
Else
.InitialFileName = startFolder
End If
End If
If .Show <> -1 Then GoTo NextCode
vItem = .SelectedItems(1)
End With
NextCode:
GetFile = vItem
Set fle = Nothing
End Function
Admittedly I'm bad at knowing the lingo, so while I think I researched this thoroughly, there may be the perfect answer somewhere. Here's my dilemma, I'm developing this Excel VBA macro to backup and restore the Worksheet (basically giving me infinite Undos to the point I specify and short-cutting around saving and reopening):
Public BULast As String
Sub Backup()
'This macro imitates videogame save-states. It will save a backup that can replace to current workbook later if you've made an irreversible mistake.
'Step 1: Agree to run away if things go wrong (establish an error handler)
On Error GoTo BackupError
'Step 2: Create some variables
Dim OriginalFile As String
Dim BUDir As String
Dim BUXAr() As String
Dim BUExt As String
Dim BUNam As String
Dim BackupFile As String
'Step 3: Define those variables
OriginalFile = ActiveWorkbook.FullName
BUDir = ActiveWorkbook.Path
BUXAr = Split(ActiveWorkbook.FullName, ".")
BUExt = BUXAr(UBound(BUXAr))
BUNam = Replace(ActiveWorkbook.Name, "." & BUExt, "") & " (Back-Up)"
BackupFile = BUDir & "\" & BUNam & "." & BUExt
'Step 4: Hide the truth
Application.ScreenUpdating = False
Application.DisplayAlerts = False
'Step 5(A): If there is no backup file, create one using the same file name as the one you're working with and throw a " (Back-up)" on it.
If Dir(BackupFile) = "" Then
ActiveWorkbook.SaveAs filename:=BackupFile
ActiveWorkbook.Close
Workbooks.Open filename:=OriginalFile
BUYoN = vbNo
BULast = Date & ", " & Time
MsgBox "A Backup has been created!"
Else
BUYoN = MsgBox("This will restore the " & BULast & " backup and undo all changes made to this project. Continue?" _
, vbYesNo, "Revert to Backup?")
End If
'Step 5(B): If a backup has been created, restore it over the current workbook and delete the backup.
If BUYoN = vbYes Then
ActiveWorkbook.Close
Workbooks.Open filename:=BackupFile
ActiveWorkbook.SaveAs filename:=OriginalFile
Kill (BackupFile)
BUCheck = "Dead"
End If
'Step 6: Put things back to the way you found them, you're done!
Application.ScreenUpdating = True
Application.DisplayAlerts = True
Exit Sub
'Step 1 (Continued): If nothing went wrong, stop worrying about it, if something did, say it didn't work and go away.
On Error GoTo 0
BackupError:
MsgBox "Attempt to Backup or Restore was unsuccessful"
End Sub
Normally it works as expected, but just yesterday it started not working and after playing around with it I realized it's because I was trying it on a file that has an Ω symbol in the file name.
The basic process is to look in the current directory for the active workbook's file name, but with (Back-up) tacked at the end. It will either create one, or replace the open one with what it finds. When done on an Ω file however, it substitutes that character with an O. When ran again, it apparently searches the the Ω correctly because it can't find any (even with the O-substitute file right there in plain sight).
I know the easiest solution would be to just make sure people keep their file names to what you can see on a keyboard, but that doesn't work for me; I'm almost religious about putting the adaptability in the code rather than the user. So with that long-winded back story, here's my specific question:
Is there a SaveAs function or practical workaround in VBA that can handle special characters within the specified file name?
The problem lies in the Dir() function as it converts the special characters to ANSI before checking for the file and thus fails for these cases. Use the FileSystemObject object instead:
Sub Backup()
On Error GoTo BackupError
Dim OriginalFile As String
OriginalFile = ActiveWorkbook.FullName
' get back up file name
Dim BackupFile As String
Dim pos As Integer
pos = InStrRev(OriginalFile, ".")
BackupFile = Mid$(OriginalFile, 1, pos - 1) & " (Back-Up)." & Mid$(OriginalFile, pos + 1)
Application.ScreenUpdating = False
Application.DisplayAlerts = False
'Step 5(A): If there is no backup file, create one using the same file name as the one you're working with and throw a " (Back-up)" on it.
Dim BUYoN As VbMsgBoxResult
Dim BULast As String
Dim fs As Object
Set fs = CreateObject("Scripting.FileSystemObject")
With fs
If Not .FileExists(BackupFile) Then
ActiveWorkbook.SaveAs Filename:=BackupFile
ActiveWorkbook.Close
Workbooks.Open Filename:=OriginalFile
BUYoN = vbNo
BULast = Date & ", " & Time
MsgBox "A Backup has been created!"
Else
BUYoN = MsgBox("This will restore the " & BULast & " backup and undo all changes made to this project. Continue?" _
, vbYesNo, "Revert to Backup?")
End If
End With
'Step 5(B): If a backup has been created, restore it over the current workbook and delete the backup.
If BUYoN = vbYes Then
ActiveWorkbook.Close
Workbooks.Open Filename:=BackupFile
ActiveWorkbook.SaveAs Filename:=OriginalFile
'Kill (BackupFile)
fs.Delete BackupFile
Dim BUCheck As String
BUCheck = "Dead"
End If
'Step 6: Put things back to the way you found them, you're done!
Application.ScreenUpdating = True
Application.DisplayAlerts = True
Exit Sub
On Error GoTo 0
BackupError:
MsgBox "Attempt to Backup or Restore was unsuccessful"
End Sub
I know we're not supposed to offer opinions, but it is my opinion that Rachel is a genius! I didn't know about the FileSystemObject, but that ended up being the key. Not only was it able to search for and recognized the file with special characters, but it appears it can delete it too. Incorporating that into the code makes it run flawlessly with or without special characters:
Public BULast As String
Sub Backup()
'This macro imitates videogame save-states. It will save a backup that can replace the
'current workbook later if you've made an irreversible mistake.
'Step 1: Agree to run away if things go wrong (establish an error handler)
On Error GoTo BackupError
'Step 2: Create some variables
Dim OriginalFile As String
Dim BUDir As String
Dim BUXAr() As String
Dim BUExt As String
Dim BUNam As String
Dim BackupFile As String
Dim BUfs As Object
'Step 3: Define those variables
OriginalFile = ActiveWorkbook.FullName
BUDir = ActiveWorkbook.Path
BUXAr = Split(ActiveWorkbook.FullName, ".")
BUExt = BUXAr(UBound(BUXAr))
BUNam = Replace(ActiveWorkbook.Name, "." & BUExt, "") & " (Back-Up)"
BackupFile = BUDir & "\" & BUNam & "." & BUExt
Set BUfs = CreateObject("Scripting.FileSystemObject")
'Step 4: Hide the truth
Application.ScreenUpdating = False
Application.DisplayAlerts = False
'Step 5(A): If there is no backup file, create one using the same file name as the one
'you're working with and throw a " (Back-up)" on it.
With BUfs
If Not .FileExists(BackupFile) Then
ActiveWorkbook.Save
ActiveWorkbook.SaveAs filename:=BackupFile
ActiveWorkbook.Close
Workbooks.Open filename:=OriginalFile
BUYoN = vbNo
BULast = Date & ", " & Time
MsgBox "A Backup has been created!"
Else
BUYoN = MsgBox("This will restore the " & BULast & " backup and undo all changes made to this project. Continue?" _
, vbYesNo, "Revert to Backup?")
End If
End With
'Step 5(B): If a backup has been created, restore it over the current workbook and
'delete the backup.
If BUYoN = vbYes Then
ActiveWorkbook.Close
Workbooks.Open filename:=BackupFile
ActiveWorkbook.SaveAs filename:=OriginalFile
BUfs.DeleteFile BackupFile, True
End If
'Step 6: Put things back to the way you found them, you're done!
Application.ScreenUpdating = True
Application.DisplayAlerts = True
Exit Sub
'Step 1 (Continued): If nothing went wrong, stop worrying about it, if something did,
'say it didn't work and go away.
On Error GoTo 0
BackupError:
MsgBox "Attempt to Backup or Restore was unsuccessful"
End Sub
The following VBA code is meant to run on MS Office 2003. Because that's what our multi-billion dollar corporation gives us to work with. =)
The good news. It works perfectly if I'm editing code in the IDE and hit save. Same if I'm working on the spreadsheet itself. Creates a backup folder if none exists, and saves a dated backup copy in it.
The bad news. When I run the main macro (too large to post), the code below executes but does not save a backup copy. The event is called correctly. In fact, it will create a backup folder if none exists. Every line gets run. The variables are all correct. Error handling works.
Simply put, ThisWorkbook.SaveCopyAs won't work if the main macros is running and calls ThisWorkbook.Save.
I only learned VBA a couple months ago for this particular project, so apologies if there is something obvious. However, I read all the relevant MSDN documentation and Googled like mad, but nothing came up.
Thanks in advance for your assistance.
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
'********************************************************************
'Purpose: Triggered by ThisWorkbook.BeforeSave event
' Creates backup folder and saves date appended copies
'********************************************************************
Dim strBackupPath As String 'Path to Backup Folder
Dim strFarkPath As String 'Path to running workbook
Dim strBackupName As String 'Filename of backup
Dim strFullName As String 'Full path & filename of running workbook
Dim strBackupExtension As String 'Extension of backup
Dim strDestination As String 'Full path & filename of backup
Dim strDrive As String 'Drive letter
strFarkPath = Application.ActiveWorkbook.Path
strDrive = Left(strFarkPath, 1)
strBackupPath = strFarkPath & "\_Backups"
strBackupName = "\Backup-" & Year(Now) & "-" & Month(Now) & "-" & Day(Now)
strFullName = Application.ActiveWorkbook.FullName
strBackupExtension = Right(strFullName, Len(strFullName) - InStrRev(strFullName, ".", -1, vbTextCompare) + 1)
strDestination = strBackupPath & strBackupName & strBackupExtension
On Error GoTo Incorrect
If Len(Dir(strBackupPath, vbDirectory)) = 0 Then
MkDir strBackupPath
End If
Application.DisplayAlerts = False
ThisWorkbook.SaveCopyAs Filename:=strDestination
Application.DisplayAlerts = True
Exit Sub
Incorrect:
MsgBox "Unable to back record keeper up. Next time, please run the program from a location where you can read and write files.", vbCritical + vbOKOnly
End Sub
Here's the last part of your existing sub, modified to create a copy.
Note you cannot use the built-in FileCopy to make the copy (you'll get "Permission Denied")
On Error GoTo Incorrect
If Len(Dir(strBackupPath, vbDirectory)) = 0 Then
MkDir strBackupPath
End If
Application.DisplayAlerts = False
Application.EnableEvents = False
ThisWorkbook.Save
CreateObject("scripting.filesystemobject").copyfile _
ThisWorkbook.FullName, strDestination
Application.EnableEvents = True '<<<<
Application.DisplayAlerts = True
Exit Sub
Incorrect:
Application.EnableEvents = True 'never leave this False!
MsgBox "Unable to back record keeper up. Next time, please run the program from a location where you can read and write files.", vbCritical + vbOKOnly
End Sub