Reading an RTF Stream


Suppose you’ve gotten a stream from WrapCompressedRTFStream and want to read what’s in it. Your code might look something like this:

#define MAXBYTES 256
void ProcessStream(LPSTREAM lpStream)
{
   HRESULT hRes = S_OK;
   BYTE bBuf[MAXBYTES];
   ULONG ulNumBytes = 0;
   LARGE_INTEGER li = {0};
 
   hRes = lpStream->Seek(
      li,
      STREAM_SEEK_SET,
      NULL);
 
   if (S_OK == hRes) do
   {
      hRes = lpStream->Read(
         bBuf,
         MAXBYTES,
         &ulNumBytes);
 
      if (ulNumBytes > 0)
      {
         // do something with the bytes read
      }
   }
   while (ulNumBytes == MAXBYTES);
}

Note that in the do/while loop, the exit condition is when ulNumBytes is not equal to MAXBYTES, the number of bytes requested. This is consistent with the documentation for ISequentialStream::Read, which states:

“If the number of bytes returned is less than the number of bytes requested, it usually means the Read method attempted to read past the end of the stream.”

However, is this a best practice? When I took a look at our implementation of Read, I found the following comment:

“pcbRead < cb is not an error and does not necessarily mean the end of the file has been reached.”

If this is case, then your exit condition should be when ulNumBytes is zero. I’ve never seen WrapCompressedRTFStream behave this way in practice (and in fact, the code above was cribbed from MFCMAPI), but I have had a customer report it.

I did observe this behavior from a different stream though. In the old EDK, there was a function, HrTextFromCompressedRTFStreamEx, which works similar to WrapCompressedRTFStream. Instead of producing an unwrapped RTF stream, it goes one step further and converts the RTF to plain text. Normally, for EDK functions you can look at the source and see what’s going on, but this function is one of the few in the EDK that does not include source. The only way to call this function is to include the library rtflib32.lib, which shipped only in the EDK. So I’ll just have to tell you how the function works.

You might think this function works by calling WrapCompressedRTFStream, then parsing the resulting RTF, but it does not. Instead, it actually does all of the unwrapping and parsing in one pass over the source stream. The problem here is the parsing is somewhat rudimentary. There are a number of conditions that it can hit within the RTF to cause it to say “I’ve had enough – try again later”. It’ll output whatever it’s read so far, usually fewer bytes than were requested, without any indication of an error. If you call back in to it, it’ll give you more. One of the conditions I know of to cause this is if HrTextFromCompressedRTFStreamEx thinks it has detected a change in the code page.

So – if you’re working with streams in MAPI, the best practice here is to read until you can’t read any more. Here’s the corrected code (MFCMAPI will be fixed in the next update):

#define MAXBYTES 256
void ProcessStream(LPSTREAM lpStream)
{
   HRESULT hRes = S_OK;
   BYTE bBuf[MAXBYTES];
   ULONG ulNumBytes = 0;
   LARGE_INTEGER li = {0};
 
   hRes = lpStream->Seek(
      li,
      STREAM_SEEK_SET,
      NULL);
 
   if (S_OK == hRes) do
   {
      hRes = lpStream->Read(
         bBuf,
         MAXBYTES,
         &ulNumBytes);
 
      if (ulNumBytes > 0)
      {
         // do something with the bytes read
      }
   }
   while (ulNumBytes > 0);
}
Comments (4)

  1. We are using HrTextFromCompressedRTFStreamEx, HrRTFFromTextStream, RTFSyncCpid, ScGetAttachTable from RTFLIb32.lib. We are migrating the code base to 64-bit, but we couldn't find 64-bit version of RTFLIb32.lib. It would be great if you help me in finding the  64-bit version of RTFLIb32.lib.  

  2. I don't believe we've published a 64 bit version of this library.

  3. Thanks for your reply.

    I am planning to use msmapi32.dll in 64-bit as we couldn’t find the RTFLIB32.lib.  But Outlook2010 onwards msmapi32.dll doesn’t export function ScGetAttachTable . Could you please suggest any alternative function for the same?

  4. maheshbg says:

    I was using HrTextFromCompressedRTFStreamEx of RTFLIb32.lib, since RTFLIb32 does not support 64-bit, now we are using msmapi32.dll/OLMAPI32.dll for HrTextFromCompressedRTFStreamEx. But problem is with msmapi32.dll/OLMAPI32.dll we are not getting proper body for Asian languages like Japanese. Same function from RTFLIb32 was giving proper body. Coudld you please tell me what could be the  problem.