I have some code which looks for a value with a given sheet name in two separate workbooks.
What I want to do is when the first workbook does not have the sheet, instead of the following prompt coming up, it cancels/throws an error and using the error handling goes to the second spreadsheet. How do I do this?
Currently I am using this code to achieve this:
fFormString1 = "'" & wkBookRef1 & firstShtName & "'!$L$6/1000"
fFormString2 = "'" & wkBookRef2 & firstShtName & "'!$L$6/1000"
Application.DisplayAlerts = False 'Does nothing to the prompt
On Error GoTo tryTwo 'Following only throws error when prompt is canceled
ThisWorkbook.Sheets("Place").Range("E53").Formula = "=" & fFormString1
GoTo endTen
tryTwo:
ThisWorkbook.Sheets("Place").Range("E53").Formula = "=IFERROR(" & fFormString2 & ","""")"
On Error Resume Next
endTen:
Application.DisplayAlerts = True 'Does nothing to the prompt
Note: I wish to do this with the spreadsheet closed ideally. Or visually not present to improve speed and smoothness of operation for my client.
ExecuteExcel4Macro will return a value from a closed workbook. If the worksheet doesn't exist it will throw an error 1004 'A formula in this worksheet contains one or more invalid references.
ExternalWorksheetExists uses this to test it the worksheet exist.
Function ExternalWorksheetExists(FilePath As String, FileName As String, WorksheetName As String) As Boolean
If Right(FilePath, 1) <> "\" Then FilePath = FilePath & "\"
On Error Resume Next
Call ExecuteExcel4Macro("'" & FilePath & "[" & FileName & "]" & WorksheetName & "'!R3C3")
ExternalWorksheetExists = Err.Number = 0
On Error GoTo 0
End Function
When using ExecuteExcel4Macro, all references must be given as R1C1 strings. Here is an example of a valid string:
ExecuteExcel4Macro("'C:\Users\tinzina\Documents\[Book1.xlsm]Sheet1'!R6C12")
Borrowing heavily from Thomas' answer (full credit is due). However it seems that this didn't work for you.
Use ExecuteExcel4Macro but ascribe the value to the variable val. Then check if this is the error you are looking for Error(2023).
Please find the code below:
'Check if the sheet exists in the workbook, used to check which forecast file one should look in
Function ExtSheetExists(formString) As Boolean 'Form string is a formula string with both the worksheet and the workbook
Dim val As Variant
'Tries to execute formula and throws error if it doesn't exist
On Error Resume Next
val = ExecuteExcel4Macro(formString)
ExtSheetExists = (val <> Error(2023)) 'Returns False if the sheet does not exist based on Error 2023
On Error GoTo 0
End Function
Related
hope you're fine,
i want a macro to read multiple excel files and insert a value in specific cell ( for example C3 or A1 or any cell declared in the code)
here firs image of my code, just a button
and here is my code:
Sub InsertValueInCell()
Range("C3").Value = _
InputBox(Prompt:="Enter the value you want to insert.")
End Sub
this code give me this result right now, it's just insert the data in the actual workbook :
TEST-1
TEST-2
Thanks in advance to help me to complete the code cause i want the code to read multiple excel files and after that i can insert a value in specific cell for all thos excel files.
Thanks in advance
Have a good day All
here is my code for the moment:
Sub InsertValueInCell()
Range("C3").Value = _
InputBox(Prompt:="Enter the value you want to insert.")
End Sub
this code give me this result right now, it's just insert the data in the actual workbook :
TEST-1
TEST-2
Thanks in advance to help me to complete the code cause i want the code to read multiple excel files and after that i can insert a value in specific cell for all those excel files (for example i want to insert a value in the C3 cell for all those excel files).
Thanks in advance
Have a good day All
This should work for you:
Option Explicit 'Must be at very top of module, before any procedures.
'Possibly the most important line in any code - forces you to declare your variables.
Public Sub AddValueToSheet()
On Error GoTo ERROR_HANDLER
'The Sheet to changed.
Dim SheetName As String
SheetName = "Sheet1"
'Get a collection of files within the folder.
Dim FileCollection As Collection
Set FileCollection = New Collection
Set FileCollection = EnumerateFiles("<folder path>\") 'Don't forget the final backslash.
Dim ValueToEnter As String 'or whatever type you're trying to enter.
ValueToEnter = InputBox("Enter the value you want to insert")
'Look at each item in the collection.
Dim wrkBkPath As Variant
For Each wrkBkPath In FileCollection
'Open the workbook.
Dim wrkBk As Workbook
Set wrkBk = Workbooks.Open(wrkBkPath)
'Check if the sheet exists.
'Add the value if it does, add the file name to the MissingSheetList string if it doesn't.
Dim MissingSheetList As String
If SheetExists(wrkBk, SheetName) Then
wrkBk.Worksheets(SheetName).Range("A1") = ValueToEnter
Else
MissingSheetList = MissingSheetList & wrkBk.Name & vbCrLf
End If
'Save and close.
wrkBk.Close SaveChanges:=True
Next wrkBkPath
'Display missing sheet list message if there's any.
If MissingSheetList <> "" Then
MsgBox "The files were missing " & SheetName & vbCr & vbCr & MissingSheetList, vbInformation + vbOKOnly
End If
Exit Sub
'If an error occurs code skips to this section.
ERROR_HANDLER:
Dim ErrMsg As String
ErrMsg = "AddValueToSheet()" & vbCr & Err.Description
'Add error handling code.
Select Case Err.Number
Case Else
End Select
End Sub
'Check if a sheet exists by trying to set a reference to it.
Private Function SheetExists(wrkBk As Workbook, SheetName As String) As Boolean
On Error Resume Next
Dim ShtRef As Worksheet
Set ShtRef = wrkBk.Worksheets(SheetName)
SheetExists = (Err.Number = 0) 'Was an error returned? True or False.
On Error GoTo 0
End Function
'Returns all xls* files from the path supplied by sDirectory.
'Each file path is added to the FilePaths collection.
Private Function EnumerateFiles(ByVal sDirectory As String) As Collection
On Error GoTo ERROR_HANDLER
'You can remove StatusBar lines if you want - code might run too fast to see anyway.
Application.StatusBar = "Collating files: " & sDirectory 'Update status bar.
Dim cTemp As Collection
Set cTemp = New Collection
Dim sTemp As String
sTemp = Dir$(sDirectory & "*.xls*")
Do While Len(sTemp) > 0
cTemp.Add sDirectory & sTemp
sTemp = Dir$
Loop
Set EnumerateFiles = cTemp
Application.StatusBar = False 'Reset status bar.
Exit Function
'If an error occurs code skips to this section.
ERROR_HANDLER:
Dim ErrMsg As String
ErrMsg = "EnumerateFiles()" & vbCr & Err.Description
'Add error handling code.
Select Case Err.Number
Case Else
End Select
End Function
I am looking for a code that will open a workbook based on the output of a formula. I have files which are named by the date e.g. 20210807 in the format JJJJMMTT. What I need is that if I run the VBA on the file "20210807" then the workbook "20210806" shall be opened. The purpose of this is, because the VBA I run on a file always reference to the previous day and I cannot retrieve data if the workbook is not open.
Is that possible? I have tried it with that code, but it doesn't work and it looks very wrong to me, but I have no clue.
Workbooks.Open FileName:= _
"=INDIRECT(CONCATENATE(LEFT(CELL(""filename""),LEN(CELL(""filename""))-13),LEFT(RIGHT(CELL(""filename""),13),8)+1,"".xlsx"",)"
You need to evaluate the formula to get its result:
Workbooks.Open FileName:=Evaluate("=INDIRECT(CONCATENATE(LEFT(CELL(""filename""),LEN(CELL(""filename""))-13),LEFT(RIGHT(CELL(""filename""),13),8)+1,"".xlsx"",)")
And you should check if the file exists or put some error handling (see VBA Error Handling – A Complete Guide) so in case the file cannot be loaded your code can handle this.
For Example:
Option Explicit
Public Sub Example()
' your code goes here …
Dim OpenFileName As String
OpenFileName = Evaluate("=INDIRECT(CONCATENATE(LEFT(CELL(""filename""),LEN(CELL(""filename""))-13),LEFT(RIGHT(CELL(""filename""),13),8)+1,"".xlsx"",)"))
On Error Goto ERR_OPEN_FILE ' on error jump to error handler
Dim WbOpen As Workbook
Set WbOpen = Workbooks.Open(FileName:=OpenFileName)
On Error Goto 0 ' re-activate error reporting
' your code goes here …
' example:
WbOpen.Worksheets("Sheet1").Range("A1").Value = "Test"
WbOpen.Close SaveChanges:=False
Exit Sub ' exit here if no error occured.
ERR_OPEN_FILE:
MsgBox "File '" & OpenFileName & "' could not be opened:" & vbCrLf & Err.Description, vbCritical, "Error " & Err.Number
Err.Clear
End Sub
If your filename is 20210807.xlsm you can subtact 1 from the day 07. But what if the file name is 20210801 then this idea ob subtracting one does not work anymore.
You need to convert the string 20210807 into a real numeric date to be able to subtract one day and get the correct result as a date that you can use to build your new file name:
Public Function GetPreviousDayFileName(ByVal ThisFileName As String) As String
'ThisFileName = "20210807.xlsm"
Dim ThisYear As String
ThisYear = Left$(ThisFileName, 4) ' 2021
Dim ThisMonth As String
ThisMonth = Mid$(ThisFileName, 5, 2) ' 08
Dim ThisDay As String
ThisDay = Mid$(ThisFileName, 7, 2) ' 07
Dim ThisDate As Date
ThisDate = DateSerial(CInt(ThisYear), CInt(ThisMonth), CInt(ThisDay))
Dim PreviousDate As Date
PreviousDate = DateAdd("d", -1, ThisDate) ' subtract one day
' generate file name
GetPreviousDayFileName = Format$(PreviousDate, "YYYYMMDD") & ".xlsx"
End Function
And use it in the first example code like this:
OpenFileName = ThisWorkbook.Path & Application.PathSeparator & GetPreviousDayFileName(ThisWorkbook.Name)
I have tried, but I don't know if I understood it perfectly. I have an additional question. So I have built this Vlookup and now want to replace a part from it with your code.
This is the code: "=IF(ISNA(VLOOKUP(RC[-1],INDIRECT(CONCATENATE(""'"",LEFT(RIGHT(CELL(""Dateiname""),13),8)-1,"".xlsx'!$A:$AP"")),2,FALSE)),DATE(LEFT(LEFT(RIGHT(CELL(""Dateiname""),13),8),4),MID(LEFT(RIGHT(CELL(""Dateiname""),13),8),5,2),RIGHT(LEFT(RIGHT(CELL(""Dateiname""),13),8),2)),VLOOKUP(RC[-1],INDIRECT(CONCATENATE(""'"",LEFT(RIGHT(CELL(""Dateiname""),13),8)-1,"".xlsx'!$A:$AP"")),2,FALSE))"
Whereas the part:
CONCATENATE(""'"",LEFT(RIGHT(CELL(""Dateiname""),13),8)-1,"".xlsx'!$A:$AP"")
is equal to:
CONCATENATE(GetPreviousDayFileName, "$A:$AP")
But this is not working. What do I miss
I need your help. I found the attached vba code but when I run the code I am getting a very strange 1004 error. Could you please give an explanation or try to fix this error?
Thank you so much all!
' Module to remove all hidden names on active workbook
Sub Remove_Hidden_Names()
' Dimension variables.
Dim xName As Variant
Dim Result As Variant
Dim Vis As Variant
' Loop once for each name in the workbook.
For Each xName In ActiveWorkbook.Names
'If a name is not visible (it is hidden)...
If xName.Visible = True Then
Vis = "Visible"
Else
Vis = "Hidden"
End If
' ...ask whether or not to delete the name.
Result = MsgBox(prompt:="Delete " & Vis & " Name " & _
Chr(10) & xName.Name & "?" & Chr(10) & _
"Which refers to: " & Chr(10) & xName.RefersTo, _
Buttons:=vbYesNo)
' If the result is true, then delete the name.
If Result = vbYes Then xName.Delete
' Loop to the next name.
Next xName
End Sub
These Excel built-in range names appear in the Excel name manager when using SUMIFS,IFERROR, COUNTIFS and other formulas.
There are a lot of ways around this, as suggested in the comments.
You can add either of these:
If Not xName.Name Like "_xlfn*" Then
'Or
If InStr(xName.Name, "_xlfn") = 0 Then
first thing in the loop (don't forget to close it), or something similar.
If you for some reason still want to see it, you can add it to the delete if:
If Result = vbYes And Not xName.Name Like "_xlfn*" Then xName.Delete
I understand similar questions with these errors have been posted before, but I found nothing when it came to formatting tables so don't close this please. In my VBA code in MS Access 2013 it exports data from MS Access to Excel. 6 different queries get exported into 1 excel file, each on a different worksheet. This works fine. I then format each sheet to have all the data in a table. I have a form which lets the user choose the path to save the file. If it is the first time creating the file, it works properly. If it is the second time creating the file in that same directory, it doesn't work and it gives me the error:
Run-time error 1004: Method Range of object _Global failed
I figured this was because I was overwriting my file instead of deleting it and recreating it. So I added in some code to check if the file exists, and if it does, delete it. I added breakpoints and while running through this part of the code, I was watching my documents folder. The file successfully got deleted and then recreated which is what I wanted. It still gave me that error. I manually went to delete the file and then reran my code again. It worked properly.
How come I need to manually delete this file in order to rerun my code? Or is it something else that is causing the problem? Here is the important parts of my code as the whole thing is too long to post:
'Checks if a file exists, then checks if it is open
Private Sub checkFile(path As String)
Dim openCheck As Boolean
'If file exists, make sure it isn't open. If it doesn't, create it
If Dir(path) <> "" Then
openCheck = IsFileLocked(path)
If openCheck = True Then
MsgBox "Please close the file in " & path & " first and try again."
End
Else
deleteFile (path)
End If
Else
End If
End Sub
Sub deleteFile(ByVal FileToDelete As String)
SetAttr FileToDelete, vbNormal
Kill FileToDelete
End Sub
Private Sub dumpButton_Click()
On Error GoTo PROC_ERR
Dim path As String
Dim testBool As Boolean
path = pathLabel4.Caption
path = path & Format(Date, "yyyy-mm-dd") & ".xlsx"
checkFile (path)
dumpQueries (path)
formatFile (path)
'Error Handling
PROC_ERR:
If Err.Number = 2001 Then
MsgBox "A file may have been sent to " & path
Exit Sub
ElseIf Err.Number = 2501 Then
MsgBox "A file may have been sent to " & path
Exit Sub
ElseIf Err.Number = 3021 Then
MsgBox "A file may have been sent to " & path
Exit Sub
ElseIf Err.Number = 2302 Then
MsgBox "A file may have been sent to " & path
Exit Sub
ElseIf Err.Number = 0 Then
MsgBox "Your file has been stored in " & pathLabel4.Caption
Exit Sub
Else
MsgBox Err.Number & ": " & Err.Description & vbCrLf & vbCrLf & "New Error. Please contact the IT department."
End If
Private Sub dumpQueries(path As String)
Dim obj As AccessObject, dB As Object
Set dB = Application.CurrentData
For Each obj In dB.AllQueries
testBool = InStr(obj.name, "Sys")
If testBool <> True Then
If obj.name = "example1" Or obj.name = "example2" Then
DoCmd.TransferSpreadsheet acExport, acSpreadsheetTypeExcel12Xml, obj.name, path, True, editWorksheetName(obj.name)
End If
End If
Next obj
End Sub
'Autofits the cells in every worksheet
Private Sub formatFile(path As String)
Dim Date1 As Date, strReportAddress As String
Dim objActiveWkb As Object, appExcel As Object
Set appExcel = CreateObject("Excel.Application")
appExcel.Visible = False
appExcel.Application.Workbooks.Open (path)
Set objActiveWkb = appExcel.Application.ActiveWorkbook
With objActiveWkb
Dim i As Integer
For i = 1 To .Worksheets.count
.Worksheets(i).Select
.Worksheets(i).Cells.EntireColumn.AutoFit
.Worksheets(i).ListObjects.Add(xlSrcRange, Range("A1").CurrentRegion, , xlYes).name = "myTable1"
Next
End With
appExcel.ActiveWindow.TabRatio = 0.7
objActiveWkb.Close savechanges:=True
appExcel.Application.Quit
Set objActiveWkb = Nothing: Set appExcel = Nothing
End Sub
The error occurs near the bottom of the code. It's the line:
.Worksheets(i).ListObjects.Add(xlSrcRange, Range("A1").CurrentRegion, , xlYes).name = "myTable1"
There may be a couple functions I left out but they work fine and shouldn't be needed for answering the question.
This is the only relevant code:
Set objActiveWkb = appExcel.Application.ActiveWorkbook
With objActiveWkb
Dim i As Integer
For i = 1 To .Worksheets.count
.Worksheets(i).Select
.Worksheets(i).Cells.EntireColumn.AutoFit
.Worksheets(i).ListObjects.Add(xlSrcRange, Range("A1").CurrentRegion, , xlYes).name = "myTable1"
Next
End With
Things get easier to follow when you trim the fluff away and start naming things - there's no need to .Select anything, appExcel is already an Application object, and there's no need to make a copy reference to the active workbook just to use in a With block, especially if that copy is going to be an Object variable anyway - if the copy were a Workbook object then you would at least get IntelliSense for its members...
Your source range is ambiguous. Range("A1") in Excel-VBA is an implicit reference to the active worksheet.. but this is Access-VBA, so there's no such thing, xlSrcRange is an enum value defined in the Excel object model, so if you don't have a reference to the Excel object model (you're late-binding this, right?), and Option Explicit isn't specified, then xlSrcRange is treated by VBA like just another undeclared/uninitialized variable, and therefore you're passing a 0 there, and the xlSrcRange enum value stands for a 1 - and 0 happens to be the underlying value for xlSrcExternal. Same with xlYes.
Since we cannot possibly guess what the actual source range is supposed to be from the code you posted, I'm leaving you with this:
Dim target As Object
Dim srcRange As Object
Set srcRange = TODO
With appExcel.ActiveWorkbook
Dim i As Integer
For i = 1 To .Worksheets.Count
.Worksheets(i).Cells.EntireColumn.AutoFit
Set target = .Worksheets(i).ListObjects.Add(1, srcRange, , 1)
If target Is Not Nothing Then target.Name = "myTable1"
Next
End With
Side question... why name the table myTable1 when Excel will already have named it Table1 anyway? Also note, if .Add fails, your code blows up with a runtime error 91 because you'd be calling .Add off Nothing. Verifying that the target is not Nothing before setting its Name will avoid that.
To answer your question in the comments:
#Mat'sMug is this what you were talking about? because it gives me this error: "438: Object doesn't support this property or method" Here's the code: .Worksheets(i).ListObjects.Add(SourceType:=xlSrcRange, Source:=.Cells(1).CurrentRegion, _ XlListObjectHasHeaders:=xlYes, TableStylename:="TableStyleMedium1").name = "Table"
The reason this throws a 438 is because your With block variable is a Workbook object, and a Workbook object doesn't have a .Range member.
What I was talking about, is that in Excel VBA unqualified calls to Range, Row, Column, and Cells are implicitly referencing the ActiveSheet, and unqualified calls to Worksheets, Sheets and Names are implicitly referencing the ActiveWorkbook - that's a recurrent problem in a lot of VBA code and a very common mistake to make. The solution is basically to say what you mean, and mean what you say; in this case the failure is on "mean what you say" - the unqualified Range("A1") call is, according to the error message, calling [_Globals].Range("A1")... which is weird because it implies that you're referencing the Excel object model library, which means your late-binding and Object variables could just as well be early-bound: why deal with Object variables and lack of IntelliSense when you're already referencing the library you're late-binding to?
Here's the basic problem: I am writing an Excel macro and I would like to use the worksheet code names to try to eliminate any errors down the road. I can use the code name for Sheet1 and it works fine, but when I try to use the other codes, like Sheet3 or Sheet7 the editor doesn't recognize them and if I run the macro Excel kicks up an error telling me that my "variable is not defined".
For example:
Option Explicit
Sub Test()
Dim SheetObject As Worksheet
Dim SheetObject2 As Worksheet
Set SheetObject = Sheet1
Set SheetObject2 = Sheet3
MsgBox (SheetObject.Name)
MsgBox (SheetObject2.Name)
End Sub
If I comment out any code referring to SheetObject2 the macro runs correctly. If I put them in I get the errors. I definitely have a Sheet3, and the code name is definitely Sheet3. I've looked around Google all day and can't seem to come up with any solutions, any help would be great.
Thanks in advance,
Jesse
My last employer collected data and created national statistics. Much of that data came in the form of Excel workbooks so I have had a lot of relevant experience.
If you are running your own macro and if this is a one-off exercise then tests like this may be adequate:
Debug.Assert WbookTgt.WsheetTgt.Range("A1").Value = "Date"
Many languages have an Assert statement as a development aid; this is the VBA version. If the assertion is not true, the macro will stop with this statement highlighted.
If this approach is not adequate, you should consider developing parameterised macros that perform checking and updating tasks. I have looked through some of my old macros but most would not be intelligible to someone new to VBA. I have extracted code to create two macros which I hope will give you some ideas.
Macro 1 - OpenWorkbook
Organisations that regularly supply data often use names like: "Xxxxx 1409.xlsx" and "Xxxxx 1410.xlsx" for the September and October versions of their data. You could, for example, update the macro each month for the latest name or you could change the filename to a standard value. Either of these possibilities would be a nuisance and I would be particularly opposed to the second idea because I like to archive all the workbooks I have processed.
OpenWorkbook() uses the Dir statement to search a folder for a file that matches a template such as “Xxxxx*.xls*”. If a single file matches this template, the macro opens the workbook and returns a reference to it.
Macro 2 – CheckWorksheets
You may have noticed that some VBA routines have a fixed number of parameters while others have a variable number of parameters. For example, the following are all valid calls of CheckWorksheets:
If CheckWorksheets(WbookTgt, WbookThis, “Name1”) then
If CheckWorksheets(WbookTgt, WbookThis, “Name1”, “Name2”) then
If CheckWorksheets(WbookTgt, WbookThis, “Name1”, “Name2”, “Name3”) then
CheckWorksheets has three parameters. The first two are workbook references. The third is ParamArray SheetName() As Variant. Any parameter after the first two is placed in array SheetName which can be as large as necessary. Here all the trailing parameters are strings but they could be of any type.
I can use OpenWorkbook to open this month’s version of the source file and then use CheckWorksheets to confirm all the worksheets required by my macro are present.
Worksheet Errors”
These two macros require a worksheet Errors be present in a specified workbook. If the macros detect an error, they add a detailed error message to this worksheet. I have found this a convenient technique for capturing the details of any errors.
Macros Demo1 and Demo2
I have included two macros that demonstrate the use of these macros with workbooks on my system. If you amend Demo1 and Demo2 to operate on some of your workbooks, you should get an idea of what OpenWorkbook and CheckWorksheets can do for you.
Come back with questions as necessary but the more you can decipher OpenWorkbook and CheckWorksheets yourself, the faster you will develop your own skills
Option Explicit
Sub Demo1()
Dim Path As String
Dim WbookThis As Workbook
Dim WbookTgt As Workbook
' Application.ThisWorkbook identifies the workbook containing this macro.
Set WbookThis = Application.ThisWorkbook
' I find it convenient to place my target workbooks in the folder
' holding the workbook containing the macro(s).
Path = WbookThis.Path
Set WbookTgt = OpenWorkbook(Path, "Combined*.xls*", WbookThis)
If WbookTgt Is Nothing Then
' Detailed error message already recorded in "Errors"
Call MsgBox("Wokbook failed checks", vbOKOnly)
Else
With WbookTgt
Debug.Print .Path & "\" & .Name & " opened."
.Close SaveChanges:=False
End With
End If
End Sub
Sub Demo2()
Dim Path As String
Dim WbookThis As Workbook
Dim WbookTgt As Workbook
' Application.ThisWorkbook identifies the workbook containing this macro.
Set WbookThis = Application.ThisWorkbook
' I find it convenient to place my target workbooks in the folder
' holding the workbook containing the macro(s).
Path = WbookThis.Path
Set WbookTgt = OpenWorkbook(Path, "Combined 2.04.xls*", WbookThis)
If WbookTgt Is Nothing Then
' Detailed error message already recorded in "Errors"
Call MsgBox("Wokbook failed checks", vbOKOnly)
Exit Sub
End If
With WbookTgt
If Not CheckWorksheets(WbookTgt, WbookThis, "Critical Path", "Dyn Dims") Then
Call MsgBox("Wokbook failed checks", vbOKOnly)
.Close SaveChanges:=False
Exit Sub
End If
Debug.Print .Path & "\" & .Name & " contains worksheets Critical and Dym Dims"
.Close SaveChanges:=False
End With
End Sub
Function CheckWorksheets(ByRef WbookTgt As Workbook, ByRef WbookError As Workbook, _
ParamArray SheetName() As Variant) As Boolean
' * Return True if WbookTgt contains every specified worksheet.
' * WbookTgt is the workbook to be checked
' * WbookError identifies the workbook containing worksheet "Error" to which any
' error message will be added.
' * SheetName() is an array of worksheet names.
Dim ErrorMsg As String
Dim FoundError As Boolean
Dim FoundSheet() As Boolean
Dim FoundSheetsCount As Long
Dim InxName As Long
Dim InxWsheet As Long
Dim NotFoundSheetsCount As Long
Dim RowErrorNext As Long
Dim SheetNamesFound As String
' Size FoundSheet to match SheetName. Array elements initialised to False
ReDim FoundSheet(LBound(SheetName) To UBound(SheetName))
FoundSheetsCount = 0
NotFoundSheetsCount = 0
With WbookTgt
For InxName = LBound(SheetName) To UBound(SheetName)
NotFoundSheetsCount = NotFoundSheetsCount + 1 ' Assume not found until found
For InxWsheet = 1 To .Worksheets.Count
If SheetName(InxName) = .Worksheets(InxWsheet).Name Then
FoundSheet(InxName) = True
FoundSheetsCount = FoundSheetsCount + 1
NotFoundSheetsCount = NotFoundSheetsCount - 1
Exit For
End If
Next
Next
End With
If NotFoundSheetsCount = 0 Then
CheckWorksheets = True
Exit Function
End If
SheetNamesFound = ""
ErrorMsg = WbookTgt.Path & "\" & WbookTgt.Name & " does not contain "
If NotFoundSheetsCount = 1 Then
ErrorMsg = ErrorMsg & "this expected worksheet:"
Else
ErrorMsg = ErrorMsg & "these expected worksheets:"
End If
For InxName = LBound(SheetName) To UBound(SheetName)
If Not FoundSheet(InxName) Then
ErrorMsg = ErrorMsg & vbLf & " " & SheetName(InxName)
Else
SheetNamesFound = SheetNamesFound & vbLf & " " & SheetName(InxName)
End If
Next
If FoundSheetsCount = 0 Then
' No need to add list of found sheet names
Else
ErrorMsg = ErrorMsg & vbLf & "but does contain "
If FoundSheetsCount = 1 Then
ErrorMsg = ErrorMsg & "this expected worksheet:"
Else
ErrorMsg = ErrorMsg & "these expected worksheets:"
End If
ErrorMsg = ErrorMsg & SheetNamesFound
End If
With WbookError
With .Worksheets("Errors")
RowErrorNext = .Cells(Rows.Count, "A").End(xlUp).Row + 1
With .Cells(RowErrorNext, "A")
.Value = Now()
.VerticalAlignment = xlTop
End With
.Cells(RowErrorNext, "B").Value = ErrorMsg
End With
End With
CheckWorksheets = False
End Function
Function OpenWorkbook(ByVal Path As String, ByVal FileTemplate As String, _
ByRef WbookError As Workbook) As Workbook
' * If Path & FileTemplate identifies a single workbook, open it and return
' it as an object. If Path & FileTemplate does not represent a single
' workbook, report the problem in worksheet Errors and return Nothing.
' * WbookError identifies the workbook containing worksheet "Error".
' * Path must be the name of the folder in which the required workbook is located
' * FileTemplate can either be a specific filename or can contain wild cards
' providing only one file matches the template.
' * WbookError identifies the workbook containing worksheet "Error" to which any
' error message will be added.
Dim ErrorMsg As String
Dim FileNameCrnt As String
Dim FileNameMatch As String
Dim RowErrorNext As Long
FileNameMatch = Dir$(Path & "\" & FileTemplate, vbNormal)
If FileNameMatch = "" Then
' No matches found
ErrorMsg = "Template " & Path & "\" & FileTemplate & " does not match any file"
Else
' At least one match.
' If only one match, its name is in FileNameMatch
Do While True
FileNameCrnt = Dir$
If FileNameCrnt = "" Then
' No more matches
Exit Do
End If
' A second or subsequent match has been found.
If FileNameMatch <> "" Then
' This is the second match.
' Initialise error message and report name of first match
ErrorMsg = "Template " & Path & "\" & FileTemplate & " matches more than one file:" & _
vbLf & " " & FileNameMatch
FileNameMatch = "" ' No single match
End If
' Add name of current match to error message
ErrorMsg = ErrorMsg & vbLf & " " & FileNameCrnt
Loop
End If
If FileNameMatch = "" Then
' No single match found.
' ErrorMsg contains an appropriate error message
With WbookError
With .Worksheets("Errors")
RowErrorNext = .Cells(Rows.Count, "A").End(xlUp).Row + 1
With .Cells(RowErrorNext, "A")
.Value = Now()
.VerticalAlignment = xlTop
End With
.Cells(RowErrorNext, "B").Value = ErrorMsg
Set OpenWorkbook = Nothing
End With
End With
Else
' Single match found
Set OpenWorkbook = Workbooks.Open(Path & "\" & FileNameMatch)
End If
End Function
Response to extra question
VBA has nothing quite as convenient as VB's Try but it does have some error handling under programmer control.
If you use a command such as:
Worksheets("Sheet2").Delete
the user will be asked to confirm the deletion. To avoid this, use:
Application.DisplayAlerts = False
Worksheets("Sheet2").Delete
Application.DisplayAlerts = True
I have seen code with Application.DisplayAlerts = False at the start of a macro which means no alert will be displayed for the user's attention even if the pogrammer was not expecting it. By bracketing the Delete, I ensure only the alert I was expecting is suppressed.
Consider:
Sub OpenFile()
Dim InputFileNum As Long
InputFileNum = FreeFile
Open "Dummy.txt" For Input As InputFileNum
Debug.Print "File successfully opened"
Close InputFileNum
End Sub
The file "Dummy.txt" does not exist so the macro will stop on the Open statement.
You will sometimes see code like this:
Sub OpenFile()
Dim InputFileNum As Long
On Error GoTo ErrorCode
InputFileNum = FreeFile
Open "Dummy.txt" For Input As InputFileNum
Call MsgBox("File successfully opened", vbOKOnly)
Close InputFileNum
Exit Sub
ErrorCode:
Debug.Print "Unexpected error: " & Err.Number & " " & Err.Description
End Sub
Here I have provided a general handler for any error condition that may occur. I do not approve although I accept that this is slightly better than having the non-technical user seeing the faulty statement highlighted. The trouble is any error will result in the same unhelpful error message.
I never include error handling during development. If an error occurs, I want the macro to stop on the faulty statement so I can consider how to avoid the error. Here I should check the file exists before attempting to open it. I prefer something like this:
Sub OpenFile()
Dim FileSysObj As Object
Dim InputFileNum As Long
On Error GoTo ErrorCode
Set FileSysObj = CreateObject("Scripting.FileSystemObject")
If Not FileSysObj.FileExists("Dummy.txt") Then
Call MsgBox("I am unable to find ""Dummy.txt"". List of helpful suggestions.", vbOKOnly)
Exit Sub
End If
InputFileNum = FreeFile
Open "Dummy.txt" For Input As InputFileNum
Call MsgBox("File successfully opened", vbOKOnly)
Close InputFileNum
Exit Sub
ErrorCode:
Debug.Print "Unexpected error: " & Err.Number & " " & Err.Description
End Sub
I have including checking code for the error I expect. If the file does not exist, I have displayed a message which I hope will help the user fix the problem for themselves.
Sometimes you cannot avoid an error. To test the code below, I created file Dummy.txt but set the "Read access denied" flag. There is no easy method (to my knowledge) for a VBA macro to test this flag. I have a general handler for unexpected errors but I switch it off for the Open statment so I can include specific code for open failures. I have removed the code that uses FileExists() to test if Dummy.txt exists because it is easier to include it with the other open file error tests.
Sub OpenFile()
Dim FileSysObj As Object
Dim InputFileNum As Long
On Error GoTo ErrorCode ' General handler for unexpected errors
InputFileNum = FreeFile
Err.Clear
On Error Resume Next ' Record error in Err object and continue
Open "Dummy.txt" For Input As InputFileNum
Select Case Err.Number
Case 0
' No error.
Case 53 ' File does not exist
Call MsgBox("I am unable to find ""Dummy.txt"". List of helpful suggestions.", vbOKOnly)
Exit Sub
Case 75 ' Path/File access error
Call MsgBox("It appears file ""Dummy.txt"" exists but I do not have permission to read it.", vbOKOnly)
Exit Sub
Case Else
Call MsgBox("My attempt to open ""Dummy.txt"" failed with an unexpected error condition" & vbLf & _
" " & Err.Number & " " & Err.Description, vbOKOnly)
Exit Sub
End Select
On Error GoTo ErrorCode ' Restore general handler for unexpected errors
Call MsgBox("File successfully opened", vbOKOnly)
Close InputFileNum
Exit Sub
ErrorCode:
Debug.Print "Unexpected error: " & Err.Number & " " & Err.Description
End Sub
Visit http://support.microsoft.com/kb/146864 for a long list of error codes and more information about error handling.