Andrei Smolin

HowTo: Handle events of an Outlook item

There are two variants of handling events of an Outlook item:

  • You can connect to events of an item shown in the active inspector window.
  • In addition, you can connect to events of an item selected in the explorer window.

This post covers the first scenario, you can download the sample VB.NET and C# projects at the end of the page.

The sample add-in starts with calling System.Windows.Forms.Application.EnableVisualStyles(). This ensures that the forms and message boxes will be shown in the XP style; if it is not called, your forms and message boxes will be drawn in the outdated Windows 2000 style.

Processing events of an Outlook item

On the AddinStartupComplte event, the add-in creates an instance of the class that handles events of an Outlook item – an Outlook item events class – it is called OutlookItemEventsClass1 in this sample project; it is descried below.

VB.NET:

Dim itemEvents As OutlookItemEventsClass1
…
Private Sub AddinModule_AddinStartupComplete(ByVal sender As Object, _
    ByVal e As System.EventArgs) Handles MyBase.AddinStartupComplete
    Debug.Print("The AddinStartupComplete event has occured.")
    Debug.Print("The add-in has been loaded by Outlook " & Me.HostMajorVersion.ToString())
    ' This creates an item of the class that handles the events of an Outlook item
    itemEvents = New OutlookItemEventsClass1(Me)
End Sub

C#:

private OutlookItemEventsClass1 itemEvents;private void AddinModule_AddinStartupComplete(object sender, EventArgs e)
{
    Debug.Print("The AddinStartupComplete event has occured.");
    Debug.Print("The add-in has been loaded by Outlook " + this.HostMajorVersion.ToString());
    // This creates an item of the class that handles the events of an Outlook item
    itemEvents = new OutlookItemEventsClass1(this);
}

All Outlook Item types e.g. MailItem, TaskItem, ContactItem etc. provide the same set of events. To highlight this, the OutlookItemEventsClass1 class is written to process all item types, see the description of the getSubject() method below.

In OutlookItemEventsClass1 you will / can find a number of methods whose names start with “Process…” e.g. ProcessOpen, ProcessClose and so on. These methods correspond to actual COM events of the Outlook item: ProcessOpen method handles the Open event, ProcessClose method handles the Close event, etc. All of these methods write a debug string; the string includes the value returned by the getSubject() method. This method retrieves the Subject property that every Outlook item provides. Because the type of the Outlook item connected to the events class is unknown at compile-time, that method uses late binding – Type.InvokeMember().

The only method providing a bit more code is ProcessClose. It contains a call that disconnects from the events of the item; we discuss disconnecting from the item’s events below.

You can cancel an event if it provides the Cancel property in the event argument objects. In Add-in Express, the Cancel property is provided by the ADXCancelEventArgs class.

Well, the events are processed. Now how to connect to them?

Connecting to the events

When the InspectorActivate event occurs, the add-in gets the Outlook item that the inspector displays (Inspector.CurrentItem) and passes it to the ConnectTo method of the events class.

VB.NET:

If GetExplorersCount() > 0 Then ' don't connect if there are NO open explorers
    itemEvents.ConnectTo((TryCast(inspector, Outlook._Inspector)).CurrentItem, _
        True)
    Debug.Print("Connected to this Outlook item.")
Else
    Debug.Print("Do not connect to this Outlook item.")
End If

C#:

if (GetExplorersCount() > 0) // don't connect if there are NO open explorers
{
    itemEvents.ConnectTo((inspector as Outlook._Inspector).CurrentItem, true);
    Debug.Print("Connected to this Outlook item.");
}
else
    Debug.Print("Do not connect to this Outlook item.");

The ConnectTo method above accepts the eventClassReleasesComObject parameter, which we set to true in this sample. This setting means that the COM object standing behind the Outlook item will be released when the event class disconnects from an item’s events. In other words, passing true to the ConnectTo method of the events class means that disconnecting from an item’s events also releases the item; it couples the disconnect and release in one operation.

If you need to use the item after you disconnect from its events; pass false in the above-mentioned parameter of the ConnectTo method.

Disconnecting from the events

Technically, it is very simple to disconnect from the events of a previously connected item: you call RemoveConnection() on the OutlookItemEventsClass1.

VB.NET:

If itemEvents.IsConnected Then
    Debug.Print("Disconnecting from the previously connected item.")
    itemEvents.RemoveConnection()
End If

C#:

if (itemEvents.IsConnected)
{
    Debug.Print("Disconnecting from the previously connected item.");
    itemEvents.RemoveConnection();
}

The main question is: When to disconnect from events?

  1. In the very first place, we disconnect from the item just before we connect to another item, see adxOutlookEvents_InspectorActivate in the add-in module code. This guarantees that only one item is connected at a time.
  2. Then, we disconnect from events of an item when the item is being closed, see the ProcessClose method of the OutlookItemEventsClass1 class and also when the inspector is closed. We do this in these seemingly coupled events in order to make sure that the item will be released. In fact, these events may occur in a different order in the same scenario in different Outlook versions!

