I am working on an Excel workbook (CollectorWorkbook) that is to open a series of about 5000 workbooks (DataWorkbooks) and collect data from specific cells in those DataWorkbooks. To do this, I want to use a UDF in about 30 cells of the CollectorWorkbook.
I have used a similar approach to gather data into a similarly constructed CollectorWorkbookForWord data to gather information from a series of Word documents and it works well there. In that case a UDF in CollectorWorkbookForWord successfully opens the WordDocument if it is closed when the UDF is called and retrieves text from about 85 fields in the WordDocument. Subsequent calls to the UDF use the same instance of the open WordDocument and steps through about 2000 of the documents and takes about 9 seconds per document.
In the CollectorWorkbook, the call to the UDF is =ExcelCellText(RangeName,Full_File_Name). The UDF is to use a currently open instance of Full_File_Name if it is available and open Full_File_Name if it is not. This works when Full_File_Name is open but does not work if Full_File_Name is closed. No error message is produced. To be clear, ExcelCellText passes Full_File_Name to a call to GetExcelWorkbook (code follows) within the VBA for the UDF ExcelCellText.
I have stepped through the applicable code beginning with ExcelCellText, through the various internal procedures and back to the calling cell in CollectorWorkbook using F8 and did not observe anything unexpected except in the few lines of GetExcelWorkbook following the comment including "BIG TROUBLE HERE". Debug.Print statements throughout produce expected results with the exception of the sequence preceded by "BIG TROUBLE HERE" and results that we'd expect after a failure to open the DataWorkbook.
I did a test on a simplified version of the call to Workbooks.Open that demonstrated to me that something I do not understand is happening. See the WonkyTest code near the end of this post.
I'm suspicious that there is something about my code, computing environment, or work habit that is producing something that would make a decent Twilight Zone episode on a programming network. I've been working on this issue a few days and have had a similar problem on and off for several years in other contexts. Short of reinstalling Windows, I'm open to suggestions because I've run out of ideas.
Relevant properties of my world are as follows:
Just to check my syntax and to know if I've been asking Excel and VBA to do something that is valid, I created the test procedures below in the same standard Module. Everything worked as intended (and still does) when I stepped through it using F8 directly and through the function call. But when I included:
Call WonkyTest
in the GetExcelWorkbook function (after Finally), it did not open the workbook correctly.
I have used a similar approach to gather data into a similarly constructed CollectorWorkbookForWord data to gather information from a series of Word documents and it works well there. In that case a UDF in CollectorWorkbookForWord successfully opens the WordDocument if it is closed when the UDF is called and retrieves text from about 85 fields in the WordDocument. Subsequent calls to the UDF use the same instance of the open WordDocument and steps through about 2000 of the documents and takes about 9 seconds per document.
In the CollectorWorkbook, the call to the UDF is =ExcelCellText(RangeName,Full_File_Name). The UDF is to use a currently open instance of Full_File_Name if it is available and open Full_File_Name if it is not. This works when Full_File_Name is open but does not work if Full_File_Name is closed. No error message is produced. To be clear, ExcelCellText passes Full_File_Name to a call to GetExcelWorkbook (code follows) within the VBA for the UDF ExcelCellText.
I have stepped through the applicable code beginning with ExcelCellText, through the various internal procedures and back to the calling cell in CollectorWorkbook using F8 and did not observe anything unexpected except in the few lines of GetExcelWorkbook following the comment including "BIG TROUBLE HERE". Debug.Print statements throughout produce expected results with the exception of the sequence preceded by "BIG TROUBLE HERE" and results that we'd expect after a failure to open the DataWorkbook.
I did a test on a simplified version of the call to Workbooks.Open that demonstrated to me that something I do not understand is happening. See the WonkyTest code near the end of this post.
I'm suspicious that there is something about my code, computing environment, or work habit that is producing something that would make a decent Twilight Zone episode on a programming network. I've been working on this issue a few days and have had a similar problem on and off for several years in other contexts. Short of reinstalling Windows, I'm open to suggestions because I've run out of ideas.
Relevant properties of my world are as follows:
- Windows10Pro validated and fully updated.
- Microsoft 365 installed locally, validated and fully up to date.
- During the last week or so of trying to solve this problem I have done Quick Repair, Online Repair, Uninstall/Reinstall M365
- I am the Administrator.
- I have completed various Shutdown, Restart cycles ad nauseum.
- My Internet is working most of the time.
- The CollectorWorkbook and DataWorkbooks are on OneDrive.
- I get the same behavior when OneDrive is closed as when it is open.
- The VBA is in my Personal.xlsb file in the XLSTART folder.
- VBE Options, General: Break on Unhandled Errors, Compile on Demand, and Background Compile are selected.
- The User, OneDrive, and XLSTART folders are trusted locations.
- In the Trust Center, Macros are Enabled and VBA Projects are trusted.
- I have Cleaned the code using MZ-Tools.
- After exporting all the modules in Personal.xlsb, I deleted Personal.xlsb, created a new one using the Macro Recorder and then imported modules.
- I have a lot of modules (about 68) in my Personal.xlsb. I have looked everywhere for functions that may have similar names (none found).
- Option Explicit is in effect.
- I get the same behavior using Late and Early binding when using this code.
- I've stepped through the code using F8 and did not find anything unexpected except for the problems following "BIG TROUBLE HERE".
- Hard coded version works (see WonkyTest) below.
VBA Code:
Public Function GetExcelWorkbook(ByVal Fullname As String, _
Optional ByVal ReadOnly As Boolean = False, _
Optional ByVal Visible As Boolean = True, _
Optional ByVal CreateAppInstanceIfClosed As Boolean = True, _
Optional ByRef ExcelWorkbookWasOpen As Boolean, _
Optional ByRef ExcelWasRunning As Boolean) As Excel.Workbook ' Object ' Excel.Workbook
' ----------------------------------------------------------------
' Procedure Name: GetExcelWorkbook
' Purpose: Returns an Excel Workbook object sa warranted.
' Procedure Kind: Function
' Procedure Access: Public
' Parameter Fullname (String): The fully qualified path to the workbook.
' Parameter ReadOnly (Boolean): ReadOnly property for a closed workbook.
' Parameter Visible (Boolean): Visibility for a closed workbook.
' Parameter CreateAppInstanceIfClosed (Boolean): Determines if Excel will be opened if it is closed.
' Parameter ExcelWorkbookWasOpen (Boolean): Returned value is True if Fullname was already open.
' Parameter ExcelWasRunning (Boolean): Returned value is True if Excel was already open.
' Return Type: Workbook
' Author: Bruce Volkert
' Date: 8/24/2020
' ----------------------------------------------------------------
' Enhanced from: _
https://stackoverflow.com/questions/9373082/detect-whether-Excel-ExcelWorkbook-is-already-open
#If LateBinding Then
Dim appExcel As Object
Dim oEWkb As Object
#Else
Dim appExcel As Excel.Application
Dim oEWkb As Excel.Workbook
#End If
Const szsource As String = "GetExcelWorkbook"
Dim bDebugging As Boolean
bDebugging = True ' If this routine is working set bDebugging = False; otherwize True
Dim bReporting As Boolean
bReporting = bDebugging
If bReporting Then Debug.Print VBA.format(Now(), "yyyy-MM-dd hh:mm:ss") & " " & m_szModule & "." & szsource & ": " & "Starting"
Dim sFullName As String
Dim sFile As String
Set GetExcelWorkbook = Nothing
sFullName = Trim(Fullname)
sFile = Dir(sFullName) ' This works.
ExcelWasRunning = False
ExcelWorkbookWasOpen = False
If bReporting Then Debug.Print VBA.format(Now(), "yyyy-MM-dd hh:mm:ss") & " " & m_szModule & "." & szsource & ": " & "sFullName = " & sFullName
If bReporting Then Debug.Print VBA.format(Now(), "yyyy-MM-dd hh:mm:ss") & " " & m_szModule & "." & szsource & ": " & "sFile = " & sFile
' Returns value of ExcelWasRunning. This works 8/24.
Set appExcel = GetExcelApp(CreateAppInstanceIfClosed:=CreateAppInstanceIfClosed, Visible:=Visible, ExcelWasRunning:=ExcelWasRunning)
If bReporting Then Debug.Print VBA.format(Now(), "yyyy-MM-dd hh:mm:ss") & " " & m_szModule & "." & szsource & ": " & "ExcelWasRunning = " & CStr(ExcelWasRunning)
If appExcel Is Nothing Then
' There is nothing to do because the Caller did not want Excel to be open if previously closed.
Else
On Error Resume Next
Set oEWkb = appExcel.Workbooks(sFile) ' This works when sFile is open. As intended, oEWkb is Nothing if sFile is not already open.
On Error GoTo 0
If oEWkb Is Nothing Then
ExcelWorkbookWasOpen = False
If bDebugging Then Debug.Print VBA.format(Now(), "yyyy-MM-dd hh:mm:ss") & " " & m_szModule & "." & szsource & ": " & sFile & " was not open already."
On Error GoTo GetExcelWorkbook_Error
' BIG TROUBLE HERE. F8 steps through this If/Then/Else sequence without hesitation; however, NO action is apparent. The workbook does not open. No error message is produced.
If FileExists(sFullName) Then ' As expected, this DOES confirm that sFullName exists. 8/25
' Set oEWkb = appExcel.Workbooks.Open(FileName:=sFullName, ReadOnly:=ReadOnly) ' This does not work. With valid sFullname oEWkb remains Nothing 8/24 PM
Set oEWkb = appExcel.Workbooks.Open(FileName:=Fullname, ReadOnly:=ReadOnly) ' This does not work. With valid sFullname oEWkb remains Nothing 8/25 PM
Else
' As expected, the following warning does not appear for valid sFullname and Fullname.
MsgBox "The requested file " & sFullName & " does not exist.", vbOKOnly Or vbCritical Or vbSystemModal, Application.Name
End If
If bDebugging Then Debug.Print VBA.format(Now(), "yyyy-MM-dd hh:mm:ss") & " " & m_szModule & "." & szsource & ": " & sFile & " should be open."
Else
ExcelWorkbookWasOpen = True
If bDebugging Then Debug.Print VBA.format(Now(), "yyyy-MM-dd hh:mm:ss") & " " & m_szModule & "." & szsource & ": " & sFile & " was open already."
End If
End If
Finally:
' TODO: Delete call to WonkyTest when issues are resolved.
Call WonkyTest ' This produces the same erroneous behavior. The workbook does not open.
Set GetExcelWorkbook = oEWkb
If bDebugging Then Debug.Print VBA.format(Now(), "yyyy-MM-dd hh:mm:ss") & " " & m_szModule & "." & szsource & ": " & "C - Excel should be running"
Set oEWkb = Nothing
Set appExcel = Nothing
Exit Function
GetExcelWorkbook_Error:
' The following message does not appear for valid sFullname or Fullname. However, the workbook does not open. It is not hidden, and does not show up as a VBE Project.
MsgBox "Error " & Err.Number & " (" & Err.Description & ") in procedure GetExcelWorkbook, line " & Erl & "."
' On Error GoTo GetExcelWorkbook_Error
GoTo Finally
End Function
Just to check my syntax and to know if I've been asking Excel and VBA to do something that is valid, I created the test procedures below in the same standard Module. Everything worked as intended (and still does) when I stepped through it using F8 directly and through the function call. But when I included:
Call WonkyTest
in the GetExcelWorkbook function (after Finally), it did not open the workbook correctly.
VBA Code:
Public Sub WonkyTest()
' When I step through this using F8 directly. the workbook opens correctly.
Dim sFullnameTyped As String
sFullnameTyped = "C:\Users\MyUserName\OneDrive\Documents\MAVA\MealPlans\A legitimate file name.xlsm"
Workbooks.Open (sFullnameTyped)
End Sub
Sub WonkyTestCallDirectly()
' When I step through this using F8, the Call to WonkyText causes the workbook to open correctly.
Call WonkyTest
End Sub
Sub WonkyTestCallThroughFunction()
Dim worked As Boolean
worked = WonkyTextWorked()
Debug.Print "WonkyTextWorked = " & worked
' When I step through this, the value of worked is True.
End Sub
Function WonkyTextWorked() As Boolean
Dim oWkb As Excel.Workbook
Call WonkyTest
On Error Resume Next
Set oWkb = Workbooks("A legitimate file name.xlsm")
On Error GoTo 0
If oWkb Is Nothing Then
WonkyTextWorked = False
Else
WonkyTextWorked = True
End If
Debug.Print "WonkyTextWorked = " & WonkyTextWorked
End Function