Ensure Excel file is not being used before opening using VBA - excel

I am using an interface made with Excel to allow users to concurrently edit a shared data file. To prevent multiple users from editing at the same time I made the following function to do the following:
Open the file
If the file was opened as a read-only, close the file and re-open until the file is opened as read-write or the maximum number of allowed attempts is crossed.
Function OpenTillCanEditC(refpath As String, pw As String) As Workbook
Dim wbtoopen As Workbook
Dim maxOpen As Long
Dim i As Long
Dim buttonClicked As Long
maxOpen = 10
i = 0
On Error GoTo ErrHandler:
Application.DisplayAlerts = False
Set wbtoopen = Workbooks.Open(refpath, True, Password:=pw, ReadOnly:=False)
Application.DisplayAlerts = True
While wbtoopen.ReadOnly And i < maxOpen
If wbtoopen.ReadOnly Then
wbtoopen.Close (False)
Application.Wait (Now + TimeValue("00:00:01"))
Set wbtoopen = Nothing
i = i + 1
If i >= maxOpen Then
buttonClicked = MsgBox("It appears the masterlist is currently being used by someone else. Do you want to retry opening?", vbRetryCancel)
If buttonClicked = vbRetry Then
maxOpen = maxOpen + 10
Application.DisplayAlerts = False
Set wbtoopen = Workbooks.Open(refpath, True, Password:=pw, ReadOnly:=False)
Application.DisplayAlerts = True
End If
Else
On Error GoTo ErrHandler:
Application.DisplayAlerts = False
Set wbtoopen = Workbooks.Open(refpath, True, Password:=pw, ReadOnly:=False)
Application.DisplayAlerts = True
End If
End If
Wend
Set OpenTillCanEditC = wbtoopen
Exit Function
ErrHandler:
Application.DisplayAlerts = True
If Err.Number = 1004 Then
MsgBox "The password keyed in is wrong."
Else
MsgBox "The masterlist found in " & refpath & " cannot be opened. It may be used by someone else or corrupted. If corrupted please open the file manually using Excel."
End If
Set wbtoopen = Nothing
Set OpenTillCanEditC = wbtoopen
End Function
OpenTillCanEditC will be used in a sub for updating as shown below:
Sub UpdateFile()
'Try to open
Dim datawb As Workbook
Dim filepath As String
Dim pw As String
filepath = "C:\Folder Containing File\Data File.xlsx"
pw = "password"
Set datawb = OpenTillCanEditC(filepath, pw)
If datawb Is Nothing Then
MsgBox "File cannot be opened or is currently in use."
Exit Sub
End If
'Do functions needed in the workbook here
datawb.Save()
datawb.Close
End Sub
However I keep getting either of the following two errors:
When multiple people are attempting to access the file, the OpenTillCanEditC function will still ask for a password even though it is already being keyed in.
datawb.Save() will sometimes throw an error stating that the save failed due to multiple users accessing the file.
How do I fix both of these issues to allow multiple users to edit a shared password-protected file using VBA?

Related

From MS Access VBA close encrypted excel after failed password

