I am trying to create a backup database on a network drive using fso.folder copy. My intention is to move all files within the folder, but if a file already exists on the backup drive, skip it, and copy the remainder of the files in the folder. I currently have
SourceFileName="C:\users\desktop\test1"
DestinFileName="C:\users\desktop\test2"
FSO.copyfolder Source:=Sourcefilename, Destination:=Destinfilename, OverwriteFiles:= False
However, the script errors when it finds the existing file. Any advice would be appreciated.
Copy Files Without Overwriting
I would recommend the first solution. The documentation is 'somewhat leading you on' (at least me) to use the second solution. It's up to you to find out if the second one is maybe more efficient. You cannot apply On Error on the folder part.
The Code
Option Explicit
Sub copyFilesNoOverwrite()
Const srcFolderPath As String = "C:\users\desktop\test1"
Const dstFolderPath As String = "C:\users\desktop\test2"
With CreateObject("Scripting.FileSystemObject")
If Not .FolderExists(srcFolderPath) Then
MsgBox "Source Folder doesn't exist.", vbCritical, "No Source"
Exit Sub
End If
If .FolderExists(dstFolderPath) Then
Dim Sep As String: Sep = Application.PathSeparator
Dim fsoFile As Object
Dim FilePath As String
For Each fsoFile In .GetFolder(srcFolderPath).Files
FilePath = dstFolderPath & Sep & fsoFile.Name
If Not .FileExists(FilePath) Then
.CopyFile _
Source:=fsoFile.Path, _
Destination:=FilePath
End If
Next fsoFile
Else
.CopyFolder _
Source:=srcFolderPath, _
Destination:=dstFolderPath
End If
End With
End Sub
Sub copyFilesNoOverwriteOnError()
Const srcFolderPath As String = "C:\users\desktop\test1"
Const dstFolderPath As String = "C:\users\desktop\test2"
With CreateObject("Scripting.FileSystemObject")
If Not .FolderExists(srcFolderPath) Then
MsgBox "Source Folder doesn't exist.", vbCritical, "No Source"
Exit Sub
End If
If .FolderExists(dstFolderPath) Then
Dim Sep As String: Sep = Application.PathSeparator
Dim fsoFile As Object
For Each fsoFile In .GetFolder(srcFolderPath).Files
On Error Resume Next
.CopyFile _
Source:=fsoFile.Path, _
Destination:=dstFolderPath & Sep & fsoFile.Name, _
OverwriteFiles:=False
On Error GoTo 0
Next fsoFile
Else
.CopyFolder _
Source:=srcFolderPath, _
Destination:=dstFolderPath
End If
End With
End Sub
Related
I wrote a script to download files using VBA. The VBA script has to download items that start with https://collaboration.company.corp/collrooms/specificfolder or with \collaboration.company.corp#SSL\DavWWWRoot\collrooms\specificfolder
The specific folders are the same.
If I allow the script to select the specific mapping, it will only recognize it if I use the definition \collaboration.company.corp#SSL\DavWWWRoot\collrooms\specificfolder
How can I create a mapping in VBA to tell Excel that https://collaboration.company.corp/collrooms/specificfolder and \collaboration.company.corp#SSL\DavWWWRoot\collrooms\specificfolder is the same and that the first specification is also valid?
My code:
Option Explicit
Sub FolderSelection()
'Shows the folder picker dialog in order the user to select the folder
'in which the downloaded files will be saved.
Dim FoldersPath As String
'Show the folder picker dialog.
With Application.FileDialog(msoFileDialogFolderPicker)
.Title = "Select a folder to save your files..."
.Show
If .SelectedItems.Count = 0 Then
Sheets("Main").Range("B4") = "-"
MsgBox "You did't select a folder!", vbExclamation, "Canceled"
Exit Sub
Else
FoldersPath = .SelectedItems(1)
End If
End With
'Pass the folder's path to the cell. HERE I AM MISSING THE MAPPING. It will show files starting with https if selected and not transfer it to the other structure.
Sheets("Main").Range("B4") = FoldersPath
End Sub
Sub Clear()
'Clears the URLs, the result column and the folder's path.
Dim LastRow As Long
'Find the last row.
With Sheets("Main")
.Activate
LastRow = .Cells(.Rows.Count, "C").End(xlUp).Row
End With
'Clear the ranges.
If LastRow > 7 Then
With Sheets("Main")
.Range("C8:D" & LastRow).ClearContents
.Range("B4:D4").ClearContents
.Range("B4").Select
End With
End If
End Sub
and the other part of the download macro is
'Check if the folder exists. I did not check whether it will also download with the https structure?
DownloadFolder = sh.Range("B4")
On Error Resume Next
If Dir(DownloadFolder, vbDirectory) = vbNullString Then
MsgBox "The path is incorrect!", vbCritical, "Folder's Path Error"
sh.Range("B4").Select
Exit Sub
End If
On Error GoTo 0
I tried with a script that I found on Stackoverflow but it does not work
I created an additional module:
Sub test()
Dim dm As New DriveMapper
Dim sharepointFolder As Scripting.Folder
Set sharepointFolder = dm.MapDrive("https://collaboration.company.corp/collrooms/")
' unsure whether I have to add something here and whether this will work with https
Debug.Print sharepointFolder.Path
End Sub
and added the following WebDAV mapping as a new CLASS
Option Explicit
Private oMappedDrive As Scripting.Drive
Private oFSO As New Scripting.FileSystemObject
Private oNetwork As New WshNetwork
Private Sub Class_Terminate()
UnmapDrive
End Sub
Public Function MapDrive(NetworkPath As String) As Scripting.Folder
Dim DriveLetter As String, i As Integer
UnmapDrive
For i = Asc("Z") To Asc("A") Step -1
DriveLetter = Chr(i)
If Not oFSO.DriveExists(DriveLetter) Then
oNetwork.MapNetworkDrive DriveLetter & ":", NetworkPath
Set oMappedDrive = oFSO.GetDrive(DriveLetter)
Set MapDrive = oMappedDrive.RootFolder
Exit For
End If
Next i
End Function
Private Sub UnmapDrive()
If Not oMappedDrive Is Nothing Then
If oMappedDrive.IsReady Then
oNetwork.RemoveNetworkDrive oMappedDrive.DriveLetter & ":"
End If
Set oMappedDrive = Nothing
End If
End Sub
The question is also whether removing the dispose method "Class_Terminate" which unmaps the drive would help? When the class goes out of scope then the drive get's unmapped. And how I could put it all together.
When saving a specific workbook, Excel creates a temp file instead of saving the data (without displaying an error or warning message). The symptoms are roughly the same as described in this post:
microsoft-excel-returns-the-error-document-not-saved-after-generating-a-2gb-temp-file
I tried several solutions, but decided to implement a work-around as ‘save as’ is working ok.
The code below performs the ‘save-as’, based on having filenames ending with a value (e.g. myFile V1.xlsm), the macro will add an incremental character (a to z) each time the workbook is saved. (e.g. myFile V1a.xlsm).
The macro works fine in a standard module, but it causes Excel to “stop responding” when moved to ‘thisWorkbook’. I ‘solved’ this by keeping it in the standard module and assigning key combination ‘control-s’ to the macro. Still interested to know if it can be made to work in the ‘thisWorkbook’.
Drawback of this workaround is that each incremental save clogs up the ‘recent file’ list. It would be nice to remove the previous file name from the recent file history, but this seems not possible to do via VBA. (VBA - How do I remove a file from the recent documents list in excel 2007?). Any suggestions?
Windows 10, Excel 2016 (version 16.0.6868.2060)
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
Dim newFilename As String
Dim oldFilename As String
oldFilename = ActiveWorkbook.Name
newFilename = Left(ActiveWorkbook.Name, Len(ActiveWorkbook.Name) - 5)
If IsNumeric(Right(newFilename, 1)) = True Then
ActiveWorkbook.SaveAs Filename:=ActiveWorkbook.Path + "\" + newFilename & "a.xlsm", FileFormat:=xlOpenXMLWorkbookMacroEnabled, CreateBackup:=False
Else
If Right(newFilename, 1) = "z" Then
MsgBox "'z' reached, please save as new version"
Exit Sub
End If
newFilename = Left(newFilename, Len(newFilename) - 1) & Chr(Asc(Right(newFilename, 1)) + 1)
ActiveWorkbook.SaveAs Filename:=ActiveWorkbook.Path + "\" + newFilename & ".xlsm", FileFormat:=xlOpenXMLWorkbookMacroEnabled, CreateBackup:=False
End If
'potential code to remove oldFilename from 'Recent File' list
End Sub
I tested this Sub in Excel 2010 and it works for me. I immediately break the loop after deleting the file as I assume the indexing may get out of alignment with the loop. A more refined variant might loop through the recent file list and create a collection of indices to delete, then iterate backward over that collection and delete each entry in turn.
Public Sub RemoveRecentFile(strFileName As String)
Dim collRecentFiles As Excel.RecentFiles
Dim objRecentFile As Excel.RecentFile
Dim intRecentFileCount As Integer
Dim intCounter As Integer
Set collRecentFiles = Application.RecentFiles
intRecentFileCount = collRecentFiles.Count
For intCounter = 1 To intRecentFileCount
Set objRecentFile = collRecentFiles(intCounter)
If objRecentFile.Name = strFileName Then
objRecentFile.Delete
Exit For
End If
Next intCounter
End Sub
Thanks to Robin the working solution is as follows:
Updated intial code:
Sub incrementSaveAs()
'to avoid that other workbooks are saved (when assigned to shortkey control-S)
If ActiveWorkbook.Name <> ThisWorkbook.Name Then ActiveWorkbook.Save: Exit Sub
Dim newFilename As String
Dim oldFilename As String
oldFilename = ActiveWorkbook.Name
newFilename = Left(ActiveWorkbook.Name, Len(ActiveWorkbook.Name) - 5)
If IsNumeric(Right(newFilename, 1)) = True Then
ActiveWorkbook.SaveAs Filename:=ActiveWorkbook.Path + "\" + newFilename & "a.xlsm", _
FileFormat:=xlOpenXMLWorkbookMacroEnabled, CreateBackup:=False, AddToMru:=True
'AddToMru:=True Added to update recent files history
Else
If Right(newFilename, 1) = "z" Then
MsgBox "'z' reached, please save as new version"
Exit Sub
End If
newFilename = Left(newFilename, Len(newFilename) - 1) & Chr(Asc(Right(newFilename, 1)) + 1)
ActiveWorkbook.SaveAs Filename:=ActiveWorkbook.Path + "\" + newFilename & ".xlsm", _
FileFormat:=xlOpenXMLWorkbookMacroEnabled, CreateBackup:=False, AddToMru:=True
End If
RemoveRecentFile (ActiveWorkbook.Path & Application.PathSeparator & oldFilename)
End Sub
Updated Robin's code:
Public Sub RemoveRecentFile(strPathAndFileName As String)
Dim collRecentFiles As Excel.RecentFiles
Dim objRecentFile As Excel.RecentFile
Dim intRecentFileCount As Integer
Dim intCounter As Integer
Set collRecentFiles = Application.RecentFiles
intRecentFileCount = collRecentFiles.Count
For intCounter = 1 To intRecentFileCount
Set objRecentFile = collRecentFiles(intCounter)
If objRecentFile.Path = strPathAndFileName Then
objRecentFile.Delete
Exit For
End If
Next intCounter
End Sub
I am trying to automate the tasks I would normally run through to compact my database, save backups, and update revision numbers for an automatic update system I am using. I am stuck on trying to make an accde file with a vba script.
Everything I find pertaining to the subject seems to point to using something like this.
function MakeACCDE(InPath As String, OutPath As String)
Dim app As New Access.Application
app.AutomationSecurity = msoAutomationSecurityLow
app.SysCmd 603, InPath, OutPath
End Function
A few users on various forums claim that this code works for them but I have not had any luck. My database runs the code without errors, but nothing actually happens.
Is there a particular piece of syntax I am not using or maybe something with the format of the file paths?
I found the following code at: http://www.experts-exchange.com/questions/28429044/How-do-I-create-an-Access-2010-accde-from-VBA.html
I inserted into my Access 2010 accdb, ran it, and it created an accde
**UPDATE: Seeing you want to run from a different DB, I tested that also... just change the line 'tmpDB_Full_Name = CurrentProject.FullName' to be your source database
Option Compare Database
Option Explicit
Function Create_MDE()
Dim tmpDB_Full_Name As String
Dim tmpDB_Name As String
Dim tmpDB_Backup_Full_Name As String
Dim tmpCopy_File As Variant
Dim tmpDirectory As String
'Call SetStartupOptions("AllowBypassKey", dbBoolean, False) '---This runs a procedure to deactivate the Shift & F11 key
'tmpDB_Full_Name = CurrentProject.FullName
tmpDB_Full_Name = "C:\data\access\MyDb.accdb"
tmpDirectory = CurrentProject.Path
tmpDB_Name = CurrentProject.Name
tmpDB_Backup_Full_Name = tmpDirectory & "\" & left(tmpDB_Name, Len(tmpDB_Name) - 6) & "-Backup.accdb"
'this removes a file created on the same day
If Dir(tmpDB_Backup_Full_Name) <> "" Then
Kill tmpDB_Backup_Full_Name
End If
'this creates a backup into destination tmpDirectory
If Dir(tmpDB_Backup_Full_Name) = "" Then
Set tmpCopy_File = CreateObject("Scripting.FileSystemObject")
tmpCopy_File.CopyFile tmpDB_Full_Name, tmpDB_Backup_Full_Name, True
End If
Dim app As New Access.Application
app.AutomationSecurity = msoAutomationSecurityLow
app.SysCmd 603, tmpDB_Backup_Full_Name, tmpDirectory & "\" & left(tmpDB_Name, Len(tmpDB_Name) - 9) & ".accde"
'Call SetStartupOptions("AllowBypassKey", dbBoolean, True) '---This runs a procedure to activate the Shift & F11
MsgBox ("Compile Complete!")
End Function
I have prepared a ready-made solution that creates an ACCDE file and at the same time allows you to protect it with a password. With frequent updates, it makes my life so much easier. I tested it on Microsoft Access 2016 and 2019.
The function SaveAccdbAsAccde() performs the following steps:
compiles and saves changes to the database
copies the database to '...(~temp~).ACCDB'
creates the file '...(~temp~).ACCDE'
if everything worked, it sets a password to the database and copies it as the target file
deletes working files
To protect the database with a password, do the following: SaveAccdbAsAccde("password")
I used a few functions that might also come in handy for other tasks:
helper functions based on Scripting.FileSystemObject for handling files use : CopyFile(),DeleteFile(),FileExists()
functions to secure / unsecure the database with a password EncryptDb() and DecryptDb()
All details below:
Option Explicit
'------------------------------------------------------------------------------------
'main function
Public Sub SaveAccdbAsAccde(Optional filePassword As String)
On Error Resume Next
Application.RunCommand acCmdCompileAndSaveAllModules
err.Clear
If err <> 0 Then MsgBox "Save changes in forms and reports before preparing the ACCDE file.": Exit Sub
On Error GoTo err_proc
Dim strFile0 As String, strFile1 As String, strFile2 As String, strFile3 As String
strFile0 = CurrentDb.Name
strFile1 = Replace(CurrentDb.Name, ".accdb", "(~temp~).accdb")
strFile2 = Replace(CurrentDb.Name, ".accdb", "(~temp~).accde")
strFile3 = Replace(CurrentDb.Name, ".accdb", ".accde")
If Not DeleteFile(strFile1) Then MsgBox "Can't felete file: " & strFile2: Exit Sub
If Not CopyFile(strFile0, strFile1) Then MsgBox "Can't copy file: " & strFile0 & " na " & strFile1: Exit Sub
If Not DeleteFile(strFile2) Then MsgBox "Can't delete file: " & strFile2: Exit Sub
MakeACCDESysCmd strFile1, strFile2
If Not FileExists(strFile2) Then MsgBox "Can't create file: " & strFile2: Exit Sub
If Not DeleteFile(strFile3) Then MsgBox "Can't delete file: " & strFile3: Exit Sub
EncryptDb strFile2, strFile3, filePassword
If Not FileExists(strFile3) Then MsgBox "Can't create file: " & strFile3: Exit Sub
If Not DeleteFile(strFile2) Then MsgBox "Can't delete file: " & strFile2: Exit Sub
If Not DeleteFile(strFile1) Then MsgBox "Can't delete file: " & strFile2: Exit Sub
MsgBox "Done: " & strFile3
exit_proc:
Exit Sub
err_proc:
MsgBox err.Description, vbCritical, "Error"
Resume exit_proc
End Sub
'------------------------------------------------------------------------------------
Public Sub EncryptDb(strSourcePath As String, strDestPath As String, pwd As String)
If pwd <> "" Then pwd = ";pwd=" & pwd
DBEngine.CompactDatabase strSourcePath, strDestPath, dbLangGeneral & pwd, dbVersion167, pwd
End Sub
Public Sub DecryptDb(strSourcePath As String, strDestPath As String, pwd As String)
If pwd <> "" Then pwd = ";pwd=" & pwd
DBEngine.CompactDatabase strSourcePath, strDestPath, dbLangGeneral & ";pwd=", dbVersion167, pwd
End Sub
Public Function MakeACCDESysCmd(InPath As String, OutPath As String)
Dim app As Access.Application
Set app = New Access.Application
app.AutomationSecurity = 1 'msoAutomationSecurityLow - Enables all macros. This is the default value when the application is started.
app.SysCmd 603, InPath, OutPath 'an undocumented action
app.Quit acQuitSaveNone
Set app = Nothing
End Function
'------------------------------------------------------------------------------------
Public Function CopyFile(strFromFile, strToFile)
On Error Resume Next
Dim objFSO
Set objFSO = CreateObject("Scripting.FileSystemObject")
err.Clear
objFSO.CopyFile strFromFile, strToFile, True
CopyFile = err = 0
Set objFSO = Nothing
End Function
Public Function DeleteFile(strFile)
If Not FileExists(strFile) Then DeleteFile = True: Exit Function
On Error Resume Next
Dim objFSO
Set objFSO = CreateObject("Scripting.FileSystemObject")
err.Clear
objFSO.DeleteFile strFile, True
DeleteFile = err = 0
Set objFSO = Nothing
End Function
Public Function FileExists(strFile)
On Error Resume Next
Dim objFSO
Set objFSO = CreateObject("Scripting.FileSystemObject")
On Error Resume Next
FileExists = objFSO.FileExists(strFile)
Set objFSO = Nothing
End Function
I have tested the following code in Access 2016 using ACCDE and ACCDR as the destination file extension:
Dim otherAccess As Access.Application
Set otherAccess = New Access.Application
otherAccess.AutomationSecurity = 1 'msoAutomationSecurityLow
otherAccess.SysCmd 603, InPath, OutPath
otherAccess.Quit acQuitSaveNone
Set otherAccess = Nothing
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