home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / mfc / ole / drawcli / propset.cpp < prev    next >
C/C++ Source or Header  |  1998-03-26  |  40KB  |  1,574 lines

  1. // This is a part of the Microsoft Foundation Classes C++ library.
  2. // Copyright (C) 1992-1998 Microsoft Corporation
  3. // All rights reserved.
  4. //
  5. // This source code is only intended as a supplement to the
  6. // Microsoft Foundation Classes Reference and related
  7. // electronic documentation provided with the library.
  8. // See these sources for detailed information regarding the
  9. // Microsoft Foundation Classes product.
  10.  
  11. #include "stdafx.h"
  12.  
  13. #include <malloc.h>
  14. #include <ole2.h>
  15. #include <oleauto.h>
  16. #include "propset.h"
  17.  
  18. #ifdef AFXCTL_PROP_SEG
  19. #pragma code_seg(AFXCTL_PROP_SEG)
  20. #endif
  21.  
  22. #ifdef _DEBUG
  23. #undef THIS_FILE
  24. static char THIS_FILE[] = __FILE__;
  25. #endif
  26.  
  27. #define new DEBUG_NEW
  28.  
  29.  
  30. static LPVOID CountPrefixedStringA(LPCSTR lpsz)
  31. {
  32.     DWORD cb = (lstrlenA( lpsz ) + 1);
  33.     LPDWORD lp = (LPDWORD)malloc( (int)cb + sizeof(DWORD) );
  34.     if (lp)
  35.     {
  36.         *lp = cb;
  37.         lstrcpyA( (LPSTR)(lp+1), lpsz );
  38.     }
  39.  
  40.     return (LPVOID)lp;
  41. }
  42.  
  43.  
  44. static LPVOID CountPrefixedStringW(LPCWSTR lpsz)
  45. {
  46.     DWORD cb = (wcslen( lpsz ) + 1);
  47.     LPDWORD lp = (LPDWORD)malloc( (int)cb * sizeof(WCHAR) + sizeof(DWORD) );
  48.     if (lp)
  49.     {
  50.         *lp = cb;
  51.         wcscpy( (LPWSTR)(lp+1), lpsz );
  52.     }
  53.  
  54.     return (LPVOID)lp;
  55. }
  56.  
  57.  
  58. #ifdef _UNICODE
  59. #define CountPrefixedStringT CountPrefixedStringW
  60. #else
  61. #define CountPrefixedStringT CountPrefixedStringA
  62. #endif
  63.  
  64.  
  65. #ifdef _UNICODE
  66.  
  67. #define MAX_STRLEN 1024
  68.  
  69. static LPBYTE ConvertStringProp(LPBYTE pbProp, DWORD dwType, ULONG nReps,
  70.     size_t cbCharSize)
  71. {
  72.     LPBYTE pbResult = NULL; // Return value
  73.     ULONG cbResult = 0;     // Number of bytes in pbResult
  74.     LPBYTE pbBuffer;        // Temporary holding space
  75.     ULONG cchOrig;          // Number of characters in original string
  76.     ULONG cchCopy;          // Number of characters to copy
  77.     ULONG cbCopy;           // Number of bytes to copy
  78.     LPBYTE pbResultNew;     // Used for realloc of pbResult
  79.  
  80.     pbBuffer = (LPBYTE)malloc(MAX_STRLEN * cbCharSize);
  81.     if (pbBuffer == NULL)
  82.         return NULL;
  83.  
  84.     // If it's a vector, the count goes first.
  85.     if (dwType & VT_VECTOR)
  86.     {
  87.         pbResult = (LPBYTE)malloc(sizeof(DWORD));
  88.         if (pbResult == NULL)
  89.         {
  90.             free(pbBuffer);
  91.             return NULL;
  92.         }
  93.         *(LPDWORD)pbResult = nReps;
  94.         cbResult = sizeof(DWORD);
  95.     }
  96.  
  97.     while (nReps--)
  98.     {
  99.         cchOrig = *(LPDWORD)pbProp;
  100.         pbProp += sizeof(DWORD);
  101.  
  102.         // Convert multibyte string to Unicode.
  103.         if (cbCharSize == sizeof(WCHAR))
  104.         {
  105.             cchCopy = _mbstowcsz((LPWSTR)pbBuffer, (LPSTR)pbProp,
  106.                 min(cchOrig, MAX_STRLEN));
  107.         }
  108.         else
  109.         {
  110.             cchCopy = _wcstombsz((LPSTR)pbBuffer, (LPWSTR)pbProp,
  111.                 min(cchOrig, MAX_STRLEN));
  112.         }
  113.  
  114.         // Allocate space to append string.
  115.         cbCopy = cchCopy * cbCharSize;
  116.         pbResultNew = (LPBYTE)realloc(pbResult, cbResult + sizeof(DWORD) +
  117.             cbCopy);
  118.  
  119.         // If allocation failed, cleanup and return NULL;
  120.         if (pbResultNew == NULL)
  121.         {
  122.             free(pbResult);
  123.             free(pbBuffer);
  124.             return NULL;
  125.         }
  126.  
  127.         pbResult = pbResultNew;
  128.  
  129.         // Copy character count and converted string into place,
  130.         // then update the total size.
  131.         memcpy(pbResult + cbResult, (LPBYTE)&cchCopy, sizeof(DWORD));
  132.         memcpy(pbResult + cbResult + sizeof(DWORD), pbBuffer, cbCopy);
  133.         cbResult += sizeof(DWORD) + cbCopy;
  134.  
  135.         // Advance to the next vector element
  136.         pbProp += (cchOrig * cbCharSize);
  137.     }
  138.  
  139.     free(pbBuffer);
  140.     return pbResult;
  141. }
  142.  
  143. #endif // _UNICODE
  144.  
  145.  
  146. /////////////////////////////////////////////////////////////////////////////
  147. //Implementation of the CProperty class
  148.  
  149. CProperty::CProperty( void )
  150. {
  151.     m_dwPropID = 0;
  152.  
  153.     m_dwType = VT_EMPTY;
  154.     m_pValue = NULL;       // must init to NULL
  155. }
  156.  
  157. CProperty::CProperty( DWORD dwID, const LPVOID pValue, DWORD dwType )
  158. {
  159.     m_dwPropID = dwID;
  160.     m_dwType = dwType;
  161.     m_pValue = NULL;       // must init to NULL
  162.     Set( dwID, pValue, dwType );
  163. }
  164.  
  165. CProperty::~CProperty()
  166. {
  167.     FreeValue();
  168. }
  169.  
  170. BOOL CProperty::Set( DWORD dwID, const LPVOID pValue, DWORD dwType )
  171. {
  172.     m_dwType = dwType;
  173.     m_dwPropID = dwID;
  174.  
  175.     return Set( pValue );
  176. }
  177.  
  178. BOOL CProperty::Set( const LPVOID pValue, DWORD dwType )
  179. {
  180.     m_dwType = dwType;
  181.     return Set( pValue );
  182. }
  183.  
  184. BOOL CProperty::Set( const  LPVOID pVal )
  185. {
  186.     ULONG           cb;
  187.     ULONG           cbItem;
  188.     ULONG           cbValue;
  189.     ULONG           nReps;
  190.     LPBYTE          pCur;
  191.     LPVOID          pValue = pVal;
  192.     DWORD           dwType = m_dwType;
  193.     LPVOID          pValueOrig = NULL;
  194.  
  195.     if (m_pValue != NULL)
  196.     {
  197.         FreeValue();
  198.     }
  199.  
  200.     if (pValue == NULL || m_dwType == 0)
  201.         return TRUE;
  202.  
  203.     // Given pValue, determine how big it is
  204.     // Then allocate a new buffer for m_pValue and copy...
  205.     nReps = 1;
  206.     cbValue = 0;
  207.     pCur = (LPBYTE)pValue;
  208.     if (m_dwType & VT_VECTOR)
  209.     {
  210.         // The next DWORD is a count of the elements
  211.         nReps = *(LPDWORD)pValue;
  212.         cb = sizeof( nReps );
  213.         pCur += cb;
  214.         cbValue += cb;
  215.         dwType &= ~VT_VECTOR;
  216.     }
  217.     else
  218.     {
  219.         // If we get any of the string-like types,
  220.         // and we are not a vector create a count-prefixed
  221.         // buffer.
  222.         switch (dwType )
  223.         {
  224.             case VT_LPSTR:          // null terminated string
  225.                 pValueOrig = pValue;
  226.                 pValue = CountPrefixedStringA( (LPSTR)pValueOrig );
  227.                 pCur = (LPBYTE)pValue;
  228.             break;
  229.  
  230.             case VT_BSTR:           // binary string
  231.             case VT_STREAM:         // Name of the stream follows
  232.             case VT_STORAGE:        // Name of the storage follows
  233.             case VT_STREAMED_OBJECT:// Stream contains an object
  234.             case VT_STORED_OBJECT:  // Storage contains an object
  235.                 pValueOrig = pValue;
  236.                 pValue = CountPrefixedStringT( (LPTSTR)pValueOrig );
  237.                 pCur = (LPBYTE)pValue;
  238.             break;
  239.  
  240.             case VT_LPWSTR:         // UNICODE string
  241.                 pValueOrig = pValue;
  242.                 pValue = CountPrefixedStringW( (LPWSTR)pValueOrig );
  243.                 pCur = (LPBYTE)pValue;
  244.             break;
  245.         }
  246.     }
  247.  
  248.     // Since a value can be made up of a vector (VT_VECTOR) of
  249.     // items, we first seek through the value, picking out
  250.     // each item, getting it's size.
  251.     //
  252.     cbItem = 0;        // Size of the current item
  253.     while (nReps--)
  254.     {
  255.         switch (dwType)
  256.         {
  257.             case VT_EMPTY:          // nothing
  258.                 cbItem = 0;
  259.             break;
  260.  
  261.             case VT_I2:             // 2 byte signed int
  262.             case VT_BOOL:           // True=-1, False=0
  263.                 cbItem = 2;
  264.             break;
  265.  
  266.             case VT_I4:             // 4 byte signed int
  267.             case VT_R4:             // 4 byte real
  268.                 cbItem = 4;
  269.             break;
  270.  
  271.             case VT_R8:             // 8 byte real
  272.             case VT_CY:             // currency
  273.             case VT_DATE:           // date
  274.             case VT_I8:             // signed 64-bit int
  275.             case VT_FILETIME:       // FILETIME
  276.                 cbItem = 8;
  277.             break;
  278.  
  279.             case VT_CLSID:          // A Class ID
  280.                 cbItem = sizeof(CLSID);
  281.             break;
  282.  
  283. #ifndef _UNICODE
  284.             case VT_BSTR:           // binary string
  285.             case VT_STREAM:         // Name of the stream follows
  286.             case VT_STORAGE:        // Name of the storage follows
  287.             case VT_STREAMED_OBJECT:// Stream contains an object
  288.             case VT_STORED_OBJECT:  // Storage contains an object
  289.             case VT_STREAMED_PROPSET:// Stream contains a propset
  290.             case VT_STORED_PROPSET: // Storage contains a propset
  291. #endif // _UNICODE
  292.             case VT_LPSTR:          // null terminated string
  293.             case VT_BLOB_OBJECT:    // Blob contains an object
  294.             case VT_BLOB_PROPSET:   // Blob contains a propset
  295.             case VT_BLOB:           // Length prefixed bytes
  296.             case VT_CF:             // Clipboard format
  297.                 // Get the DWORD that gives us the size, making
  298.                 // sure we increment cbValue.
  299.                 cbItem = *(LPDWORD)pCur;
  300.                 cb = sizeof(cbItem);
  301.                 pCur += cb;
  302.                 cbValue += cb;
  303.             break;
  304.  
  305. #ifdef _UNICODE
  306.             case VT_BSTR:           // binary string
  307.             case VT_STREAM:         // Name of the stream follows
  308.             case VT_STORAGE:        // Name of the storage follows
  309.             case VT_STREAMED_OBJECT:// Stream contains an object
  310.             case VT_STORED_OBJECT:  // Storage contains an object
  311.             case VT_STREAMED_PROPSET:// Stream contains a propset
  312.             case VT_STORED_PROPSET: // Storage contains a propset
  313. #endif // _UNICODE
  314.             case VT_LPWSTR:         // UNICODE string
  315.                 cbItem = *(LPDWORD)pCur * sizeof(WCHAR);
  316.                 cb = sizeof( cbItem );
  317.                 pCur += cb;
  318.                 cbValue += cb;
  319.             break;
  320.             case VT_VARIANT:        // VARIANT*
  321.             break;
  322.  
  323.             default:
  324.                 if (pValueOrig)
  325.                     free( pValue );
  326.                 return FALSE;
  327.         }
  328.  
  329.         // Add 'cb' to cbItem before seeking...
  330.         //
  331.         // Seek to the next item
  332.         pCur += cbItem;
  333.         cbValue += cbItem;
  334.     }
  335.  
  336.     if (NULL == AllocValue(cbValue))
  337.     {
  338.         TRACE0("CProperty::AllocValue failed");
  339.         return FALSE;
  340.     }
  341.     memcpy( m_pValue, pValue, (int)cbValue );
  342.  
  343.     if (pValueOrig)
  344.         free( pValue );
  345.  
  346.     return TRUE;
  347. }
  348.  
  349. LPVOID CProperty::Get( void )
  350. {   return Get( (DWORD*)NULL );   }
  351.  
  352. LPVOID CProperty::Get( DWORD* pcb )
  353. {
  354.     DWORD   cb;
  355.     LPBYTE  p = NULL;
  356.  
  357.     p = (LPBYTE)m_pValue;
  358.  
  359.     // m_pValue points to a Property "Value" which may
  360.     // have size information included...
  361.     switch( m_dwType )
  362.     {
  363.         case VT_EMPTY:          // nothing
  364.             cb = 0;
  365.         break;
  366.  
  367.         case VT_I2:             // 2 byte signed int
  368.         case VT_BOOL:           // True=-1, False=0
  369.             cb = 2;
  370.         break;
  371.  
  372.         case VT_I4:             // 4 byte signed int
  373.         case VT_R4:             // 4 byte real
  374.             cb = 4;
  375.         break;
  376.  
  377.         case VT_R8:             // 8 byte real
  378.         case VT_CY:             // currency
  379.         case VT_DATE:           // date
  380.         case VT_I8:             // signed 64-bit int
  381.         case VT_FILETIME:       // FILETIME
  382.             cb = 8;
  383.         break;
  384.  
  385. #ifndef _UNICODE
  386.         case VT_BSTR:           // binary string
  387.         case VT_STREAM:         // Name of the stream follows
  388.         case VT_STORAGE:        // Name of the storage follows
  389.         case VT_STREAMED_OBJECT:// Stream contains an object
  390.         case VT_STORED_OBJECT:  // Storage contains an object
  391.         case VT_STREAMED_PROPSET:// Stream contains a propset
  392.         case VT_STORED_PROPSET: // Storage contains a propset
  393. #endif // UNICODE
  394.         case VT_LPSTR:          // null terminated string
  395.         case VT_CF:             // Clipboard format
  396.             // Read the DWORD that gives us the size, making
  397.             // sure we increment cbValue.
  398.             cb = *(LPDWORD)p;
  399.             p += sizeof( DWORD );
  400.         break;
  401.  
  402.         case VT_BLOB:           // Length prefixed bytes
  403.         case VT_BLOB_OBJECT:    // Blob contains an object
  404.         case VT_BLOB_PROPSET:   // Blob contains a propset
  405.             // Read the DWORD that gives us the size.
  406.             cb = *(LPDWORD)p;
  407.         break;
  408.  
  409. #ifdef _UNICODE
  410.         case VT_BSTR:           // binary string
  411.         case VT_STREAM:         // Name of the stream follows
  412.         case VT_STORAGE:        // Name of the storage follows
  413.         case VT_STREAMED_OBJECT:// Stream contains an object
  414.         case VT_STORED_OBJECT:  // Storage contains an object
  415.         case VT_STREAMED_PROPSET:// Stream contains a propset
  416.         case VT_STORED_PROPSET: // Storage contains a propset
  417. #endif // _UNICODE
  418.         case VT_LPWSTR:         // UNICODE string
  419.             cb = *(LPDWORD)p * sizeof(WCHAR);
  420.             p += sizeof( DWORD );
  421.         break;
  422.  
  423.         case VT_CLSID:          // A Class ID
  424.             cb = sizeof(CLSID);
  425.         break;
  426.  
  427.         case VT_VARIANT:        // VARIANT*
  428.         break;
  429.  
  430.         default:
  431.             return NULL;
  432.     }
  433.     if (pcb != NULL)
  434.         *pcb = cb;
  435.  
  436.     return p;
  437. }
  438.  
  439. DWORD  CProperty::GetType( void )
  440. {   return m_dwType;  }
  441.  
  442. void   CProperty::SetType( DWORD dwType )
  443. {   m_dwType = dwType; }
  444.  
  445. DWORD CProperty::GetID( void )
  446. {   return m_dwPropID;   }
  447.  
  448. void CProperty::SetID( DWORD dwPropID )
  449. {    m_dwPropID = dwPropID;   }
  450.  
  451. LPVOID CProperty::GetRawValue( void )
  452. {   return m_pValue; }
  453.  
  454. BOOL CProperty::WriteToStream( IStream* pIStream )
  455. {
  456.     ULONG           cb;
  457.     ULONG           cbTotal; // Total size of the whole value
  458.     DWORD           dwType = m_dwType;
  459.     DWORD           nReps;
  460.     LPBYTE          pValue;
  461.     LPBYTE          pCur;
  462.     BOOL            bSuccess = FALSE;
  463.     BYTE            b = 0;
  464.  
  465.     nReps = 1;
  466.     pValue = (LPBYTE)m_pValue;
  467.     pCur = pValue;
  468.     cbTotal = 0;
  469.     if (m_dwType & VT_VECTOR)
  470.     {
  471.         // Value is a DWORD count of elements followed by
  472.         // that many repititions of the value.
  473.         //
  474.         nReps = *(LPDWORD)pCur;
  475.         cbTotal = sizeof(DWORD);
  476.         pCur += cbTotal;
  477.         dwType &= ~VT_VECTOR;
  478.     }
  479.  
  480. #ifdef _UNICODE
  481.     switch (dwType)
  482.     {
  483.         case VT_BSTR:           // binary string
  484.         case VT_STREAM:         // Name of the stream follows
  485.         case VT_STORAGE:        // Name of the storage follows
  486.         case VT_STREAMED_OBJECT:// Stream contains an object
  487.         case VT_STORED_OBJECT:  // Storage contains an object
  488.         case VT_STREAMED_PROPSET:// Stream contains a propset
  489.         case VT_STORED_PROPSET: // Storage contains a propset
  490.             pValue = ConvertStringProp(pCur, m_dwType, nReps, sizeof(char));
  491.             if (m_dwType & VT_VECTOR)
  492.                 pCur = pValue + sizeof(DWORD);
  493.         break;
  494.     }
  495. #endif // _UNICODE
  496.  
  497.     // Figure out how big the data is.
  498.     while (nReps--)
  499.     {
  500.         switch (dwType)
  501.         {
  502.             case VT_EMPTY:          // nothing
  503.                 cb = 0;
  504.             break;
  505.  
  506.             case VT_I2:             // 2 byte signed int
  507.             case VT_BOOL:           // True=-1, False=0
  508.                 cb = 2;
  509.             break;
  510.  
  511.             case VT_I4:             // 4 byte signed int
  512.             case VT_R4:             // 4 byte real
  513.                 cb = 4;
  514.             break;
  515.  
  516.             case VT_R8:             // 8 byte real
  517.             case VT_CY:             // currency
  518.             case VT_DATE:           // date
  519.             case VT_I8:             // signed 64-bit int
  520.             case VT_FILETIME:       // FILETIME
  521.                 cb = 8;
  522.             break;
  523.  
  524.             case VT_LPSTR:          // null terminated string
  525.             case VT_BSTR:           // binary string
  526.             case VT_STREAM:         // Name of the stream follows
  527.             case VT_STORAGE:        // Name of the storage follows
  528.             case VT_STREAMED_OBJECT:// Stream contains an object
  529.             case VT_STORED_OBJECT:  // Storage contains an object
  530.             case VT_STREAMED_PROPSET:// Stream contains a propset
  531.             case VT_STORED_PROPSET: // Storage contains a propset
  532.             case VT_BLOB:           // Length prefixed bytes
  533.             case VT_BLOB_OBJECT:    // Blob contains an object
  534.             case VT_BLOB_PROPSET:   // Blob contains a propset
  535.             case VT_CF:             // Clipboard format
  536.                 cb = sizeof(DWORD) + *(LPDWORD)pCur;
  537.             break;
  538.  
  539.             case VT_LPWSTR:         // UNICODE string
  540.                 cb = sizeof(DWORD) + (*(LPDWORD)pCur * sizeof(WCHAR));
  541.             break;
  542.  
  543.             case VT_CLSID:          // A Class ID
  544.                 cb = sizeof(CLSID);
  545.             break;
  546.  
  547.             case VT_VARIANT:        // VARIANT*
  548.             break;
  549.  
  550.             default:
  551.                 return FALSE;
  552.         }
  553.  
  554.         pCur += cb;
  555.         cbTotal+= cb;
  556.     }
  557.  
  558.     // Write the type
  559.     pIStream->Write((LPVOID)&m_dwType, sizeof(m_dwType), &cb);
  560.     if (cb != sizeof(m_dwType))
  561.         goto Cleanup;
  562.  
  563.     // Write the value
  564.     pIStream->Write((LPVOID)pValue, cbTotal, &cb);
  565.     if (cb != cbTotal)
  566.         goto Cleanup;
  567.  
  568.     // Make sure we are 32 bit aligned
  569.     cbTotal = (((cbTotal + 3) >> 2) << 2) - cbTotal;
  570.     while (cbTotal--)
  571.     {
  572.         pIStream->Write((LPVOID)&b, 1, &cb);
  573.         if (cb != sizeof(BYTE))
  574.             goto Cleanup;
  575.     }
  576.  
  577.     bSuccess = TRUE;
  578.  
  579. Cleanup:
  580.     if (pValue != m_pValue)
  581.         free(pValue);
  582.  
  583.     return bSuccess;
  584. }
  585.  
  586. BOOL CProperty::ReadFromStream( IStream* pIStream )
  587. {
  588.     ULONG           cb;
  589.     ULONG           cbItem;
  590.     ULONG           cbValue;
  591.     DWORD           dwType;
  592.     ULONG           nReps;
  593.     ULONG           iReps;
  594.     LPSTREAM        pIStrItem;
  595.     LARGE_INTEGER   li;
  596.  
  597.     // All properties are made up of a type/value pair.
  598.     // The obvious first thing to do is to get the type...
  599.     pIStream->Read( (LPVOID)&m_dwType, sizeof(m_dwType), &cb );
  600.     if (cb != sizeof(m_dwType))
  601.         return FALSE;
  602.  
  603.     dwType = m_dwType;
  604.     nReps = 1;
  605.     cbValue = 0;
  606.     if (m_dwType & VT_VECTOR)
  607.     {
  608.         // The next DWORD in the stream is a count of the
  609.         // elements
  610.         pIStream->Read( (LPVOID)&nReps, sizeof(nReps), &cb );
  611.         if (cb != sizeof(nReps))
  612.             return FALSE;
  613.         cbValue += cb;
  614.         dwType &= ~VT_VECTOR;
  615.     }
  616.  
  617.     // Since a value can be made up of a vector (VT_VECTOR) of
  618.     // items, we first seek through the value, picking out
  619.     // each item, getting it's size.  We use a cloned
  620.     // stream for this (pIStrItem).
  621.     // We then use our pIStream to read the entire 'blob' into
  622.     // the allocated buffer.
  623.     //
  624.     cbItem = 0;        // Size of the current item
  625.     pIStream->Clone( &pIStrItem );
  626.     ASSERT(pIStrItem != NULL);
  627.     iReps = nReps;
  628.     while (iReps--)
  629.     {
  630.         switch (dwType)
  631.         {
  632.             case VT_EMPTY:          // nothing
  633.                 cbItem = 0;
  634.             break;
  635.  
  636.             case VT_I2:             // 2 byte signed int
  637.             case VT_BOOL:           // True=-1, False=0
  638.                 cbItem = 2;
  639.             break;
  640.  
  641.             case VT_I4:             // 4 byte signed int
  642.             case VT_R4:             // 4 byte real
  643.                 cbItem = 4;
  644.             break;
  645.  
  646.             case VT_R8:             // 8 byte real
  647.             case VT_CY:             // currency
  648.             case VT_DATE:           // date
  649.             case VT_I8:             // signed 64-bit int
  650.             case VT_FILETIME:       // FILETIME
  651.                 cbItem = 8;
  652.             break;
  653.  
  654.             case VT_LPSTR:          // null terminated string
  655.             case VT_BSTR:           // binary string
  656.             case VT_STREAM:         // Name of the stream follows
  657.             case VT_STORAGE:        // Name of the storage follows
  658.             case VT_STREAMED_OBJECT:// Stream contains an object
  659.             case VT_STORED_OBJECT:  // Storage contains an object
  660.             case VT_STREAMED_PROPSET:// Stream contains a propset
  661.             case VT_STORED_PROPSET: // Storage contains a propset
  662.             case VT_BLOB:           // Length prefixed bytes
  663.             case VT_BLOB_OBJECT:    // Blob contains an object
  664.             case VT_BLOB_PROPSET:   // Blob contains a propset
  665.             case VT_CF:             // Clipboard format
  666.                 // Read the DWORD that gives us the size, making
  667.                 // sure we increment cbValue.
  668.                 pIStream->Read( (LPVOID)&cbItem, sizeof(cbItem), &cb );
  669.                 if (cb != sizeof(cbItem))
  670.                     return FALSE;
  671.                 LISet32( li, -(LONG)cb );
  672.                 pIStream->Seek( li, STREAM_SEEK_CUR, NULL );
  673.                 cbValue += cb;
  674.             break;
  675.  
  676.             case VT_LPWSTR:         // UNICODE string
  677.                 pIStream->Read( (LPVOID)&cbItem, sizeof(cbItem), &cb );
  678.                 if (cb != sizeof(cbItem))
  679.                     return FALSE;
  680.                 LISet32( li, -(LONG)cb );
  681.                 pIStream->Seek( li, STREAM_SEEK_CUR, NULL );
  682.                 cbValue += cb;
  683.                 cbItem *= sizeof(WCHAR);
  684.             break;
  685.  
  686.             case VT_CLSID:          // A Class ID
  687.                 cbItem = sizeof(CLSID);
  688.             break;
  689.  
  690.             case VT_VARIANT:        // VARIANT*
  691.             break;
  692.  
  693.             default:
  694.                 pIStrItem->Release();
  695.                 return FALSE;
  696.         }
  697.  
  698.         // Add 'cb' to cbItem before seeking...
  699.         //
  700.         // Seek to the next item
  701.         LISet32( li, cbItem );
  702.         pIStrItem->Seek( li, STREAM_SEEK_CUR, NULL);
  703.         cbValue += cbItem;
  704.     }
  705.  
  706.     pIStrItem->Release();
  707.  
  708. #ifdef _UNICODE
  709.     LPBYTE pTmp;
  710.  
  711.     switch (dwType)
  712.     {
  713.         case VT_BSTR:           // binary string
  714.         case VT_STREAM:         // Name of the stream follows
  715.         case VT_STORAGE:        // Name of the storage follows
  716.         case VT_STREAMED_OBJECT:// Stream contains an object
  717.         case VT_STORED_OBJECT:  // Storage contains an object
  718.         case VT_STREAMED_PROPSET:// Stream contains a propset
  719.         case VT_STORED_PROPSET: // Storage contains a propset
  720.             pTmp = (LPBYTE)malloc((int)cbValue);
  721.             pIStream->Read( pTmp, cbValue, &cb );
  722.             m_pValue = ConvertStringProp(pTmp, m_dwType, nReps, sizeof(WCHAR));
  723.             free(pTmp);
  724.         break;
  725.  
  726.         default:
  727. #endif // _UNICODE
  728.             // Allocate cbValue bytes
  729.             if (NULL == AllocValue(cbValue))
  730.                 return FALSE;
  731.  
  732.             // Read the buffer from pIStream
  733.             pIStream->Read( m_pValue, cbValue, &cb );
  734.             if (cb != cbValue)
  735.                 return FALSE;
  736. #ifdef _UNICODE
  737.         break;
  738.     }
  739. #endif // _UNICODE
  740.  
  741.     // Done!
  742.     return TRUE;
  743. }
  744.  
  745.  
  746. LPVOID CProperty::AllocValue(ULONG cb)
  747. {
  748.     return m_pValue = malloc((int)cb);
  749. }
  750.  
  751.  
  752. void CProperty::FreeValue()
  753. {
  754.     if (m_pValue != NULL)
  755.     {
  756.         free(m_pValue);
  757.         m_pValue = NULL;
  758.     }
  759. }
  760.  
  761. /////////////////////////////////////////////////////////////////////////////
  762. // Implementation of the CPropertySection Class
  763.  
  764. CPropertySection::CPropertySection( void )
  765. {
  766.     m_FormatID = GUID_NULL;
  767.     m_SH.cbSection = 0;
  768.     m_SH.cProperties = 0;
  769. }
  770.  
  771. CPropertySection::CPropertySection( CLSID FormatID )
  772. {
  773.     m_FormatID = FormatID;
  774.     m_SH.cbSection = 0;
  775.     m_SH.cProperties = 0;
  776. }
  777.  
  778. CPropertySection::~CPropertySection( void )
  779. {
  780.     RemoveAll();
  781.     return;
  782. }
  783.  
  784. CLSID CPropertySection::GetFormatID( void )
  785. {   return m_FormatID; }
  786.  
  787. void CPropertySection::SetFormatID( CLSID FormatID )
  788. {   m_FormatID = FormatID; }
  789.  
  790. BOOL CPropertySection::Set( DWORD dwPropID, LPVOID pValue, DWORD dwType )
  791. {
  792.     CProperty* pProp = GetProperty( dwPropID );
  793.     if (pProp == NULL)
  794.     {
  795.         if ((pProp = new CProperty( dwPropID, pValue, dwType )) != NULL)
  796.             AddProperty( pProp );
  797.         return (pProp != NULL);
  798.     }
  799.  
  800.     pProp->Set( dwPropID, pValue, dwType );
  801.     return TRUE;
  802. }
  803.  
  804. BOOL CPropertySection::Set( DWORD dwPropID, LPVOID pValue )
  805. {
  806.     // Since no dwType was specified, the property is assumed
  807.     // to exist.   Fail if it does not.
  808.     CProperty* pProp = GetProperty( dwPropID );
  809.     if (pProp != NULL && pProp->m_dwType)
  810.     {
  811.         pProp->Set( dwPropID, pValue, pProp->m_dwType );
  812.         return TRUE;
  813.     }
  814.     else
  815.         return FALSE;
  816. }
  817.  
  818. LPVOID CPropertySection::Get( DWORD dwPropID )
  819. {   return Get( dwPropID, (DWORD*)NULL );  }
  820.  
  821. LPVOID CPropertySection::Get( DWORD dwPropID, DWORD* pcb )
  822. {
  823.     CProperty* pProp = GetProperty( dwPropID );
  824.     if (pProp)
  825.         {
  826.                 pProp->AssertValid();
  827.                 return pProp->Get( pcb );
  828.         }
  829.     else
  830.         return NULL;
  831. }
  832.  
  833. void CPropertySection::Remove( DWORD dwID )
  834. {
  835.     POSITION pos = m_PropList.GetHeadPosition();
  836.     CProperty*  pProp;
  837.     while( pos != NULL )
  838.     {
  839.         POSITION posRemove = pos;
  840.         pProp = (CProperty*)m_PropList.GetNext( pos );
  841.         if (pProp->m_dwPropID == dwID)
  842.         {
  843.             m_PropList.RemoveAt( posRemove );
  844.             delete pProp;
  845.             m_SH.cProperties--;
  846.             return;
  847.         }
  848.     }
  849. }
  850.  
  851. void CPropertySection::RemoveAll( )
  852. {
  853.     POSITION pos = m_PropList.GetHeadPosition();
  854.     while( pos != NULL )
  855.         delete (CProperty*)m_PropList.GetNext( pos );
  856.     m_PropList.RemoveAll();
  857.     m_SH.cProperties = 0;
  858. }
  859.  
  860.  
  861. CProperty* CPropertySection::GetProperty( DWORD dwPropID )
  862. {
  863.     POSITION pos = m_PropList.GetHeadPosition();
  864.     m_PropList.AssertValid();
  865.     CProperty* pProp;
  866.     while (pos != NULL)
  867.     {
  868.         pProp= (CProperty*)m_PropList.GetNext( pos );
  869.         pProp->AssertValid();
  870.         if (pProp->m_dwPropID == dwPropID)
  871.             return pProp;
  872.     }
  873.     return NULL;
  874. }
  875.  
  876. void CPropertySection::AddProperty( CProperty* pProp )
  877. {
  878.     m_PropList.AddTail( pProp );
  879.     m_SH.cProperties++;
  880. }
  881.  
  882. DWORD CPropertySection::GetSize( void )
  883. {   return m_SH.cbSection; }
  884.  
  885. DWORD CPropertySection::GetCount( void )
  886. {   return m_PropList.GetCount();  }
  887.  
  888. CObList* CPropertySection::GetList( void )
  889. {   return &m_PropList;  }
  890.  
  891. BOOL CPropertySection::WriteToStream( IStream* pIStream )
  892. {
  893.     // Create a dummy property entry for the name dictionary (ID == 0).
  894.     Set(0, NULL, VT_EMPTY);
  895.  
  896.     ULONG           cb;
  897.     ULARGE_INTEGER  ulSeekOld;
  898.     ULARGE_INTEGER  ulSeek;
  899.     LPSTREAM        pIStrPIDO;
  900.     PROPERTYIDOFFSET  pido;
  901.     LARGE_INTEGER   li;
  902.  
  903.     // The Section header contains the number of bytes in the
  904.     // section.  Thus we need  to go back to where we should
  905.     // write the count of bytes
  906.     // after we write all the property sets..
  907.     // We accomplish this by saving the seek pointer to where
  908.     // the size should be written in ulSeekOld
  909.     m_SH.cbSection = 0;
  910.     m_SH.cProperties = m_PropList.GetCount();
  911.     LISet32( li, 0 );
  912.     pIStream->Seek( li, STREAM_SEEK_CUR, &ulSeekOld);
  913.  
  914.     pIStream->Write((LPVOID)&m_SH, sizeof(m_SH), &cb);
  915.     if (sizeof(m_SH) != cb)
  916.     {
  917.         TRACE0("Write of section header failed (1).\n");
  918.         return FALSE;
  919.     }
  920.  
  921.     if (m_PropList.IsEmpty())
  922.     {
  923.         TRACE0("Warning: Wrote empty property section.\n");
  924.         return TRUE;
  925.     }
  926.  
  927.     // After the section header is the list of property ID/Offset pairs
  928.     // Since there is an ID/Offset pair for each property and we
  929.     // need to write the ID/Offset pair as we write each property
  930.     // we clone the stream and use the clone to access the
  931.     // table of ID/offset pairs (PIDO)...
  932.     //
  933.     pIStream->Clone( &pIStrPIDO );
  934.  
  935.     // Now seek pIStream past the PIDO list
  936.     //
  937.     LISet32( li,  m_SH.cProperties * sizeof( PROPERTYIDOFFSET ) );
  938.     pIStream->Seek( li, STREAM_SEEK_CUR, &ulSeek);
  939.  
  940.     // Now write each section to pIStream.
  941.     CProperty* pProp = NULL;
  942.     POSITION pos = m_PropList.GetHeadPosition();
  943.     while( pos != NULL )
  944.     {
  945.         // Get next element (note cast)
  946.         pProp = (CProperty*)m_PropList.GetNext( pos );
  947.  
  948.         if (pProp->m_dwPropID != 0)
  949.         {
  950.             // Write it
  951.             if (!pProp->WriteToStream( pIStream ))
  952.             {
  953.                 pIStrPIDO->Release();
  954.                 return FALSE;
  955.             }
  956.         }
  957.         else
  958.         {
  959.             if (!WriteNameDictToStream( pIStream ))
  960.             {
  961.                 pIStrPIDO->Release();
  962.                 return FALSE;
  963.             }
  964.         }
  965.  
  966.         // Using our cloned stream write the Format ID / Offset pair
  967.         // The offset to this property is the current seek pointer
  968.         // minus the pointer to the beginning of the section
  969.         pido.dwOffset = ulSeek.LowPart - ulSeekOld.LowPart;
  970.         pido.propertyID = pProp->m_dwPropID;
  971.         pIStrPIDO->Write((LPVOID)&pido, sizeof(pido), &cb);
  972.         if (sizeof(pido) != cb)
  973.         {
  974.             TRACE0("Write of 'pido' failed\n");
  975.             pIStrPIDO->Release();
  976.             return FALSE;
  977.         }
  978.  
  979.         // Get the seek offset after the write
  980.         LISet32( li, 0 );
  981.         pIStream->Seek( li, STREAM_SEEK_CUR, &ulSeek );
  982.     }
  983.  
  984.     pIStrPIDO->Release();
  985.  
  986.     // Now go back to ulSeekOld and write the section header.
  987.     // Size of section is current seek point minus old seek point
  988.     //
  989.     m_SH.cbSection = ulSeek.LowPart - ulSeekOld.LowPart;
  990.  
  991.     // Seek to beginning of this section and write the section header.
  992.     LISet32( li, ulSeekOld.LowPart );
  993.     pIStream->Seek( li, STREAM_SEEK_SET, NULL );
  994.     pIStream->Write((LPVOID)&m_SH, sizeof(m_SH), &cb);
  995.     if (sizeof(m_SH) != cb)
  996.     {
  997.         TRACE0("Write of section header failed (2).\n");
  998.         return FALSE;
  999.     }
  1000.  
  1001.     // Now seek to end of of the now written section
  1002.     LISet32(li, ulSeek.LowPart);
  1003.     pIStream->Seek(li, STREAM_SEEK_SET, NULL);
  1004.  
  1005.     return TRUE;
  1006. }
  1007.  
  1008. BOOL CPropertySection::ReadFromStream( IStream* pIStream,
  1009.     LARGE_INTEGER liPropSet )
  1010. {
  1011.     ULONG               cb;
  1012.     PROPERTYIDOFFSET    pido;
  1013.     ULONG               cProperties;
  1014.     LPSTREAM            pIStrPIDO;
  1015.     ULARGE_INTEGER      ulSectionStart;
  1016.     LARGE_INTEGER       li;
  1017.     CProperty*          pProp;
  1018.  
  1019.     if (m_SH.cProperties || !m_PropList.IsEmpty())
  1020.         RemoveAll();
  1021.  
  1022.     // pIStream is pointing to the beginning of the section we
  1023.     // are to read.  First there is a DWORD that is the count
  1024.     // of bytes in this section, then there is a count
  1025.     // of properties, followed by a list of propertyID/offset pairs,
  1026.     // followed by type/value pairs.
  1027.     //
  1028.     LISet32( li, 0 );
  1029.     pIStream->Seek( li, STREAM_SEEK_CUR, &ulSectionStart );
  1030.     pIStream->Read( (LPVOID)&m_SH, sizeof(m_SH), &cb );
  1031.     if (cb != sizeof(m_SH))
  1032.         return FALSE;
  1033.  
  1034.     // Now we're pointing at the first of the PropID/Offset pairs
  1035.     // (PIDOs).   To get to each property we use a cloned stream
  1036.     // to stay back and point at the PIDOs (pIStrPIDO).  We seek
  1037.     // pIStream to each of the Type/Value pairs, creating CProperites
  1038.     // and so forth as we go...
  1039.     //
  1040.     pIStream->Clone( &pIStrPIDO );
  1041.  
  1042.     cProperties = m_SH.cProperties;
  1043.     while (cProperties--)
  1044.     {
  1045.         pIStrPIDO->Read( (LPVOID)&pido, sizeof( pido ), &cb );
  1046.         if (cb != sizeof(pido))
  1047.         {
  1048.             pIStrPIDO->Release();
  1049.             return FALSE;
  1050.         }
  1051.  
  1052.         // Do a seek from the beginning of the property set.
  1053.         LISet32( li, ulSectionStart.LowPart + pido.dwOffset );
  1054.         pIStream->Seek( liPropSet, STREAM_SEEK_SET, NULL );
  1055.         pIStream->Seek( li, STREAM_SEEK_CUR, NULL );
  1056.  
  1057.         // Now pIStream is at the type/value pair
  1058.         if (pido.propertyID != 0)
  1059.         {
  1060.             pProp = new CProperty( pido.propertyID, NULL, 0 );
  1061.             pProp->ReadFromStream( pIStream );
  1062.             pProp->AssertValid();
  1063.             m_PropList.AddTail( pProp );
  1064.             m_PropList.AssertValid();
  1065.         }
  1066.         else
  1067.         {
  1068.             ReadNameDictFromStream( pIStream );
  1069.         }
  1070.     }
  1071.  
  1072.     pIStrPIDO->Release();
  1073.  
  1074.     return TRUE;
  1075. }
  1076.  
  1077. BOOL CPropertySection::GetID( LPCTSTR pszName, DWORD* pdwPropID )
  1078. {
  1079.     CString strName(pszName);
  1080.     strName.MakeLower();        // Dictionary stores all names in lowercase
  1081.  
  1082.     void* pvID;
  1083.     if (m_NameDict.Lookup(strName, pvID))
  1084.     {
  1085.         *pdwPropID = (DWORD)pvID;
  1086.         return TRUE;
  1087.     }
  1088.  
  1089.     // Failed to find entry in dictionary
  1090.     return FALSE;
  1091. }
  1092.  
  1093. BOOL CPropertySection::SetName( DWORD dwPropID, LPCTSTR pszName )
  1094. {
  1095.     BOOL bSuccess = TRUE;
  1096.     CString strName(pszName);
  1097.     strName.MakeLower();        // Dictionary stores all names in lowercase
  1098.  
  1099.     TRY
  1100.     {
  1101.         void* pDummy;
  1102.         BOOL bNameExists = m_NameDict.Lookup(strName, pDummy);
  1103.  
  1104.         ASSERT(! bNameExists);  // Property names must be unique.
  1105.  
  1106.         if (bNameExists)
  1107.             bSuccess = FALSE;
  1108.         else
  1109.             m_NameDict.SetAt(strName, (void*)dwPropID);
  1110.     }
  1111.     CATCH (CException, e)
  1112.     {
  1113.         TRACE0("Failed to add entry to dictionary.\n");
  1114.         bSuccess = FALSE;
  1115.     }
  1116.     END_CATCH
  1117.  
  1118.     return bSuccess;
  1119. }
  1120.  
  1121. struct DICTENTRYHEADER
  1122. {
  1123.     DWORD dwPropID;
  1124.     DWORD cb;
  1125. };
  1126.  
  1127. struct DICTENTRY
  1128. {
  1129.     DICTENTRYHEADER hdr;
  1130.     char sz[256];
  1131. };
  1132.  
  1133. BOOL CPropertySection::ReadNameDictFromStream( IStream* pIStream )
  1134. {
  1135.     ULONG cb;
  1136.     ULONG cbRead = 0;
  1137.  
  1138.     // Read dictionary header (count).
  1139.     ULONG cProperties = 0;
  1140.     pIStream->Read((LPVOID)&cProperties, sizeof(cProperties), &cb);
  1141.     if (sizeof(cProperties) != cb)
  1142.     {
  1143.         TRACE0("Read of dictionary header failed.\n");
  1144.         return FALSE;
  1145.     }
  1146.  
  1147.     ULONG iProp;
  1148.     DICTENTRY entry;
  1149.  
  1150.     for (iProp = 0; iProp < cProperties; iProp++)
  1151.     {
  1152.         // Read entry header (dwPropID, cch).
  1153.         if (FAILED(pIStream->Read((LPVOID)&entry, sizeof(DICTENTRYHEADER),
  1154.             &cbRead)) ||
  1155.             (sizeof(DICTENTRYHEADER) != cbRead))
  1156.         {
  1157.             TRACE0("Read of dictionary entry failed.\n");
  1158.             return FALSE;
  1159.         }
  1160.  
  1161.         // Read entry data (name).
  1162.  
  1163.         cb = entry.hdr.cb;
  1164.  
  1165.         if (FAILED(pIStream->Read((LPVOID)&entry.sz, cb, &cbRead)) ||
  1166.             (cbRead != cb))
  1167.         {
  1168.             TRACE0("Read of dictionary entry failed.\n");
  1169.             return FALSE;
  1170.         }
  1171.  
  1172.         LPTSTR pszName;
  1173.  
  1174. #ifdef _UNICODE
  1175.         // Persistent form is always ANSI/DBCS.  Convert to Unicode.
  1176.         WCHAR wszName[256];
  1177.         _mbstowcsz(wszName, entry.sz, 256);
  1178.         pszName = wszName;
  1179. #else // _UNICODE
  1180.         pszName = entry.sz;
  1181. #endif // _UNICODE
  1182.  
  1183.         // Section's "name" appears first in list and has dwPropID == 0.
  1184.         if ((iProp == 0) && (entry.hdr.dwPropID == 0))
  1185.             m_strSectionName = pszName;             // Section name
  1186.         else
  1187.             SetName(entry.hdr.dwPropID, pszName);   // Some other property
  1188.     }
  1189.  
  1190.     return TRUE;
  1191. }
  1192.  
  1193. static BOOL WriteNameDictEntry(IStream* pIStream, DWORD dwPropID, CString& strName)
  1194. {
  1195.     ULONG cb;
  1196.     ULONG cbWritten = 0;
  1197.     DICTENTRY entry;
  1198.  
  1199.     entry.hdr.dwPropID = dwPropID;
  1200.     entry.hdr.cb = min(strName.GetLength() + 1, 255);
  1201. #ifdef _UNICODE
  1202.     // Persistent form is always ANSI/DBCS.  Convert from Unicode.
  1203.     _wcstombsz(entry.sz, (LPCWSTR)strName, 256);
  1204. #else // _UNICODE
  1205.     memcpy(entry.sz, (LPCSTR)strName, (size_t)entry.hdr.cb);
  1206. #endif // _UNICODE
  1207.  
  1208.     cb = sizeof(DICTENTRYHEADER) + entry.hdr.cb;
  1209.  
  1210.     if (FAILED(pIStream->Write((LPVOID)&entry, cb, &cbWritten)) ||
  1211.         (cbWritten != cb))
  1212.     {
  1213.         TRACE0("Write of dictionary entry failed.\n");
  1214.         return FALSE;
  1215.     }
  1216.  
  1217.     return TRUE;
  1218. }
  1219.  
  1220. BOOL CPropertySection::WriteNameDictToStream( IStream* pIStream )
  1221. {
  1222.     ULONG cb;
  1223.  
  1224.     // Write dictionary header (count).
  1225.     ULONG cProperties = m_NameDict.GetCount() + 1;
  1226.     pIStream->Write((LPVOID)&cProperties, sizeof(cProperties), &cb);
  1227.     if (sizeof(cProperties) != cb)
  1228.     {
  1229.         TRACE0("Write of dictionary header failed.\n");
  1230.         return FALSE;
  1231.     }
  1232.  
  1233.     POSITION pos;
  1234.     CString strName;
  1235.     void* pvID;
  1236.  
  1237.     // Write out section's "name" with dwPropID == 0 first
  1238.     if (! WriteNameDictEntry(pIStream, 0, m_strSectionName))
  1239.         return FALSE;
  1240.  
  1241.     // Enumerate contents of dictionary and write out (dwPropID, cb, name).
  1242.     pos = m_NameDict.GetStartPosition();
  1243.     while (pos != NULL)
  1244.     {
  1245.         m_NameDict.GetNextAssoc( pos, strName, pvID );
  1246.         if (! WriteNameDictEntry(pIStream, (DWORD)pvID, strName))
  1247.             return FALSE;
  1248.     }
  1249.  
  1250.     return TRUE;
  1251. }
  1252.  
  1253. BOOL CPropertySection::SetSectionName( LPCTSTR pszName )
  1254. {
  1255.     m_strSectionName = pszName;
  1256.     return TRUE;
  1257. }
  1258.  
  1259. LPCTSTR CPropertySection::GetSectionName( void )
  1260. {
  1261.     return (LPCTSTR)m_strSectionName;
  1262. }
  1263.  
  1264.  
  1265. /////////////////////////////////////////////////////////////////////////////
  1266. // Implementation of the CPropertySet class
  1267.  
  1268. CPropertySet::CPropertySet( void )
  1269. {
  1270.     m_PH.wByteOrder = 0xFFFE;
  1271.     m_PH.wFormat = 0;
  1272.     m_PH.dwOSVer = (DWORD)MAKELONG( LOWORD(GetVersion()), 2 );
  1273.     m_PH.clsID =  GUID_NULL;
  1274.     m_PH.cSections = 0;
  1275.  
  1276. }
  1277.  
  1278. CPropertySet::CPropertySet( CLSID clsID )
  1279. {
  1280.     m_PH.wByteOrder = 0xFFFE;
  1281.     m_PH.wFormat = 0;
  1282.     m_PH.dwOSVer = (DWORD)MAKELONG( LOWORD(GetVersion()), 2 );
  1283.     m_PH.clsID = clsID;
  1284.     m_PH.cSections = 0;
  1285. }
  1286.  
  1287. CPropertySet::~CPropertySet()
  1288. {   RemoveAll();  }
  1289.  
  1290. BOOL CPropertySet::Set( CLSID FormatID, DWORD dwPropID, LPVOID pValue, DWORD dwType )
  1291. {
  1292.     CPropertySection* pSect = GetSection( FormatID );
  1293.     if (pSect == NULL)
  1294.     {
  1295.         if ((pSect = new CPropertySection( FormatID )) != NULL)
  1296.             AddSection( pSect );
  1297.     }
  1298.     pSect->Set( dwPropID, pValue, dwType );
  1299.     return TRUE;
  1300. }
  1301.  
  1302. BOOL CPropertySet::Set( CLSID FormatID, DWORD dwPropID, LPVOID pValue )
  1303. {
  1304.     // Since there is no dwType, we have to assume that the property
  1305.     // already exists.  If it doesn't, fail.
  1306.     CPropertySection* pSect = GetSection( FormatID );
  1307.     if (pSect != NULL)
  1308.         return pSect->Set( dwPropID, pValue );
  1309.     else
  1310.         return FALSE;
  1311. }
  1312.  
  1313. LPVOID CPropertySet::Get( CLSID FormatID, DWORD dwPropID, DWORD* pcb )
  1314. {
  1315.     CPropertySection* pSect = GetSection( FormatID );
  1316.     if (pSect)
  1317.         return pSect->Get( dwPropID, pcb );
  1318.     else
  1319.         return NULL;
  1320. }
  1321.  
  1322. LPVOID CPropertySet::Get( CLSID FormatID, DWORD dwPropID )
  1323. {   return Get( FormatID, dwPropID, (DWORD*)NULL ); }
  1324.  
  1325. void CPropertySet::Remove( CLSID FormatID, DWORD dwPropID )
  1326. {
  1327.     CPropertySection*  pSect = GetSection( FormatID );
  1328.     if (pSect)
  1329.         pSect->Remove( dwPropID );
  1330. }
  1331.  
  1332. void CPropertySet::Remove( CLSID FormatID )
  1333. {
  1334.     CPropertySection* pSect;
  1335.     POSITION posRemove = m_SectionList.GetHeadPosition();
  1336.     POSITION pos = posRemove;
  1337.     while( posRemove != NULL )
  1338.     {
  1339.         pSect = (CPropertySection*)m_SectionList.GetNext( pos );
  1340.         if (IsEqualCLSID( pSect->m_FormatID, FormatID ))
  1341.         {
  1342.             m_SectionList.RemoveAt( posRemove );
  1343.             delete pSect;
  1344.             m_PH.cSections--;
  1345.             return;
  1346.         }
  1347.         posRemove = pos;
  1348.     }
  1349. }
  1350.  
  1351. void CPropertySet::RemoveAll( )
  1352. {
  1353.     POSITION pos = m_SectionList.GetHeadPosition();
  1354.     while( pos != NULL )
  1355.     {
  1356.         delete (CPropertySection*)m_SectionList.GetNext( pos );
  1357.     }
  1358.     m_SectionList.RemoveAll();
  1359.     m_PH.cSections = 0;
  1360. }
  1361.  
  1362. CPropertySection* CPropertySet::GetSection( CLSID FormatID )
  1363. {
  1364.     POSITION pos = m_SectionList.GetHeadPosition();
  1365.     CPropertySection* pSect;
  1366.     while (pos != NULL)
  1367.     {
  1368.         pSect = (CPropertySection*)m_SectionList.GetNext( pos );
  1369.         if (IsEqualCLSID( pSect->m_FormatID, FormatID ))
  1370.             return pSect;
  1371.     }
  1372.     return NULL;
  1373. }
  1374.  
  1375. CPropertySection* CPropertySet::AddSection( CLSID FormatID )
  1376. {
  1377.     CPropertySection* pSect = GetSection( FormatID );
  1378.     if (pSect)
  1379.         return pSect;
  1380.  
  1381.     pSect = new CPropertySection( FormatID ) ;
  1382.     if (pSect)
  1383.         AddSection( pSect );
  1384.     return pSect;
  1385. }
  1386.  
  1387. void CPropertySet::AddSection( CPropertySection* pSect )
  1388. {
  1389.     m_SectionList.AddTail( pSect );
  1390.     m_PH.cSections++;
  1391. }
  1392.  
  1393. CProperty* CPropertySet::GetProperty( CLSID FormatID, DWORD dwPropID )
  1394. {
  1395.     CPropertySection* pSect = GetSection( FormatID );
  1396.     if (pSect)
  1397.         return pSect->GetProperty( dwPropID );
  1398.     else
  1399.         return NULL;
  1400. }
  1401.  
  1402. void CPropertySet::AddProperty( CLSID FormatID, CProperty* pProp )
  1403. {
  1404.     CPropertySection* pSect = GetSection( FormatID );
  1405.     if (pSect)
  1406.         pSect->AddProperty( pProp );
  1407. }
  1408.  
  1409. WORD CPropertySet::GetByteOrder( void )
  1410. {   return m_PH.wByteOrder;  }
  1411.  
  1412. WORD CPropertySet::GetFormatVersion( void )
  1413. {   return m_PH.wFormat;  }
  1414.  
  1415. void CPropertySet::SetFormatVersion( WORD wFmtVersion )
  1416. {   m_PH.wFormat = wFmtVersion;  }
  1417.  
  1418. DWORD CPropertySet::GetOSVersion( void )
  1419. {   return m_PH.dwOSVer;  }
  1420.  
  1421. void CPropertySet::SetOSVersion( DWORD dwOSVer )
  1422. {   m_PH.dwOSVer = dwOSVer;  }
  1423.  
  1424. CLSID CPropertySet::GetClassID( void )
  1425. {   return m_PH.clsID;  }
  1426.  
  1427. void CPropertySet::SetClassID( CLSID clsID )
  1428. {   m_PH.clsID = clsID;  }
  1429.  
  1430. DWORD CPropertySet::GetCount( void )
  1431. {   return m_SectionList.GetCount();  }
  1432.  
  1433. CObList* CPropertySet::GetList( void )
  1434. {   return &m_SectionList;  }
  1435.  
  1436.  
  1437. BOOL CPropertySet::WriteToStream( IStream* pIStream )
  1438. {
  1439.     LPSTREAM        pIStrFIDO;
  1440.     FORMATIDOFFSET  fido;
  1441.     ULONG           cb;
  1442.     ULARGE_INTEGER  ulSeek;
  1443.     LARGE_INTEGER   li;
  1444.  
  1445.     // Write the Property List Header
  1446.     m_PH.cSections = m_SectionList.GetCount();
  1447.     pIStream->Write((LPVOID)&m_PH, sizeof(m_PH), &cb);
  1448.     if (sizeof(m_PH) != cb)
  1449.     {
  1450.         TRACE0("Write of Property Set Header failed.\n");
  1451.         return FALSE;
  1452.     }
  1453.  
  1454.     if (m_SectionList.IsEmpty())
  1455.     {
  1456.         TRACE0("Warning: Wrote empty property set.\n");
  1457.         return TRUE;
  1458.     }
  1459.  
  1460.     // After the header is the list of Format ID/Offset pairs
  1461.     // Since there is an ID/Offset pair for each section and we
  1462.     // need to write the ID/Offset pair as we write each section
  1463.     // we clone the stream and use the clone to access the
  1464.     // table of ID/offset pairs (FIDO)...
  1465.     //
  1466.     pIStream->Clone( &pIStrFIDO );
  1467.  
  1468.     // Now seek pIStream past the FIDO list
  1469.     //
  1470.     LISet32( li, m_PH.cSections * sizeof( FORMATIDOFFSET ) );
  1471.     pIStream->Seek( li, STREAM_SEEK_CUR, &ulSeek);
  1472.  
  1473.     // Write each section.
  1474.     CPropertySection*   pSect = NULL;
  1475.     POSITION            pos = m_SectionList.GetHeadPosition();
  1476.     while( pos != NULL )
  1477.     {
  1478.         // Get next element (note cast)
  1479.         pSect = (CPropertySection*)m_SectionList.GetNext( pos );
  1480.  
  1481.         // Write it
  1482.         if (!pSect->WriteToStream( pIStream ))
  1483.         {
  1484.             pIStrFIDO->Release();
  1485.             return FALSE;
  1486.         }
  1487.  
  1488.         // Using our cloned stream write the Format ID / Offset pair
  1489.         fido.formatID = pSect->m_FormatID;
  1490.         fido.dwOffset = ulSeek.LowPart;
  1491.         pIStrFIDO->Write((LPVOID)&fido, sizeof(fido), &cb);
  1492.         if (sizeof(fido) != cb)
  1493.         {
  1494.             TRACE0("Write of 'fido' failed.\n");
  1495.             pIStrFIDO->Release();
  1496.             return FALSE;
  1497.         }
  1498.  
  1499.         // Get the seek offset (for pIStream) after the write
  1500.         LISet32( li, 0 );
  1501.         pIStream->Seek( li, STREAM_SEEK_CUR, &ulSeek );
  1502.     }
  1503.  
  1504.     pIStrFIDO->Release();
  1505.  
  1506.     return TRUE;
  1507. }
  1508.  
  1509. BOOL CPropertySet::ReadFromStream( IStream* pIStream )
  1510. {
  1511.     ULONG               cb;
  1512.     FORMATIDOFFSET      fido;
  1513.     ULONG               cSections;
  1514.     LPSTREAM            pIStrFIDO;
  1515.     CPropertySection*   pSect;
  1516.     LARGE_INTEGER       li;
  1517.     LARGE_INTEGER       liPropSet;
  1518.  
  1519.     // Save the stream position at which the property set starts.
  1520.     LARGE_INTEGER liZero = {0,0};
  1521.     pIStream->Seek( liZero, STREAM_SEEK_CUR, (ULARGE_INTEGER*)&liPropSet );
  1522.  
  1523.     if (m_PH.cSections || !m_SectionList.IsEmpty())
  1524.          RemoveAll();
  1525.  
  1526.     // The stream starts like this:
  1527.     //  wByteOrder   wFmtVer   dwOSVer   clsID  cSections
  1528.     // Which is nice, because our PROPHEADER is the same!
  1529.     pIStream->Read( (LPVOID)&m_PH, sizeof( m_PH ), &cb );
  1530.     if (cb != sizeof(m_PH))
  1531.         return FALSE;
  1532.  
  1533.     // Now we're pointing at the first of the FormatID/Offset pairs
  1534.     // (FIDOs).   To get to each section we use a cloned stream
  1535.     // to stay back and point at the FIDOs (pIStrFIDO).  We seek
  1536.     // pIStream to each of the sections, creating CProperitySection
  1537.     // and so forth as we go...
  1538.     //
  1539.     pIStream->Clone( &pIStrFIDO );
  1540.  
  1541.     cSections = m_PH.cSections;
  1542.     while (cSections--)
  1543.     {
  1544.         pIStrFIDO->Read( (LPVOID)&fido, sizeof( fido ), &cb );
  1545.         if (cb != sizeof(fido))
  1546.         {
  1547.             pIStrFIDO->Release();
  1548.             return FALSE;
  1549.         }
  1550.  
  1551.         // Do a seek from the beginning of the property set.
  1552.         LISet32( li, fido.dwOffset );
  1553.         pIStream->Seek( liPropSet, STREAM_SEEK_SET, NULL );
  1554.         pIStream->Seek( li, STREAM_SEEK_CUR, NULL );
  1555.  
  1556.         // Now pIStream is at the type/value pair
  1557.         pSect = new CPropertySection;
  1558.         pSect->SetFormatID( fido.formatID );
  1559.         pSect->ReadFromStream( pIStream, liPropSet );
  1560.         m_SectionList.AddTail( pSect );
  1561.     }
  1562.  
  1563.     pIStrFIDO->Release();
  1564.     return TRUE;
  1565. }
  1566.  
  1567.  
  1568. /////////////////////////////////////////////////////////////////////////////
  1569. // Force any extra compiler-generated code into AFX_INIT_SEG
  1570.  
  1571. #ifdef AFX_INIT_SEG
  1572. #pragma code_seg(AFX_INIT_SEG)
  1573. #endif
  1574.