I'm keeping an ID for API in an encrypted excel file (open to alternative suggestions) and using Microsoft access VBA to open the encrypted excel and extract the ID.
The issue is that it will not close excel if the password is incorrect. This code works fine if you enter the password correctly
Public Function getDeploymentID() As String
Dim fileLocation As String
fileLocation = "___DeploymentID.xlsx"
Dim objExcel As Object
Set objExcel = CreateObject("Excel.Application")
Dim wb As Excel.Workbook
On Error GoTo getDeploymentID_ERROR
MsgBox "The development password is in a password protected excel. It will prompt you for the password next"
Set wb = Workbooks.Open(fileLocation, True)
'User must enter password to continue. If they don't it'll error out on above line
DoEvents
'Get deploymentID
getDeploymentID = wb.Worksheets("Sheet1").Cells(1, 1)
'Close it
'wb.Close 'will close workbook, won't close excel
wb.Application.Quit 'will close workbook and excel
DoEvents
GoTo getDeploymentID_Cleanup
getDeploymentID_ERROR:
Debug.Print "Failed to open DeploymentID excel file. Error " & err.Number & ":" & err.description
objExcel.Quit 'THIS IS NOT WORKING
DoEvents
getDeploymentID_Cleanup:
Set wb = Nothing
Set objExcel = Nothing
End Function
I believe you need to access the Workbooks collection through the objExcel.
Set wb = objExcel.Workbooks.Open(fileLocation, True)
Then,
wb.Close 'close workbook
objExcel.Quit 'quit excel app
References:
Workbook.Close
Application.Quit
Regarding the structure of the function, I would add the error handling at the bottom and call Resume to avoid the 2nd GoTo statement.
'...
On Error GoTo getDeploymentID_ERROR
'...
'Get deploymentID
getDeploymentID = wb.Worksheets("Sheet1").Cells(1, 1)
getDeploymentID_Cleanup:
wb.Close
objExcel.Quit
Exit Function
getDeploymentID_ERROR:
Debug.Print "Failed to open DeploymentID excel file. Error " & err.Number & ":" & err.description
Resume getDeploymentID_Cleanup
End Function

VBA overwrite file if already exists

I'd like to check if a file located in my_path exists, if it does overwrite it with a file declared "garcat" containing 1 single sheet. Creating a file for the 1st time works, however overwriting it gives me this error 1004: Method 'SaveAs' of object'_Workbook' failed
Sub FileCreate(ByVal TGName As String)
Dim garcat As Workbook
Dim file As String
file = my_path
If Dir(file) <> "" Then
SetAttr file, vbNormal
Kill file
MsgBox "The file already exists, it will be replaced"
End If
Application.SheetsInNewWorkbook = 1
Set garcat = Workbooks.Add
garcat.SaveAs FileName:=file
MsgBox "File Saved"
garcat.Sheets(1).name = "GARCAT " & TGName
garcat.Close (True)
End Sub
EDIT: I set Application.DisplayAlerts = False and Application.EnableEvents = False before saving the file and I still get the pop-up error message.
This works by just telling the file to save regardless of any other factors: "And no back-talk! If there's already a file there... Just overwrite it."
Sub FileCreate(ByVal TGName As String)
Dim garcat As Workbook
Dim chemin As String
file = my_path
Application.SheetsInNewWorkbook = 1
Set garcat = Workbooks.Add
Application.DisplayAlerts = False
garcat.SaveAs FileName:=file
Application.DisplayAlerts = True
MsgBox "File Saved"
garcat.Sheets(1).name = "GARCAT " & TGName
garcat.Close (True)

How do you run a Word mail merge macro from Excel?

