Eugene Astafiev

Advanced search in Outlook programmatically: C#, VB.NET

Today I will tell you a story about searching items in Outlook. The most powerful and reliable search is using the AdvancedSearch method of the Application class. However, be aware that the Outlook 2000 Object Model doesn’t provide such a method. You need to use the Outlook’s Find/FindNext or Restrict method of the Items class in this case.

As a user, you may have seen the following well-known dialog in Outlook:

Advanced Find dialog in Outlook

The background mechanics of this dialog is the subject of our today’s discussion. Let’s consider the AdvancedSearch method in depth.

AdvancedSearch method in Outlook

The key benefits of using the AdvancedSearch method in Outlook are:

  • The search is performed in another thread. You don’t need to run another thread manually since the AdvancedSearch method runs it automatically in the background.
  • Possibility to search for any item types: mail, appointment, calendar, notes etc. in any location, i.e. beyond the scope of a certain folder. The Restrict and Find/FindNext methods can be applied to a particular Items collection (see the Items property of the Folder class in Outlook).
  • Full support for DASL queries (custom properties can be used for searching too). You can read more about this in the Filtering article in MSDN. To improve the search performance, Instant Search keywords can be used if Instant Search is enabled for the store (see the IsInstantSearchEnabled property of the Store class).
  • Finally, you can stop the search process at any moment using the Stop method of the Search class.

The AdvancedSearch method accepts four parameters. The first parameter is required and the other three parameters are optional:

  1. Scope – The scope of the search. Here you specify in what folders you would like to search for items.
  2. Filter – Parameters of the search (the DASL query).
  3. SearchSubFolders – Allows you to specify whether to include subfolders to the search results (scope).
  4. Tag – The name of the search. Allows you to identify the search later.

Multiple searches can be run in Outlook simultaneously. The main restriction of this is the Outlook performance penalty.

Starting from Outlook 2002, the Outlook’s Object Model provides the AdvanvedSearchComplete event of the Application class. An instance of the Search class containing the search results is passed to the event handler (see the Results property). However, because multiple searches can be run simultaneously we need to identify our search in the event handler. The Tag property of the Search class does exactly what we need. It returns a string we passed to the AdvancedSearch method to identify the search. So, you can compare the ID (the Tag property) in the event handler and process the found results.

The Outlook Object Model also provides the AdvancedSearchStopped event. The signatures of the AdvancedSearchStopped and AdvanvedSearchComplete event handlers are the same. The AdvancedSearchStopped event is triggered when the Stop method is called on the Search object to cancel searching. However, the AdvancedSearchComplete is called afterwards anyway.

Admittedly, the Search class allows you to save the results of searching in a search folder (actually, it doesn’t contain any items, only references to items from the scope folders). You just need to call the Save method on the Search object in the AdvanvedSearchComplete event handler. I used the late binding technology for calling it to draw your attention to the fact that there is no such method in Outlook 2002. It was introduced in Outlook 2003. So, you need to handle results in the code manually if you/your customers still use Outlook 2003.

I have developed a sample code in VB.NET and C# for Add-in Express and VSTO based add-ins to illustrate the functionality of the AdvancedSearch method in action. The RunAdvancedSearch method accepts an instance of the Application class and a string that indicates a word you want to use in the search filter. I passed the “ID” string to search for items with a subject containing ID symbols. For the Scope parameter, I used the Inbox and Sent Mail folders.

    C# and Add-in Express:
using Outlook = Microsoft.Office.Interop.Outlook;
// ...
string advancedSearchTag = "Our first advanced search in Outlook";
 
private void RunAdvancedSearch(Outlook._Application OutlookApp, string wordInSubject)
{
    string scope = "Inbox";
    string filter = "urn:schemas:mailheader:subject LIKE \'%"+ wordInSubject +"%\'";            
    Outlook.Search advancedSearch = null;
    Outlook.MAPIFolder folderInbox = null;
    Outlook.MAPIFolder folderSentMail = null;
    Outlook.NameSpace ns = null;
    try
    {
        ns = OutlookApp.GetNamespace("MAPI");
        folderInbox = ns.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox);
        folderSentMail = ns.GetDefaultFolder(
                                       Outlook.OlDefaultFolders.olFolderSentMail);
        scope = "\'" + folderInbox.FolderPath + 
                                      "\',\'" + folderSentMail.FolderPath + "\'";
        advancedSearch = OutlookApp.AdvancedSearch(
                                        scope, filter, true, advancedSearchTag );
     }
     catch(Exception ex)
     {
         MessageBox.Show(ex.Message, "An eexception is thrown");
     }
     finally
     {
         if(advancedSearch!=null) Marshal.ReleaseComObject(advancedSearch);
         if (folderSentMail != null) Marshal.ReleaseComObject(folderSentMail);
         if (folderInbox != null) Marshal.ReleaseComObject(folderInbox);
         if (ns != null) Marshal.ReleaseComObject(ns);
    }                
}
 
