VBA send email in outlook 2016 efficiency worsened? - excel

There is a simple piece of standard VBA code that I use to send emails by looking through a list and generating around 2000 email to the draft folder. In my old version of excel & outlook 2012, it took only around 20 min or less to generate all. But after migrated to versions of 2016, the same piece of code take up to 4 hour. Is there any way to speed it up? Thanks!
Function CreateEmail(ToSend As String, ccs As String) As Integer
Dim olapp As Outlook.Application
Dim olemail As Outlook.MailItem
Dim OutlookTemplate As String
OutlookTemplate = "TemplatePath.oft"
Set olapp = New Outlook.Application
Set olemail = olapp.CreateItemFromTemplate(OutlookTemplate)
Dim recipientCheck As Outlook.recipient
With olemail
.HTMLBody ="Body String"
.SentOnBehalfOfName = "sender email address"
.Subject = "subject string"
.To = ToSend
.CC = ccs
.Attachments.Add "attachment.pdf" ' pdf size is 200k
'this is a check for tosend email addresses,
'already tried remove this piece of code, no significant improvement there
Dim recipient As Variant
Dim i, j As Integer
For Each recipient In .recipients
recipient.Resolve
Next
.Save
End With
CreateEmail = 1
End Function

Of course, in the code each time you create a new Outlook Application instance. You can optimize the codebase by extracting such pieces into separate methods (outer) that may call the CreateEmail multiple times only for creating items.
Dim olapp As Outlook.Application
Set olapp = New Outlook.Application
Function CreateEmail(app as Outlook.Application, ToSend As String, ccs As String) As Integer
Dim olemail As Outlook.MailItem
Dim OutlookTemplate As String
OutlookTemplate = "TemplatePath.oft"
Dim recipientCheck As Outlook.recipient
Set olemail = olapp.CreateItemFromTemplate(OutlookTemplate)
With olemail
.HTMLBody ="Body String"
.SentOnBehalfOfName = "sender email address"
.Subject = "subject string"
.To = ToSend
.CC = ccs
.Attachments.Add "attachment.pdf" ' pdf size is 200k
'this is a check for tosend email addresses,
'already tried remove this piece of code, no significant improvement there
Dim recipient As Variant
Dim i, j As Integer
For Each recipient In .recipients
recipient.Resolve
Next
.Save
End With
CreateEmail = 1
End Function
You may also find the following articles helpful:
How To: Create and send an Outlook message programmatically
How to create and show a new Outlook mail item programmatically: C#, VB.NET
How To: Create a new Outlook message based on a template
How To: Fill TO,CC and BCC fields in Outlook programmatically

Related

How to Reply to All using a template?

I'm trying to do a "reply all" command using a specific template.
This is what I have so far:
Sub my_test()
Dim mail 'object/mail item iterator
Dim replyall 'object which will represent the reply email
For Each mail In Outlook.Application.ActiveExplorer.Selection
If mail.Class = olMail Then
Set replyall = mail.replyall
With replyall
.Body = "My template from a oft file"
.Display
End With
End If
Next
End Sub
In the body, I'd like to use a template which I have in a oft file in c:\mytemplate.oft.
When I reply, in the bottom I want the original email and in the top of the email body I want the text from the existing template.
The idea is to use this code (if possible), and place the context of the template body file (text and a table), inside of this reply email (in the top).
Code for Outlook. No apparent purpose for the Excel tag.
Option Explicit
Sub my_test()
Dim objItem As Object
Dim mail As MailItem
Dim replyall As MailItem
Dim templateItem As MailItem
For Each objItem In ActiveExplorer.Selection
If objItem.Class = olMail Then
Set mail = objItem
Set replyall = mail.replyall
Set templateItem = CreateItemFromTemplate("C:\template.oft")
With replyall
.HTMLBody = templateItem.HTMLBody & .HTMLBody
.Display
End With
End If
Next
End Sub

How do I save Excel attachments in new mail based on both time received and subject line?

