Using Outlook Recipient and Recipients collection – guide for developers
Writing email can get you in a lot of trouble if you are not paying attention. I can tell you about some seriously funny (on retrospect) situations caused when I inadvertently added a recipient that I did not intend to add. I can tell you but I won’t (unless we meet in person and you buy me a beer). Let’s just say that Outlook’s autofill feature is not your friend and is not to be trusted.
Moving on. Today’s topic is the Outlook Recipient… what is it and what can you do with it?
- What is the Outlook Recipients collection?
- What is a Recipient?
- When to use Recipients?
- How to work with a recipient
- What are the main properties and methods?
- What events should I typically use?
What is the Outlook Recipients collection?
This will surprise you. The Recipients collection in Outlook is the object that contains all recipients in an email.
What is a Recipient?
I think you know this already. But for the sake of covering all the facts, the Recipient object is a single entry/address within the Recipients collection. For those of you from West Texas (I can make fun of them, I grew up there), Recipients are the email addresses you input in the TO, CC and BCC fields using the Outlook user interface.
When to use Recipients?
Use them anytime you use code to create MailItems (or AppointmentItems).
How to work with a recipient
We’ll follow tradition here and begin with the parent collection. Where there is a Recipient object there is most definitely a Recipients collection.
Enumerate Recipients
The Recipients class is a child of Outlook’s MailItem object. The sample EnumerateRecipients procedure takes a passed MailItem object and loops through its each recipient.
Public Sub EnumerateRecipients(ByRef email As Outlook._MailItem) Dim recs As Outlook.Recipients = email.Recipients For i As Integer = 1 To recs.Count Dim r As Outlook.Recipient = recs.Item(i) If Not r.Resolved Then r.Resolve() End If Marshal.ReleaseComObject(r) Next Marshal.ReleaseComObject(recs) End Sub
As the procedure accesses each Recipient object, it resolves the recipient address against the Outlook address book.
How to add a recipient
To add a Recipient to an email, you need to access the Recipients collection and call its Add method.
Public Sub AddRecipient(ByRef email As Outlook._MailItem) 'BCC My Self on all emails Dim recs As Outlook.Recipients = email.Recipients Dim r As Outlook.Recipient = recs.Add("Ty Anderson") r.Resolve() Marshal.ReleaseComObject(r) Marshal.ReleaseComObject(recs) End Sub
The Add method needs to know the name of the recipient you want to add. After you add them, I recommend you call Resolve.
How to edit recipients
Editing recipients is done by adding or deleting email recipients. Meaning, you already know 50% of the recipients editing routine. I’ll show you the other 50% now.
How to remove a recipient
Removing a recipient is as accessing a MailItem‘s Recipients collection and calling the Remove method. Unfortunately, you can’t pass the name of the recipient you want to remove. Instead, you need to loop through the Recipients collection and compare the Recipient.Name to a passed string.
Public Sub RemoveRecipient(ByRef email As Outlook._MailItem, name As String) Dim recs As Outlook.Recipients = email.Recipients For i As Integer = recs.Count To 1 Step -1 Dim r As Outlook.Recipient = recs.Item(i) If r.Name = name Then recs.Remove(i) End If Marshal.ReleaseComObject(r) Next Marshal.ReleaseComObject(recs) End Sub
I’m looping backwards because if I remove an item, the collection’s Count property lowers. Thus, it’s best to start counting at the highest number and work to the lowest.
How to retrieve a recipients SMTP email address
You want to get the SMTP address for a Recipient? Sounds simple but that isn’t necessarily so. Exchange has a peculiar habit of return nonsensical strings when you access the Recipient.Address property. The best method is to access a MAPI property that will reliably provide you with the SMTP email address.
Public Sub RetrieveRecipientSMTPAddresses(ByRef email As Outlook._MailItem) 'An assist from: ' https://msdn.microsoft.com/en-us/library/office/ff868695(v=office.15).aspx Dim recs As Outlook.Recipients = email.Recipients Dim stringOfEmails As String = Nothing Const PR_SMTP_ADDRESS As String = "https://schemas.microsoft.com/mapi/proptag/0x39FE001E" For i As Integer = 1 To recs.Count Dim r As Outlook.Recipient = recs.Item(i) Dim pa As Outlook.PropertyAccessor = r.PropertyAccessor If Not r.Resolved Then r.Resolve() stringOfEmails = stringOfEmails & pa.GetProperty(PR_SMTP_ADDRESS) & vbCrLf Marshal.ReleaseComObject(pa) Marshal.ReleaseComObject(r) Next Marshal.ReleaseComObject(recs) MsgBox(stringOfEmails, MsgBoxStyle.OkOnly, "Email Addresses") End Sub
The sample above moves through the Recipients collection and builds a string containing all recipient emails. The trick is to use the Recipient.PropertyAccessor and its GetProperty method (along with the correct MAPI property).
Resolve against the address book
When you use the Outlook user interface to add recipients, sometimes Outlook doesn’t recognize them automatically as a valid email address. The way you know this is the recipient is not underlined. To force Outlook to check the Outlook Address book for matching recipients, you click the Check Names button in the Outlook Ribbons. If Outlook finds a match or the email is a valid email address, Outlook underlines the recipient and considers the recipient resolved. This is the same as calling Recipient.Resolve as I’ve shown in several of the previous examples.
I just wanted to give a proper explanation of what was going on AND to point out Resolve as a valid code sample. You might have missed it earlier as I sort of threw it with other samples. If you missed, check it out. It’s worth knowing.
What are the main properties and methods?
Surprisingly, the Outlook Recipients collection is not property and method “rich”. It’s a bit of a pauper compared to the other Outlook objects. In the samples that follow, I cover the ones that matter.
Delete
The Delete method achieves the same results as the Recipients.Remove method. The difference is the Delete method belongs to the Recipient object.
Public Sub DeleteExternalAddresses(ByRef email As Outlook._MailItem) Dim recs As Outlook.Recipients = email.Recipients Dim myDomain As String = "add-in-express.com" For i As Integer = recs.Count To 1 Step -1 Dim r As Outlook.Recipient = recs.Item(i) If Not r.Address.Contains(myDomain) Then r.Delete() End If Marshal.ReleaseComObject(r) Next Marshal.ReleaseComObject(recs) End Sub
In the sample above, the code loops through the Recipients collection. For each Recipient, it checks if the recipient’s Address is outside of the company domain. If it is, the code deletes the Recipient.
We don’t need anyone leaking news of upcoming products to the press before the big event.
FreeBusy
Sometimes, when sending email, you might want to know if they are currently available to read your email or not. If not, you might want to create a reminder to check back with that person later. This is just what the following sample does.
Public Sub CheckIfFree(ByRef email As Outlook._MailItem) Dim recs As Outlook.Recipients = email.Recipients For i As Integer = 1 To recs.Count Dim r As Outlook.Recipient = recs.Item(i) Dim scheduleInfo As String = r.FreeBusy(Now(), 5) If scheduleInfo.Contains("00") Then Else 'create reminder Dim reminder As Outlook._AppointmentItem = _ OutlookApp.CreateItem(Outlook.OlItemType.olAppointmentItem) With reminder .Subject = "Check that " & r.Address & " read your email." .Start = Now().AddHours(1) .End = Now().AddHours(1) .ReminderSet = True .ReminderMinutesBeforeStart = 0 .Save() End With Marshal.ReleaseComObject(reminder) End If Marshal.ReleaseComObject(r) Next Marshal.ReleaseComObject(recs) End Sub
The code checks the recipient’s schedule for the next 90 minutes. If they are busy during this time, the code creates an AppointmentItem and sets a reminder (or alert).
What events should I typically use?
None. There aren’t any. The sooner you accept this fact, the better it will go for you.
*******
I’ll admit to not giving Recipients their due. Some of these samples are based upon real issues caused by “bad emailing”. I bet you can think of a few ideas as well from your emailing history. But now, you know you can do something about it.
Available downloads:
This sample Outlook add-in was created using Add-in Express for Office and .net:
VB.NET Outlook Recipients sample
27 Comments
Nice article! Here’s a tip: Resolve will sometimes fail even for valid SMTP addresses, but it’s unlikely. If it does fail, remove unnecessary Outlook Address Books or change their resolution order. Note that unresolved Recipient objects cannot be added to a DistListItem via AddMember.
Great explanation of Recipient! One question though, is it even necessary to Resolve the Recipients? I am running into issues when e-mailing outside of our company and therefore the names are not in my address book. If I do not use Resolve, then the Exchange server should send an error e-mail back to me if the recipients address is errant, correct?
Thanks!
Hi Randy,
The Resolve method works with an Outlook Address book (exchange, local contacts, etc). If the user is not in any of the address books, the Resolve method will fail.
In my example, I use Resolve because the Recipient.Add method asks for the name of a contact (e.g “Ty Anderson”). I use Resolve to force Outlook to validate the addition against an address book.
If you are only adding an email address to the TO, CC, or BCC fields, you can add the email and send.
But, if you are adding names to the Recipients collection, I recommend trying to Resolve them as you add them. This way, you can respond to a failed Resolve how you choose.
Does this info help?
Ty
How to retrieve a recipients SMTP email address
This code do not work for me because I am not getting PropertyAccessor for Recipient instance.
I use – Add in express version 8.0 from
https://visualstudiogallery.msdn.microsoft.com/A4880BFE-A230-44B6-9D23-86AFAA1A2997
Hi Amol,
The PropertyAccessor object was introduced in the Outlook 2007 Object Model. Please make sure your project targets Outlook 2007 or higher.
Hi,
Great code samples. Can you help me? When finding out (in VB6 code) that I cannot Resolve and address, I would like to either add it to Address book and carry on or not add it but also not display it. I.e. I want to send the email without any outside “interfering”
Regards,
HWW
Hello Henning,
I would try to call Recipient.AddressEntry.Update() to save a recipient to the Address book; find details at https://msdn.microsoft.com/en-us/library/office/ff860722%28v=office.15%29.aspx. I don’t think you will be able to do this if the recipient is unresolved. You need to call ResolveAll, delete unresolved recipients, and send the email if there are recipients remaining (otherwise, sending the email will fail).
Hello,
Great Code..this code will work for outlook 2016 also. Please help me.
Hello Ketan,
This code will work in Outlook 2016 as well.
Dmitry Kostochko said “make sure your project targets Outlook 2007 or higher.” That is not an option for me since I need to target 2003 or higher. Is there a way to avoid getting what the OP called “nonsensical strings when you access the Recipient.Address” from Exchange?
Hello Roger,
Check https://www.add-in-express.com/creating-addins-blog/outlook-exchange-email-address-smtp/.
I get an “Object Required” error on the below code (at the bottom line). I’m using the code above exactly as-is. Please help!
Sub ImportEmails(dtmFrom As Date, dtmTo As Date)
On Error GoTo Err_cmdTest_Click
‘Use Early Binding for Outlook Declarations
‘from
‘https://www.utteraccess.com/forum/index.php?showtopic=2044461&st=0&gopid=2651043&#entry2651043
Dim myOlApp As Object
Dim myOlItems As Object
Dim objMailItem As Object
Dim objNS As Object
Dim oMAPIFldr As Object
Dim intCtr As Integer
Dim MyDB As DAO.Database
Dim rst As DAO.Recordset
Dim recip As Outlook.Recipient ‘ see TEST below
DoCmd.Hourglass True
‘>>BEGIN TEST 080417
‘Dim recip As Recipient
Dim allRecips As String
‘>>END TEST 080417
Set MyDB = CurrentDb
Set rst = MyDB.OpenRecordset(“tblEMails”, dbOpenDynaset, dbAppendOnly)
Set myOlApp = CreateObject(“Outlook.Application”)
Set myOlItems = myOlApp.GetNamespace(“MAPI”).GetDefaultFolder(6).Items
‘Set Outlook Folders to use (Inbox)
Set objNS = myOlApp.GetNamespace(“MAPI”)
Set oMAPIFldr = objNS.GetDefaultFolder(6) ‘my inbox
‘******* USER DEFINED SECTION *******
Const conFROM As Date = #7/1/2017#
Const conTO As Date = #7/6/2017#
‘************************************
CurrentDb.Execute “DELETE * FROM tblEMails”, dbFailOnError ‘Clear Table
Set objMailItem = oMAPIFldr.Items(1)
For Each objMailItem In myOlItems
If TypeName(objMailItem) = “MailItem” Then ‘An E-Mail
If objMailItem.SentOn >= dtmFrom And objMailItem.SentOn <= dtmTo Then 'in Date Range?
intCtr = intCtr + 1
'BEGIN TEST 080417B
RetrieveRecipientSMTPAddresses (objMailItem)
Hello Lex,
As far as I understand you are using VBA, correct? If so, try to use the following code:
Call RetrieveRecipientSMTPAddresses(objMailItem)
Hi, Dmitry! Thanks for listening.
I am using VBA (within MS Access).
I think your comment helped … but
I’m working through a couple of other issues right now (if you have comment – they’d be appreciated).
First – I think there must be something different with what I’m doing here (maybe using Access VBA?)… I got an object error at this line
Dim stringOfEmails As String = Nothing
changing it to … String=”” fixed that
Second – Run-time error ’91’: Object variable or With block variable not set
and the debug cursor goes to
recs = email.Recipients
When I use immediate window, I type “?email.” the Recipients pops up. Since we’re passing the MailItem Byref – shouldn’t it work?
Thanks
Hello Lex,
I have ported the VB.NET code to VBA, please try it:
Sub RetrieveRecipientSMTPAddresses(ByRef email As Outlook.MailItem)
‘ An assist from:
‘ https://msdn.microsoft.com/en-us/library/office/ff868695(v=office.15).aspx
Dim recs As Outlook.Recipients
Set recs = email.Recipients
Dim stringOfEmails As String
Dim PR_SMTP_ADDRESS As String
PR_SMTP_ADDRESS = “https://schemas.microsoft.com/mapi/proptag/0x39FE001E”
Dim i As Integer
For i = 1 To recs.Count
Dim r As Outlook.Recipient
Set r = recs.Item(i)
Dim pa As Outlook.PropertyAccessor
Set pa = r.PropertyAccessor
If Not r.Resolved Then
Call r.Resolve
End If
stringOfEmails = stringOfEmails & pa.GetProperty(PR_SMTP_ADDRESS) & vbCrLf
Set pa = Nothing
Set r = Nothing
Next
Set recs = Nothing
MsgBox (stringOfEmails)
End Sub
Worked Great.
Thank you, Dmitry.
You’re very kind!
Lex
Hello Again, Dmitry.
objMailItem.SenderEmailAddress
gives something like
/O=EXCHANGELABS/OU=EXCHANGE ADMINISTRATIVE GROUP (FYDIBOHF23SPDLT)/CN=RECIPIENTS/CN=976E92F494C440A78BBB5EC961F51FBA-[UserName]
Do you know why? do I need the
PropertyAccessor.GetProperty(PR_SMTP_ADDRESS)
for this too?
Thanks again
Hello Lex,
It is an Exchange-based email address. I would suggest that you get the Sender property instead of SenderEmailAddress. The Sender property returns the AddressEntry object and you can use its GetExchangeUser() method to retrieve ExchangeUser.PrimarySmtpAddress.
Thank you, Dmitry!
Lex
Hopefully you’re still listening Dmitri!
when email is sent to a distribution list, the recipients list does not include all the addresses – only the distribution email address. Have you ever ran into this?
I a importing emails into Access and was simply going to store as Recipients – not as To, CC, BCC, etc.
Thanks again for your thoughts.
Lex
Hello Lex,
In this case, try to check the Members collection of the AddressEntry property of the Recipient object. BTW, the AddressEntry.AddressEntryUserType property may be useful for you as well.
I am VSTO for outlook addin.
Just want to know how to check if the sender and recipients domain are same or different.
any advice Will be helpful.
Hello Pratima,
To retrieve a list of recipients, you can handle the Recipients collection:
https://docs.microsoft.com/en-us/office/vba/api/outlook.mailitem.recipients
As for the sender email address, please see the Sender property:
https://docs.microsoft.com/en-us/office/vba/api/outlook.mailitem.sender
Hello,
I’m new to vba scripting and have an issue with recipients.count. I want to get the value of recipients.count and use that value in a if statement to see how many recipients decline my meeting and based on the declined value change the outlook calendar a specified color. How can I achieve this in vba? Currently my code gets a compile error: Can’t assign to read-only property.
Thanks,
Hello Alex,
What code do you have?
Hello Andrei,
This is the code im working on.
Sub CheckDeclines(oRequest As MeetingItem)
‘ only check declines
If oRequest.MessageClass “IPM.Schedule.Meeting.Resp.Neg” Then
Exit Sub
End If
Dim oAppt As AppointmentItem
Set oAppt = oRequest.GetAssociatedAppointment(True)
Dim myAttendees As Integer
Dim objAttendees As Outlook.recipients
Set objAttendees = oAppt.recipients
For x = 1 To objAttendees.Count
If objAttendees(x).Type = olRequired Then
oAppt.Categories = “Declined;”
objAttendees.Count = myAttendees
End If
Next
If myAttendees = 1 Then
oAppt.Categories = “Declined;”
oAppt.Categories = “Yellow Category”
Else
If myAttendees = 2 Then
oAppt.Categories = “Declined;”
oAppt.Categories = “Orange Category”
Else
oAppt.Categories = “Declined;”
oAppt.Categories = “Red Category”
End If
End If
oAppt.Display
End Sub
Hello Alex,
In order to write “If myAttendees = 2”, you must change myAttendees somewhere in your code. You don’t do this anywhere. You should debug your code to find out how it works and how this differs from your intentions. Debugging allows you to locate the error (hint: see objAttendees.Count = myAttendees). Use google to learn how to debug VBA code, how to set breakpoints, how to get the values of local variables while the code execution is stopped. Place the text cursor on a property/method (such as Count) and press {F1} to get to the description of the property; pay attention whether the property is writable. Press {F2} to open the VBA Object browser; it lets you find other classes/properties/methods available; google for how to use it.