PROPSET.CPP
//========================================================================= 
// PROPSET.CPP 
// 
// Copyright (C) 1986-1996.  Microsoft Corp.  All Rights Reserved. 
// 
// Purpose: 
//    Provides OLE and MAPI property set routines. 
//========================================================================= 
 
#include "stdafx.h" 
#include <windowsx.h> 
#include <winnls.h> 
#include <mapinls.h> 
#include "edk.h" 
#include "PostSmpl.h" 
#include <initguid.h> 
#include "freedoc.h" 
 
 
 
/* 
 * Data Strcuctures 
 */ 
 
typedef GUID   FMTID; 
typedef struct _fmtidoffset 
{ 
   FMTID fmtid;               // FMTID for a section 
   DWORD dwOffset;            // offset of section from start of stream 
} FMTIDOFFSET; 
 
typedef struct _propsethdr 
{ 
   WORD  wByteOrder;    // Always 0xFFFE 
   WORD  wFormat;    // Always 0 
   DWORD dwOSVer;    // Hiword: 0=Win16; 1=Mac; 2=Win32 
                     // Loword: GetVersion() 
   CLSID clsid;         // App CLSID 
   DWORD cSections;     // Number of sections 
} PROPSETHDR; 
 
typedef DWORD  PID; 
typedef struct _pidoffset 
{ 
   PID      pid;              // Property ID 
   DWORD dwOffset;            // offset of property from start of section 
} PIDOFFSET; 
 
typedef struct _propsectionhdr 
{ 
   DWORD    cbSection;        // Size of this section 
   DWORD    cProps;           // Number of properties in this section 
} PROPSECTIONHDR; 
 
 
/* 
 * Define some of the guids we use 
 */ 
DEFINE_GUID(FMTID_SumInfo, 0xF29F85E0L, 0x4FF9, 0x1068, 0xAB, 0x91, 0x08, 0x00, 0x2B, 0x27, 0xB3, 0xD9); 
 
typedef struct _mppidmapiprop 
{ 
   WORD     wMapiType;        // MAPI Type 
   LPWSTR      szW;           // MAPI Name 
} MPPIDMAPIPROP; 
 
enum pidSumInfo { 
   PID_TITLE = 2, 
   PID_SUBJECT, 
   PID_AUTHOR, 
   PID_KEYWORDS, 
   PID_COMMENTS, 
   cpidSumInfoMax 
}; 
 
 
typedef struct _propsetstminfo 
{ 
   LPOLESTR       szName;              // stream name 
   FMTID const   *pfmtid;              // FMTID of section to promote 
   INT            cmppidmapipropMax;      // Mapping for property names 
   MPPIDMAPIPROP *mppidmapiprop; 
} PROPSETSTMINFO; 
 
 
// Lego workaround - define the symbols for the array 
 
WCHAR szTitle[]      = L"Title"; 
WCHAR szSubject[]    = L"Subject"; 
WCHAR szAuthor[]     = L"Author"; 
WCHAR szKeywords[]   = L"Keywords"; 
WCHAR szComments[]   = L"Comments"; 
 
MPPIDMAPIPROP mppidmapipropSumInfo[] = 
{ 
   {  PT_NULL, NULL        }, // Dictionary 
   {  PT_NULL, NULL        }, // Code page 
   {  PT_STRING8,    szTitle     }, 
   {  PT_STRING8,    szSubject   }, 
   {  PT_STRING8,    szAuthor    }, 
   {  PT_MV_STRING8, szKeywords  }, // Special case VT_LPSTR -> MV 
   {  PT_STRING8,    szComments  } 
}; 
 
OLECHAR szSumInfo[] = OLESTR("\005SummaryInformation"); 
 
PROPSETSTMINFO rgpropsetstminfo[] = 
{ 
   {szSumInfo, &FMTID_SumInfo, cpidSumInfoMax, mppidmapipropSumInfo}, 
   {NULL, NULL, 0, NULL} 
}; 
 
 
 
/* 
 * SzDupSz 
 * 
 * Purpose: 
 *    This function allocates a new buffer and copies the 
 *    supplied string into it. 
 * 
 * Parameters: 
 *    sz    the string to duplicate 
 * 
 * Returns: 
 *    a pointer to the new string (or NULL on failure) 
 */ 
