Updating Recipients From Outlook Add-Ins

Suppose you had an Exchange Client Extension in which you hooked events such as IExchExtMessageEvents::OnCheckNames. From this event, you’d get an IExchExtCallBack interface from which you could use the functions GetRecipients and SetRecipients to modify the recipient collection. Now suppose you’re trying to convert this code to an Outlook Add-In so you can work with Outlook 2010. The question becomes – how can I use the Outlook Object Model to modify recipients?

The short answer is: You can’t. There’s nothing in the Outlook Object Model that allows you to modify a recipient. Of course, there’s nothing in MAPI that allows you to modify a recipient either. In the MAPI world, if you wish to change a recipient on a message, you first get the recipient list, then you read the information from there and build a new recipient list with your changes. You then set the new recipient list on the message. This is what you would have done with GetRecipients/SetRecipients, and you can do something very similar with the Outlook Object Model.

Randy asked me to share this code with you to demonstrate this technique. Note what this code does: It reads the recipients off of the message, removes them, and then adds them back with whatever changes we wished to make.

The usual caveats for samples apply here - use at your own risk. Error handling is minimal, so if it crashes - it's not my fault. Enjoy!

 void OnBeforeCheckNames(Outlook::_MailItem* mailItem, bool* Cancel)
{
       // In order to be able to add recipients, set their addressEntries, and have them display, we need to
       // 1) Remove ALL recipients from the message
       // 2) Re-add them, and set their address entries
       // 3) To get the UI to update, add and remove a final recipient
    
       // Step 1: remove all recipients
       Outlook::Recipients* recipients = NULL;
       mailItem->get_Recipients(&recipients);
       long Count = 0;
       recipients->get_Count(&Count);
       while (Count > 0) {
              recipients->Remove(1);
              recipients->get_Count(&Count);
       }
   
       // Step 2: re-add them, and set their address entries
       LPCWSTR RecipentAddresses[] = { L"test1", L"test2" };
       BSTR bsUserName = SysAllocString(L"some user");
       for (int i = 0; i < 2; i++)
       {
              Outlook::Recipient* recip = NULL;
              recipients->Add(bsUserName,&recip);
              long addrType = 0;
              recip->get_Type(&addrType);
              Outlook::AddressEntry* ae = GetAddressEntry(mailItem,RecipentAddresses[i]);
              recip->putref_AddressEntry(ae);
              // need to set recipient type back to old value after re-adding
              recip->put_Type(addrType);
              VARIANT_BOOL bSuccess = NULL;
              recip->Resolve(&bSuccess);
              recip->Release();
              ae->Release();
       }
    
       // Step 3: (only needed in some event callbacks)
       // Now addresses have changed, but sometimes the UI doesn't update until
       // we add and remove a final recipient.
       // This workaround may not be needed in the BeforeCheckNames event, but is
       // needed in some other events if updating the recipients
       Outlook::Recipient* recipIgnore = NULL;
       BSTR bsUserNameIgnore = SysAllocString(L"some user");
       recipients->Add(bsUserNameIgnore,&recipIgnore);
       recipIgnore->Release();
       SysFreeString(bsUserNameIgnore);
       recipients->get_Count(&Count);
       recipients->Remove(Count);
       recipients->Release();
       SysFreeString(bsUserName);
}
Outlook::AddressEntry* GetAddressEntry(Outlook::_MailItem* mailItem,LPCWSTR name)
{
       BSTR bstrGlobalAddressList = SysAllocString(L"Global Address List");
       BSTR bstrUserName = SysAllocString(name);
       // find an address entry to use from the supplied name
       Outlook::_Application* app = NULL;
       mailItem->get_Application(&app);
       Outlook::_NameSpace* ns = NULL;
       app->get_Session(&ns);
       app->Release();
       Outlook::AddressLists* addrLists = NULL; 
       ns->get_AddressLists(&addrLists);
       ns->Release();
       VARIANT vGal = { 0 };
       vGal.vt = VT_BSTR;
       vGal.bstrVal = bstrGlobalAddressList;
       Outlook::AddressList* addrList = NULL; 
       addrLists->Item(vGal,&addrList);
       addrLists->Release();
       Outlook::AddressEntries* addrEntries = NULL; 
       addrList->get_AddressEntries(&addrEntries);
       addrList->Release();
       VARIANT vUser = { 0 };
       vUser.vt = VT_BSTR;
       vUser.bstrVal = bstrUserName;
       Outlook::AddressEntry* addrEntry = NULL; 
       addrEntries->Item(vUser,&addrEntry);
       addrEntries->Release();
       SysFreeString(bstrGlobalAddressList);
       SysFreeString(bstrUserName);
       // caller needs to call addrEntry->Release()
       return addrEntry;
}