Search code examples
c#outlookvstooutlook-addinadd-in

C# - Outlook VSTO - Getting AdvancedSearchComplete to trigger


I am converting an old Outlook VBA macro I created to a C# VSTO Add-in. This tool is for bulk archiving of emails to various public folders and a local .pst file and needs to be able to find emails based on a user's search criteria. I had a whale of a time getting the AdvancedSearchComplete event to trigger in VBA, but eventually got it to work. Of course, I am now have the same problem in the C# VSTO add-in.

To use the tool, the user will input their criteria on a userform, then click the Find Emails button to perform the search with the results populating a datagridview.

public partial class Email_Archiver : Form
{

    public Email_Archiver()
    {
        InitializeComponent();

        //Add event handlers
        Globals.ThisAddIn.Application.AdvancedSearchComplete += new Microsoft.Office.Interop.Outlook.ApplicationEvents_11_AdvancedSearchCompleteEventHandler(Application_AdvancedSearchComplete);
    }

    public void Application_AdvancedSearchComplete(Microsoft.Office.Interop.Outlook.Search SearchObject)
    { //Handles AdvancedSearchComplete event

        if (SearchObject.Tag == "Archiver Search")
        {
            OutlookFunctions.SearchComplete = true;
            MessageBox.Show(this, "Search Complete!","Email Search Error",MessageBoxButtons.OK);
        }

    }

    private void Find_Button_Click(object sender, EventArgs e)
    {

        //Set datagridview datasource
        EmailList.DataSource = FindEmails();
        EmailList.Columns["EntryID"].Visible = false;

    }

    public DataTable FindEmails()
    {
        var dt = new DataTable();

        //Format DataTable
        dt.Columns.Add("EntryID", typeof(string));
        dt.Columns.Add("Project No.", typeof(int));
        dt.Columns.Add("Subject", typeof(string));
        dt.Columns.Add("Sender", typeof(string));
        dt.Columns.Add("Recipient", typeof(string));
        dt.Columns.Add("Time Sent", typeof(DateTime));
        dt.Columns.Add("Size", typeof(decimal));

        //Do stuff to get "searchFolders" and "searchCriteria" from form
        //...

        dt = OutlookFunctions.RunAdvancedSearch(searchFolders, searchCriteria);

        return dt;
    }
}


class OutlookFunctions
{

    public static bool SearchComplete;

    public static DataTable RunAdvancedSearch(string[] searchFolders, string[] searchCriteria)
    {

        //Get Outlook namespace
        var oApp = Globals.ThisAddIn.Application;
        var oNS = oApp.GetNamespace("mapi");
        Microsoft.Office.Interop.Outlook.Search advSearch = null;

        //Do other stuff to properly set up the scope and filter
        //...

        //Perform search
        SearchComplete = false;
        try
        {
            advSearch = oApp.AdvancedSearch(scope, filter, false, "Archiver Search");
        }
        catch (System.Exception ex)
        {
            MessageBox.Show("An error has occurred during the search for emails. \n \n"
                    + ex.Message);
            return null;
        }

        Stopwatch timer = new Stopwatch();
        timer.Start();

        while (!SearchComplete && timer.Elapsed.TotalSeconds < 10)
        {
            //Do nothing
        }

        if (!SearchComplete)
        {
            advSearch.Stop();
        }

        if (advSearch != null)
        {
            var resultTable = new DataTable();

            //Send results to resultTable

            return resultTable;
        }

        return null;

    }

}

The search works and I get the proper results, but I had to add in the timer to make things move along, otherwise the code never finishes because AdvancedSearchComplete isn't triggered. I still get all of the emails I'm looking for and doing essentially the same search just in the Outlook explorer, I get the same results in a fraction of a second.

After the timer ends the search and the datagridview is filled with the results, THEN the AdvancedSearchComplete fires and I get my "Search Complete!" message.

What am I missing here that is keeping the event from triggering to end the search (in a timely fashion)?


Solution

  • It appears that any code following the initiation for the AdvancedSearch method will essentially end the search without triggering the AdvancedSearchComplete or AdvancedSearchStopped events.

    Instead, I now have the following in the ThisAddIn class:

    private void ThisAddIn_Startup(object sender, System.EventArgs e)
    {
        //Create task pane object
        archiverTaskPane1 = new ArchiverTaskPane();
        customTaskPane = this.CustomTaskPanes.Add(archiverTaskPane1, "Title");
    
        //Add event handler for opening task pane
        customTaskPane.VisibleChanged += new EventHandler(customTaskPane_VisibleChanged);
    
        //Add event handler for Outlook's AdvancedSearch
        Globals.ThisAddIn.Application.AdvancedSearchComplete += new Outlook.ApplicationEvents_11_AdvancedSearchCompleteEventHandler(Application_AdvancedSearchComplete);
    }
    
    public void Application_AdvancedSearchComplete(Microsoft.Office.Interop.Outlook.Search SearchObject)
    {
        DataTable dt = new DataTable();
        OutlookFunctions oFunctions = new OutlookFunctions();
        dt = oFunctions.BuildResults(SearchObject.Results);
        this.archiverTaskPane1.DataGridFiller(dt);
    }
    

    In the ArchiverTaskPane Class (formerly Email_Archiver Form - I REALLY liked the Task Pane idea)

    private void Find_Button_Click(object sender, EventArgs e)
    {
        FindEmails();
    }
    
    public void FindEmails()
    {
        DataTable dt = new DataTable();
    
        //Add columns to DataTable
    
        //Do stuff to get "searchFolders" and "searchCriteria" from form
        //...
    
        //Create OutlookFunctions instance
        OutlookFunctions oFunctions = new OutlookFunctions();
        oFunctions.RunAdvancedSearch(searchFolders, searchCriteria);
    }
    
    public void DataGridFiller(DataTable results)
    {
        EmailList.DataSource = results;
    }
    

    In the OutlookFunctions Class:

    public static DataTable RunAdvancedSearch(string[] searchFolders, string[] searchCriteria)
    {
        //Get Outlook namespace
        var oApp = Globals.ThisAddIn.Application;
        var oNS = oApp.GetNamespace("mapi");
        Microsoft.Office.Interop.Outlook.Search advSearch = null;
    
        //Do other stuff to properly set up the scope and filter
        //...
    
        //Perform search
        SearchComplete = false;
        try
        {
            advSearch = oApp.AdvancedSearch(scope, filter, false, "Archiver Search");
        }
        catch (System.Exception ex)
        {
            MessageBox.Show("An error has occurred during the search for emails. \n \n"
                    + ex.Message);
        }
    }
    
    public DataTable BuildResults(Results searchResults)
    {
        DataTable resultTable = new DataTable();
    
        //Do stuff to build DataTable from search results
        //...
    
        return resultTable;
    }