I'm fairly new to coding. I would like to know how to save particular Excel attachments in Outlook inbox ("Morning Emails") using subject line keywords and the times received.
I receive five new emails each day, with Excel attachments to save to the same drive folder.
The time received can be either last night or early this morning.
The names of all five files and times received are different.
The inbox does not empty. I don't want to save what I saved yesterday or 2 weeks ago.
Sub SaveAttachments()
Dim ol As Outlook.Application
Dim ns As Outlook.Namespace
Dim fol As Outlook.Folder
Dim i As Object
Dim mi As Outlook.MailItem
Dim at As Outlook.Attachment
Set ol = New Outlook.Application
Set ns = ol.GetNamespace("MAPI")
Set fol = ns.Folders(1).Folders("Morning Emails")
For Each i In fol.Items
If i.Class = olMail Then
Set mi = i
If mi.Attachments.Count > 0 Then
For Each at In mi.Attachments
at.SaveAsFile "C:\Users\nader\OneDrive\Documents\" & _
at.Filename & Format(mi.ReceivedTime, " MM-DD-YYYY")
Next at
End If
End If
Next i
End Sub
"Attempted operation failed; object could not be found" for line: Set fol = ns.Folders(1).Folders("Morning Emails"), even though I have created that sub-folder under my Outlook inbox.
Set ol = New Outlook.Application
There is no need to create a new Outlook Application instance in the code. Use the Application property to get the host application instance in Outlook VBA macros.
I'd recommend handling the NewMailEx event of the Application class. This event fires once for every received item that is processed by Microsoft Outlook. The item can be one of several different item types, for example, MailItem, MeetingItem, or SharingItem. The EntryIDsCollection string contains the Entry ID that corresponds to that item.
The NewMailEx event fires when a new message arrives in the Inbox and before client rule processing occurs. You can use the Entry ID returned in the EntryIDCollection array to call the NameSpace.GetItemFromID method and process the item.
Private Sub outApp_NewMailEx(ByVal EntryIDCollection As String)
Dim itm as Object
Set itm = NS.GetItemFromID(EntryIDCollection)
Debug.Print "mail received"
If itm.Class = olMail Then
Dim it as Outlook.MailItem
Set it = itm
if it.Subject = "your subject" then
If it.Attachments.Count > 0 Then
For Each at In mi.Attachments
at.SaveAsFile "C:\Users\nader\OneDrive\Documents\" & _
at.Filename & Format(mi.ReceivedTime, " MM-DD-YYYY")
Next at
End If
End If
End If
End Sub

mailitem.entryID in Excel VBA

Can I use mailitem.entryID in Excel VBA?
I have a tool using excel where I can send an outlook email to recipients using spreadsheet as the UI to display user data. I need to store the entryID of each of the emails send to the user in the excel table. Can I set in the code (excel vba) mailitem.entryID = worksheet.cells().value ? Will it retrieve the entryID? Can you give me your input regarding this? Thank you for your help.
Dim AppOutlook As Object
Dim MailOutlook As Object
Dim Emailto, ccto, sendfrom As String
Set AppOutlook = CreateObject("Outlook.Application")
Set MailOutlook =AppOutlook.CreateItem(0)
Emailto = worksheet.Cells().Value
ccto = worksheet.Cells().Value
sendfrom = "email"
With OutMail
.SentOnBehalfOfName = sendfrom
.To = Emailto
.CC = ccto
.BCC = ""
.Subject =
.BodyFormat = olFormatHTML
.HTMLBody = "body here"
.Send
This is my code, and I plan to add the code worksheet.cells.value = MailOutlook.entryID at the last line of the code. Is it possible? and where to add the AddItem event?
You can read the EntryID property after the message is sent. You cannot do that before or immediately after sending the message - it will be changed when the message is asynchronously sent and moved to the Sent Item folder. The erliest you can access the entry id in the Sent Items folder is when the Items.ItemAdd event fires in the Sent Items folder.
The mail item may not exist any longer after calling the Send method. It can be moved to the Outbox folder for further processing by the transport provide. Item can be marked for processing by the transport provider, not being yet sent. So, we need to handle the ItemSend event in the code.
If you need to be sure that the mail item was sent for sure I'd recommend handling the ItemAdd event of the Items class (see the corresponding property of the Folder class). For example, when an Outlook item is sent, a sent copy is placed to the Sent Items folder in Outlook. You may handle the ItemAdd event for that folder to be sure that the item was sent for sure. Consider adding a user property before displaying the Outlook item and checking it in the ItemAdd event handler to identify the item uniquely.
Demo code based on your code:
Sub Test3()
Dim AppOutlook As Object
Dim MailOutlook As Object
Dim Emailto, ccto, sendfrom As String
Set AppOutlook = CreateObject("Outlook.Application")
Set MailOutlook = AppOutlook.CreateItem(0)
Emailto = Worksheets("Sheet3").Cells(1, 1).Value
ccto = Worksheets("Sheet3").Cells(2, 1).Value
sendfrom = "test#outlook.com"
With MailOutlook
.SentOnBehalfOfName = sendfrom
.To = Emailto
.CC = ccto
.BCC = ""
.Subject = "Test"
.BodyFormat = olFormatHTML
.HTMLBody = "body here"
'.Display
.Send
End With
End Sub
Some ItemAdd snippet for you reference(The current event is not the right one, we still need to test it):
Option Explicit
Private objNS As Outlook.NameSpace
Private WithEvents objItems As Outlook.Items
‘Private Sub Application_Startup()
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim objWatchFolder As Outlook.Folder
Dim AppOutlook As Object
Set AppOutlook = CreateObject("Outlook.Application")
Set objNS = AppOutlook.GetNamespace("MAPI")
'Set the folder and items to watch:
Set objWatchFolder = objNS.GetDefaultFolder(olFolderInbox)
Set objItems = objWatchFolder.Items
Set objWatchFolder = Nothing
End Sub
Private Sub objItems_ItemAdd(ByVal Item As Object)
' Your code goes here
MsgBox "Message subject: " & Item.Subject & vbcrlf & "Message sender: " & Item.SenderName &" (" & Item.SenderEmailAddress & ")"
Worksheets("Sheet3").Cells(3, 1).Value = Item.EntryID
Set Item = Nothing
End Sub
The MailItem object is part of Outlook's VBA Object library. You can see the documentation for the MailItem object on MSDN here.
To use VBA objects from a different program in Microsoft Office (eg. calling Outlook from Excel, calling Visio from Word, calling Excel from Powerpoint) you first need to make sure you have the right References selected in your Visual Basic Editor (VBE).
How to turn on Outlook references in Excel:
In Excel's VBE, go to Tools > References.
A References - VBAProject box will appear.
Under Available References: scroll down until you reach something like Microsoft Outlook 16.0 Object Library (This will differ depending on the version of Office you are using)
Tick the box and press OK.
Now the Outlook Object references have been enabled, you should be able to call Outlook objects and methods from Excel, including MailItem.

