I have a number of Excel files that refresh themselves via macro, and a master file to control opening the other. The master file has an Auto_Open macro that opens each doc, runs the macro in the doc, then closes it. Each doc's macro essentially refreshes all the queries inside and saves the doc in two different places. To top it all of, I am using Window's Task Scheduler to open the master doc every two hours, kicking off the whole process.
This generally works pretty well. However, I will occasionally get an error for one the docs that says "Cannot find project or library." This occurs seemingly at random and with a different doc each time (though never the master doc). Once this error appears, Excel will crash completely every time I try to open the VBA window. The macro in said doc will no longer run via the master file's Auto_Open macro, and I have to recreate said doc from scratch.
I have tried to find the library as suggested by Microsoft (https://learn.microsoft.com/en-us/office/vba/Language/Reference/User-Interface-Help/can-t-find-project-or-library) but to no avail. Opening the VBA window in the affected file causes an immediate crash, and following the steps in the article above for an unaffected file reveals nothing wrong. The crash returns this error text:
Problem signature:
Problem Event Name: APPCRASH
Application Name: EXCEL.EXE
Application Version: 16.0.11231.20130
Application Timestamp: 5c518be9
Fault Module Name: VBE7.DLL
Fault Module Version: 0.0.0.0
Fault Module Timestamp: 5c064824
Exception Code: c0000005
Exception Offset: 00000000000b555a
OS Version: 6.3.9600.2.0.0.272.7
Locale ID: 1033
Code in Master File:
Sub Auto_Open()
Application.Wait (Now + TimeValue("0:00:10"))
Application.Calculation = xlCalculationManual
Workbooks.Open ("C:\Users\aowens\Desktop\Queries\ATSReports\ATSReports.xlsm")
Application.Run "'C:\Users\aowens\Desktop\Queries\ATSReports\ATSReports.xlsm'!Macro"
Workbooks("ATSReports.xlsm").Close False
Workbooks.Open ("C:\Users\aowens\Desktop\Queries\MiscLookups\MiscLookups.xlsm")
Application.Run "'C:\Users\aowens\Desktop\Queries\MiscLookups\MiscLookups.xlsm'!Macro"
Workbooks("MiscLookups.xlsm").Close False
(this pattern repeats for 5 other files)
Sample macro within one the files:
Sub Macro()
Dim errorcount
Dim broken
Dim this As Date
this = now()
errorcount = 0
On Error Resume Next
ThisWorkbook.Connections("Query - MasterROCL").Refresh
If Err <> 0 Then
errorcount = errorcount + 1
broken = broken & " ROCL"
End If
Err = 0
ThisWorkbook.Connections("Query - MasterRMEL").Refresh
If Err <> 0 Then
errorcount = errorcount + 1
broken = broken & " RMEL"
End If
Err = 0
ThisWorkbook.Connections("Query - MasterRHIL").Refresh
If Err <> 0 Then
errorcount = errorcount + 1
broken = broken & " RHIL"
End If
Err = 0
ThisWorkbook.Connections("Query - MasterREXH").Refresh
If Err <> 0 Then
errorcount = errorcount + 1
broken = broken & " REXH"
End If
Err = 0
Calculate
ThisWorkbook.Save
Application.DisplayAlerts = False
ThisWorkbook.SaveAs ("R:\Operations\Dashboards\Queries\ATSReports.xlsm")
Dim OutApp As Object
Dim OutMail As Object
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
With OutMail
.to = "aowens#explorenetwork.org"
.Subject = errorcount & " Errors for " & Format(now(), "MM/DD HH:MM") & " ATS Refresh"
.htmlBody = " ~ " & Round(1440 * (TimeValue(now()) - TimeValue(this)), 0) & " mins. Broken:" & broken
.Send
End With
End Sub
I often run into the same error as you under similar circumstances. I can't tell you what causes the error or how to stop it but i can help with this
"and I have to recreate said doc from scratch."
To recover the file you need to open the file in Excel's safe mode (hold ctrl and open Excel to activate Safe Mode) then open VBE > Debug > Compile Project. Then save and close, next time you open the file it should be fine.
Related
I am trying to automate Query refresh in MS Office Professional Plus 2016.
I have a cmd script which runs vbs script which runs Excel macro. Everything works if I run it manually. The problem occurs when I set up Windows Task Scheduler and select the option "run whether user is logged on or not".
My macro is saving query result log to text file so I can determine where the code breaks. Looks to me that Excel displays an alert box (or something similar) when running with Task Scheduler. I can not determine what is expected from user since the scheduler hides all alerts. There are no alerts/prompts if I run the cmd script manually or via Task Scheduler with option "run only if user is logged on".
Here is my RefreshQueries() sub. I tried commenting the code and confirmed that line that breaks the whole automation is .Refresh inside With iTable.QueryTable .
Private Sub RefreshQueries()
AddToLogFile ("Hello from subroutine RefreshQueries().")
Dim iWorksheet As Excel.Worksheet
Dim iTable As Excel.ListObject
'Check each worksheet.
For Each iWorksheet In Excel.ActiveWorkbook.Worksheets
AddToLogFile ("For-loop for iWorksheet " & iWorksheet.Name)
'Check all Objects if it is a query object.
For Each iTable In iWorksheet.ListObjects
If iTable.SourceType = Excel.XlListObjectSourceType.xlSrcQuery Then
AddToLogFile ("Trying to refresh iTable: " & iTable.Name)
QueryTimeStart = Timer
On Error Resume Next
With iTable.QueryTable 'Refresh the query data.
.BackgroundQuery = False
.EnableRefresh = True
.Refresh
End With
If Err.Number <> 0 Then
QueryRunTime = CalculateRunTime("QueryRunTime") 'Stop timer and get the duration.
Call AddToHtmlErrorTable(iTable.Name, Err.Number, Err.Description, QueryRunTime) 'Add entry to error table.
AddToLogFile ("Query in iTable " & iTable.Name & " failed. Description: " & Err.Description)
NumberOfFailedQueries = NumberOfFailedQueries + 1 'IMPORTANT: increment must be after updating html error table!
Err.Clear 'Clear errors between for loops.
Else
NumberOfSuccessfulQueries = NumberOfSuccessfulQueries + 1
AddToLogFile ("Query in iTable " & iTable.Name & " successfully refreshed.")
End If
End If
Next iTable
Next iWorksheet
AddToLogFile ("Exiting subroutine RefreshQueries().")
End Sub
I guess my question is as follows:
can we somehow catch what prompt Excel is showing in the background (nothing pops up if I run it manually), or
can we confirm any shown message in Excel automatically (without knowing what it is), or
are there any known settings which would execute the connection without any confirmation.
Does anyone have an idea, experience, or suggestion regarding this issue?
You need to add error catcher to your VBA routine like described here
Private Sub RefreshQueries()
On Error Goto MyError
' .... All your code
Exit sub
MyError:
'Do your magic here with Err.object to log the event or whatever
AddToLogFile ("#Error in RefreshQueries().:" & Err.Discription)
Resume Next
End Sub
I have a folder on sharepoint/onedrive business that contains Excel files. These files are produced daily by system and named by that date.
22.05.2021.xlsx
21.05.2021.xlsx
20.05.2021.xlsx
I am trying VBA script that can detect what is latest date that the system creates in this folder.
Sub Latest_file_in_range()
On Error Resume Next
Application.DisplayAlerts = False
Application.ScreenUpdating = False
For x = Now() To (Now() - 15) Step -1
Workbooks.Open Filename:="https://***-my.sharepoint.com/***/" & Format(x, "dd.mm.yyyy") & ".xlsx", UpdateLinks:=xlUpdateLinksNever
If Err = 0 Then
MsgBox x
Exit For
End If
Next
End Sub
Basically, I try to run a for loop backward from today and to exit at any loop that file is detected. This does not work. The script opens all files in the folder without stopping at the first file.
i.e. my expected result is that MsgBox shows latest file:
22.05.2021.xlsx
Please help me with this, great thanks.
As Rory wrote in the comments, your problem is that if the first file cannot be found (that's the one with the date of today), the err-object is set to an error (1004). Once the error is set, it stays there unless another error occurs that set it to the new error number. Opening a file successfully does not set the err to 0.
Two ways to handle that. A) Reset the error manually using Clear.
For x = Now() To (Now() - 15) Step -1
Workbooks.Open Filename:="https://***-my.sharepoint.com/***/" & Format(x, "dd.mm.yyyy") & ".xlsx", UpdateLinks:=xlUpdateLinksNever
If Err = 0 Then
MsgBox x
Exit For
End If
Err.Clear
Next
B) Write the result of the open-Command into a variable and check if it is set:
For x = Now() To (Now() - 15) Step -1
Dim wb as Workbook
Set wb = Workbooks.Open("https://***-my.sharepoint.com/***/" & Format(x, "dd.mm.yyyy") & ".xlsx", UpdateLinks:=xlUpdateLinksNever)
If Not wb Is Nothing Then
MsgBox x
Exit For
End If
Next
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
I sometimes use the MSForms.DataObject object from the Microsoft Forms 2.0 Object Library in Excel VBA. It is absolutely wonderful for reading / writing text data from / to the clipboard. Recently, I stumbled across this article which shows how to instantiate the object using late binding and found that it works beautifully in VBA. Now, I don't have to worry about adding the reference library each time I port my code to new projects.
That discovery made me wonder if it were possible to use the same object in VBScript. There have been several instances in the past when I wanted to manipulate the clipboard with VBScript but all my research at the time indicated that it wasn't possible (aside from using internet explorer, mshta, clip, etc). To my surprise, the DataObject worked exactly as expected when I tried to read the clibboard. However, it would not put data back into the clipboard and threw an error which makes no sense to me. Below are the details.
Error Number: -2147221008 (800401F0)
Error Description: DataObject:PutInClipboard CoInitialize has not been called.
So, is there a workaround for the error I'm getting or is it simply part of the same VBScript limitation described on MSDN and this answer?
Here is the VBScript code I used for testing on my 64 bit Windows 7 PC:
Option Explicit
Dim DObj
Sub TestClipboard()
Dim ClipData
VerifyArchitecture
If Not InitClipboardObject Then
Terminate "Unable to initialize the clipboard object"
ElseIf Not ClipboardPaste(ClipData) Then
Terminate "Unable to retrieve the clipboard data"
End If
' The message box will have the current clipboard text (if any exist)
MsgBox "The clipboard contains the following text:" & _
vbCrLf & vbCrLf & ClipData
ClipData = "Text we put in the clipboard"
' However, this function will not succeed.
If Not ClipboardCopy(ClipData) Then Terminate "Unable to put data into the clipboard"
End Sub
Function InitClipboardObject()
On Error Resume Next
' If the code is run in VBA, the following reference library
' can be used as an alternative to late binding:
' Microsoft Forms 2.0 Object Library
' Note: The reference library will not show up on the
' list unless a userform has already been added in Excel.
' If not, browse for the FM20.DLL file
Set DObj = GetObject("new:{1C3B4210-F441-11CE-B9EA-00AA006B1A69}")
InitClipboardObject = Err = 0
End Function
' Put data in the clipboard similar to pressing Ctrl + C
Function ClipboardCopy(ByVal Data)
On Error Resume Next
DObj.SetText Data
' This line of code will throw the following error
' Error Number: -2147221008 (800401F0)
' Description: DataObject:PutInClipboard CoInitialize has not been called.
' However, it works perfectly in VBA
DObj.PutInClipboard
ClipboardCopy = Err = 0
End Function
' Retrieve data from the clipboard similar to pressing Ctrl + V
Function ClipboardPaste(ByRef Data)
On Error Resume Next
DObj.GetFromClipboard
Data = DObj.GetText(1)
ClipboardPaste = Err = 0
End Function
' This sub will re-load the script using the 32 bit host
' if it is loaded on the 64 bit version. This is necessary
' since the clipboard object is 32 bit.
Sub VerifyArchitecture()
' The code in this sub is a modified version of Vozzie's answer
' and I do not take credit for the idea:
' https://stackoverflow.com/a/15320768/2734431
Dim Arch, Arg, Args, Cmd, ExeFullName, ExeShortName
Dim Path32, Path64, ProcEnv, q, Reload, ScriptName
Dim WinDir, WShell
q = Chr(34)
Reload = False
ExeFullName = WScript.FullName
ScriptName = WScript.ScriptFullName
ExeShortName = Mid(ExeFullName, InStrRev(ExeFullName, "\") + 1)
Set WShell = CreateObject("WScript.Shell")
Set ProcEnv = WShell.Environment("Process")
WinDir = ProcEnv("windir") & "\"
Path32 = WinDir & "SysWOW64\"
Path64 = WinDir & "System32\"
Arch = ProcEnv("PROCESSOR_ARCHITECTURE")
For Each Arg In WScript.Arguments
Args = " " & q & Arg & q
Next
Cmd = q & Path32 & ExeShortName & q & " " & q & ScriptName & q & Args
If InStr(LCase(ExeFullName), LCase(Path64)) <> 0 And Arch = "AMD64" Then
Reload = True
WShell.Run Cmd
End If
Set WShell = Nothing
Set ProcEnv = Nothing
If Reload Then Terminate ""
End Sub
' This sub is designed to clear any global variables, optionally
' display an error message, and stop the script
Sub Terminate(ByVal ErrMsg)
Dim ErrNbr
Set DObj = Nothing
If ErrMsg <> "" Then
ErrNbr = "Error"
If Err <> 0 Then
ErrNbr = ErrNbr & " " & Err & " (" & Hex(Err) & ")"
ErrMsg = ErrMsg & vbCrLf & vbCrLf
ErrMsg = ErrMsg & "Code Error: " & Err.Description
End If
' &H10 = vbCritical
MsgBox ErrMsg, &H10, ErrNbr
End If
WScript.Quit
End Sub
Excel AddIn, .NET 4.0, NetOffice 1.5.1.2, ExcelDNA 1.29, C#
installers calls a xls (install.xls) with VBA as follow
At the end of install.xls, Excel will close.
However, after Excel closes, Excel crashes saying "Excel stops working... please send report to Microsoft" with two buttons, one is "Don't Send", the other is send
This ONLY happens on Windows XP + Excel 2007 or WinXP + Excel 2010.
Also during debug I notice if I replace Application.Wait with MsgBox, then there is no crashes issue at all. I feel there is some kind of timing issue but really has no clue no control.
The issue drives me crazy. Please help. thanks!
Private Sub Workbook_Open()
Dim quit As Integer
Dim added As Boolean
added = Add_Addin
Application.Wait (Now + TimeValue("0:00:02"))
If Workbooks.Count = 1 Then
Application.Wait Now + TimeValue("0:00:03")
Application.quit
Else
Application.Wait Now + TimeValue("0:00:03")
Me.Close
End If
End Sub
Private Function Add_Addin() As Boolean
On Error GoTo ERR_
Dim addinFile As String
addinFile = ThisWorkbook.Path & "\" & "MyAdd-In.xll"
If Len(addinFile) > 0 Then
Dim LEA As AddIn
Set LEA = Application.AddIns.Add(addinFile)
If (Not LEA Is Nothing) Then
LEA.Installed = True
Else
MsgBox "Failed to add XLL"
End If
'If (Application.RegisterXLL(addinFile) = True) Then
' MsgBox "Yeah, succeed registering XLL"
'Else
' MsgBox "Failed to register XLL"
'End If
Else
MsgBox "XLL file not found"
End If
addinFile = ThisWorkbook.Path & "\" & "MyFunc.xla"
If Len(addinFile) > 0 Then
Dim LEA2 As AddIn
Set LEA2 = Application.AddIns.Add(addinFile)
If (Not LEA2 Is Nothing) Then
LEA2.Installed = True
Else
MsgBox "Failed to add xla"
End If
Else
MsgBox "xla file not found"
End If
Add_Addin = True
Exit Function
ERR_:
MsgBox ("Error " & Err.Number & " " & Err.Description)
Add_Addin = False
End Function
I figured it out. I kicked off a web service call asychronously with callback When Excel opens. When the callback of the web service call is executed after Excel is disposed or close, the crash occurred. The callback disbales/enables ribbon buttons based on the result from web service. I fixed it by checking if Excel is null or disposed before doing anything else in the callback.