4

I have a semi-working macro that

  1. Loops through a list of Managers
  2. Generates a email body for each manager
  3. Filters a sheet of all data relevant for each manager
  4. Converts the visible cells to a HTML table
  5. Adds the table to email
  6. Send

The issue is the macro stops generating emails every 50 iterations in and does not error out - it just appears to "run" without doing anything. I have manually stopped the macro and there is no consistent line that appears to be getting stuck. Cutting this down to bare bones as much as I can, but I have no clue where the issue is. When I step through, I can't recreate the issue. When I re-run, the first 50ish go fine and then it stops generating.

I have also tried adding Application.Wait call at the end of each loop iteration and get same issue

I end up having to CTRL + BREAK to stop the macro. When I restart its coded to pick up right where it left off and it sends the next batch just fine (meaning the line it gets paused on runs just fine when I start again). Issue is not every once in a while - it's gets stuck like clock work.


Start of macro (just generates a text body)

Sub Initiate()

    Dim EmailBody As String
    EmailBody = "HTML TEXT BODY HERE"

    Builder EmailBody     '<---- Call loop

End Sub

Performs the loop on managers and filters the other sheet for relevant data. Passes all ranges on to the macro to build email

Sub Builder(EmailBody As String)

Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Distro List")
Dim Raw As Worksheet: Set Raw = ThisWorkbook.Sheets("Email Data")

Dim LR As Long, LR2 As Long
Dim EmailTable As Range, Target As Range, EmailRange As Range

LR = ws.Range("B" & ws.Rows.Count).End(xlUp).Row
Set EmailRange = ws.Range("C2:C" & LR)
LR2 = Raw.Range("A" & Raw.Rows.Count).End(xlUp).Row

Application.ScreenUpdating = False

For Each Target In EmailRange
    If Target.Offset(, -2) = "y" Then
        If Len(Target.Offset(, -1)) = 6 Then
            If Right(Target.Offset(, 1), 7) = "@so.com" Or Right(Target.Offset(, 1), 11) = "@StackO.com" Then
            

                Raw.Range("A1:H" & LR2).AutoFilter 1, Target.Offset(, -1), VisibleDropDown:=False
                Raw.Range("A1:H" & LR2).SpecialCells(xlCellTypeVisible).Columns.AutoFit
                Set EmailTable = Raw.Range("A1:H" & LR2).SpecialCells(xlCellTypeVisible)
                            
                Sender EmailBody, EmailTable, Target
                        
                Set EmailTable = Nothing
            
            End If
        End If
    End If
Next Target

Application.ScreenUpdating = True

End Sub

Build email, call HTML Table generator macro, add HTML Table, SEND email

Sub Sender(EmailBody As String, EmailTable As Range, Target As Range)

Dim OutApp As Object
Dim OutMail As Object

On Error GoTo BNP:

    Set OutApp = CreateObject("Outlook.Application")
    Set OutMail = OutApp.CreateItem(0)
        With OutMail
            .SentOnBehalfOfName = "urdearboy@so.com"
            .to = Target.Offset(, 1)
            .Subject = "Your Employees....."
            .HTMLBody = "<p style = 'font-family:arial' >" _
                        & EmailBody & "</p>" _
                        & RangetoHTML(EmailTable) _
                        & "<p style = 'font-family:arial' >"
        
            .Send
            
            Target.Offset(, -2) = "Sent"
        End With
        
BNP:
    Set OutApp = Nothing
    Set OutMail = Nothing

End Sub

Macro I found online that converts a excel range to a HTML table that can be inserted into email.

Function RangetoHTML(EmailTable As Range)

    Dim fso As Object
    Dim ts As Object
    Dim TempFile As String
    Dim TempWB As Workbook

    TempFile = Environ$("temp") & "\" & Format(Now, "dd-mm-yy h-mm-ss") & ".htm"


    EmailTable.Copy
    Set TempWB = Workbooks.Add(1)
    With TempWB.Sheets(1)
        .Cells(1).PasteSpecial Paste:=8
        .Cells(1).PasteSpecial xlPasteValues, , False, False
        .Cells(1).PasteSpecial xlPasteFormats, , False, False
        .Cells(1).Select
        Application.CutCopyMode = False
        On Error Resume Next
        .DrawingObjects.Visible = True
        .DrawingObjects.Delete
        On Error GoTo 0
    End With

    'Publish the sheet to a htm file
    With TempWB.PublishObjects.Add( _
         SourceType:=xlSourceRange, _
         Filename:=TempFile, _
         Sheet:=TempWB.Sheets(1).Name, _
         Source:=TempWB.Sheets(1).UsedRange.Address, _
         HtmlType:=xlHtmlStatic)
        .Publish (True)
    End With

    'Read all data from the htm file into RangetoHTML
    Set fso = CreateObject("Scripting.FileSystemObject")
    Set ts = fso.GetFile(TempFile).OpenAsTextStream(1, -2)
    RangetoHTML = ts.readall
    ts.Close
    RangetoHTML = Replace(RangetoHTML, "align=center x:publishsource=", _
                          "align=left x:publishsource=")

    'Close TempWB
    TempWB.Close savechanges:=False

    'Delete the htm file we used in this function
    Kill TempFile

    Set ts = Nothing
    Set fso = Nothing
    Set TempWB = Nothing