HRESULT SzDupSz(LPCTSTR sz, LPSTR *lppstr) 
{ 
   INT         cb = 0; 
   HRESULT   hr = NOERROR; 
 
   if (!sz) 
      return hr; 
 
   cb = (lstrlen(sz) + 1) * sizeof(TCHAR); 
   if (!SUCCEEDED(hr = MAPIAllocateBuffer(cb, (LPVOID *)lppstr))) 
         return hr; 
 
   if (!lstrcpy(*lppstr, sz)) 
      return hr; 
 
   return hr; 
} 
 
 
 
 
 
/* 
 * ScParseKeywords 
 * 
 * Purpose: 
 *    Parses a string of keywords listed by separators in a pval, into a 
 *    multi valued property. 
 * 
 * Arguments: 
 *    pval     Contains the keywords list string in pval->Value.lpszA 
 *    szDefSep Default list separator 
 * 
 * Returns: 
 *    SCODE    The status code 
 */ 
SCODE ScParseKeywords(LPSPropValue pval, TCHAR * szDefSep) 
{ 
   SCODE    sc= S_OK; 
   LPSTR    szKeywords= pval->Value.lpszA; 
   CHAR *   pch= szKeywords; 
   CHAR *   pchPrev= NULL; 
   LPSTR *  rgsz= NULL; 
   LONG     cKeywords= 0; 
   LONG     isz= 0; 
   TCHAR    szSep[3]; 
   BOOL     fInWord= FALSE; 
   BOOL     fFakeOneWord= FALSE; 
   HRESULT  hr= NOERROR; 
 
   static TCHAR   szScParseKeywords1[] = TEXT("intl"); 
   static TCHAR   szScParseKeywords2[] = TEXT("sList"); 
   static TCHAR   szScParseKeywords3[] = TEXT(","); 
 
   // Get the list separator character 
   GetProfileString(szScParseKeywords1, szScParseKeywords2, 
         szScParseKeywords3, szSep, sizeof(szSep) / sizeof(TCHAR)); 
 
 
   // Default list separator is the space character 
   if (!szDefSep) 
      szDefSep = TEXT(" "); 
 
   // Loop through the string zero filling non-keywords 
   while (*pch) 
   { 
#ifdef DBCS 
      if (!FGLeadByte(*pch) && (*pch == szDefSep[0] || *pch == szSep[0])) 
#else 
      if (*pch == szDefSep[0] || *pch == szSep[0]) 
#endif 
      { 
         fInWord = FALSE; 
      } 
      else 
      { 
         // If we aren't in a word, we are now 
         if (!fInWord) 
            cKeywords++; 
         fInWord = TRUE; 
      } 
 
      // Remember this position 
      pchPrev = pch; 
 
      // Move forward 
#ifdef DBCS 
      pch = SzGNext(pch); 
#else 
      pch = AnsiNext(pch); 
#endif 
 
      // If we aren't in a word, zap area from our previous position 
      if (!fInWord) 
         ZeroMemory(pchPrev, pch - pchPrev); 
   } 
 
 
   // Remove leading and trailing spaces from keywords 
 
   pch = szKeywords; 
   isz = cKeywords; 
   while (isz) 
   { 
      isz--; 
 
      // Skip consecutive separators 
      while (!*pch) 
         pch++; 
 
      if (*pch) 
      { 
         // Zero out leading spaces 
         pchPrev = pch; 
         while (isspace(*pch)) 
            pch = AnsiNext(pch); 
         ZeroMemory(pchPrev, pch - pchPrev); 
 
         // Blank keyword 
         if (!*pch) 
         { 
            cKeywords--; 
            continue; 
         } 
 
         // Go to end of string 
         pch = &pch[lstrlen(pch)]; 
 
         // Zero out trailing spaces 
         pchPrev = AnsiPrev(szKeywords, pch); 
         while (isspace(*pchPrev)) 
            pchPrev = AnsiPrev(szKeywords, pchPrev); 
         pchPrev = AnsiNext(pchPrev); 
         ZeroMemory(pchPrev, pch - pchPrev); 
      } 
   } 
 
   // Handle denegerate case where we have no keywords. MAPI requires that 
   // there be a value, though, so we fake up a single empty string 
   if (!cKeywords) 
   { 
      cKeywords = 1; 
      fFakeOneWord = TRUE; 
   } 
 
   // Now that we know how many keywords there are, it's time to allocate 
   // space 
   pval->Value.MVszA.cValues = cKeywords; 
    
   if (!SUCCEEDED(hr = MAPIAllocateBuffer(cKeywords * sizeof(LPSTR), (LPVOID *)&pval->Value.MVszA.lppszA))) 
   { 
       sc = E_OUTOFMEMORY; 
   goto CleanUp; 
   } 
 
   rgsz = pval->Value.MVszA.lppszA; 
 
   if (fFakeOneWord) 
   { 
      if (!SUCCEEDED(hr = SzDupSz((LPSTR) TEXT(""), &rgsz[0]))) 
  sc = E_OUTOFMEMORY; 
      goto CleanUp; 
   } 
 
   // Find the strings we had left over 
   pch = szKeywords; 
   while (isz < cKeywords) 
   { 
   if (*pch) 
   { 
// Remember the start of the string and then zoom to the end 
   if ( !SUCCEEDED(hr = SzDupSz(pch, &rgsz[isz++]))) 
   { 
   sc = E_OUTOFMEMORY; 
   goto CleanUp; 
   } 
    
   while (*pch) 
#ifdef DBCS 
            pch = SzGNext(pch); 
#else 
            ++pch; 
#endif 
   } 
      ++pch; 
   } 
 
CleanUp: 
   return sc; 
} 
 
 
 