Using VBA in Outlook to Send Texts with Emojis

I have some VBA code that I'm using to send texts from Outlook to team members of my project at work. For some background: for non-AT&T subscribers, we have no issue sending text messages from Outlook by plugging in peoples' numbers en masse into the To: field of Outlook emails. However, all AT&T subscribers will receive the text as a group message, which we want to avoid. The non-AT&T subscribers correctly receive individual texts when we do a group send.
We've written some VBA code to loop through a spreadsheet of AT&T numbers so that Outlook sends one email per AT&T number. This has been working fine for us, however, we were hoping to add some emojis into the texts that we're sending. I've done a lot of Googling and searching through stackoverflows questions, and I can't seem to find any code built for this purpose. I'm also a complete noob when it comes to VBA, and I've pieced this solution together thus far from getting help from a coworker and reading through threads on the internet. This bit about emojis has given me enough trouble that I thought I'd break down and submit this post.
For reference, here is my code:
Sub EmojiTest()
Dim objOutlook As Outlook.Application
Dim objOutlookMsg As Outlook.MailItem
Dim objOutlookRecip As Outlook.Recipient
Dim MobileNumber As String
' Create the Outlook session.
Set objOutlook = CreateObject("Outlook.Application")
Set xlApp = CreateObject("Excel.Application")
'Grab list from Excel
Set xlAtt = xlApp.Workbooks.Open("C:\Users\Username\Desktop\Spreadsheet with AT&T numbers.xlsx")
xlAtt.Activate
LastRow = xlAtt.ActiveSheet.Range("B" & xlAtt.ActiveSheet.Rows.Count).End(-4162).Row
For i = 1 To LastRow
xlAtt.Activate
MobileNumber = xlAtt.ActiveSheet.Range("B" & i).Value
' Create the message.
Set objOutlookMsg = objOutlook.CreateItem(olMailItem)
objOutlookMsg.SentOnBehalfOfName = "TeamAccount#work.com"
With objOutlookMsg
' Add the To recipient(s) to the message.
Set objOutlookRecip = .Recipients.Add(MobileNumber)
objOutlookRecip.Type = olTo
' Set the Subject, Body, and Importance of the message.
.Subject = "Emoji Test"
.Body = "Text with emojis"
.Save
.Send
End With
Next i
Set objOutlook = Nothing
xlApp.Workbooks.Close
Set xlApp = Nothing
End Sub
This is code I never could have come up with myself due to my complete lack of experience with VBA, and limited experience coding in general. Any help is much appreciated.
Change:
.Body = "Text with emojis"
To:
.Body = "\ud83d\ude03"
Full list available here. Copy the box called Java escape string.
The \u escapes the unicode sequence, so typing "\u" and the UTF-16 sequence should let you insert any Emoji.
Some Emojis are actually 2 seperate char sequences, so you have to chain them together.
There is a solution at this link.
As per this, you paste smiley in an Excel cell and then read the cell value, it will be string with length 2, find the code of these 2 characters using AscW() and then chain them using Chrw, e.g. to have a smiley you can use ChrW(-10179) & ChrW(-8638).
I got it!
Sub EmojiTest()
Dim objOutlook As Outlook.Application
Dim objOutlookMsg As Outlook.MailItem
Dim objOutlookRecip As Outlook.Recipient
Dim MobileNumber As String
Dim strbody As String
' Create the Outlook session.
Set objOutlook = CreateObject("Outlook.Application")
Set xlApp = CreateObject("Excel.Application")
'Grab list from Excel
Set xlAtt = xlApp.Workbooks.Open("C:\Users\user\Desktop\Spreadsheet.xlsx")
xlAtt.Activate
LastRow = xlAtt.ActiveSheet.Range("B" & xlAtt.ActiveSheet.Rows.Count).End(-4162).Row
For i = 1 To LastRow
xlAtt.Activate
MobileNumber = xlAtt.ActiveSheet.Range("B" & i).Value
' Create the message.
Set objOutlookMsg = objOutlook.CreateItem(olMailItem)
objOutlookMsg.SentOnBehalfOfName = "Team#work.com"
strbody = "<BODY style=font-size:11pt;font-family:Segoe UI Symbol>🎉Congrats!<p>Paragraph 2.<p>Paragraph 3.</BODY>"
With objOutlookMsg
' Add the To recipient(s) to the message.
Set objOutlookRecip = .Recipients.Add(MobileNumber)
objOutlookRecip.Type = olTo
' Set the Subject, Body, and Importance of the message.
.Subject = "Emoji Test"
.HTMLBody = strbody
.Save
.Send
End With
Next i
Set objOutlook = Nothing
xlApp.Workbooks.Close
Set xlApp = Nothing
End Sub
Basically, I had to convert my message into HTML. I did that using, "Dim strbody As String" at the top, and then using ".HTMLBody = strbody" in my With statement. Once I did that, it was trivial to use the HTML hex code to enter in my emoji. Here is a page with the HTML hex code that I used (&#127881): http://www.fileformat.info/info/unicode/char/1f389/index.htm.
Learned a lot about using VBA doing this, so it was fun.
Thanks for your help Sam.

Pasting formatted Excel range into Outlook message

I would like to paste a range of formatted Excel cells into an Outlook message.
The following code (that I lifted from various sources), runs without error and sends an empty message.
Sub SendMessage(SubjectText As String, Importance As OlImportance)
Dim objOutlook As Outlook.Application
Dim objOutlookMsg As Outlook.MailItem
Dim objOutlookRecip As Outlook.Recipient
Dim objOutlookAttach As Outlook.Attachment
Dim iAddr As Integer, Col As Integer, SendLink As Boolean
'Dim Doc As Word.Document, wdRn As Word.Range
Dim Doc As Object, wdRn As Object
' Create the Outlook session.
Set objOutlook = CreateObject("Outlook.Application")
' Create the message.
Set objOutlookMsg = objOutlook.CreateItem(olMailItem)
Set Doc = objOutlookMsg.GetInspector.WordEditor
'Set Doc = objOutlookMsg.ActiveInspector.WordEditor
Set wdRn = Doc.Range
wdRn.Paste
Set objOutlookRecip = objOutlookMsg.Recipients.Add("MyAddress#MyDomain.com")
objOutlookRecip.Type = 1
objOutlookMsg.Subject = SubjectText
objOutlookMsg.Importance = Importance
With objOutlookMsg
For Each objOutlookRecip In .Recipients
objOutlookRecip.Resolve
' Set the Subject, Body, and Importance of the message.
'.Subject = "Coverage Requests"
'objDrafts.GetFromClipboard
Next
.Send
End With
Set objOutlookMsg = Nothing
Set objOutlook = Nothing
End Sub
I think you need to call .Save on your Mail Item (objOutlookMsg) after you've made all the changes.
Put .Display before .Send,
Simple but Quick fix, your problem is the email is not refreshing with the pasted contents before it sends, forcing it to Display first gives it time...
Also make sure you have another macro which runs before this to Copy the Range into your clipboard...
There is a button in excel to do this, "Send to mail recipent" its not normally on the ribbon.
You can also use the simple mapi built into office using the MailEnvelope in VBA
.. a good article on what you are trying to do http://www.rondebruin.nl/mail/folder3/mail4.htm

Resources