Large Multivalued Properties

We recently encountered an issue where, in the customer’s code, a call to GetProps for a particular message failed with MAPI_E_CALL_FAILED. We found that MFCMAPI had trouble getting properties on the message as well. The curious thing was that if we made a GetProps call for one or two properties, it would work. Only when we did GetProps(NULL) or called GetProps with the result of GetPropList did we see the problem. Further investigation of the RPC Client Access logs on the Exchange server (c:Program FilesMicrosoftExchange ServerV14LoggingRPC Client Access) showed several BufferTooSmall errors logged at the same time as our GetProps calls:

2011-05-27T17:45:57.201Z,13537,0,/o=SPRINGFIELD14/ou=Exchange Administrative Group (FYDIBOHF23SPDLT)/cn=Recipients/cn=Administrator,,MFCMapi.exe,12.0.6539.5000,Classic,,,ncacn_ip_tcp,,,1149 (rpc::BufferTooSmall),00:00:00.0156244,,RpcDispatch: [BufferTooSmallException] Exception of type 'Microsoft.Exchange.RpcClientAccess.BufferTooSmallException' was thrown.

I had the opportunity to debug this and found some interesting details in how Exchange responds to a GetProps call. The first thing we do is gather all the properties that were requested from the store. Next, we have to pack these results into an RPC buffer to send them back to the client. An RPC buffer is of limited size, so we have to be careful not to pack too much data in to it. We assume everything will fit and count up how many bytes we used. If we used more bytes than is available in the RPC buffer, we look for the largest PT_BINARY, PT_STRING8 or PT_UNICODE property in our list (over 255 bytes) and replace it with the error MAPI_E_NOT_ENOUGH_MEMORY. Then we repeat, repacking the buffer, checking the size, and removing the largest property if necessary. If we reach a point where we’ve removed every string and binary property we can and our buffer is still too large, we fail the call and throw the BufferTooSmall exception. On the client side, we get MAPI_E_CALL_FAILED, and extended error information shows the error ecBufferTooSmall (0x47D == 1149).

This is what was happening with this message – we had removed every binary and string property we could and still the buffer needed to serialize the remaining properties was larger than the RPC buffer we were given to fill. What took up all the space? This is one of MAPI’s dirty little secrets: large multivalued properties can fill your GetProps buffer and there is no alternative way to request them. This message had a single PT_MV_UNICODE property in it which had entries on the order of a few kilobytes in size. If this property was excluded from the GetProps call, the call would work.. If this property was requested by itself, it would work. But when this property was requested alongside a number of other properties, that was enough to fill the buffer and the call failed.

We saw this problem with Exchange 2010, but this same packing logic exists in all versions of Exchange. All versions of MAPI (both Outlook and MAPICDO) can potentially hit this  as well.

Recommendations for Dealing With Large Multivalued Properties

  1. Don’t do it. If you use multivalued properties, keep the overall total size relatively small. This will greatly reduce the chance that your multivalued property will cause a failure in GetProps.
  2. A large number of small multivalued properties can be just as bad as a small number of large multivalued properties. Don’t go adding 50 different multivalued properties to the same message and expect to be able to ask for everything in one call.
  3. If you must put large amounts of data into multivalued properties, never ask for them alongside other properties. Always ask for them on their own. Put a comment in your code noting why you asked for them by themselves instead of rolling them into another GetProps call so the developer who comes after you doesn’t optimize the code by combining the calls.