private void adxOutlookEvents_AdvancedSearchComplete(object sender, object hostObj)
{
    Outlook.Search advancedSearch = null;
    Outlook.Results advancedSearchResults = null;
    Outlook.MailItem resultItem = null;
    System.Text.StringBuilder strBuilder = null;
    try
    {
        advancedSearch = hostObj as Outlook.Search;
        if (advancedSearch.Tag == advancedSearchTag)
        {
            advancedSearchResults = advancedSearch.Results;
            if (advancedSearchResults.Count > 0)
            {
                if (HostMajorVersion > 10)
                {
                    object folder = advancedSearch.GetType().InvokeMember("Save", 
                                       System.Reflection.BindingFlags.Instance |
                                       System.Reflection.BindingFlags.InvokeMethod | 
                                       System.Reflection.BindingFlags.Public,
                                       null, advancedSearch, 
                                       new object[] { advancedSearchTag });
 
                }
                else
                {
                    strBuilder = new System.Text.StringBuilder();
                    strBuilder.AppendLine("Number of items found: " +
                              advancedSearchResults.Count.ToString());                            
                    for (int i = 1; i < = advancedSearchResults.Count; i++)
                    {                                
                        resultItem = advancedSearchResults[i] 
                                          as Outlook.MailItem;
                        if (resultItem != null)
                        {
                            strBuilder.Append("#" + i.ToString());
                            strBuilder.Append(" Subject: " + resultItem.Subject);
                            strBuilder.Append(" \t To: " + resultItem.To);
                            strBuilder.AppendLine(" \t Importance: " + 
                                               resultItem.Importance.ToString());
                            Marshal.ReleaseComObject(resultItem);
                        }
                    }
                    if (strBuilder.Length > 0)
                        System.Diagnostics.Debug.WriteLine(strBuilder.ToString());   
                    else
                        System.Diagnostics.Debug.WriteLine(
                                                "There are no Mail items found.");
                 }
            }
            else
            {
                 System.Diagnostics.Debug.WriteLine("There are no items found.");
            }
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message, "An exception is occured");
    }
    finally
    {
        if (resultItem != null) Marshal.ReleaseComObject(resultItem);
        if (advancedSearchResults != null) 
                     Marshal.ReleaseComObject(advancedSearchResults); 
    }
}
    VB.NET and Add-in Express:
Imports Outlook = Microsoft.Office.Interop.Outlook
'...
Dim advancedSearchTag As String = "Our first advanced search in Outlook"
 
Private Sub RunAdvancedSearch(OutlookApp As Outlook._Application, wordInSubject As String)
    Dim scope As String = "Inbox"
    Dim filter As String = "urn:schemas:mailheader:subject LIKE '%" + wordInSubject + "%'"
    Dim advancedSearch As Outlook.Search = Nothing
    Dim folderInbox As Outlook.MAPIFolder = Nothing
    Dim folderSentMail As Outlook.MAPIFolder = Nothing
    Dim ns As Outlook.NameSpace = Nothing
    Try
        ns = OutlookApp.GetNamespace("MAPI")
        folderInbox = ns.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox)
        folderSentMail = ns.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderSentMail)
        scope = "'" + folderInbox.FolderPath + "','" + folderSentMail.FolderPath + "'"
        advancedSearch = OutlookApp.AdvancedSearch(scope, filter, True, advancedSearchTag)
    Catch ex As Exception
        MessageBox.Show(ex.Message, "An eexception is thrown")
    Finally
        If Not IsNothing(advancedSearch) Then Marshal.ReleaseComObject(advancedSearch)
        If Not IsNothing(folderSentMail) Then Marshal.ReleaseComObject(folderSentMail)
        If Not IsNothing(folderInbox) Then Marshal.ReleaseComObject(folderInbox)
        If Not IsNothing(ns) Then Marshal.ReleaseComObject(ns)
    End Try