End Function
urdearboy
  • 14,439
  • 5
  • 28
  • 58
  • This is way too much code. No idea how to trim this down and show the right part because I have no idea what is causing the pause – urdearboy Jan 22 '20 at 19:13
  • Have you tried commenting out the error handler in `Sender` ? – Tim Williams Jan 22 '20 at 19:31
  • Yes @TimWilliams - same issue. No error occurs and macro continues to run but the Outlook Outbox does not continue to climb. I pretty much keep doing the Pause/Break hot key, hit Debug, and then hit play and it picks right back up where it left off with no issue. Having to do this every 50 emails (out of about 10K so not ideal) – urdearboy Jan 22 '20 at 19:32
  • banging my head against the wall on this one. can find no error and vba wont give me one either. Just a pause – urdearboy Jan 22 '20 at 19:34
  • 1
    No other ideas - maybe add some logging into your code so you know what it's doing - if it's still running then it must be doing something... – Tim Williams Jan 22 '20 at 19:34
  • Did you remove *all* instances of "on error" statements? – Dmitry Streblechenko Jan 22 '20 at 19:44
  • @DmitryStreblechenko - that's been discussed above. When I remove the only `On Error` line the same issue persists. That is only there because I need to know when a email sent or did not send. It's purpose is to skip over `Range = Sent` so I can spot them – urdearboy Jan 22 '20 at 19:44
  • Does it make a difference if Outlook is running at the time your code is executed? – Dmitry Streblechenko Jan 23 '20 at 05:21
  • Can you share the spreadsheet? – 0m3r Mar 26 '20 at 00:41
  • @DmitryStreblechenko - no I have tried with outlook closed as well – urdearboy Aug 14 '20 at 17:17
  • Could your SMTP provider be governing your send-rate? Defense against bots? You could test this hypotheses by debug.print now() with other key info to see if the pause is happening during the send. – John Joseph Aug 14 '20 at 17:20
  • @JohnJoseph - doesn't the macro just add these to the outbox though? It seems if that is a rule on my system, VBA would be indifferent. Id be fine if the emails just land in the outbox and waiting but it's just pausing in the actual macro. Not sure why VBA would stop because of that. – urdearboy Aug 14 '20 at 17:23
  • @urdearboy, good point. I would just add that there might be some undocumented anti-automation built into Outlook to prevent high-volume botting. I recall - ten years or so ago - Outlook taking exception to some VBA automation I was attempting. – John Joseph Aug 14 '20 at 17:24
  • @urdearboy, that said, why not drop some debug.prints around your .send method to at least isolate if that's where you're getting stopped. – John Joseph Aug 14 '20 at 17:26
  • I run another email macro that sends thousands of emails with no issue. The main difference is the rangetoHTML. In hindsight that’s probably very relevant to the question and should have been added to post – urdearboy Aug 14 '20 at 17:26
  • @urdearboy, is the other send in HTML format? Spam handlers react differently to HTML emails - and to HTML emails that don't have plain-text versions - than plain-text emails. I think you really need to be at least open to the idea that your SMTP provider - or undocumented Outlook anti-automation - is the culprit here. Easy to test with some logging code. – John Joseph Aug 14 '20 at 17:32
  • The other one is using HTML body. By logging do you just mean dropping in debug.prints? Or if you had something more sophisticated in mind, hoping you can share some references/share potential ways to implement @JohnJoseph – urdearboy Aug 14 '20 at 17:49
  • @JohnJoseph just added a wait call after killing the temp workbook and it ran through the remaining 2K emails without getting hung. – urdearboy Aug 14 '20 at 18:40
  • @urdearboy, yes, I just meant debug.prints. Sounds like you solved the problem - good job! – John Joseph Aug 14 '20 at 19:02

1 Answers1

3

Extremely glad, yet also annoyed, to say that adding a Applitcation.Wait for 1 second to the function RangetoHTML fixed the issue.

    'Close TempWB
    TempWB.Close savechanges:=False

    'Delete the htm file we used in this function
    Kill TempFile
    
    Application.Wait Now + #12:00:01 AM#                 '<------ Resolved Issue

    Set ts = Nothing
    Set fso = Nothing
    Set TempWB = Nothing
End Function

Still curious to know what the actual issue is since I suspect that this is a work around to the actual issue. Just glad I can finally use this macro to send large distro's without it pausing every 4 minutes!

urdearboy
  • 14,439
  • 5
  • 28
  • 58