Can't send multiple Outlook Messages - vba

I can send a single Outlook message using Excel VBA. However, I want to loop through my rows and send an email for each row that meets a certain condition.
Unfortunately, when I put the email code in a for loop only one email gets sent or none at all (depending on how I structure the code).
Is there something about calling Outlook multiple times that I should know?
Private Sub CommandButton1_Click()
Dim OutApp As Object
Dim OutMail As Object
Dim myValue As Variant
Dim contactRange As Range
Dim cell As Range
Dim toAddy As String, nextAddy As String
Dim i As Integer
Set contactRange = Me.Range("ContactYesNo")
myValue = InputBox("Enter body of email message.")
For Each cell In contactRange
If Range(Cells(cell.Row, cell.Column).Address).Value = "Yes" Then
nextAddy = Range(Cells(cell.Row, cell.Column).Address).Offset(0, 5).Value
toAddy = nextAddy & ", " & toAddy
End If
Next cell
If Len(toAddy) > 0 Then
toAddy = Left(toAddy, Len(toAddy) - 2)
End If
For i = 0 To 1 'short loop for testing purposes
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
With OutMail
.To = toAddy
.CC = ""
.BCC = ""
.Subject = "test email"
.Body = myValue
.Send
End With
Set OutMail = Nothing
Set OutApp = Nothing
Next i
End Sub

Take the CreateObject line out of the loop:
Set OutApp = CreateObject("Outlook.Application")
For i = 0 To 1 'short loop for testing purposes
Set OutMail = OutApp.CreateItem(0)
...

I've tried to clean up your logic stream but there are many unanswered questions due to the lack of sample data, explicit error messages and output.
Private Sub CommandButton1_Click()
Dim outApp As Object
Dim outMail As Object
Dim myValue As Variant
Dim contactRange As Range
Dim cell As Range
Dim toAddy As String, nextAddy As String
Dim i As Integer
Set outApp = CreateObject("Outlook.Application")
Set contactRange = Me.Range("ContactYesNo")
myValue = InputBox("Enter body of email message.")
With Worksheets(contactRange.Parent.Name) '<~~ surely you know what worksheet you are on..!?!
For Each cell In contactRange
If cell.Value = "Yes" Then 'no need to define a range by the range's address
nextAddy = cell.Offset(0, 5).Value 'again, no need to define a range by the range's address
toAddy = nextAddy & ";" & toAddy 'use a semi-colon to concatenate email addresses
End If
Next cell
End With
If Len(toAddy) > 0 Then
toAddy = Left(toAddy, Len(toAddy) - 2) 'I have no idea why you need to shorten the toAddy by 2
'only send mail where one or more addresses exist
For i = 0 To 1 'short loop for testing purposes
Set outMail = outApp.CreateItem(0)
With outMail
.To = toAddy
.CC = ""
.BCC = ""
.Subject = "test email"
.Body = myValue
.Send
End With
Set outMail = Nothing
Next i
End If
Set outApp = Nothing
End Sub