End Sub
 
Private Sub adxOutlookEvents_AdvancedSearchComplete(sender As System.Object, 
              hostObj As System.Object) Handles adxOutlookEvents.AdvancedSearchComplete
    Dim advancedSearch As Outlook.Search = Nothing
    Dim advancedSearchResults As Outlook.Results = Nothing
    Dim resultItem As Outlook.MailItem = Nothing
    Dim strBuilder As System.Text.StringBuilder = Nothing
    Try
        advancedSearch = CType(hostObj, Outlook.Search)
        If (advancedSearch.Tag = advancedSearchTag) Then
            advancedSearchResults = advancedSearch.Results
            If (advancedSearchResults.Count > 0) Then
                If (HostMajorVersion > 10) Then
                    Dim folder As Object = advancedSearch.GetType().InvokeMember(
                                     "Save", System.Reflection.BindingFlags.Instance Or
                                     System.Reflection.BindingFlags.InvokeMethod Or 
                                     System.Reflection.BindingFlags.Public,
                                     Nothing, advancedSearch, 
                                     New [Object]() {advancedSearchTag})
                Else
                    strBuilder = New System.Text.StringBuilder()
                    strBuilder.AppendLine("Number of items found: " + 
                                           advancedSearchResults.Count.ToString())
                    For i As Integer = 1 To advancedSearchResults.Count Step 1
                        resultItem = CType(advancedSearchResults(i), Outlook.MailItem)
                        If Not IsNothing(resultItem) Then
                            strBuilder.Append("#" + i.ToString())
                            strBuilder.Append(" Subject: " + resultItem.Subject)
                            strBuilder.Append(" To: " + resultItem.To)
                            strBuilder.AppendLine(" Importance: " + 
                                                   resultItem.Importance.ToString())
                            Marshal.ReleaseComObject(resultItem)
                        End If
                    Next
                    If (strBuilder.Length > 0) Then
                        System.Diagnostics.Debug.WriteLine(strBuilder.ToString())
                    Else
                        System.Diagnostics.Debug.WriteLine(
                                               "There are no Mail items found.")
                    End If
                End If
            Else
                System.Diagnostics.Debug.WriteLine("There are no items found.")
            End If
        End If
    Catch ex As Exception
        MessageBox.Show(ex.Message, "An exception is occured")
    Finally
        If Not IsNothing(resultItem) Then Marshal.ReleaseComObject(resultItem)
        If Not IsNothing(advancedSearchResults) Then 
            Marshal.ReleaseComObject(advancedSearchResults)
        End If
    End Try
End Sub
    C# and VSTO:
string advancedSearchTag = "Our first advanced search in Outlook";
using System.Windows.Forms;
using System.Runtime.InteropServices;
// ...
private void RunAdvancedSearch(Outlook.Application Application, string wordInSubject)
{
    string scope = "Inbox";
    string filter = "urn:schemas:mailheader:subject LIKE \'%" + wordInSubject + "%\'";
    Outlook.Search advancedSearch = null;
    Outlook.MAPIFolder folderInbox = null;
    Outlook.MAPIFolder folderSentMail = null;
    Outlook.NameSpace ns = null;
    try
    {
        ns = Application.GetNamespace("MAPI");
        folderInbox = ns.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox);
        folderSentMail = ns.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderSentMail);
        scope = "\'" + folderInbox.FolderPath + "\',\'" + 
                                                   folderSentMail.FolderPath + "\'";
        advancedSearch = Application.AdvancedSearch(
                                            scope, filter, true, advancedSearchTag);
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message, "An eexception is thrown");
    }
    finally
    {
        if (advancedSearch != null) Marshal.ReleaseComObject(advancedSearch);
        if (folderSentMail != null) Marshal.ReleaseComObject(folderSentMail);
        if (folderInbox != null) Marshal.ReleaseComObject(folderInbox);
        if (ns != null) Marshal.ReleaseComObject(ns);
    }
}
 
