Have you ever seen this message? It’s not an error. You can’t click Debug and go see which line of code it’s on when this happens. You can’t even click Cancel. All you can do is click OK every 10 seconds or so until it’s done. Brutal.
I ran into this message recently on some code that someone else wrote but that I’d modified (see how I’m already deflecting the blame). The code runs through a hundred or so customers and sends them an email. Each customer has its own worksheet and that worksheet is turned into HTML to be used in the body of the email. Incidentally they used Ron de Bruin’s RangeToHTML function to do the conversion. I happened to have written that function back when I had a website called dicks-clicks.com. Ah, memories.
The code I modified was working well for a few weeks before it started acting up. One line in the code looks like this
1 |
sh.ExportAsFixedFormat xlTypePDF, sAttachFile |
That saves the sheet as a PDF. I use the ExportAsFixedFormat method a lot in loops and I get the
1 |
Run-time error 1004. Document not saved. the document may be open, or an error may have been encountered when saving |
error every so often. When I get this error I hit Debug and F5 and everything works fine. I know it’s a timing issue, but haven’t taken the time to figure out how to avoid it. It started happening on this customer email workbook. I couldn’t hardly ask a normal user to click Debug, F5, and close the VBE when it’s done. I’m not a monster.
I put a one second delay before line to allow Windows to have time to release the file lock or whatever the heck is going on. It only executes on about seven of the 100 customers, so it makes a 10 second procedure run in 17 seconds. We can live with that. And it worked. No more errors.
Everything was fine until the OLE Action message started popping up a couple days later. Of course when anything goes wrong after a code change, you have to blame the code change. The angry villagers were at my door demanding that the one second delay be removed. I wasn’t convinced (spoiler: I’m the hero in this story). I sat at the user’s computer, ran the code, and got the message. I changed the status bar to show me which customer it was on when the message appears. When I ran the code again, it was on Vandelay Industries. We looked at some past Vandelay emails and we noticed that the format was all messed up in yesterday’s email, but otherwise looked OK. Formatting problems don’t cause OLE messages, so I ignored it.
I did some Binging and saw my old buddy Shane Devonshire recommended checking the
1 |
Ignore other applications that use Dynamic Data Exchange |
checkbox in Tools – Options. I could tell he was grasping at straws, but I was at the straw-grasping stage, so I went with it. I ran the code for the third time (a tedious process because of all the OLE messages) and it got stuck on Vandelay Industries again. A clue!
I discussed this new information with the user. Since Vandelay did not get a pdf attachment, I concluded that the problem was Outlook and not whatever generates PDFs from Office. Maybe we messed up the email address and Outlook was churning away trying to resolve it. Nope, no change there. But he did mention that he added a note to the bottom of their worksheet. More specifically, he copied the note from another customer’s sheet and pasted it to Vandalay’s. Oh, and one other thing. When he pasted the message, he accidentally selected the entire row, which put the message in every cell in that row, rather than just the first one. But he deleted all the extraneous messages, so it was fine.
“Aha!”, I said. I went to their sheet and pressed Ctrl+End. That took me to cell XFD92. In the code, the (now enormous) UsedRange was being passed to RangeToHTML. I went to the Outbox in Outlook and there was a 43MB message sitting there staring back at me. The OLE Action that Excel was waiting for Outlook to complete was rending 43MBs of HTML in a message.
The quick fix was to delete all the columns in that sheet that I didn’t want, save, close, and reopen. Fixed. As for the code, it’s tempting to use an alternative method for finding the real used range. That solves the email problem, but it doesn’t fix the root of the problem – a messed up UsedRange that’s unnecessarily bloating the file.
In the end, I decided to test the number of columns and rows in the UsedRange, and if they’re over a threshold, raise an error. That will allow the user to fix the root and rerun the procedure.
I don’t use usedrange, too unreliable. I picked this up somewhere, give me the last row and column if I need it.
Sub Real_lastrow()
Dim lastcol As Long
Dim RealLastRow As Long
Dim ws As Worksheet
Dim arr As Variant
Dim i As Long
Set ws = Worksheets(1)
lastcol = ws.Cells(1, Columns.Count).End(xlToLeft).Column
ReDim arr(1 To lastcol)
For i = 1 To lastcol
arr(i) = ws.Cells(Rows.Count, i).End(xlUp).Row
Next
RealLastRow = Application.Max(arr)
Debug.Print RealLastRow
End Sub
Its fast but only works with visible rows and columns: so if there is a Filter active it may give the wrong answer.
I had/have this problem when using the Matlab and SpreadsheetLink – which allows you to send and received data and code to and from Matlab within Excel/VBA. When Matlab takes too long and Excel gets bored, it starts popping up this message. Solution? Get the Matlab dev to optimise his code!
I’ve been using the following for awhile. I’ve attempted to handle hidden rows and also check for pictures.
I have run into a few times when I was creating files and attaching them where file locks caused odd errors. When you write Microsoft Visual Basic for Applications macros or routines, you may need to test whether another process already opened a file or still has it opened before you perform any operations on the file. https://support.microsoft.com/en-us/kb/209189