I have a macro in an Excel workbook that currently does the following:
Create a data.csv file with data in the first two rows (for a mail merge)
Pull a template of a selected Word document and make the data.csv file the source for the mail merge
If the user chooses, it finishes the merge for the document
If the user chooses, it opens the document when the macro is complete. If they don't choose to open, the word documents all close.
I've been running into a couple major issues:
The macro only seems to run smoothly if Word is entirely closed beforehand. My current workaround is a popup message if Word is open, telling the user to close word, but this is not ideal because it disrupts flow for some users who may have several instances of Word open.
The macro has been running slowly, especially for some of the document templates that have thousands of merge fields pre-entered in the template. It sometimes take longer than a minute, and sometimes completely freezes.
Would the macro would run more smoothly if I have the Excel VBA open the Word template and have most of the code for setting up and finishing the mail merge in Word VBA? I'm much less familiar with Word VBA - can anyone help me with bringing over my code to word (but still initiated by Excel)? Also, if you can figure out why the macro struggles when Word is already open, I'd greatly appreciate it.
I didn't include the entire code for proprietary reasons, but please let me know if there's something else you need to see.
Thank you!!
Sub Mail_Merge_Dynamic()
Dim mergeFile, tempFilePath As String
Dim WordDoc, WordApp As Object
Dim tempPath, mergePath, finalPath, curDir As String
Dim mergeFilePath, finalFilePath As String
Dim dataPath, FileNameCell, PrincCertCell, MMPrefix As String
Dim FileCount As Integer
Dim Close_Choice, ActiveWindow As String
Dim WarningMsg, WarningMsg2 As String
Dim NotFound, Overwrite1, Overwrite2 As Boolean
Dim oBook As Workbook
'Update csv file for Data Merge
narrative_merge
Call WarpSpeed_On
Sheets("Navigation").Select
Range("Merge_File_1").Select
Set WordApp = CreateObject("Word.Application")
'//////////////////////////////MAIL MERGE MACRO\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
'Set up Mail Merge Documents from Template Folder based on selections on Navigation tab
For i = 1 To FileCount
FileNameCell = "Merge_File_" & i
If Range(FileNameCell) = "" Then
Else
mergeFile = Range(FileNameCell)
tempFilePath = tempPath & mergeFile
mergeFilePath = mergePath & "MM_" & mergeFile
finalFilePath = finalPath & mergeFile
'Activate Mail Merge
If Range("MM_Activate") = 0 Then
Else
Set WordDoc = WordApp.Documents.Open(tempFilePath)
With WordDoc.MailMerge
.MainDocumentType = wdFormLetters
'Set up the mail merge data source
dataPath = curDir & "\data.csv"
.OpenDataSource Name:=dataPath
'Show values in the mail merge fields
.ViewMailMergeFieldCodes = wdToggle
End With
'WordDoc.ShowFieldCodes = False
'WordDoc.MailMerge.ViewMailMergeFieldCodes = False
WordDoc.SaveAs FileName:=mergeFilePath
End If
' Finish mail merge
If Range("MM_Finish") = 0 Then
Else
With WordDoc.MailMerge
.Destination = wdSendToNewDocument
.SuppressBlankLines = True
.Execute Pause:=False
End With
WordDoc.Application.ActiveDocument.SaveAs finalFilePath
End If
End If
Next i
Call CloseWordDocuments
'Make word visible if an Open command has been selected
If Range("MM_Open_Merge") = 1 Or Range("MM_Open_Doc") = 1 Then
curDir = ThisWorkbook.Path
Set WordApp = CreateObject("Word.Application")
For i = 1 To FileCount
FileNameCell = "Merge_File_" & i
If Range(FileNameCell) = "" Then
Else
mergeFile = Range(FileNameCell)
mergeFilePath = curDir & "\Merge-Active Forms\" & "MM_" & mergeFile
finalFilePath = curDir & "\Merge-Complete Forms\" & mergeFile
If Range("MM_Open_Merge") = 1 Then
Set WordDoc = WordApp.Documents.Open(mergeFilePath)
End If
If Range("MM_Open_Doc") = 1 Then
Set WordDoc = WordApp.Documents.Open(finalFilePath)
End If
End If
Next i
WordApp.Visible = True
'Windows(mergeFile).Activate
End If
GoTo Reset
Reset:
Call WarpSpeed_Off
End Sub
Sub WarpSpeed_On_Calcs_Off()
'Optimize Macro Speed
Application.ScreenUpdating = False
Application.EnableEvents = False
Application.Calculation = xlCalculationManual
' Turn off display alerts
Application.DisplayAlerts = False
End Sub
Sub WarpSpeed_On()
'Optimize Macro Speed
Application.ScreenUpdating = False
Application.EnableEvents = False
' Turn off display alerts
Application.DisplayAlerts = False
End Sub
Sub WarpSpeed_Off()
'Reset Macro Optimization Settings
Application.EnableEvents = True
Application.ScreenUpdating = True
Application.DisplayAlerts = True
Application.Calculation = xlCalculationAutomatic
End Sub
Instead of:
Set WordApp = CreateObject("Word.Application")
this will open Word if it is not already open:
Set WordApp = GetObject(, "Word.Application")
Edit#1
In VBA you may do something like:
On Error GoTo CreateObj
' Is Word application already running ?
Set WordApp = GetObject(, "Word.Application")
GoTo gotApp
CreateObj:
' Not running, create first instance:
Set WordApp = CreateObject("Word.Application")
gotApp:
On Error GoTo 0 ' disable error handling
' continue
....
....

