MAPI Memory Management – or – How To Leak Memory For Fun and Profit

Some of you may know that I wrote the MAPI utility/sample MFCMAPI. Someday I'll write some posts directly about it. This post, however, is about memory management in MAPI.

The latest build of MFCMAPI was just added to the internal dev tools collection for the next version of Office. This means a lot of the developers and testers are running MFCMAPI against their private, debug builds of Outlook. One of the developers e-mailed me last week to let me know that this build of MFCMAPI was causing debug assertions. They took a look at the stack and told me the function throwing the assertion was MAPIFreeBuffer. They also pointed out where I was making the call.

Horror of horrors! Had I somehow attempted to MAPIFreeBuffer memory that hadn't been allocated via MAPI? Or was I freeing the same memory twice? After all the preaching I do to my customers over the importance of good MAPI memory management had I committed one of the sins I caution against?

Well, yes and no. I had indeed made an error, but it wasn't one I had seen before.

MFCMAPI displays a lot of data in list boxes. A row in a list box has a single data pointer. I have a structure that contains pointers to various buffers to hold things like Entry IDs. To simplify cleanup, I allocate the main structure with MAPIAllocateBuffer, then I allocate buffers that hang off of this structure with MAPIAllocateMore. That way, a single call to MAPIFreeBuffer can free the structure and all of the extra buffers. This makes my code very clean.

Sometimes I need to wipe out one of these buffers and replace the contents. This is where I got into trouble. I didn't want to leak any memory, so I called MAPIFreeBuffer on the buffer, set the pointer to NULL, then allocated a new buffer with MAPIAllocateMore. That call to MAPIFreeBuffer is the one that caused the assertion. You cannot free memory allocated with MAPIAllocateMore by calling MAPIFreeBuffer on it. The only way to free that memory is to call MAPIFreeBuffer on the 'parent' memory which you indicated in the call to MAPIAllocateMore.

So, lesson learned. Here are some other common MAPI memory leaks I see in customer's code (heck, I think I've seen every one of them in our code at one point or another):

  • Freeing LPSRowSet and LPAdrList pointers with MAPIFreeBuffer: Big mistake, big leak. These special array based structures are not built with MAPIAllocateMore. Each entry in the list is allocated with it's own call to MAPIAllocateBuffer. Special structures need special cleanup functions, FreeProws and FreePadrlist. These functions walk the arrays and call MAPIFreeBuffer on each entry before calling MAPIFreeBuffer on the array itself. (A neat side effect of this is you can 'rip' an entry out of an array and stuff it somewhere else without having to copy anything. Just NULL out the array entry and it's yours. Make sure you free it when you're done!)
  • Calling MAPIFreeBuffer on MAPI objects: You wouldn't believe how many times I've seen this.
  • Calling UlRelease on MAPI structures: Or this.
  • Not calling Release when you're done with MAPI objects: Developers come up with all sorts of crazy reasons to justify this practice. My favorite was a developer who was deleting messages with IMAPIFolder::DeleteMessage. He had an IMessage object pointing at the message he was deleting and wouldn't call Release on it because 'the message had been deleted'. He was suprised to when he started getting MAPI_E_CALL_FAILED when calling OpenEntry after his app had been running a long time.

So how did I fix my bug? The answer was quite simple - don't call MAPIFreeBuffer here! The memory I had allocated will be freed when the parent memory is freed, regardless of whether or not I still have a pointer to it. Since this scenario was rare, I can afford the memory hit of having a few extra buffers allocated for the lifetime of the structure.

[Comments for this post have been closed]