void Application_AdvancedSearchComplete(Outlook.Search SearchObject)
{            
    Outlook.Results advancedSearchResults = null;
    Outlook.MailItem resultItem = null;
    System.Text.StringBuilder strBuilder = null;
    try
    {
        if (SearchObject.Tag == advancedSearchTag)
        {
            advancedSearchResults = SearchObject.Results;
            if (advancedSearchResults.Count > 0)
            {
                string[] version = Application.Version.Split('.');
                int hostMajorVersion = Convert.ToInt32(version[0]);
                if (hostMajorVersion > 10)
                {
                    object folder = SearchObject.GetType().InvokeMember("Save",
                                            System.Reflection.BindingFlags.Instance |
                                            System.Reflection.BindingFlags.InvokeMethod |
                                            System.Reflection.BindingFlags.Public,
                                            null, SearchObject, 
                                            new object[] { advancedSearchTag });
 
                }
                else
                {
                    strBuilder = new System.Text.StringBuilder();
                    strBuilder.AppendLine("Number of items found: " + 
                                           advancedSearchResults.Count.ToString());
                    for (int i = 1; i < = advancedSearchResults.Count; i++)
                    {
                        resultItem = advancedSearchResults[i] as Outlook.MailItem;
                        if (resultItem != null)
                        {
                            strBuilder.Append("#" + i.ToString());
                            strBuilder.Append(" Subject: " + resultItem.Subject);
                            strBuilder.Append(" \t To: " + resultItem.To);
                            strBuilder.AppendLine(" \t Importance: " + 
                                                   resultItem.Importance.ToString());
                            Marshal.ReleaseComObject(resultItem);
                        }
                    }
                    if (strBuilder.Length > 0)
                        System.Diagnostics.Debug.WriteLine(strBuilder.ToString());
                    else
                        System.Diagnostics.Debug.WriteLine(
                                                  "There are no Mail items found.");
                }
            }
            else
            {
                System.Diagnostics.Debug.WriteLine("There are no items found.");
            }
       }
   }
   catch (Exception ex)
   {
       MessageBox.Show(ex.Message, "An exception is occured");
   }
   finally
   {
       if (resultItem != null) Marshal.ReleaseComObject(resultItem);
       if (advancedSearchResults != null) 
           Marshal.ReleaseComObject(advancedSearchResults);
   }
}
    VB.NET and VSTO:
Imports System.Windows.Forms
Imports System.Runtime.InteropServices
' ...
Dim advancedSearchTag As String = "Our first advanced search in Outlook"
 
Private Sub RunAdvancedSearch(Application As Outlook.Application, wordInSubject As String)
    Dim scope As String = "Inbox"
    Dim filter As String = "urn:schemas:mailheader:subject LIKE '%" + wordInSubject + "%'"
    Dim advancedSearch As Outlook.Search = Nothing
    Dim folderInbox As Outlook.MAPIFolder = Nothing
    Dim folderSentMail As Outlook.MAPIFolder = Nothing
    Dim ns As Outlook.NameSpace = Nothing
    Try
        ns = Application.GetNamespace("MAPI")
        folderInbox = ns.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox)
        folderSentMail = ns.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderSentMail)
        scope = "'" + folderInbox.FolderPath + "','" + folderSentMail.FolderPath + "'"
        advancedSearch = Application.AdvancedSearch(scope, filter, True, advancedSearchTag)
    Catch ex As Exception
        MessageBox.Show(ex.Message, "An eexception is thrown")
    Finally
        If Not IsNothing(advancedSearch) Then Marshal.ReleaseComObject(advancedSearch)
        If Not IsNothing(folderSentMail) Then Marshal.ReleaseComObject(folderSentMail)
        If Not IsNothing(folderInbox) Then Marshal.ReleaseComObject(folderInbox)
        If Not IsNothing(ns) Then Marshal.ReleaseComObject(ns)
    End Try
End Sub
 