How To Search And Replace Across Multiple Files In Word?

I have a folder with a few hundred-word documents. I want to be able to replace the words ORG NAME by clicking a macro, filling in an input box and letting it iterate. There are thousands of instances of ORG NAME across these documents and this process needs to happen a few dozen times a year.
We've got some challenges with trust centre policies in place that can't be changed so the macro needs to be done via excel.
The below was sort of working as word macro although it was crashing a lot, I moved it over to excel and now I'm getting the error: Named argument not found against macroname:
I can't find any similar questions that aren't solved by correcting spelling.
I'm also open to better solutions if they exist, this has been my first attempt so far.
Sub Button1_Click()
Dim xFileDialog As FileDialog, GetStr(1 To 300) As String '300 files is the maximum applying this code
Dim xFindStr As String
Dim xReplaceStr As String
Dim xDoc As Object
On Error Resume Next
Set xFileDialog = Application.FileDialog(msoFileDialogFilePicker)
With xFileDialog
.Filters.Clear
.Filters.Add "All WORD File ", "*.docx", 1
.AllowMultiSelect = True
i = 1
If .Show = -1 Then
For Each stiSelectedItem In .SelectedItems
GetStr(i) = stiSelectedItem
i = i + 1
Next
i = i - 1
End If
Application.ScreenUpdating = False
xFindStr = "ORG NAME"
xReplaceStr = InputBox("Enter the name of the organisation:", "Document Updater for Word", xReplaceStr)
For j = 1 To i Step 1
Set xDoc = Documents.Open(Filename:=GetStr(j), Visible:=True)
Windows(GetStr(j)).Activate
Selection.Find.ClearFormatting
Selection.Find.Replacement.ClearFormatting
With Selection.Find
.Text = xFindStr 'Find What
.Replacement.Text = xReplaceStr 'Replace With
.Forward = True
.Wrap = wdFindAsk
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchByte = True
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
End With
Selection.Find.Execute Replace:=wdReplaceAll
Application.Run macroname:="NEWMACROS"
ActiveDocument.Save
ActiveWindow.Close
Next
Application.ScreenUpdating = True
End With
MsgBox "Operation end, please view", vbInformation
End Sub
See this page by Ibby on the Word MVP website.
How to Find & ReplaceAll on a batch of documents in the same folder
The following code, if stored in a Global template, will perform a
Find & ReplaceAll in all of the documents in a specified folder. The
FindReplace dialog is displayed for the first document only. The user
sets the parameters in the dialog and presses Replace All and then
Close. The user is then asked whether to process all of the files in
the specified directory – if Yes, the rest of the files are processed
with the settings as entered in the original FindReplace dialog.
Option Explicit
Public Sub BatchReplaceAll()
Dim FirstLoop As Boolean
Dim myFile As String
Dim PathToUse As String
Dim myDoc As Document
Dim Response As Long
PathToUse = "C:\Test\"
'Error handler to handle error generated whenever
'the FindReplace dialog is closed
On Error Resume Next
'Close all open documents before beginning
Documents.Close SaveChanges:=wdPromptToSaveChanges
'Boolean expression to test whether first loop
'This is used so that the FindReplace dialog will
'only be displayed for the first document
FirstLoop = True
'Set the directory and type of file to batch process
myFile = Dir$(PathToUse & "*.doc")
While myFile <> ""
'Open document
Set myDoc = Documents.Open(PathToUse & myFile)
If FirstLoop Then
'Display dialog on first loop only
Dialogs(wdDialogEditReplace).Show
FirstLoop = False
Response = MsgBox("Do you want to process " & _
"the rest of the files in this folder", vbYesNo)
If Response = vbNo Then Exit Sub
Else
'On subsequent loops (files), a ReplaceAll is
'executed with the original settings and without
'displaying the dialog box again
With Dialogs(wdDialogEditReplace)
.ReplaceAll = 1
.Execute
End With
End If
'Close the modified document after saving changes
myDoc.Close SaveChanges:=wdSaveChanges
'Next file in folder
myFile = Dir$()
Wend
End Sub
That should get you started. The page has more on subfolders and password protected files.
If you need to do more than find and replace, see also utilities for batch processing documents by Greg Maxey and by Graham Mayor.

