Thursday, September 10, 2009

Outlook Add-Ins can be tricky

Lately I have been doing some development of an outlook add-in, and I learned that this can be challenging. I would like to share on this blog some of my experiences during this development.

So here is one of the more "fun" experiences:

As you probably know, it is possible in outlook to open more than one explorer window (the one with list of mail items, or the calendar, etc).

Another feature of outlook is that when you exit while there are unsent items in the outbox, you get a message box notifying you, and asking if you want to exit without sending, or send and then exit.

I had a bug: When the add-in is installed, and the user exits outlook, and there are unsent items in the outbox, the user gets asked if he really wants to exit not once, but several times, as many as the number of explorers that were ever opened in the session.

This is an annoying bug. It also doesn't seem connectd to what my add-in was doing, and this was weird. I scrambled to figure out the source of the bug by cutting out code from the add-in until I came to a simple addin reproducing the bug and containing these lines only:

private void SidebarAddIn_Startup(object sender, System.EventArgs e)
{
foreach (Explorer explorer in Application.Explorers)
{
}
}


A puzzlement indeed.

After a lot of pondering I came to the conclusion that the foreach() generates Explorer objects that are COM wrappers, and contain reference to the underlying Explorer objects. This keeps COM Explorer objects roaming around, and is probably the cause of this bug. And a simple workaround:

private void SidebarAddIn_Startup(object sender, System.EventArgs e)
{
foreach (Explorer explorer in Application.Explorers)
{
}
GC.Collect();
}

and the bug is solved.

Of course in my add-in, this did not actually work. It turns out there are more operations that leak managed Explorer objects. One of them is adding a CustomTaskPane to an explorer and removing it.

Good luck to all Outlook add-in developers....

Noam

3 comments:

Mister Tragic said...

If your working with COM objects (which is what you're doing) then use this API: http://bit.ly/2PCBil

It's kinda like calling Dispose()

Noam Lampert said...

Although Mister Tragic advice above is usually correct, in this specific case I don't think that usage of Marshal.ReleaseComObject is a good idea.

Specifically, if I replace the code with

foreach (Explorer explorer in Application.Explorers) {
Marshal.ReleaseComObject(explorer);
}

I will break any future usage of the Explorer object. Next time I do foreach on Application.Explorers, I will receive broken Explorer objects with an InvalidComObjectException: COM object that has been separated from its underlying RCW cannot be used.


This is because the enumeration on Application.Explorers returns the same COM wrappers that I just released and broke.
I don't see a better solution than GC.Collect()

Alex said...

Once my friend opened MS Outlook and showed that all mails were damaged.But fortunately I advised him next utility-converting ost to pst files.Program solved his problems in a minute and free of charge.Besides tool showed how read Microsoft Outlook .ost files and extract all necessary data from it.