Compartilhar via


MAPICDO Handle Leak

There’s a handle leak in MAPICDO. It’s been there just about forever and can easily be avoided. I just wanted to describe it here to help you recognize it so you can code around it.

The basic setup to see the leak is this: A program has multiple child threads which are each logging in and out of MAPI in a loop. Each thread is doing basic MAPI operations (MAPIInitialize, MAPILogonEx, GetMsgStoresTable, OpenMsgStore), and everything is released in the order it was obtained. MAPI is uninitialized properly. The message store being opened is an Exchange mailbox, meaning the Exchange provider, emsmdb32,  is loaded. Every thread which uses MAPI is doing this loop.

In such a setup, if you watch the process with PerfMon, you’ll see a steady increase in the number of handles being used. If you use Handle from SysInternals you’ll also see this increase. In fact, using Handle, you can track this leak specifically to a file handle, “DeviceAfd”. The following command line will show the leak in action:
handle.exe –a –p testapp.exe afd

What’s going on? During OpenMsgStore, the Exchange provider is loaded if it’s not loaded already. It checks to see if it has opened the sockets it needs to communicate with the Exchange server. If it determines it has not, it opens them with a call to socket. This is the point where the process grabs a handle to “DeviceAfd”. When we’re done using emsmdb32, we unload it, and the socket (and hence the handle) is released. The problem here is when two threads both think their the ones loading emsmdb32. We protect most of the process of loading the provider behind critical sections, but the loading of these sockets is not protected. So both threads can create the sockets, and both put the handle of the socket they created in the same memory location. The last one to write the location wins, and the other one is leaked.

The workaround is simple – as long as some thread causes emsmdb32 to load before any of the other threads, and holds it open until the other threads have completed their work, we will never encounter the scenario where two threads are loading emsmdb32 at the same time. It will always be loaded and we will never reenter the code which loads the sockets. Supposing all of your MAPI work is being done on background threads, this just means logging on to the mailbox once on your main thread before you create your background threads. After your background threads have completed their work, you can release the message store and close down MAPI.

Most multithreaded MAPI programs I’ve encountered already have similar behavior as the workaround, which is why this leak has never been noticed before.