VBA Error Handling when trying to open Workbook

I am trying to loop through all files in a folder, open them and remove document info. I am having trouble dealing with files that cannot be opened or when opened have a pop us regarding disabling macros. I tried to solve this using on error resume next and on error goto 0. But then I get a runtime failure because my workbook object (wb) has not been set when I was trying to close files that did open.
I have read the documentation on "On Error Resume Next" & "On error goto 0" but I do not believe I am using them correctly here.
Any help is greatly appreciated, Thanks.
Option Explicit
Sub test_Scrubber_New()
Dim directory As String, fileName As String, i As Variant, wb As Workbook
Application.DisplayAlerts = False
Application.ScreenUpdating = False
'directory = "C:\Users\bayli\Desktop\Files for Testing\"
directory = "C:\Users\bayli\Desktop\excel files\"
fileName = Dir(directory & "*.xl??")
i = 0
Do While fileName <> ""
On Error Resume Next
Set wb = Workbooks.Open(directory & fileName)
On Error GoTo 0
'remove info
ActiveWorkbook.RemoveDocumentInformation (xlRDIAll)
wb.Close True
i = i + 1
fileName = Dir()
Application.StatusBar = "Files Completed: " & i
Loop
Application.StatusBar = False
Application.ScreenUpdating = True
Application.DisplayAlerts = True
MsgBox "Complete"
End Sub
I updated my code to include: If Not wb Is Nothing Then remove the info as #PatricK suggested and it is working however it keeps stopping with a pop up about updating links. If I click "Do not update" my code continues working as needed but is there a way to handle this problem. I am looping through over 5k files so as you can imagine it is taking a while. The time it is taking is not a problem but currently I am sitting here having to click "dont update" quite a few times. I thought Application.DisplayAlerts = False would prevent these pop ups however it is not.
OK, so there are a couple questions here. First, regarding the error handling. When you're using inline error handling (On Error Resume Next), the basic pattern is to turn off the automatic error handling, run the line of code that you want to "catch" the error for, then test to see if the Err.Number is zero:
On Error Resume Next
ProcedureThatCanError
If Err.Number <> 0 Then
'handle it.
End If
On Error GoTo 0
The rest of the questions deal with dialogs you can encounter when you're opening workbooks. Most of this is documented on the MSDN page for Workbook.Open, but you'll want to change the Application.AutomationSecurity property to deal with the macro prompts as appropriate. For the updates, you should pass the appropriate UpdateLinks parameter. I'd also recommend specifying IgnoreReadOnlyRecommended, Notify, and CorruptLoad. Something like this should work (untested), or at least get you a lot closer:
Sub TestScrubberNew() 'Underscores should be avoided in method names.
Dim directory As String, fileName As String, i As Variant, wb As Workbook
Application.DisplayAlerts = False
Application.ScreenUpdating = False
Dim security As MsoAutomationSecurity
security = Application.AutomationSecurity
Application.AutomationSecurity = msoAutomationSecurityForceDisable
directory = "C:\Users\bayli\Desktop\excel files\"
fileName = Dir(directory & "*.xl??")
i = 0
Do While fileName <> vbNullString
On Error Resume Next
Set wb = Workbooks.Open(fileName:=directory & fileName, _
UpdateLinks:=0, _
IgnoreReadOnlyRecommended:=True, _
Notify:=False, _
CorruptLoad:=xlNormalLoad)
If Err.Number = 0 And Not wb Is Nothing Then
On Error GoTo 0
wb.RemoveDocumentInformation xlRDIAll
wb.Close True
i = i + 1
Application.StatusBar = "Files Completed: " & i
fileName = Dir()
Else
Err.Clear
On Error GoTo 0
'Handle (maybe log?) file that didn't open.
End If
Loop
Application.AutomationSecurity = security
Application.StatusBar = False
Application.ScreenUpdating = True
Application.DisplayAlerts = True
MsgBox "Complete"
End Sub

Resources