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