Question

I am developing an Outlook add-in which hooks into the Application.ItemSend event in order to make some changes to the email before it is sent. Specifically it adds/removes some attachments depending on the email content.

This post-processing involves going off to the internet, some calculation and possibly some interaction with the user. The user could even cancel the process so we may need to cancel the send altogether by setting the Cancel argument to the ItemSend event handler to true.

The problem is that, while this is generally quick, it could take a while - during which Outlook freezes entirely.

So, how can I do this in a separate thread whilst still maintaining the right to cancel the send? It seems that the only way to delay the send is to actually hold up the entire message thread, which seems ... odd.

Code:

private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
    Application.ItemSend+=OnItemSend;
}

private void OnItemSend(object Item, ref bool Cancel)
{
    Outlook.MailItem mail=Item as Outlook.MailItem;
    if (mail!=null)
    {
        Outlook.Attachments atts=mail.Attachments;

        /* ...lengthy processing on 'atts', during which Outlook is frozen... */

        Cancel=/* result of lengthy processing */
    }
}
Was it helpful?

Solution

Cancel the submission, save the message, read its entry id. Start a separate thread to do the processing (do not touch the OOM in that thread!). When the secondary thread finishes, run on the main thread code that will reopen the item by its entry id and modify the attachments, send the message again. ItemSend even will fire again, but you can ignore it this time (custom property or flag?).

UPDATE (2019) - keep in mind that Outlook 2016 or newer raises an exception as soon as it detects access on a secondary thread. If you need to touch OOM, you can only do that on the main thread in your addin. If you need to access any Outlook data on the secondary thread, you can only use Extended MAPI (C++ or Delphi) or Redemption (any language, I am its author - the RDOSession object can be accessed on a secondary thread: save the value of the Namespace.MAPIOBJECT property in a dedicated variable, then on a secondary thread create a new instance of RDOSession - that will initialize the MAPI system on that thread - and set the RDOSession.MAPIOBJECT property to the value saved on the secondary thread).

OTHER TIPS

Using OOM on a background thread is not supported.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top