As developers we often find ourselves writing small one off applications to perform a specific “here and now” requirement. The task was to produce a list of e-mail addresses from a Microsoft Outlook folder. I’m reasonably happy with my understanding of the Microsoft Outlook object model, well, happy enough that I’ve managed to get it to do what I need it to do. I am by no means an expert, however since I’ve been making reasonable progress with my “duplicate e-mail remover” application, I figured that iterating over an Outlook folder extracting the e-mail address on they way couldn’t be too hard.
Indeed, for SMTP e-mail addresses, it’s fairly straightforward – a mail message in Outlook has a SenderEmailAddress property that holds the e-mail address in the format name@domain.com.
In a corporate environment you may find X400 formatted addresses similar to this one:
/O=/OU=GLOBAL/CN=RECIPIENTS/CN=FBUTCHER76409312
The aforementioned SenderEmailAddress is also used to store this address, so if you were expecting an SMTP address, too bad!
However, mail messages also have a string property SenderEmailType which returns EX for mail items that use Exchange Server addressing. Here’s how you might use it:
// Catch e-mails sitting on an Exchange Server...
if (oMsg.SenderEmailType == "EX")
{
display = GetEmailAddressFromExchange(oMsg.SenderName);
}
else
// Otherwise, assume SMTP addresses
display = string.Format("{0}", oMsg.SenderEmailAddress);
The eagle-eyed reader will have noticed the call to GetEmailAddressFromExchange. I really wanted an SMTP address for all the mail items in the folder(s) in question. A little bit of searching revealed this well-written article by Julian Biddle at An Original Idea.
Julian’s article explained how to get an e-mail address from an Exchange Server address using Visual Basic in .NET. I prefer to use C#, so set about converting Julian’s code (which was known to work). There are other flavours of this code floating on the Internet, this one works for me, your mileage may vary.
The entire application can be downloaded here. This little application is crude, but it solved a problem for me at the time of writing. It demonstrates how to iterate over Microsoft Outlook folders (not just the Inbox) building a treeview on the way. It also demonstrates how to save the contents of a ListView control to a CSV file.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.Office.Interop.Outlook;
using System.Runtime.InteropServices;
using System.IO;
namespace OutlookEmailAddressExtractor
{
public partial class frmExtractEmailAddresses : Form
{
public frmExtractEmailAddresses()
{
InitializeComponent();
}
const int S_OK = 0;
[DllImport("MAPI32.DLL", CharSet = CharSet.Ansi, EntryPoint = "HrGetOneProp@12")]
private static extern void HrGetOneProp(IntPtr pmp, uint ulPropTag, out IntPtr ppProp);
[DllImport("MAPI32.DLL", CharSet = CharSet.Ansi, EntryPoint = "HrSetOneProp@8")]
private static extern void HrSetOneProp(IntPtr pmp, IntPtr pprop);
[DllImport("MAPI32.DLL", CharSet = CharSet.Ansi, EntryPoint = "MAPIFreeBuffer@4")]
private static extern void MAPIFreeBuffer(IntPtr lpBuffer);
[DllImport("MAPI32.DLL", CharSet = CharSet.Ansi)]
private static extern int MAPIInitialize(IntPtr lpMapiInit);
[DllImport("MAPI32.DLL", CharSet = CharSet.Ansi)]
private static extern void MAPIUninitialize();
const string IID_IMAPIProp = "00020303-0000-0000-C000-000000000046";
const uint PR_SMTP_ADDRESS = 972947486;
struct SPropValue
{
public uint ulPropTag;
public uint dwAlignedPad;
public long Value;
}
Microsoft.Office.Interop.Outlook.Application oApp;
NameSpace oNS;
private string GetEmailAddressFromExchange(string emailName)
{
MailItem loDummyMsg = (MailItem)oApp.CreateItem(OlItemType.olMailItem);
Recipient loAddress = loDummyMsg.Recipients.Add(emailName);
loAddress.Resolve();
string SMTPAddress = GetMAPIProperty(loAddress.AddressEntry.MAPIOBJECT, PR_SMTP_ADDRESS);
return SMTPAddress;
}
private string GetMAPIProperty(object oMAPIObject, uint uiPropertyTag)
{
if (oMAPIObject == null)
{
return "";
}
string sProperty = "";
IntPtr pPropValue = IntPtr.Zero;
IntPtr IUnknown = IntPtr.Zero;
IntPtr IMAPIProperty = IntPtr.Zero;
try
{
MAPIInitialize(IntPtr.Zero);
IUnknown = Marshal.GetIUnknownForObject(oMAPIObject);
Guid guidMAPIProp = new Guid(IID_IMAPIProp);
if (Marshal.QueryInterface(IUnknown, ref guidMAPIProp, out IMAPIProperty) != S_OK)
{
return "";
}
try
{
HrGetOneProp(IMAPIProperty, uiPropertyTag, out pPropValue);
if (pPropValue == IntPtr.Zero)
{
return "";
}
SPropValue propValue = (SPropValue)Marshal.PtrToStructure(pPropValue, typeof(SPropValue));
sProperty = Marshal.PtrToStringAnsi(new IntPtr(propValue.Value));
}
catch (System.Exception ex)
{
throw ex;
}
}
finally
{
if (pPropValue != IntPtr.Zero)
{
MAPIFreeBuffer(pPropValue);
}
if (IMAPIProperty != IntPtr.Zero)
{
Marshal.Release(IMAPIProperty);
}
if (IUnknown != IntPtr.Zero)
{
Marshal.Release(IUnknown);
}
MAPIUninitialize();
}
return sProperty;
}
void _PopulateFolderList(MAPIFolder oFolder, TreeNode node)
{
foreach (MAPIFolder folder in oFolder.Folders)
{
string s =String.Format("{0} ({1}) [{2}]",
folder.Name,
folder.Folders.Count,
folder.Items.Count);
TreeNode thisNode = node.Nodes.Add(s);
thisNode.Tag = folder;
if (folder.Folders.Count > 0)
{
_PopulateFolderList(folder, thisNode);
}
}
}
private void btnGetFolders_Click(object sender, EventArgs e)
{
oApp = new Microsoft.Office.Interop.Outlook.Application();
oNS = oApp.GetNamespace("MAPI");
progressBar.Minimum = 0;
progressBar.Maximum = oNS.Folders.Count;
progressBar.Value = 0;
this.Cursor = Cursors.WaitCursor;
foreach (MAPIFolder f in oNS.Folders)
{
TreeNode node = tvOutlookFolders.Nodes.Add(f.Name);
if (f.Name != "Public Folders" && f.Name != "Internet Calendars")
_PopulateFolderList(f, node);
progressBar.Value++;
}
progressBar.Value = 0;
this.Cursor = Cursors.Arrow;
}
private void btnGetEmailAddresses_Click(object sender, EventArgs e)
{
TreeNode nodeSelected = tvOutlookFolders.SelectedNode;
MAPIFolder folder = (MAPIFolder)nodeSelected.Tag;
this.Cursor = Cursors.WaitCursor;
lvEMailInSelectedFolder.Items.Clear();
if (folder != null)
{
progressBar.Maximum = folder.Items.Count;
progressBar.Minimum = 0;
progressBar.Value = 0;
string sClassComp = "IPM.Note";
Items oItems = folder.Items;
label1.Text = oItems.Count.ToString();
MailItem oMsg = (MailItem)oItems.GetFirst();
while (oMsg != null)
{
if (oMsg.MessageClass == sClassComp)
{
if (oMsg.Body != null)
{
string display;
// Catch e-mails sitting on an Exchange Server...
if (oMsg.SenderEmailType == "EX")
{
display = GetEmailAddressFromExchange(oMsg.SenderName);
}
else
// Otherwise, assume SMTP addresses
display = string.Format("{0}", oMsg.SenderEmailAddress);
bool okToAdd = true;
if (cbIgnoreDuplicates.Checked)
okToAdd = lvEMailInSelectedFolder.FindItemWithText(display) == null;
if (okToAdd)
{
ListViewItem item;
item = lvEMailInSelectedFolder.Items.Add(display);
item.SubItems.Add(oMsg.SenderName.ToString());
if (oMsg.Subject != null)
item.SubItems.Add(oMsg.Subject.ToString());
else
item.SubItems.Add(" ");
item.SubItems.Add(oMsg.SenderEmailType);
}
}
}
bool exceptionRaised = true;
while (exceptionRaised)
{
try
{
oMsg = (MailItem)oItems.GetNext();
progressBar.Value++;
exceptionRaised = false;
}
catch
{
exceptionRaised = true;
}
}
}
lvEMailInSelectedFolder.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
btnSaveToCSV.Enabled = true;
progressBar.Value = 0;
this.Cursor = Cursors.Arrow;
}
}
private void btnSaveToCSV_Click(object sender, EventArgs e)
{
StringBuilder listViewContent = new StringBuilder();
foreach (ListViewItem item in this.lvEMailInSelectedFolder.Items)
{
listViewContent.Append("\"");
listViewContent.Append(item.Text);
listViewContent.Append("\"");
listViewContent.Append(",");
listViewContent.Append("\"");
listViewContent.Append(item.SubItems[1].Text);
listViewContent.Append("\"");
listViewContent.Append(Environment.NewLine);
}
saveFileDialog.FileName = "e-mail addresses.csv";
saveFileDialog.Filter = "CSV files (*.csv)|*.csv|All files (*.*)|*.*";
saveFileDialog.ShowDialog();
if (saveFileDialog.FileName != "")
{
TextWriter tw = new StreamWriter(saveFileDialog.FileName);
tw.WriteLine(listViewContent.ToString());
tw.Close();
}
}
private void tvOutlookFolders_AfterSelect(object sender, TreeViewEventArgs e)
{
if (tvOutlookFolders.SelectedNode != null)
{
btnGetEmailAddresses.Enabled = true;
}
}
}
}
Technorati Tags: GetEmailAddressFromExchange, Julian Biddle, An Original Idea, Microsoft Outlook, Outlook, Microsoft Exchange Server, Exchange Server, Exchange, e-mail address, ListView, Save, CSV, SaveToCSV