OK, so I re-wrote the code based on the feedback. I used a loop to send emails one at a time instead of concatenating the addresses together as I wanted to personalize each email. I also needed to create a form to handle the input as inputbox only accepts 256 characters.
A form was pretty much required as I needed to capture the subject line, message body, salutation, path the to the attachment etc.:
Private Sub CommandButton1_Click()
Dim subject As String, msg As String, path As String
subject = TextBox1.Value
msg = TextBox2.Value & vbCrLf & vbCrLf & "Sincerely," & vbCrLf & TextBox4.Value & vbCrLf & TextBox5
path = TextBox3.Value
UserForm1.Hide
Module1.sendEmail subject, msg, path
End Sub
I placed the email code in Module1. Note, be sure to set the .sentOnBehalfOfName attribute or Outlook will simply pick an account which may not be the one you want if you have multiple accounts registered:
Public Sub sendEmail(subject As String, msg As String, path As String)
Dim outApp As Object
Dim outMail As Object
Dim contactRange As Range, cell As Range
Dim toAddy As String, emailMsg As String
Dim count As Integer
Set outApp = CreateObject("Outlook.Application")
Set contactRange = Range("ContactYesNo")
With Worksheets("IT consulting")
For Each cell In contactRange
If cell.Value = "Yes" Then
count = count + 1
toAddy = cell.Offset(0, 6).Value
emailMsg = "Dear " & cell.Offset(0, 2).Value & "," & vbCrLf & vbCrLf & msg
Set outMail = outApp.CreateItem(0)
With outMail
.SentOnBehalfOfName = "me#someemail.com"
.To = toAddy
.CC = ""
.BCC = ""
.subject = subject
.Body = emailMsg
.Attachments.Add path
'.Display
.Send
End With
'log the action
cell.Offset(0, 1).Value = Now & vbCrLf & cell.Offset(0, 1).Value
End If
Set outMail = Nothing
Next cell
End With
Set outApp = Nothing
MsgBox "total emails sent: " & count
End Sub

Related

Sending an automatic email based on cell value

I have this code that I have cobbled together but, sadly I am stuck I can’t seem to work out how to only have the email addresses for overdue entries in the BCC.
I want it to create a single email to multiple email addresses from a list of emails that have a due date that is overdue and a previous email hasn't already been sent.
Sub Over_due()
Dim OutApp As Object
Dim OutMail As Object
Dim lLastRow As Long
Dim lRow As Long
Dim rng As Range
strbody = "Text goes here"
lLastRow = Cells(Rows.Count, 3).End(xlUp).Row
For lRow = 2 To lLastRow
If Cells(lRow, 6) <> "Email Sent" Then
If Cells(lRow, 5) <= Date Then
Set xOutlook = CreateObject("Outlook.Application")
Set xMailItem = xOutlook.CreateItem(0)
For Each rng In Range("C:C")
If rng.Value Like "*#*" Then
If xEmailAddr = "" Then
xEmailAddr = rng.Value
Else
xEmailAddr = xEmailAddr & ";" & rng.Value
End If
End If
Next
On Error Resume Next
With xMailItem
.To = ""
.CC = ""
.BCC = xEmailAddr
.Subject = Range("A1").Value
.HTMLBody = strbody
'.Attachments.Add
.Display
End With
MsgBox "E-mail successfully created", 64
Application.DisplayAlerts = False
Set Mail_Object = Nothing
Cells(lRow, 6) = "Sent email"
Cells(lRow, 7) = "" & Now()
End If
End If
Next
Set OutApp = Nothing
End Sub
I use a sub which automatically creates emails. and call it from various other subs - might come in handy:
Sub SendEmail(Optional ToAddresses As String, Optional CcAddresses As String, _
Optional BccAddresses As String, Optional Subject As String, _
Optional Body As String, Optional AttachFiles As Variant = False, Optional AutoSend As Boolean = False)
'RULES:
' Where there are multiple Addresses in ToAddresses, CCAddresses
' etc, they have to be separated by a semicolon
' AttachFiles should either be a string containing the full
' filename including the path, or (for multiple files) an array
' of same.
' Body can be HTML or just plain text.
Dim OutApp As Object
Dim OutMail As Object
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
With OutMail
.to = ToAddresses
.CC = CcAddresses
.Bcc = BccAddresses
.Subject = Subject
If Body Like "*</*>*" Then
.HtmlBody = Body
Else
.Body = Body
End If
If Not AttachFiles = False Then
If IsArray(AttachFiles) Then
For x = LBound(AttachFiles) To UBound(AttachFiles)
.Attachments.Add (AttachFiles(x))
Next
Else
.Attachments.Add (AttachFiles)
End If
End If
If AutoSend = True Then
.Send
Else
.Display
End If
End With
Set OutMail = Nothing
Set OutApp = Nothing
End Sub
It's not totally my code, I adapted it from here.
It won't completely solve your problem, but it might condense it down to something simpler and more like:
Sub OverDue()
Dim strBody as String
Dim Row as Long
Dim lLastRow as Long
StrBody = "Text here"
lLastRow = UsedRange.Rows.Count
For a = 2 to lLastRow
If Cells(a, 6) <> "Email Sent" And Cells(a, 5)<= Date Then 'This checks each row to see if that person needs an email
' DO STUFF HERE
' Either Call the other sub separately each time
' (which can allow for more personalised messages, like a mail merge),
' or add the person's email address to a string and call the sub
' after the loop.
Next
End Sub
Over to you to work out the rest of the details though!!
I fixed your code like that
Sub Over_due()
Dim OutApp As Object
Dim OutMail As Object
Dim lLastRow As Long
Dim lRow As Long
Dim rng As Range
Dim strbody As String
Dim xOutlook
Dim xMailItem
Dim xEmailAddr
strbody = "Text goes here"
lLastRow = Cells(Rows.Count, 3).End(xlUp).Row
For lRow = 2 To lLastRow
If Cells(lRow, 6) <> "Email Sent" Then
If Cells(lRow, 5) <= Date Then
Set xOutlook = CreateObject("Outlook.Application")
Set xMailItem = xOutlook.CreateItem(0)
' For Each rng In Range("C:C")
' If rng.Value Like "*#*" Then
' If xEmailAddr = "" Then
' xEmailAddr = rng.Value
' Else
' xEmailAddr = xEmailAddr & ";" & rng.Value
' End If
' End If
' Next
'Do you really want to have all emails addresses in BCC because thats what you are doing
'I changed the above code to the following lines which will not take the complete column
Set rng = Range("C2:C" & lRow)
xEmailAddr = Join(WorksheetFunction.Transpose(rng), ",")
On Error Resume Next
With xMailItem
.To = ""
.CC = ""
.BCC = xEmailAddr
.Subject = Range("A1").Value
.HTMLBody = strbody
'.Attachments.Add
.Display
End With
MsgBox "E-mail successfully created", 64
Application.DisplayAlerts = False
' I changed that to Email Sent otherwise it will create the mai over and over again
Cells(lRow, 6) = "Email Sent"
Cells(lRow, 7) = "" & Now()
End If
End If
Next
Set OutApp = Nothing
End Sub

