Edit: We have compiled this program for those without coding tools.

Recently, we had a migration for a customer that did not want to bring some older email accounts into the new email system. This occurs often and we recommend removing these inactive accounts if there are not any compelling reasons to keep them. The customer initially just wanted all the emails saved onto disk so that they could search them if desired.

A follow on request to extract all the email addresses from the mailboxes was new for us. After a bit of scoping and examining the Outlook object model, we saw that this should be fairly easy. Fairly easy usually means that we can go to TechNet and get a solution to solve our problem. We did find solutions but they did not work or failed us in a few areas. Therefore, we wrote our own solution to the problem.

This post will cover a considerably pared down version of that solution so that you can accomplish the base task without much difficulty. In this version we are only looking at emails in and under the Inbox root folder.

Create a console project and add a reference to your installed version of the Outlook interop via Microsoft.Office.Interop.Outlook. We also need something to store the email addresses before we save them to a CSV. I like dictionary objects in situations like this because I can be lazy and not need to write code to check for duplicates.

This is what we have so far.

[pastacode lang=”cpp” manual=”using%20System%3B%0Ausing%20System.IO%3B%0Ausing%20System.Collections.Generic%3B%0Ausing%20System.Linq%3B%0Ausing%20Outlook%20%3D%20Microsoft.Office.Interop.Outlook%3B%0A%0Anamespace%20SyllogisticGroup.Operations.Outlook.ExtractEmailAddress%0A%7B%0A%20%20%20%20class%20Program%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%2F%2FStatic%20Variables%0A%0A%20%20%20%20%20%20%20%20%2F%2F%20Dictionary%20to%20store%20the%20emails%0A%20%20%20%20%20%20%20%20public%20static%20Dictionary%26lt%3Bstring%2C%20string%26gt%3B%20EmailAddresses%20%3D%20new%20Dictionary%26lt%3Bstring%2C%20string%26gt%3B()%3B%0A%20%20%20%20%20%20%20%20%2F%2F%20To%20store%20the%20sorted%20addresses%0A%20%20%20%20%20%20%20%20public%20static%20SortedDictionary%26lt%3Bstring%2C%20string%26gt%3B%20SortedAddresses%20%3D%20new%20SortedDictionary%26lt%3Bstring%2C%20string%26gt%3B()%3B%0A%0A%20%20%20%20%20%20%20%20static%20void%20Main()%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%7D” message=”” highlight=”” provider=”manual”/]

Note that instead of typing out Microsoft.Office.Interop.Outlook each time we created an alias of Outlook on line 5.

We are now going to look at the heart of the program, the Main method.

We want to tell our users what is happening by changing the display periodically and so you will notice a copious amount of Console.WriteLines in the code to do just that. I personally hate programs that sit there, appearing to be idle, because they fail to provide some type of feedback to the user.

The first thing we will do is grab the currently running Outlook instance as an application. If Outlook is not running, then this will start a new application.

[pastacode lang=”cpp” manual=”%20%20%20%20%20%20%20static%20void%20Main()%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%2F%2FKick%20off%20the%20program%0A%20%20%20%20%20%20%20%20%20%20%20%20Console.WriteLine(%22Outlook%20Address%20Extractor%22)%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%2F%2FIf%20Outlook%20is%20not%20running%20already%2C%20this%20will%20also%20start%20it%0A%20%20%20%20%20%20%20%20%20%20%20%20Outlook.Application%20application%20%3D%20Activator.CreateInstance(Type.GetTypeFromProgID(%22Outlook.Application%22))%20as%20Outlook.Application%3B” message=”” highlight=”” provider=”manual”/]

Next, we are first going to check that the application variable is not null. Then we are going to pull all the accounts from the profile and create a way cool, ah, simple menu to choose from. If this was a GUI application, then we could make a cool menu.

2016-09-27_19-57-19

[pastacode lang=”cpp” manual=”%20%20%20%20%20%20%20%20%20%20%20%20if%20(application%20!%3D%20null)%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2FLets%20get%20the%20accounts%20in%20the%20active%20Outlook%20profile%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Outlook.Accounts%20accounts%20%3D%20application.Session.Accounts%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20while%20(true)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20We%20clear%20the%20dictionary%20here%2C%20so%20that%20we%20don’t%20mix%20the%20emails%20up%20with%20other%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20if%20we%20ran%20it%20more%20than%20twice%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20EmailAddresses.Clear()%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2FThis%20next%20section%20generates%20the%20cool%20selector%20list%20%3A)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20int%20id%20%3D%201%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20foreach%20(Outlook.Account%20account%20in%20accounts)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Console.WriteLine(id%20%2B%20%22%3A%22%20%2B%20GetAccountEmailAddress(account))%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20id%2B%2B%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Console.WriteLine(%22Q%3A%20Quit%20Application%22)%3B” message=”” highlight=”” provider=”manual”/]

We will then wait for the user to make a selection. Notice that again, we keep checking for null values. You should always try to make your application crash proof. Well, reasonably crash proof. We will analyze the selection to determine which account the user selected. We also ensure that their selection matched one of the selections presented.