/* 
 * ScSaveStr 
 * 
 * Purpose: 
 *    Saves a string 
 * 
 * Arguments: 
 *    pstm              The stream to load from 
 *    psz               String pointer  
 * 
 * Returns: 
 *    SCODE             The status code 
 */ 
SCODE ScSaveStr(LPSTREAM pstm, LPCSTR  lpstr) 
{ 
   SCODEsc= S_OK; 
   HRESULT  hr= NOERROR; 
   DWORDdwSize= sizeof(CHAR) * (lstrlen(lpstr) + 1); 
   ULONGcb= 0; 
 
   // Save string size 
   if (hr = pstm->Write(&dwSize, sizeof(DWORD), &cb)) 
      goto CleanUp; 
 
   // Save the string 
   if (dwSize && 
      (hr = pstm->Write(lpstr, dwSize, &cb))) 
      goto CleanUp; 
    
CleanUp: 
   sc = GetScode(hr); 
   return sc; 
} 
 
 
/* 
 * ScLoadStr 
 * 
 * Purpose: 
 *    Load a string 
 * 
 * Arguments: 
 *    pstm              The stream to load from 
 *    psz               String pointer  
 * 
 * Returns: 
 *    SCODE             The status code 
 */ 
SCODE ScLoadStr(LPSTREAM pstm, CString *lpcstring) 
{ 
   SCODEsc= S_OK; 
   HRESULT  hr= NOERROR; 
   LPSTRlpstr= NULL; 
   DWORD    dwSize= 0; 
 
    
     // Save string size 
   if (hr = pstm->Read(&dwSize, sizeof(DWORD), NULL)) 
      goto CleanUp; 
 
   // Allocate space for string and then load the string 
   if (dwSize) 
      hr = MAPIAllocateBuffer(dwSize, (LPVOID *)&lpstr); 
   else 
      hr = SzDupSz((LPTSTR)TEXT(""), &lpstr); 
 
   if (!SUCCEEDED(hr)) 
   { 
      sc = E_OUTOFMEMORY; 
      goto CleanUp; 
   } 
 
   if (dwSize && (hr = pstm->Read(lpstr, dwSize, NULL))) 
   { 
      sc = GetScode(hr); 
      goto CleanUp; 
   } 
 
   *lpcstring = lpstr; 
 
CleanUp: 
 
   MAPIFREEBUFFER(lpstr); 
 
   return sc; 
} 
 
 
 
/* 
 * SetSummaryInfo 
 * 
 */ 