For you to feel the taste of debugging and testing Outlook add-ins, here’s my favorite scenario:

  • Click the close button to close the newly created message, this invokes the Close event on the item; in this sample, we handle this event to disconnect from the item’s events.
  • The message box “Do you want to save changes?” opens and you click “Yes”.
  • This closes the dialog box, the focus moves back to the inspector window and this generates Inspector.Activate; in this sample, we handle this event to connect to the item’s events.
  • Outlook saves the item – this generate a number of Item.PropertyChange
    events and then Item.Write.
  • Finally, Outlook closes the Inspector and generates Inspector.Close.

Well guys, your thoughts? Do you see anything wrong with this?

Good luck!

Available downloads:

This sample COM Add-in was developed using Add-in Express for Office and .net:

C# sample COM add-in
VB.NET sample COM add-in

13 Comments

  • tayllor says:

    How does this change with Outlook 2013? If the user clicks reply or forward, it allows the user to reply in the current window. This means no inspector activate was fired, so you can’t make the connection for the outlook events object.

  • tayllor says:

    Note about my previous comment, if you click the Pop Out button, then the inspector is created, and everything works fine.

  • Andrei Smolin (Add-in Express Team) says:

    Hello Tayllor,

    I suppose you talk about the inline respone feature of Outlook 2013. If so, you can try handle the Explorer.InlineResponse event, retrieve the Outlook item object passed in the event handler, and connect to the item’s events.

  • Kim Bratteli says:

    Hello
    Trying to use OutlookItemEvents.

    Could you please explain why this is not working:

    Dim addModule As New ADXAddinModule
    itmevt = New OutlookItemEvents(addModule)
    olMail = olApp.CreateItem(Outlook.OlItemType.olMailItem)
    itmevt.ConnectTo(olMail, False)

    It will no Connect.

    Best regards Kim

  • Andrei Smolin (Add-in Express Team) says:

    Hello Kim,

    ADXAddinModule represents your add-in. You cannot just create an instance of it. Instead, you need to use the existing (pre-created) instance. If your code is located in the module, you write new OutlookItemEvents(Me). Otherwise, you can access the pre-created instance using {project name e.g. MyAddin1}.AddinModule.CurrentInstance.{public property or method defined on the add-in module}.

  • Nick Schiffler says:

    Hello!
    I’m developing a small Outlook add-in.
    I tried to use the next code to handle the Explorer.InlineResponse event:

    private void adxOutlookEvents_ExplorerInlineResponse(object sender, object itemObject)
    {
    Outlook.MailItem mailItem = itemObject as Outlook.MailItem;
    if (mailItem != null)
    {
    if (outlookItemEvents.IsConnected)
    {
    outlookItemEvents.RemoveConnection();
    }
    this.outlookItemEvents.ConnectTo(mailItem, true);
    }
    }

    But if I change the value of the fields (“TO:” or “CC:”), the outlookItemEvents.PropertyChangeEvent event does not fire.

    Can you explain me why? Maybe I’m doing something wrong?

  • Andrei Smolin (Add-in Express Team) says:

    Hello Nick,

    As far as I remember the PropertyChange event occurs when you save the item. You can check this yourself: please see our Outlook Events Logger add-in at https://www.add-in-express.com/creating-addins-blog/2010/07/01/outlook-events-tool/.

  • Jurko Gospodneti? says:

    However, what to do when we want to do something like process newly added attachments on any e-mail, even those open/created programmatically?

    We can use the `Application.ItemLoad` event to hook up events we want to handle on it, and that works nice, but when to remove those events? And how to keep those event handlers alive, i.e. guard them against being GCed?

    In the `Application.ItemLoad` event we get a proxy object (MSDN calls it a weak-ref) for the actual loaded item and if we release that object, its RCW gets garbage collected, together with any event handlers we might have hooked onto it. That would mean that we need to keep that RCW alive. Fine, we can keep a reference
    to the .NET COM object around until we unhook those events for real.

    And when to unhook those? The obvious choice is the `MailItem.Unload` event plus when our add-in gets shut down, but that’s where the fun starts. :-)

    We need to unhook the event handlers from the same object that we hooked them onto, which means we need to keep the original weak reference alive until then. That’s in line with what we decided we need before. But there we encounter two problems:

    1. we need to reference this object from somewhere in our application as just referencing it in the event handler which is only referenced from the COM object’s RCW makes both those entities eligible for .NET GC, which would simply make our event handlers cease to exist at some random time
    2. if we do a `Marshal.ReleaseComObject()` call on it in the `MailItem.Unload` event handler – that seems to occasionally cause access violations in Outlook (seen with both Outlook 2013 & Outlook 2016 and with accessing the object’s IMailItem & IItemEvents_10_Event interfaces – causing the access violations to occur either during this call or from inside Outlook code very soon after this call)

    The first we can resolve by holding a dictionary in our main add-in object keyed by the COM object’s IUnknown interface `IntPtr` value (we get it by calling `Marshal.GetIUnknownFromObject()` on the COM object and then we just clean up the added native COM object interface reference count by calling `Marshal.Release()` on that value) – this will:
    – guard the objects from .NET GC
    – provide us with constant-time lookup & removal from the structure
    – allow us to iterate through all the items there and clean up all the objects registered there in case we get an `Application.Quit` event

    The latter I’m still not 100% sure what to do about, but my current hunch is that the random access violations are caused by conflicts between that weak-ref objects internal cleanup implementation details and how .NET cleans up its native COM object interface references held inside its RCW object. Currently, one technique seems to be successful at avoiding the sporadic access violations:
    – we first get an raw IUnknown interface reference for the COM object
    – then call `Marshal.ReleaseComObject()` on the object to make .NET perform its cleanup
    – and then we release our raw IUnknown interface reference using `Marshal.Release()`

    My current `work in progress` guess as to why & how this works is that .NET RCW objects hold multiple COM interface references for various interface types, e.g. IUnknown, IDispatch and possibly others, and that Outlook’s weak-ref COM object implementation is vulnerable to the order in which its interfaces get released. It could be that it performs its internal cleanup when the last IUnknown interface gets released without checking for any other outstanding interface types, and then when those other interface types get released, it attempts to access some internal memory that has already been released or something equally silly.

    I’m actually quite amazed that I did not manage to find any example code on the Net related to this.

  • Andrei Smolin (Add-in Express Team) says:

    Hello Jurko,

    Add-in Express doesn’t release the item it passes in parameters of the ItemLoad event after the event handler is invoked. I would create a collection to store Outlook items obtained in this event and delete items from that collection using MailItem.Unload. I assume getting the objects in the collection would be enough to keep the corresponding RCWs alive. That is, I wouldn’t use Marshal.GetIUnknownFromObject() and corresponding calls.

  • Roger Levy says:

    I am new to Office add-ins and this sample will help me make my first add-in better, so thank you. I am using VS-2015 and a new, registered copy of Add-In Express. I had to make some changes to references in order to build and run cleanly. Particularly, the project has a reference to an old version of Interop.Outlook that is in the project’s Interops directory. This caused an exception at run time. I corrected the problem by removing the reference and adding a reference to a new version of Microsoft.Office.Interop.Outlook. Will the installer project get the new reference from MS for users of my add-in or do I need to copy this file to the local Interops directory where the old one resided?

  • Andrei Smolin (Add-in Express Team) says:

    Hello Roger,

    I remember your case. You get an exception when starting debugging your add-in that uses interops for Office 2003. Are you saying that using interops for another Office version solved the issue?

    Before you create a setup project, I recommend that you return to using interops for Office 2003. Then create a setup project and use interop for the other Office version. Before you build the setup project, return to using interops for Office 2003. In other words, replacing interops may be okay for debugging purposes only. For all other purposes, use interops for Office 2003.

  • Roger Levy says:

    Andrei,
    No, the exception in Debug mode still occurs for all my projects regardless of Interops. If you recall, your recommendation was to start in non-Debug mode, stop by using a MsgBox, and then join the debugger to the process. It’s inconvenient but it works.

    My current question is about running your VB code discussed in this article. I get an exception when GetExplorersCount is called. The exception includes a message that Interop.Office version 2.1.0.0 could not be loaded. The same message has been discussed in many different forums, including an ADX forum, and mostly there were no good solutions identified. Luckily, I removed the reference to Interop.Outlook that was associated with a file in the project’s Interops directory and I added a new reference to Microsoft.Office.Interop.Outlook and that eliminated the exception.

    My question is why was an Interop included in a project directory and do I need to store Microsoft.Office.Interop.Outlook in that directory in order to build a working installer.

  • Andrei Smolin (Add-in Express Team) says:

    Hello Roger,

    Thank you for the explanation.

    I assume you’ve specified “Minimum supported Office version: Microsoft Office 2000” while created your add-in. This creates a project referencing Interop.*.dll files (see the Interops folder of your project). An indirect confirmation of this would be the minOfficeVersionSupported attribute missing in adxloader.dll.manifest; see the Loader folder of your project. Or, you’ve referenced such an interop yourself.

    Anyway, these could explain why your add-in references Interop.Office version 2.1.0.0 at all. These don’t explain why you get that message.

    If your add-in references any of the Interop.*.dll files – these are interops for Office 2000 – you should switch to using interops for the minimal Office version that your add-in supports (see instruction below); it’s Office 2003 – right? Replacing just one reference isn’t correct; you need to replace all Office 2000 references to Office XXXX references. Doing this non-coordinately may produce run-time problems.

    After you replace the references, use the Add-in Express Setup Project Wizard to create a setup project.

    Replacing references from Office 2000 to Office XXXX:
    – delete references to Interop.*.dll files
    – delete the Interop.*.dll files from the Interops folder of your project
    – copy all required DLLs from {Add-in Express}\Redistributables\Interop Assemblies\Office XXXX to the Interops folder
    – add references to the DLLs in the Interops folder
    – rebuild your project and fix compile-time issues (if applicable); you’ll need to replace e.g. {Office collection such as Attachments}.Item({an index}) to {Office collection such as Attachments}[{an index}] – C#.

    HTH

Post a comment

Have any questions? Ask us right now!