Email Multiple Recipients VBA Error

Looking for help with sending emails to a list of people. My code has a simple loop and grabs the value each time through of where to send the email. While testing, the first email will always get sent. After that, the 2nd time through I get error on ".To"
Run-time error - '-2147221238 (8004010a):
The item has been moved or deleted.
This is puzzling to me because the code does accurately grab the next email value?
The emails need to be sent one by one, instead of adding the recipients to a list of bcc. Is this possible with VBA? Thanks in advance!
Sub TestingAgain()
'Setting up the Excel variables.
Dim outApp As Object
Dim outMailItem As Object
Dim iCounter As Integer
Dim sDest As String
Dim sName As String
'Create the Outlook application and the empty email.
Set outApp = CreateObject("Outlook.Application")
Set outMailItem = outApp.CreateItem(0)
With outMailItem
sDest = ""
For i = 2 To WorksheetFunction.CountA(Columns(1))
If i <> "" Then
'Grab first name and email
sDest = Cells(i, 5).Value
sName = Cells(i, 1).Value
'Send each email
.To = sDest
.Subject = "FYI"
.htmlbody = "Some stuff"
.Send
Else
MsgBox ("Error")
End If
Next i
End With
'Clean up the Outlook application.
Set outMailItem = Nothing
Set outApp = Nothing
End Sub
When you send the e-mail, the mailItem instance is done and not available anymore. Refactor your code like :
Sub TestingAgain()
'Setting up the Excel variables.
Dim outApp As Object
Dim outMailItem As Object
Dim iCounter As Integer
Dim sDest As String
Dim sName As String
'Create the Outlook application and the empty email.
Set outApp = CreateObject("Outlook.Application")
sDest = ""
For i = 2 To WorksheetFunction.CountA(Columns(1))
If i <> "" Then
'/ Create the mail item instance.
Set outMailItem = outApp.CreateItem(0)
With outMailItem
'Grab first name and email
sDest = Cells(i, 5).Value
sName = Cells(i, 1).Value
'Send each email
.To = sDest
.Subject = "FYI"
.htmlbody = "Some stuff"
.send
'/ Once sent, mail item is no more available.
End With
Else
MsgBox ("Error")
End If
Next
'Clean up the Outlook application.
Set outMailItem = Nothing
Set outApp = Nothing
End Sub