[pastacode lang=”cpp” manual=”%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2FNow%20we%20wait%20for%20a%20selection%20to%20be%20made%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var%20readLine%20%3D%20Console.ReadLine()%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(readLine%20!%3D%20null)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2FWe%20examine%20the%20selection%20to%20determine%20which%20account%20to%20use%20or%20quit%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20string%20response%20%3D%20readLine.ToUpper()%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(response%20%3D%3D%20%22Q%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Console.WriteLine(%22Quitting%22)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(response%20!%3D%20%22%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(Int32.Parse(response.Trim())%20%26gt%3B%3D%201%20%26amp%3B%26amp%3B%20Int32.Parse(response.Trim())%20%26lt%3B%20id)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Console.WriteLine(%22Processing%3A%20%22%20%2B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20accounts%5BInt32.Parse(response.Trim())%5D.DisplayName)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Console.WriteLine(%22Processing%3A%20%22%20%2B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20GetAccountEmailAddress(accounts%5BInt32.Parse(response.Trim())%5D))%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2FHere%20we%20parse%20the%20response%20and%20move%20on%20to%20getting%20our%20email%20addresses%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Outlook.Folder%20selectedFolder%20%3D%20GetFolder(%40%22%5C%5C%22%20%2B%20accounts%5BInt32.Parse(response.Trim())%5D.DisplayName)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20GetFolders(selectedFolder)%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2FNow%20will%20sort%20the%20results%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Console.WriteLine(%22Sorting%20the%20results.%22)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SortEmailAddresses()%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2FNow%20saving%20the%20results%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Console.WriteLine(%22Saving%20the%20results.%22)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SaveAddressesToFile(accounts%5BInt32.Parse(response.Trim())%5D.DisplayName)%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2FAnd%20finally%2C%20we%20tell%20the%20user%20what%20we%20found%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Console.WriteLine(%22Finished%20Processing%20%22%20%2B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20accounts%5BInt32.Parse(response.Trim())%5D.DisplayName)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Console.WriteLine(EmailAddresses.Count%20%2B%20%22%20addresses%20Found%22)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Console.WriteLine(%22%22)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20else%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Console.WriteLine(%22Invalid%20Selection%22)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D” message=”” highlight=”” provider=”manual”/]

In the snippet above, notice that after we validated that the user selected a proper account, we get the display name of the account and send that to a method called GetFolders. In addition, you see that we will call methods to sort, save, and then display messages to the user about the results. We will briefly cover those later.

The GetFolders method is a doozy! It calls itself repeatedly in the middle of the method. What is going on here? We of course know that this is recursion. Recursion simply means that the method or function calls itself. It is great for dealing with situations like this. We do not know how many child folders a user may have in their inbox or how many folders those folders may have. Instead of just counting folders and keeping a list of them, we use recursion. If a folder has a child folder, then we call GetFolders with that folder’s name. Eventually, we get to the bottom of the tree and at that point, we start getting messages by using the GetMessages method. As each child folder completes, the folder above it gets focus again. Once all the child folders in that folder are processed, then it is that folders turn.

So let us say that our user has the following folder tree.

Inbox

  • Contracts
    • Signed
  • Personal
    • Fishing Tips
    • Vacation Ideas

What would be the expected processing order of the folders? If you said, Signed, Contracts, Fishing Tips, Vacation Ideas, Personal, and then Inbox, pat yourself on the back as you understand how recursion works! Yeah!

After all that, now you see why the first thing the method does is look for child folders. You may ask why we are not using the term subfolder instead of child folder. Simple, Outlook does not. We are trying to use the names that the Outlook object model uses. The method is below and I hope that it is easy to understand now.

[pastacode lang=”cpp” manual=”%20%20%20%20%20%20%20%20%2F%2F%20We%20will%20recursion%20to%20dive%20through%20the%20entire%20Outlook%20tree%0A%20%20%20%20%20%20%20%20private%20static%20void%20GetFolders(Outlook.Folder%20folder)%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20Outlook.Folders%20childFolders%20%3D%20folder.Folders%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20(childFolders.Count%20%3E%200)%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20foreach%20(Outlook.Folder%20childFolder%20in%20childFolders)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F*%20This%20is%20where%20we%20limit%20ourselves%20to%20the%20inbox%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20*%20%20This%20can%20easily%20be%20changed%20to%20look%20at%20the%20entire%20mailbox%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20*%20%20And%20every%20object%20in%20it%20but%20we%20won’t%20do%20that%20here%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20*%2F%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(childFolder.FolderPath.Contains(%22Inbox%22))%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20So%20the%20user%20some%20progress%20indicators%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Console.WriteLine(childFolder.FolderPath)%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20Now%20this%20function%20calls%20itself%2C%20which%20is%20recursion%2C%20to%20again%20look%20for%20subfolders%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20GetFolders(childFolder)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20If%20there%20are%20not%20any%20more%20subfolders%2C%20then%20we%20start%20looking%20at%20messages%0A%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20This%20will%20eventually%20run%20on%20every%20folder%20due%20to%20the%20recursion%20without%20us%20having%20to%20write%0A%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20a%20lot%20of%20code%20structure%20to%20handle%20it%0A%20%20%20%20%20%20%20%20%20%20%20%20Console.WriteLine(%22Checking%20in%20%22%20%2B%20folder.FolderPath)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20GetMessages(folder)%3B%0A%20%20%20%20%20%20%20%20%7D%0A” message=”” highlight=”” provider=”manual”/]

Jump to the next page so that we can continue.