Searching for Contacts in VSTO


I LOVE VSTO v3!


As part of my demos for Graham Seach’s Office DevCon, I’ve developed a VSTO v3 Outlook Add-in that adds a form region to an incoming e-mail if:



  1. The sender is in my contacts (based on the e-mail address); and
  2. The contact has a non-null, not-empty company name

Here’s what I did to start off with:

// look up the contact from the sender of the mail
Outlook.MailItem thisMail = (Outlook.MailItem)e.OutlookItem;
Outlook.MAPIFolder ContactFolder =
(Outlook.MAPIFolder)thisMail.Application.Session.GetDefaultFolder(Microsoft.Office.Interop.Outlook.OlDefaultFolders.olFolderContacts);
foreach (Outlook.ContactItem contact in ContactFolder.Items)
{

Outlook.ContactItem contact = (Outlook.ContactItem)obj;

if ((contact.Email1Address == thisMail.SenderEmailAddress ||
contact.Email2Address == thisMail.SenderEmailAddress ||
contact.Email3Address == thisMail.SenderEmailAddress) &&
!(contact.CompanyName.Trim() == “”))
{
// Instantiate the service and get the details of the company’s sales
SalesDetails.svcSalesDetails.SalesDetailsServiceClient svc =
new SalesDetails.svcSalesDetails.SalesDetailsServiceClient();

_salesDetails = svc.GetSalesDetails(contact.CompanyName);
found = true;
break;
}
}


This threw an InvalidCastException in the foreach line complaining that the object (of type System.__comobject) couldn’t be cast to a ContactItem.


So next I looped through the items in the folder as a collection of objects and did the cast within the loop. 

foreach (System.Object obj in ContactFolder.Items)
{
Outlook.ContactItem contact = (Outlook.ContactItem)obj;

Same issue (at the cast line again). I’m not sure why I thought this would work any better.


I got a little sidetracked by the first paragraph of Sue Mosher‘s response to this question on OutlookCode.com and added

Marshal.ReleaseComObject(contact);

at the bottom of the loop (especially as by this stage I’d discovered that it wasn’t the first object in the collection that was throwing the exception – it was about the 270th – near enough to Sue’s 250).


Actually, I was running into the issue addressed in her second paragraph, but I didn’t read that. It took me a call to Nick Randolph (just before we went off to play hockey) to realise that a contact folder can contain things other than Contacts. Thanks Nick.


An easy way to filter the collection is to use the Restrict() method:

Outlook.Items colItems = ContactFolder.Items.Restrict(“[MessageClass]=’IPM.Contact'”);

Finally things were working, but they were still SLOW. Turns out it’s lots quicker to let the built-in search function do the heavy lifting; in particular the Find() method. The final incarnation of my find code now looks like this:

Outlook.ContactItem contact;
if (e.OutlookItem is Outlook.MailItem)
{
// look up the contact from the sender of the mail
Outlook.MailItem thisMail = (Outlook.MailItem)e.OutlookItem;
Outlook.MAPIFolder ContactFolder =
(Outlook.MAPIFolder)thisMail.Application.Session.GetDefaultFolder(Microsoft.Office.Interop.Outlook.OlDefaultFolders.olFolderContacts);

Outlook.Items colItems = ContactFolder.Items.Restrict(“[MessageClass]=’IPM.Contact'”);
string FilterString = “[Email1Address] = ‘” + thisMail.SenderEmailAddress + “‘ OR “ +
“[Email2Address] = ‘” + thisMail.SenderEmailAddress + “‘ OR “ +
“[Email3Address] = ‘” + thisMail.SenderEmailAddress + “‘”;
contact = (Outlook.ContactItem)colItems.Find(FilterString);
}
else
{ …


The moral of the story is two-fold:



  1. Check your types before attempting to cast; and

  2. The built-in methods for searching or filtering are almost always better than those you try to roll yourself (it’s all about the platform, man!)

Comments (0)