Returning a String Array in Outmail.body

I am using the already known Subroutine to send a warning email to outlook whenever a condition is met.
In the routine I define an string array under the name DatePassed in which I store some dynamical values and I intent to return it's content in the Subject of the email.
The problem is that I don't know how exactly to handle DatePassed so to return me the whole array not just the first element.
How would I do this?
Sub Mail_workbook_Outlook_1()
Dim OutApp As Object
Dim OutMail As Object
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
On Error Resume Next
Dim DatePassed(100) As Variant
Dim i As Integer
For i = 6 To 13
If Cells(i, 1) < Date Then
DatePassed(i - 6) = Cells(i, 2)
End If
Next i
With OutMail
.To = "Joerge#Johnson.com"
.CC = "James#Johnson.com"
.BCC = ""
.Subject = "Unmanaged Clients"
.Body = DataPassed
.Attachments.Add ActiveWorkbook.FullName
.Send
End With
On Error GoTo 0
Set OutMail = Nothing
Set OutApp = Nothing
End Sub
Try this.
I have just added a Loop to run through the Array and stored it as a string which then gets assigned to the .Body
Sub Mail_workbook_Outlook_1()
Dim OutApp As Object
Dim OutMail As Object
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
On Error Resume Next
Dim DatePassed(100) As Variant
Dim i As Integer
For i = 6 To 13
If Cells(i, 1) < Date Then
DatePassed(i - 6) = Cells(i, 2)
End If
Next i
'=================================================
'New Section
Dim DataPassedElementReference As Long
Dim DataPassedString As String
DataPassedString = ""
'Using 100 as this is waht you used to define the array
For DataPassedElementReference = 1 To 100
DataPassedString = DataPassedString & DataPassed(DataPassedElementReference) & " "
Next DataPassedElementReference
'=================================================
With OutMail
.To = "Joerge#Johnson.com"
.CC = "James#Johnson.com"
.BCC = ""
.Subject = "Unmanaged Clients"
'Note the difference here
'.Body = DataPassed
.Body = DataPassedString
.Attachments.Add ActiveWorkbook.FullName
.Send
End With
On Error GoTo 0
Set OutMail = Nothing
Set OutApp = Nothing
End Sub

Excel email macro seems to be triggered whenever a cell is populated