Comments (19)

  1. WOW. Yet another reason why .NET is fantastic and I can’t wait until the day that MS kills all of this old C(C++) crap!

    What a pain in the ass!

  2. Amit says:

    OL/MAPI development is an humbling experience after working on other MS technologies. The documentation, Samples, Support (or lack thereof) for developers are in stark contrast as compared to any other SDKs, take for example, DirectX, IE, etc. Fine that there’s no documentation, but there are no debug symbols as well. If you are doing anything other than "Hello World" you are going to run into issues and it’s extreamly difficult to figure out if it is your own bug or an OL/MAPI bug without symbols.

    For anyone, who wants to go one step further and need to aggregate a MAPI object, tough luck, MAPI objects don’t support aggregation. In OL2000 they typecast Message store interface as a C++ object (Mail Folder Properties – Synchronize).

    Speaking of bugs, we have come across following issues in OL/MAPI:

    1. NewInspector event does not fire when OL is launched as form server:

    When Outlook is launched as a form server (using IMAPIFormMgr::LoadForm followed by IPersistMessage::Load and IMAPIForm::DoVerb) to display a particular IMessage, and if this is done when Outlook is not already running, an inspector window launches to display the IMessage. However the NewInspector event does not fire in this case.

    2. Outlook crashes when you return E_ABORT from the IOutlookExtItemEvents::OnOpen while opening an embedded message:

    When an embedded message within another message is opened from the preview pane by double-clicking on it, the IOutlookExtItemEvents::OnOpen method is called. If we wish to dismiss the inspector window that launches to display this message, the documentation recommends us to return E_ABORT. However, in this case (opening an embedded message) returning E_ABORT (or any other failure code) causes Outlook to crash.

    3. An ECF file is needed for the IOutlookExtItemEvents method calls to fire:

    Not documented!

    4. The ECF file entries required to handle the IExchAttachedFileEvents methods are unintuitive:

    Normally the ECF file entries to handle the methods are the names of the methods in the Events key under the relevant section(s). But in the case of the above interface the entries are different. They are OnOpenAttachment; OnLoadAttachmentFromFile; OnSaveAttachmentToFile

    5. The olDiscard flag does not work on Outlook XP:

    The _MailItem(and other item types such as _AppointmentItem etc) AND _Inspector interfaces in the Outlook object model support a Close method. This method takes in a flag which is of the enum type OlInspectorClose. The valid enum values are olSave (which means save the changes to the item before closing the window), olDiscard (discard any changes made to the item before closing the window) and

    olPromptForSave (which means prompt for save if there are any changes). In Outlook XP, the olDiscard flag does NOT work. It treats the value as an olSave value and actually saves the changes.

    6. PR_ATTACH_NUM returns a large value when OL2003 is running in Cached Exchange Mode:

    Normally the PR_ATTACH_NUM property of the attachment returns the zero-based index of the attachment in the attachment table. However, in offline mode (seen in "Cached Exchange mode" in OL 2003), Outlook returns a random large value for the PR_ATTACH_NUM property

    7. Adding a new command bar to a Word Inspector window fails

    When an Outlook addin or Exchange Client Extension tries to add a command bar to the CommandBars collection of a Word Inspector window (this happens when Word is used to display rich text emails) this call fails with E_FAIL. In fact addition of a command bar into Word from any out-of-process code fails

    We would like more documentation ideally, but debug symbols are absolute necessity. One can argue that OL symbols are intellectual property, but what about MAPI symbols?

    Is there any hope of change in this picture?

    amit at gmail dot com

  3. Amit,

    Just got back from vacation. Thanks for your reply!

    I agree that debugging MAPI issues without the MAPI symbols is not ideal, but MAPI is part of the Outlook/Exchange product, so we don’t treat MAPI symbols differently from Outlook or Exchange symbols. Have you tried our public symbol server:

    I’m not positive, but I think we’re publishing Office and Exchange (hence MAPI) symbols up there.

    Commenting on some of your other issues:

    1. NewInspector event does not fire when OL is launched as form server:

    I think you’ll see the same thing when we open a message through Simple MAPI (which under the covers, does a modal form server). I recall hearing something about this but I can’t locate an issue or bug on it right now – if this is a problem for you, you should open an issue with PSS.

    2. Outlook crashes when you return E_ABORT from the IOutlookExtItemEvents::OnOpen while opening an embedded message:

    Definitely report this to PSS so we can work on a fix.

    3. An ECF file is needed for the IOutlookExtItemEvents method calls to fire:

    216693 OL98: New Optional Programming Interface Supported for Outlook Extensions

    4. The ECF file entries required to handle the IExchAttachedFileEvents methods are unintuitive:


    5. The olDiscard flag does not work on Outlook XP:

    Good catch – I found the work item to fix this in Outlook 2003, but it looks like no customer has reported it. Again, file an issue with PSS so we can consider fixing it.

    6. PR_ATTACH_NUM returns a large value when OL2003 is running in Cached Exchange Mode:

    I see this, but I don’t see how it’s a problem. The number returned can be used to access the attachment, so the fact that it’s large does not appear to affect anything.

    7. Adding a new command bar to a Word Inspector window fails

    This would appear to be a general Word issue. I’ve asked one of my colleagues to look at this one since I’m not familiar with it.

  4. Amit says:

    Hi Stephen,

    Thanks for your detailed reply, very much appreciate it.

    Symbol server at is like our bread-and-butter. 🙂 Windbg pointed to symbol server is our standard debugging enviornment. There’re no MAPI/Outlook symbols available for download from the above site, however.

    I am not familiar with the process to open an issue with PSS. Please let me know how to.

    The biggest problem we are facing right now is due to the nature of interaction between MAPI message store and Outlook. In our product, we need to aggregate Message store (and a few other MAPI) interfaces to provide value addition over standard OL features. However, we are getting killed because of undocumented private message store interfaces that outlook uses, for e.g. related to Synchronize operation. All we need is to be able to expose those to Outlook correctly (exactly like msmapi32.dll) and wish that those interface definitions were documented.

    Here’s a list of a few such interfaces:











    This is the biggest issue we have. Though we would like to see other issues (mentioned in the first email) fixed, we are able to manage with work-arounds for now.

    I would really appreciate if we could find some help regarding those interfaces.

    BTW, There’s new ISV buddy program from microsoft: I would love to have contact with someone from a MAPI/Outlook dev team as a dev-buddy.

    Many thanks and best regards,

    amjoshi at gmail dot com (correct email address) 🙂

  5. Here’s the URL for contacting support in the US:

    It may differ for other countries. The first link is for phone support and the second link is for web support. Both have an up front cost, but for bugs the incident can be refunded.

    I’ve forwarded you observation that we don’t have Outlook and Exchange symbols on the public server to some folks internally who may be able to help fix that. I’ll post back the results later.

    Now – when you say aggregate I assume you’re trying to wrap the usual providers inside a provider of your own, right? If that’s the case, then you should be handling those interfaces you don’t recognize by passing the QI call on to the wrapped provider.

  6. Amit says:

    It will be really really great if MAPI symbols become available.

    Yes, we are wrapping the original provider inside our own implementation. However it is tricky when it comes to QI for private interfaces. We initially started out with AGGREGATE_BLIND for the wrapped MAPI provider. There are two problems with this approach:

    1. We depend on wrapping other MAPI objects like IMessage to provide features. Now if we blindly delegate QI to the original MAPI messate store provider, we found out that in some cases, Outlook will obtain IMessage instances directly through one of those interfaces. This is inconsistant and detrimental to our functionality.

    2. A bunch of unexpected crashes. One example, in the context of interface:

    interface __declspec(__uuid("9dd28672-0da0-4f3c-9946-19546b94bc1a")) IMsgStorePrivate : IUnknown


    STDMETHOD(Method1)( LPVOID* pIn, LPVOID** ppOut );


    EmsMdb32.dll will crash because somehow it has obtained a pointer to our message store wrapper and it is treating it as its own. It will try to access a member (some IMAPIProp*), call HrGetOneProp and crash.

    Crash details:

    Path: C:Program FilesCommon FilesSystemMAPI1033EMSMDB32.DLL

    EMSMDB version: 10.0.6515.0

    Load Address: 353f0000 35472000 EMSMDB32

    Offset Op Code Instr Comments


    35425418 ff7510 push dword ptr [ebp+0x10] ; HrGetOneProp arg 3 : LPSPropValue FAR * ppprop

    3542541b 680201097c push 0x7c090102 ; HrGetOneProp arg 2 : Property: Tag: 0x7c09, Type: PT_BINARY.

    35425420 ffb78c000000 push dword ptr [edi+0x8c] ; HrGetOneProp arg 2 : IMAPIProp pointer. In this case edi contains pointer to our wrapper object viz. CMsgStoreWrap*. edi+8c will contain some random data !!!

    35425426 e88566fdff call EMSMDB32!MSProviderInit+0x8390(353fbab0) ; Call HrGetOneProp indirectly.

    One interesting thing about this is that the actual IMsgStore implemented in EmsMdb32.dll is wrapped by msmapi32.dll and we are aiming to do the same thing above msmapi32.dll. msmapi32.dll seems to be using similar kind of delegation of COM interfaces as we do. So either we are missing something obvious or msmapi has special code to wrap those private interface.



  7. No promises, but I think we may be getting Outlook 2003 symbols up on the symbol server soon – we were publishing stripped (public) symbols to our internal company wide server already, so pushing them to the Internet facing server should be easy.

    On the aggregate issue – I see now what you meant by your original comment that MAPI objects don’t support aggregation. You might try opening a PSS case for us to investigate some of these issues, but I suspect we’ll end up concluding that aggregation support is not part of our design.

    In that case, I’d agree that your wrapper should not pass the QI’s on to avoid issues like that. Of course, this means you won’t be supporting anything those interfaces are used for (mainly offline).

  8. Amit says:

    Hey, thanks for such a prompt reply. If we get OL2003 symbols, that will be awesome.

    On aggregation support, COM aggregation support for MAPI objects as such is not going to solve this problem. This problem is that we don’t completely understand the interfaces of the object we are trying to aggregate.

    However, we don’t have an option not to support those private interfaces. Outlook without cached exchange mode not working properly, simply is not going to fly. So we HAVE to support those provate interfaces. Otherwise we are in this loop:

    while( success != google.find( "MsgStore private interfaces") ) { "run in circles scream and shout" }


  9. Amit,

    I’ve actually worked several cases with regards to message stores "not working" in the latest version of Outlook, and I’ve never seen a case where this was caused by the message store provider not providing an undocumented interface. As long as you are implementing the publicly documented MAPI interfaces, you should be OK. If your provider is not working with Exchange cached mode, my guess is that it is something else 😉

    However, I urge you to contact Microsoft Support as Steve suggests so my team can assist you in tracking this down. If there is some undocumented interface that message store providers need to work properly, then we can evaluate the possibility of getting it documented.



  10. The difference here is that Amit is wrapping the Exchange provider, so he was wanting to expose the Exchange provider’s capabilities through his. Unless we were to provide documentation for every interface Outlook could request out of emsmdb, I don’t think this will be possible. As Amti already found out, simply proxying the requests is bad mojo.

  11. Eric Bauersachs says:

    Great article. I always step over it again and again searching for something.

    The debug symbols for MAPI are still not available and still needed by many developers trying to debug Outlook, their plugins for Outlook etc.

    And I agree that there is almost NO DOCUMENTATION, samples, etc. about MAPI.

    The only available resource is that book "Inside MAPI" that is out of stock (but available somewhere on CD).

  12. to_amitmohan says:

    We are working on to develop a message store provider by providing a very thin wrapper over MSFT’s PST implementation.

    OL 2002 is asking for a private inyerface "D9F7AEF8-60B0-11D3-9A52-00500476D23B". If we don’t return this interface, our message store provider does not work properly.- Body text is garbled when you right click a file ->SendTo Mail Recipient.

    So should we support these private interfaces or not??.

  13. Check out my wrapped PST sample:

    The key here is that for all interfaces you don’t explicitely handle, you should be passing the QI on to the wrapped object. So, for instance, you might write code like this:
    STDMETHODIMP MyMessage::QueryInterface(REFIID riid, LPVOID * ppvObj)
       HRESULT hr = S_OK;
       if(riid == IID_IMessage || riid == IID_IMAPIProp || riid == IID_IUnknown)
           *ppvObj = (LPVOID)this;
           hr = m_lpIMessage->QueryInterface(riid, ppvObj);
       return hr;

    This way, you don’t know or care whether ‘private’ interfaces have been requested.

  14. to_amitmohan says:

    If I pass the QI on to the wrapped object, I have the following concern.

    1) What if Outlook 2002 uses one of these private interfaces to get IMessage interface pointer. In that case we will lose the control and the whole purpose of wrapping will be defeated.

  15. You’re right – that scenario would defeat the purpose of wrapping. However, the PST provider expects to be wrapped here, so that scenario shouldn’t exist.

  16. Useful blog says:

    Your blog is really very interesting.

  17. to_amitmohan says:

    Historically, we know for a fact that letting the private interfaces go through creates major problems which are typically *very hard* to detect. According to this blog we are not the only ones. It might take us months before we can link a particular crash to this condition.

    So let me ask a different question: how does Outlook know that it is dealing with a PST wrapper? (Since Outlook MAPI client is asking for a private interface, which only mspst32.dll service provider knows about)

    If we can fool Outlook into thinking that it is *not* talking to a PST wrapper, the initial problem should disappear, right ?

  18. Ok – we’ve getten way off topic for this post. Sure, you COULD wrap the PST provider and never tell Outlook that it’s a PST so Outlook won’t treat it as a PST. But then your next comment will ask how to get Outlook to treat it as a PST only when you really want to treat it as a PST. Either Outlook knows this is a PST and expects to treat it as such or it doesn’t. You don’t get to pick which features of the PST you like and which you don’t.

    Sorry, but that’s just the way it is.

  19. Do What? says:

      The Problem Here in Duet Support, there are times when we have issues where processes are consuming

Skip to main content