Private Sub adxOutlookEvents_AdvancedSearchComplete(SearchObject As Outlook.Search) 
                                          Handles Application.AdvancedSearchComplete
    Dim advancedSearchResults As Outlook.Results = Nothing
    Dim resultItem As Outlook.MailItem = Nothing
    Dim strBuilder As System.Text.StringBuilder = Nothing
    Try
        If (SearchObject.Tag = advancedSearchTag) Then
            advancedSearchResults = SearchObject.Results
            If (advancedSearchResults.Count > 0) Then
                Dim version As String() = Application.Version.Split(".")
                Dim hostMajorVersion As Integer = Convert.ToInt32(version(0))
                If (hostMajorVersion > 10) Then
                    Dim folder As Object = SearchObject.GetType().InvokeMember("Save",
                                             System.Reflection.BindingFlags.Instance Or
                                             System.Reflection.BindingFlags.InvokeMethod Or 
                                             System.Reflection.BindingFlags.Public,
                                             Nothing, SearchObject, 
                                             New [Object]() {advancedSearchTag})
                Else
                    strBuilder = New System.Text.StringBuilder()
                    strBuilder.AppendLine("Number of items found: " + 
                                           advancedSearchResults.Count.ToString())
                    For i As Integer = 1 To advancedSearchResults.Count Step 1
                        resultItem = CType(advancedSearchResults(i), Outlook.MailItem)
                        If Not IsNothing(resultItem) Then
                            strBuilder.Append("#" + i.ToString())
                            strBuilder.Append(" Subject: " + resultItem.Subject)
                            strBuilder.Append(" To: " + resultItem.To)
                            strBuilder.AppendLine(" Importance: " + 
                                                    resultItem.Importance.ToString())
                            Marshal.ReleaseComObject(resultItem)
                        End If
                    Next
                    If (strBuilder.Length > 0) Then
                        System.Diagnostics.Debug.WriteLine(strBuilder.ToString())
                    Else
                        System.Diagnostics.Debug.WriteLine( _
                                                "There are no Mail items found.")
                    End If
                End If
            Else
                System.Diagnostics.Debug.WriteLine("There are no items found.")
            End If
        End If
    Catch ex As Exception
        MessageBox.Show(ex.Message, "An exception is occured")
    Finally
        If Not IsNothing(resultItem) Then Marshal.ReleaseComObject(resultItem)
        If Not IsNothing(advancedSearchResults) Then 
            Marshal.ReleaseComObject(advancedSearchResults)
        End If
    End Try
End Sub

See you on our forums and in the e-mail support!

8 Comments

  • http://0.gravatar.com/avatar/255f83046631f8e621bba626ab6808dc?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G Raf says:

    Hi Eugene,
    I want use add-in-express DLL in my project. I want to create a class project or using your existing one (if you have any sample).
    Goal is this:
    Once I run the project, it will get me the calendars items from the outlook.
    so both the exe (a small desktop app) & outlook will be installed in same machine.
    Is that possible with you DLL?

    Thanks

  • Hello Rafiqul,

    As I described in our mail correspondense, you can access the Outlook object model right from your application. Note that in this case, you may need to use our Security Manager for Outlook (http://www.add-in-express.com/outlook-security/index.php) to suppress Outlook security warnings.

    Also, you can create a COM add-in and your application can communicate to the COM add-in in this fashion:

    OutlookApp.COMAddins.Item(strMyComAddinProgId).Object.MyPublicPropertyOrMethod

    1. strMyComAddinProgId – see the ProgId attribute of your add-in module.
    2. MyPublicPropertyOrMethod is called via late binding (see System.Type.InvokeMember in MSDN or search through our forums)

  • http://1.gravatar.com/avatar/d44729ced41b863bc25d4f102bd53bee?s=32&d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G Hemant says:

    Hi Eugene,
    I want to search on to ,cc ,from fields of the mail will you please tell me the DASL query for this ?

  • Hello Hemant,

    You can construct a required filter in the Outlook UI. Click View Settings on the View tab of an Outlook Explorer (OL 2010-2013) and then the button Filter. When the filter is set as required, see the SQL tab of the same dialog.

    Also, please have a look at this topic:
    http://social.msdn.microsoft.com/Forums/en-US/4b6dc55c-eadf-4dfd-8849-b855f465ffe2/find-or-restrict-all-cc-and-bcc

  • http://0.gravatar.com/avatar/6f515cdd082a864cce2fd7a7bc541135?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G Hemant says:

    Hi Eugene,
    Will you please tell me how to construct DASL querry for finding those mails having some subject and mails must have attachment within it.
    i used the following DASL Syntaxt for this

    “urn:schemas:mailheader:subject LIKE ‘%” And “urn:schemas:httpmail:hasattachment = “1″

  • Hello Hemant,

    You can create such a filter in the Outlook UI, see View | Current View | View Settings | Filter. Then you switch to the SQL tab of the Filter dialog and copy the query.

  • http://0.gravatar.com/avatar/ad516503a11cd5ca435acc9bb6523536?s=32 Fel says:

    Hi Eugene,
    Thank you for the example. I was wondering why is the code that subscribes Application_AdvancedSearchComplete to the AdvancedSearchComplete eventhandler is omitted, even from the examples provided by MSDN.

  • Hello,

    We don’t know the answer. I can only guess.

Post a comment

Have any questions? Ask us right now!