I have a excel spreadsheet which tracks important dates, if you have missed an invoice for that date it will automatically send out an email to the relevant person reminding them to do so. The macro functions well and works off a trigger
`
Private Sub Worksheet_Change(ByVal Target As Range)
Dim lr As Long, lr2 As Long, r As Long
lr = Sheets("2015").Cells(Rows.Count, "A").End(xlUp).Row
For r = lr To 2 Step -1
If Range("Q" & r).Value = "Received" Then
Call Macro1
End If
If Range("K" & r).Value = "Overdue" Then
Call email
End If
Next r
End Sub
`
Sub email()
Dim OutApp As Object
Dim OutMail As Object
Dim FName As String
Sheets("2015").Activate
For i = 1 To 100
If Sheets("2015").Range("K" & i).Value = "Overdue" Then
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
FName = ActiveWorkbook.Path & "\" & ActiveWorkbook.Name
With OutMail
.To = Sheets("2015").Range("S" & i).Value
.CC = ""
.Subject = Sheets("2015").Range("B" & i).Value
.Body = "You have missed a valuation date for this project!"
.Send
End With
Set OutMail = Nothing
Set OutApp = Nothing
End If
Next i
End Sub
'
As you can see I am new to this so most of my code is from different examples on the internet. The problem I have is that when a cell in column K meets the requirements "Overdue" the macro will repeatedly email the user when a change in any other cell occurs. That is if K5 = "Overdue" and I go to A6 to enter a new user, the user in A5 will be emailed again. This happens for all cells until I change cell k5 to "sent".
I suspect part of the problem may also be in my e-mail macro...
I give up XD
Your macro's are working fine, I mean at least as you code them. You need to remove your macro's form FOR loops. What you can do is just getting the address of the changed cell and use it to define the mail address and subject. Something like below
** Worksheet macros**
Public mailAddress As String
Public TargetRow As Long
Public mailSubject
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Value = "Overdue" Then
mailAddress = Sheets("2015").Range("S" & Target.Row)
mailSubject = Sheets("2015").Range("B" & Target.Row)
TargetRow = Target.Row
Call email
ElseIf Target.Value = "Received" Then
Call Macro1
End If
End Sub
And your macro should like something like this:
Sub email()
Dim OutApp As Object
Dim OutMail As Object
Dim FName As String
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
FName = ActiveWorkbook.Path & "\" & ActiveWorkbook.Name
With OutMail
.To = mailAddress
.CC = ""
.Subject = mailSubject
.Body = "You have missed a valuation date for this project!"
.Send
End With
Set OutMail = Nothing
Set OutApp = Nothing
End Sub
Can you try and post answer here? I dont have excel right now, I could not try it.

Sending multiple e-mail messages VBA

I tried the following code (I changed the real e-mail address), and it does work on the first cell in range, but after the first one, it gives me an error says: "run time error, the item has been moved or deleted", and then, it does not sending the others.... what should I need to fix in code ?
Sub sendMailWithLoop()
Dim missmatchCell As Range
Dim Missmatches_Rng As Range
Dim entityForRepeatedValues_Rng As Range
Dim OutMail As Object
Dim OutApp As Object
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
If Range("D1000").End(xlUp).Value <> "Name" Then
Set Missmatches_Rng = Range(Range("D1000").End(xlUp), Range("D1000").End(xlUp).End(xlUp).Offset(1, 0))
Missmatches_Rng.Select
For Each missmatchCell In Selection
With OutMail
.To = "mymail#servername.com"
.Subject = "Attention !! missmatch found"
.Body = "The missmatch name is: " & missmatchCell.Offset(0, 1) & ", on: " & missmatchCell
.Send
End With
Next
End If
End Sub
thx !!
Move your Set OutMail = OutApp.CreateItem(0) inside the For as:
Sub sendMailWithLoop()
Dim missmatchCell As Range
Dim Missmatches_Rng As Range
Dim entityForRepeatedValues_Rng As Range
Dim OutMail As Object
Dim OutApp As Object
Set OutApp = CreateObject("Outlook.Application")
If Range("D1000").End(xlUp).Value <> "Name" Then
Set Missmatches_Rng = Range(Range("D1000").End(xlUp), Range("D1000").End(xlUp).End(xlUp).Offset(1, 0))
Missmatches_Rng.Select
For Each missmatchCell In Selection
Set OutMail = OutApp.CreateItem(olMailItem)
With OutMail
.To = "mymail#servername.com"
.Subject = "Attention !! missmatch found"
.Body = "The missmatch name is: " & missmatchCell.Offset(0, 1) & ", on: " & missmatchCell
.Send
End With
Next
End If
End Sub