home *** CD-ROM | disk | FTP | other *** search
/ QBasic & Borland Pascal & C / Delphi5.iso / C / BC_502 / DRAWCL.PAK / PROPSET.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1997-05-06  |  40.1 KB  |  1,575 lines

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