STDAPI SetSummaryInfo 
   ( 
   LPSTORAGE pstg, 
   LPCSTRlpsTitle, 
   LPCSTR   lpsSubject, 
   LPCSTR   lpsAuthor, 
   LPCSTR   lpsKeywords, 
   LPCSTR   lpsComments 
   ) 
{ 
   SCODE             sc= S_OK; 
   HRESULT           hr= NOERROR; 
   PROPSETHDR        propsethdr; 
   PROPSECTIONHDR    propsectionhdr; 
   FMTIDOFFSET*      rgfmtidoffset= NULL; 
   FMTIDOFFSET*      pfmtidoffset= NULL; 
   PIDOFFSET*        rgpidoffset= NULL; 
   PIDOFFSET*        ppidoffset= NULL; 
   PROPSETSTMINFO*   ppropsetstminfo= rgpropsetstminfo;    // point to summary info part 
   ULONG             cb= 0; 
   BOOL              fFound= FALSE; 
   BOOL              fSumInfoStm= FALSE; 
   LARGE_INTEGER     li= {0}; 
   DWORD             dwType= 0; 
   LPSTREAM          pstm= NULL; 
 
    // Open the summay info stream 
   sc = pstg->OpenStream(ppropsetstminfo->szName, 
                         NULL, 
                         STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 
                         0, 
                         &pstm); 
 
//If doesn't exist, create a new summary info stream 
if (sc == STG_E_FILENOTFOUND) 
sc = pstg->CreateStream(ppropsetstminfo->szName, 
                              STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 
  0, 
  0, 
  &pstm); 
 
 
   if (FAILED(sc)) 
   goto CleanUp; 
 
    
    // Write the PropSet header 
propsethdr.cSections= 1; 
propsethdr.wByteOrder= 0xFFFE ; 
propsethdr.wFormat=  0; 
propsethdr.dwOSVer= MAKELONG(LOWORD(GetVersion()),2); 
//propsethdr.clsid=; 
 
if (hr = pstm->Write(&propsethdr, sizeof(propsethdr), &cb)) 
      goto WriteError; 
 
   // Write the FMTID/OFFSET pair 
   cb = propsethdr.cSections * sizeof(FMTIDOFFSET); 
    
   if (!SUCCEEDED(hr = MAPIAllocateBuffer(cb, (LPVOID *)&rgfmtidoffset))) 
   { 
      sc = E_OUTOFMEMORY; 
  goto CleanUp; 
   } 
    
   pfmtidoffset = rgfmtidoffset; 
   pfmtidoffset->fmtid = FMTID_SumInfo; 
   pfmtidoffset->dwOffset = sizeof(PROPSETHDR) + sizeof(FMTIDOFFSET); 
    
   if (hr = pstm->Write(rgfmtidoffset, cb, &cb)) 
      goto WriteError; 
 
  
   // Seek to that location in the stream 
   LISet32(li, pfmtidoffset->dwOffset); 
   if (hr = pstm->Seek(li, STREAM_SEEK_SET, NULL)) 
      goto CleanUp; 
 
   // Write the section header  
   propsectionhdr.cProps = 5; 
   propsectionhdr.cbSection = 2 * sizeof(DWORD) + 
  propsectionhdr.cProps * 4 * sizeof(DWORD)+  
  sizeof(CHAR) * (lstrlen(lpsSubject) + 1)+ 
  sizeof(CHAR) * (lstrlen(lpsTitle) +1)+ 
  sizeof(CHAR) * (lstrlen(lpsComments) + 1)+ 
  sizeof(CHAR) * (lstrlen(lpsAuthor) +  1)+ 
  sizeof(CHAR) * (lstrlen(lpsKeywords) + 1) ; 
 
   if (hr = pstm->Write(&propsectionhdr, sizeof(propsectionhdr), NULL)) 
      goto WriteError; 
    
 
   // Write the PID/OFFSET pairs 
   cb = propsectionhdr.cProps * sizeof(PIDOFFSET); 
    
    
   if (!SUCCEEDED(hr = MAPIAllocateBuffer(cb, (LPVOID *)&rgpidoffset))) 
   { 
      sc = E_OUTOFMEMORY; 
  goto CleanUp; 
   } 
   ppidoffset = rgpidoffset; 
 
   ppidoffset->pid = PID_TITLE;  
   ppidoffset->dwOffset = 12 * sizeof(DWORD); 
   ppidoffset++; 
   ppidoffset->pid = PID_SUBJECT;  
   ppidoffset->dwOffset = (ppidoffset - 1)->dwOffset +  
sizeof(CHAR) * (lstrlen(lpsTitle) + 1) + sizeof(DWORD)*2; 
   ppidoffset++; 
   ppidoffset->pid = PID_AUTHOR;  
   ppidoffset->dwOffset = (ppidoffset - 1)->dwOffset +  
sizeof(CHAR) * (lstrlen(lpsSubject) + 1) + sizeof(DWORD)*2; 
   ppidoffset++; 
   ppidoffset->pid = PID_COMMENTS;  
   ppidoffset->dwOffset = (ppidoffset - 1)->dwOffset +  
sizeof(CHAR) * (lstrlen(lpsAuthor) + 1) + sizeof(DWORD)*2; 
   ppidoffset++; 
   ppidoffset->pid = PID_KEYWORDS;  
   ppidoffset->dwOffset = (ppidoffset - 1)->dwOffset + 
sizeof(CHAR) * (lstrlen(lpsComments) + 1) + sizeof(DWORD)*2; 
   ppidoffset = rgpidoffset; 
 
   if (hr = pstm->Write(rgpidoffset, cb, NULL)) 
      goto WriteError; 
 
   // Go through all the properties 
   for (; propsectionhdr.cProps--; ++ppidoffset) 
   { 
      // Seek to the data 
      LISet32(li, ppidoffset->dwOffset + pfmtidoffset->dwOffset); 
      if (hr = pstm->Seek(li, STREAM_SEEK_SET, NULL)) 
         goto CleanUp; 
 
      // Write the type and property value 
      hr = NOERROR; 
      switch (ppidoffset->pid) 
      { 
      case PID_TITLE: 
 
  dwType = PT_STRING8; 
  if (hr = pstm->Write(&dwType, sizeof(DWORD), &cb)) 
goto WriteError; 
         sc = ScSaveStr(pstm, lpsTitle); 
         break; 
 
      case PID_SUBJECT: 
   
  dwType = PT_STRING8; 
  if (hr = pstm->Write(&dwType, sizeof(DWORD), &cb)) 
goto WriteError; 
 
          sc = ScSaveStr(pstm, lpsSubject); 
          break; 
 
      case PID_AUTHOR: 
          
  dwType = PT_STRING8; 
  if (hr = pstm->Write(&dwType, sizeof(DWORD), &cb)) 
goto WriteError; 
  
  sc = ScSaveStr(pstm, lpsAuthor); 
  break; 
 
      case PID_KEYWORDS: 
           
  dwType = PT_MV_STRING8; 
  if (hr = pstm->Write(&dwType, sizeof(DWORD), &cb)) 
goto WriteError; 
 
  sc = ScSaveStr(pstm, lpsKeywords); 
  break; 
 
      case PID_COMMENTS: 
         
  dwType = PT_STRING8; 
  if (hr = pstm->Write(&dwType, sizeof(DWORD), &cb)) 
  goto WriteError; 
 
  sc = ScSaveStr(pstm, lpsComments); 
          break; 
 
      default: 
         continue; 
         break; 
      } 
      if (hr) goto WriteError; 
   } 
 
   pstm->Commit(STGC_DEFAULT); 
 
   goto CleanUp; 
 
WriteError: 
   sc = GetScode(hr); 
 
CleanUp: 
   MAPIFREEBUFFER(rgfmtidoffset); 
   MAPIFREEBUFFER(rgpidoffset); 
   ULRELEASE(pstm); 
   return sc; 
} 
 
 
 
 
/* 
 * GetSummaryInfo 
 * 
 */ 
