Ty Anderson

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?

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

You may also be interested in:

27 Comments

  • Eric Legault says:

    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.

  • Randy Erickson says:

    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!

  • Ty Anderson says:

    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

  • Amol Thikane says:

    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

  • Dmitry Kostochko (Add-in Express Team) says:

    Hi Amol,

    The PropertyAccessor object was introduced in the Outlook 2007 Object Model. Please make sure your project targets Outlook 2007 or higher.

  • Henning Weltz says:

    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

  • Andrei Smolin (Add-in Express Team) says:

    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).

  • Ketan says:

    Hello,

    Great Code..this code will work for outlook 2016 also. Please help me.

  • Andrei Smolin (Add-in Express Team) says:

    Hello Ketan,

    This code will work in Outlook 2016 as well.

  • Roger Levy says:

    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?

  • Lex Kinter says:

    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)

  • Dmitry Kostochko (Add-in Express Team) says:

    Hello Lex,

    As far as I understand you are using VBA, correct? If so, try to use the following code:
    Call RetrieveRecipientSMTPAddresses(objMailItem)

  • Lex Kinter says:

    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

  • Dmitry Kostochko (Add-in Express Team) says:

    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

  • lex kinter says:

    Worked Great.
    Thank you, Dmitry.
    You’re very kind!
    Lex

  • Lex Kinter says:

    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

  • Dmitry Kostochko (Add-in Express Team) says:

    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.

  • lex kinter says:

    Thank you, Dmitry!
    Lex

  • lex kinter says:

    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

  • Dmitry Kostochko (Add-in Express Team) says:

    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.

  • Pratima says:

    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.

  • Dmitry Kostochko (Add-in Express Team) says:

    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

  • Alex says:

    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,

  • Andrei Smolin (Add-in Express Team) says:

    Hello Alex,

    What code do you have?

  • Alex says:

    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

  • Andrei Smolin (Add-in Express Team) says:

    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.

Post a comment

Have any questions? Ask us right now!