STDAPI GetSummaryInfo 
   ( 
   LPSTORAGE  pstg, 
   CString*  lpsTitle, 
   CString*   lpsSubject, 
   CString*   lpsAuthor, 
   CString*   lpsKeywords, 
   CString*   lpsComments 
   ) 
{ 
   SCODE             sc= S_OK; 
   HRESULT           hr= NOERROR; 
   PROPSETHDR        propsethdr; 
   PROPSECTIONHDR    propsectionhdr; 
   FMTIDOFFSET*      rgfmtidoffset= NULL; 
   FMTIDOFFSET*      pfmtidoffset= NULL; 
   PIDOFFSET*        rgpidoffset= NULL; 
   PIDOFFSET*        ppidoffset= NULL; 
   PROPSETSTMINFO*   ppropsetstminfo = rgpropsetstminfo;    // point to summary info part 
   ULONG             cb= 0; 
   BOOL              fFound= FALSE; 
   BOOL              fSumInfoStm= FALSE; 
   LARGE_INTEGER     li={0}; 
   DWORD             dwType= 0; 
   LPSTREAM          pstm= NULL; 
 
      
   *lpsTitle   = TEXT(""); 
   *lpsSubject = TEXT(""); 
   *lpsAuthor    = TEXT(""); 
   *lpsKeywords= TEXT(""); 
   *lpsComments= TEXT(""); 
    
   // Open the summay info stream 
   sc = pstg->OpenStream(ppropsetstminfo->szName, 
                         NULL, 
                         STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 
                         0, 
                         &pstm); 
   if (FAILED(sc)) 
      { 
      goto CleanUp; 
      } 
 
   // Read the header to find out how many sections we need to search 
   if (hr = pstm->Read(&propsethdr, sizeof(propsethdr), NULL)) 
      goto ReadError; 
   if (propsethdr.cSections == 0) 
      goto CleanUp; 
 
   // Allocate space for the sections list and read it in 
   cb = propsethdr.cSections * sizeof(FMTIDOFFSET); 
       
   if (!SUCCEEDED(hr = MAPIAllocateBuffer(cb, (LPVOID *)&rgfmtidoffset))) 
   { 
      sc = E_OUTOFMEMORY; 
  goto CleanUp; 
   } 
   pfmtidoffset = rgfmtidoffset; 
 
   if (hr = pstm->Read(rgfmtidoffset, cb, NULL)) 
      goto ReadError; 
 
   // Look for the section with the matching FMTID 
   for (fFound = FALSE; propsethdr.cSections--; ++pfmtidoffset) 
   { 
      if (IsEqualGUID(*ppropsetstminfo->pfmtid, pfmtidoffset->fmtid)) 
      { 
         fFound = TRUE; 
         break; 
      } 
   } 
   if (!fFound) 
      goto CleanUp; 
 
   // Are we in the SumInfo stream 
   fSumInfoStm = IsEqualGUID(*ppropsetstminfo->pfmtid, FMTID_SumInfo); 
 
   // Seek to that location in the stream 
   LISet32(li, pfmtidoffset->dwOffset); 
   if (hr = pstm->Seek(li, STREAM_SEEK_SET, NULL)) 
      goto ReadError; 
 
   // Read the section header to find out how many props we need to read 
   if (hr = pstm->Read(&propsectionhdr, sizeof(propsectionhdr), NULL)) 
      goto ReadError; 
   if (propsectionhdr.cProps == 0) 
      goto CleanUp; 
 
   // Allocate space for the props list and read it in 
   cb = propsectionhdr.cProps * sizeof(PIDOFFSET); 
       
   if (!SUCCEEDED(hr = MAPIAllocateBuffer(cb, (LPVOID *)&rgpidoffset))) 
   { 
      sc = E_OUTOFMEMORY; 
  goto CleanUp; 
   } 
 
   ppidoffset = rgpidoffset; 
 
   if (hr = pstm->Read(rgpidoffset, cb, NULL)) 
      goto ReadError; 
 
   // Go through all the properties 
   for (; propsectionhdr.cProps--; ++ppidoffset) 
   { 
      // Seek to the data 
      LISet32(li, ppidoffset->dwOffset + pfmtidoffset->dwOffset); 
      if (hr = pstm->Seek(li, STREAM_SEEK_SET, NULL)) 
         goto ReadError; 
 
      // Read the type 
      if (hr = pstm->Read(&dwType, sizeof(DWORD), NULL)) 
         goto ReadError; 
 
 
  // Load the data 
      hr = NOERROR; 
      switch (ppidoffset->pid) 
      { 
      case PID_TITLE: 
         sc = ScLoadStr(pstm, lpsTitle); 
         break; 
 
      case PID_SUBJECT: 
         sc = ScLoadStr(pstm, lpsSubject); 
         break; 
 
      case PID_AUTHOR: 
         sc = ScLoadStr(pstm, lpsAuthor); 
         break; 
 
      case PID_KEYWORDS: 
 sc = ScLoadStr(pstm, lpsKeywords); 
         break; 
 
      case PID_COMMENTS: 
         sc = ScLoadStr(pstm, lpsComments); 
         break; 
 
      default: 
         continue; 
         break; 
      } 
      if (hr) goto ReadError; 
   } 
 
   goto CleanUp; 
 
ReadError: 
   sc = GetScode(hr); 
 
CleanUp: 
   MAPIFREEBUFFER(rgfmtidoffset); 
   MAPIFREEBUFFER(rgpidoffset); 
   ULRELEASE(pstm); 
   return sc; 
} 
 
 
 
/* 
 * PromoteSummaryInfo 
 * 
 */ 
 STDAPI PromoteSummaryInfo 
   ( 
   LPMESSAGE   pmsg, 
   LPSTR     lpszTitle, 
   LPSTR     lpszSubject, 
   LPSTR     lpszAuthor, 
   LPSTR     lpszKeywords, 
   LPSTR     lpszComments 
   ) 
{ 
   CONST UINT        cProps = 5; 
   HRESULT           hr= E_OUTOFMEMORY; 
   UINT              i= 0; 
   MPPIDMAPIPROP*    pmppidmapipropSumInfo = &mppidmapipropSumInfo[2];  // start at szTitle 
   LPMAPINAMEID*     rgpmnid = NULL; 
   LPMAPINAMEID      rgmnid = NULL; 
   LPSPropTagArray   ptaga = NULL; 
   SPropValue        rgvalSumInfo[cProps]; 
 
   if (SUCCEEDED(hr = MAPIAllocateBuffer( cProps * sizeof(LPMAPINAMEID), (LPVOID *)&rgpmnid))) 
   { 
      if (SUCCEEDED(hr = MAPIAllocateBuffer(cProps * sizeof(MAPINAMEID), (LPVOID*)&rgmnid))) 
      { 
         // Initialize the name id structure and the array of pointers to the name id structurs 
         for (i = 0; i < cProps; i++) 
         { 
            rgmnid[i].lpguid          = (LPGUID)&PS_PUBLIC_STRINGS; 
            rgmnid[i].ulKind          = MNID_STRING; 
            rgmnid[i].Kind.lpwstrName = pmppidmapipropSumInfo[i].szW; 
            rgpmnid[i] = &rgmnid[i]; 
         } 
 
         if (SUCCEEDED(hr = pmsg->GetIDsFromNames(cProps, rgpmnid, MAPI_CREATE, &ptaga))) 
         { 
            if (cProps == ptaga->cValues) 
            { 
               // Plug the IDs into the values 
               for (i = 0; i < cProps && SUCCEEDED(hr);  i++) 
               { 
                  rgvalSumInfo[i].ulPropTag = PROP_TAG(pmppidmapipropSumInfo[i].wMapiType, 
                                                       PROP_ID(ptaga->aulPropTag[i])); 
 
                  switch (i) 
                  { 
                     case 0: 
                        rgvalSumInfo[i].Value.lpszA = (LPTSTR)lpszTitle; 
                        break; 
                     case 1: 
                        rgvalSumInfo[i].Value.lpszA = (LPTSTR)lpszSubject; 
                        break; 
                     case 2: 
                        rgvalSumInfo[i].Value.lpszA = (LPTSTR)lpszAuthor; 
                        break; 
                     case 3: 
                     { 
                        rgvalSumInfo[i].Value.lpszA = (LPTSTR)lpszKeywords; 
 
                        hr = ScParseKeywords(&rgvalSumInfo[i], TEXT(";")); 
                     } 
                        break; 
                     case 4: 
                        rgvalSumInfo[i].Value.lpszA = (LPTSTR)lpszComments; 
                        break; 
                  } 
             
               } 
 
               if (SUCCEEDED(hr)) 
               { 
                  // Set the values 
                  hr = pmsg->SetProps(cProps, rgvalSumInfo, NULL); 
               } 
 
               // Free our prop tag array 
               MAPIFREEBUFFER(ptaga); 
            } 
         } 
         MAPIFREEBUFFER(rgmnid); 
      } 
      MAPIFREEBUFFER(rgpmnid); 
   } 
 
   return hr; 
}