mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1545 lines
36 KiB
1545 lines
36 KiB
// This is a part of the Microsoft Foundation Classes C++ library.
|
|
// Copyright (C) 1992-1995 Microsoft Corporation
|
|
// All rights reserved.
|
|
//
|
|
// This source code is only intended as a supplement to the
|
|
// Microsoft Foundation Classes Reference and related
|
|
// electronic documentation provided with the library.
|
|
// See these sources for detailed information regarding the
|
|
// Microsoft Foundation Classes product.
|
|
|
|
#include "stdafx.h"
|
|
#include <malloc.h>
|
|
#include <ole2.h>
|
|
#include <oleauto.h>
|
|
|
|
#ifdef AFXCTL_PROP_SEG
|
|
#pragma code_seg(AFXCTL_PROP_SEG)
|
|
#endif
|
|
|
|
#ifdef _DEBUG
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
#define new DEBUG_NEW
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Helper functions used by CProperty, CPropertySection, CPropertySet classes
|
|
|
|
static LPVOID CountPrefixedStringA(LPCSTR lpsz)
|
|
{
|
|
DWORD cb = (lstrlenA(lpsz) + 1);
|
|
LPDWORD lp = (LPDWORD)malloc((int)cb + sizeof(DWORD));
|
|
if (lp)
|
|
{
|
|
*lp = cb;
|
|
lstrcpyA((LPSTR)(lp+1), lpsz);
|
|
}
|
|
|
|
return (LPVOID)lp;
|
|
}
|
|
|
|
|
|
static LPVOID CountPrefixedStringW(LPCWSTR lpsz)
|
|
{
|
|
DWORD cb = (wcslen(lpsz) + 1);
|
|
LPDWORD lp = (LPDWORD)malloc((int)cb * sizeof(WCHAR) + sizeof(DWORD));
|
|
if (lp)
|
|
{
|
|
*lp = cb;
|
|
wcscpy((LPWSTR)(lp+1), lpsz);
|
|
}
|
|
|
|
return (LPVOID)lp;
|
|
}
|
|
|
|
|
|
#ifdef _UNICODE
|
|
#define CountPrefixedStringT CountPrefixedStringW
|
|
#else
|
|
#define CountPrefixedStringT CountPrefixedStringA
|
|
#endif
|
|
|
|
|
|
#ifdef _UNICODE
|
|
|
|
#define MAX_STRLEN 1024
|
|
|
|
static LPBYTE ConvertStringProp(LPBYTE pbProp, DWORD dwType, ULONG nReps,
|
|
size_t cbCharSize)
|
|
{
|
|
LPBYTE pbResult = NULL; // Return value
|
|
ULONG cbResult = 0; // Number of bytes in pbResult
|
|
LPBYTE pbBuffer; // Temporary holding space
|
|
ULONG cchOrig; // Number of characters in original string
|
|
ULONG cchCopy; // Number of characters to copy
|
|
ULONG cbCopy; // Number of bytes to copy
|
|
LPBYTE pbResultNew; // Used for realloc of pbResult
|
|
|
|
pbBuffer = (LPBYTE)malloc(MAX_STRLEN * cbCharSize);
|
|
if (pbBuffer == NULL)
|
|
return NULL;
|
|
|
|
// If it's a vector, the count goes first.
|
|
if (dwType & VT_VECTOR)
|
|
{
|
|
pbResult = (LPBYTE)malloc(sizeof(DWORD));
|
|
if (pbResult == NULL)
|
|
{
|
|
free(pbBuffer);
|
|
return NULL;
|
|
}
|
|
*(LPDWORD)pbResult = nReps;
|
|
cbResult = sizeof(DWORD);
|
|
}
|
|
|
|
while (nReps--)
|
|
{
|
|
cchOrig = *(LPDWORD)pbProp;
|
|
pbProp += sizeof(DWORD);
|
|
|
|
// Convert multibyte string to Unicode.
|
|
if (cbCharSize == sizeof(WCHAR))
|
|
{
|
|
cchCopy = _mbstowcsz((LPWSTR)pbBuffer, (LPSTR)pbProp,
|
|
min(cchOrig, MAX_STRLEN));
|
|
}
|
|
else
|
|
{
|
|
cchCopy = _wcstombsz((LPSTR)pbBuffer, (LPWSTR)pbProp,
|
|
min(cchOrig, MAX_STRLEN));
|
|
}
|
|
|
|
// Allocate space to append string.
|
|
cbCopy = cchCopy * cbCharSize;
|
|
pbResultNew = (LPBYTE)realloc(pbResult, cbResult + sizeof(DWORD) +
|
|
cbCopy);
|
|
|
|
// If allocation failed, cleanup and return NULL;
|
|
if (pbResultNew == NULL)
|
|
{
|
|
free(pbResult);
|
|
free(pbBuffer);
|
|
return NULL;
|
|
}
|
|
|
|
pbResult = pbResultNew;
|
|
|
|
// Copy character count and converted string into place,
|
|
// then update the total size.
|
|
memcpy(pbResult + cbResult, (LPBYTE)&cchCopy, sizeof(DWORD));
|
|
memcpy(pbResult + cbResult + sizeof(DWORD), pbBuffer, cbCopy);
|
|
cbResult += sizeof(DWORD) + cbCopy;
|
|
|
|
// Advance to the next vector element
|
|
pbProp += (cchOrig * cbCharSize);
|
|
}
|
|
|
|
free(pbBuffer);
|
|
return pbResult;
|
|
}
|
|
|
|
#endif // _UNICODE
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Implementation of the CProperty class
|
|
|
|
CProperty::CProperty()
|
|
{
|
|
m_dwPropID = 0;
|
|
|
|
m_dwType = VT_EMPTY;
|
|
m_pValue = NULL; // must init to NULL
|
|
}
|
|
|
|
CProperty::CProperty(DWORD dwID, const LPVOID pValue, DWORD dwType)
|
|
{
|
|
m_dwPropID = dwID;
|
|
m_dwType = dwType;
|
|
m_pValue = NULL; // must init to NULL
|
|
Set(dwID, pValue, dwType);
|
|
}
|
|
|
|
CProperty::~CProperty()
|
|
{
|
|
FreeValue();
|
|
}
|
|
|
|
BOOL CProperty::Set(DWORD dwID, const LPVOID pValue, DWORD dwType)
|
|
{
|
|
m_dwType = dwType;
|
|
m_dwPropID = dwID;
|
|
|
|
return Set(pValue);
|
|
}
|
|
|
|
BOOL CProperty::Set(const LPVOID pValue, DWORD dwType)
|
|
{
|
|
m_dwType = dwType;
|
|
return Set(pValue);
|
|
}
|
|
|
|
BOOL CProperty::Set(const LPVOID pVal)
|
|
{
|
|
ULONG cb;
|
|
ULONG cbItem;
|
|
ULONG cbValue;
|
|
ULONG nReps;
|
|
LPBYTE pCur;
|
|
LPVOID pValue = pVal;
|
|
DWORD dwType = m_dwType;
|
|
LPVOID pValueOrig = NULL;
|
|
|
|
if (m_pValue != NULL)
|
|
{
|
|
FreeValue();
|
|
}
|
|
|
|
if (pValue == NULL || m_dwType == 0)
|
|
return TRUE;
|
|
|
|
// Given pValue, determine how big it is
|
|
// Then allocate a new buffer for m_pValue and copy...
|
|
nReps = 1;
|
|
cbValue = 0;
|
|
pCur = (LPBYTE)pValue;
|
|
if (m_dwType & VT_VECTOR)
|
|
{
|
|
// The next DWORD is a count of the elements
|
|
nReps = *(LPDWORD)pValue;
|
|
cb = sizeof(nReps);
|
|
pCur += cb;
|
|
cbValue += cb;
|
|
dwType &= ~VT_VECTOR;
|
|
}
|
|
else
|
|
{
|
|
// If we get any of the string-like types,
|
|
// and we are not a vector create a count-prefixed
|
|
// buffer.
|
|
switch (dwType)
|
|
{
|
|
case VT_LPSTR: // null terminated string
|
|
pValueOrig = pValue;
|
|
pValue = CountPrefixedStringA((LPSTR)pValueOrig);
|
|
pCur = (LPBYTE)pValue;
|
|
break;
|
|
|
|
case VT_BSTR: // binary string
|
|
case VT_STREAM: // Name of the stream follows
|
|
case VT_STORAGE: // Name of the storage follows
|
|
case VT_STREAMED_OBJECT:// Stream contains an object
|
|
case VT_STORED_OBJECT: // Storage contains an object
|
|
pValueOrig = pValue;
|
|
pValue = CountPrefixedStringT((LPTSTR)pValueOrig);
|
|
pCur = (LPBYTE)pValue;
|
|
break;
|
|
|
|
case VT_LPWSTR: // UNICODE string
|
|
pValueOrig = pValue;
|
|
pValue = CountPrefixedStringW((LPWSTR)pValueOrig);
|
|
pCur = (LPBYTE)pValue;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Since a value can be made up of a vector (VT_VECTOR) of
|
|
// items, we first seek through the value, picking out
|
|
// each item, getting it's size.
|
|
//
|
|
cbItem = 0; // Size of the current item
|
|
while (nReps--)
|
|
{
|
|
switch (dwType)
|
|
{
|
|
case VT_EMPTY: // nothing
|
|
cbItem = 0;
|
|
break;
|
|
|
|
case VT_I2: // 2 byte signed int
|
|
case VT_BOOL: // True=-1, False=0
|
|
cbItem = 2;
|
|
break;
|
|
|
|
case VT_I4: // 4 byte signed int
|
|
case VT_R4: // 4 byte real
|
|
cbItem = 4;
|
|
break;
|
|
|
|
case VT_R8: // 8 byte real
|
|
case VT_CY: // currency
|
|
case VT_DATE: // date
|
|
case VT_I8: // signed 64-bit int
|
|
case VT_FILETIME: // FILETIME
|
|
cbItem = 8;
|
|
break;
|
|
|
|
case VT_CLSID: // A Class ID
|
|
cbItem = sizeof(CLSID);
|
|
break;
|
|
|
|
#ifndef _UNICODE
|
|
case VT_BSTR: // binary string
|
|
case VT_STREAM: // Name of the stream follows
|
|
case VT_STORAGE: // Name of the storage follows
|
|
case VT_STREAMED_OBJECT:// Stream contains an object
|
|
case VT_STORED_OBJECT: // Storage contains an object
|
|
case VT_STREAMED_PROPSET:// Stream contains a propset
|
|
case VT_STORED_PROPSET: // Storage contains a propset
|
|
#endif // _UNICODE
|
|
case VT_LPSTR: // null terminated string
|
|
case VT_BLOB_OBJECT: // Blob contains an object
|
|
case VT_BLOB_PROPSET: // Blob contains a propset
|
|
case VT_BLOB: // Length prefixed bytes
|
|
case VT_CF: // Clipboard format
|
|
// Get the DWORD that gives us the size, making
|
|
// sure we increment cbValue.
|
|
cbItem = *(LPDWORD)pCur;
|
|
cb = sizeof(cbItem);
|
|
pCur += cb;
|
|
cbValue += cb;
|
|
break;
|
|
|
|
#ifdef _UNICODE
|
|
case VT_BSTR: // binary string
|
|
case VT_STREAM: // Name of the stream follows
|
|
case VT_STORAGE: // Name of the storage follows
|
|
case VT_STREAMED_OBJECT:// Stream contains an object
|
|
case VT_STORED_OBJECT: // Storage contains an object
|
|
case VT_STREAMED_PROPSET:// Stream contains a propset
|
|
case VT_STORED_PROPSET: // Storage contains a propset
|
|
#endif // _UNICODE
|
|
case VT_LPWSTR: // UNICODE string
|
|
cbItem = *(LPDWORD)pCur * sizeof(WCHAR);
|
|
cb = sizeof(cbItem);
|
|
pCur += cb;
|
|
cbValue += cb;
|
|
break;
|
|
|
|
default:
|
|
if (pValueOrig)
|
|
free(pValue);
|
|
return FALSE;
|
|
}
|
|
|
|
// Seek to the next item
|
|
pCur += cbItem;
|
|
cbValue += cbItem;
|
|
}
|
|
|
|
if (NULL == AllocValue(cbValue))
|
|
{
|
|
TRACE0("CProperty::AllocValue failed");
|
|
return FALSE;
|
|
}
|
|
memcpy(m_pValue, pValue, (int)cbValue);
|
|
|
|
if (pValueOrig)
|
|
free(pValue);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
LPVOID CProperty::Get()
|
|
{ return Get((DWORD*)NULL); }
|
|
|
|
LPVOID CProperty::Get(DWORD* pcb)
|
|
{
|
|
DWORD cb;
|
|
LPBYTE p = NULL;
|
|
|
|
p = (LPBYTE)m_pValue;
|
|
|
|
// m_pValue points to a Property "Value" which may
|
|
// have size information included...
|
|
switch (m_dwType)
|
|
{
|
|
case VT_EMPTY: // nothing
|
|
cb = 0;
|
|
break;
|
|
|
|
case VT_I2: // 2 byte signed int
|
|
case VT_BOOL: // True=-1, False=0
|
|
cb = 2;
|
|
break;
|
|
|
|
case VT_I4: // 4 byte signed int
|
|
case VT_R4: // 4 byte real
|
|
cb = 4;
|
|
break;
|
|
|
|
case VT_R8: // 8 byte real
|
|
case VT_CY: // currency
|
|
case VT_DATE: // date
|
|
case VT_I8: // signed 64-bit int
|
|
case VT_FILETIME: // FILETIME
|
|
cb = 8;
|
|
break;
|
|
|
|
#ifndef _UNICODE
|
|
case VT_BSTR: // binary string
|
|
case VT_STREAM: // Name of the stream follows
|
|
case VT_STORAGE: // Name of the storage follows
|
|
case VT_STREAMED_OBJECT:// Stream contains an object
|
|
case VT_STORED_OBJECT: // Storage contains an object
|
|
case VT_STREAMED_PROPSET:// Stream contains a propset
|
|
case VT_STORED_PROPSET: // Storage contains a propset
|
|
#endif // UNICODE
|
|
case VT_LPSTR: // null terminated string
|
|
case VT_CF: // Clipboard format
|
|
// Read the DWORD that gives us the size, making
|
|
// sure we increment cbValue.
|
|
cb = *(LPDWORD)p;
|
|
p += sizeof(DWORD);
|
|
break;
|
|
|
|
case VT_BLOB: // Length prefixed bytes
|
|
case VT_BLOB_OBJECT: // Blob contains an object
|
|
case VT_BLOB_PROPSET: // Blob contains a propset
|
|
// Read the DWORD that gives us the size.
|
|
cb = *(LPDWORD)p;
|
|
break;
|
|
|
|
#ifdef _UNICODE
|
|
case VT_BSTR: // binary string
|
|
case VT_STREAM: // Name of the stream follows
|
|
case VT_STORAGE: // Name of the storage follows
|
|
case VT_STREAMED_OBJECT:// Stream contains an object
|
|
case VT_STORED_OBJECT: // Storage contains an object
|
|
case VT_STREAMED_PROPSET:// Stream contains a propset
|
|
case VT_STORED_PROPSET: // Storage contains a propset
|
|
#endif // _UNICODE
|
|
case VT_LPWSTR: // UNICODE string
|
|
cb = *(LPDWORD)p * sizeof(WCHAR);
|
|
p += sizeof(DWORD);
|
|
break;
|
|
|
|
case VT_CLSID: // A Class ID
|
|
cb = sizeof(CLSID);
|
|
break;
|
|
|
|
default:
|
|
return NULL;
|
|
}
|
|
if (pcb != NULL)
|
|
*pcb = cb;
|
|
|
|
return p;
|
|
}
|
|
|
|
DWORD CProperty::GetType()
|
|
{ return m_dwType; }
|
|
|
|
void CProperty::SetType(DWORD dwType)
|
|
{ m_dwType = dwType; }
|
|
|
|
DWORD CProperty::GetID()
|
|
{ return m_dwPropID; }
|
|
|
|
void CProperty::SetID(DWORD dwPropID)
|
|
{ m_dwPropID = dwPropID; }
|
|
|
|
LPVOID CProperty::GetRawValue()
|
|
{ return m_pValue; }
|
|
|
|
BOOL CProperty::WriteToStream(IStream* pIStream)
|
|
{
|
|
ULONG cb;
|
|
ULONG cbTotal; // Total size of the whole value
|
|
DWORD dwType = m_dwType;
|
|
DWORD nReps;
|
|
LPBYTE pValue;
|
|
LPBYTE pCur;
|
|
BOOL bSuccess = FALSE;
|
|
BYTE b = 0;
|
|
|
|
nReps = 1;
|
|
pValue = (LPBYTE)m_pValue;
|
|
pCur = pValue;
|
|
cbTotal = 0;
|
|
if (m_dwType & VT_VECTOR)
|
|
{
|
|
// Value is a DWORD count of elements followed by
|
|
// that many repititions of the value.
|
|
//
|
|
nReps = *(LPDWORD)pCur;
|
|
cbTotal = sizeof(DWORD);
|
|
pCur += cbTotal;
|
|
dwType &= ~VT_VECTOR;
|
|
}
|
|
|
|
#ifdef _UNICODE
|
|
switch (dwType)
|
|
{
|
|
case VT_BSTR: // binary string
|
|
case VT_STREAM: // Name of the stream follows
|
|
case VT_STORAGE: // Name of the storage follows
|
|
case VT_STREAMED_OBJECT:// Stream contains an object
|
|
case VT_STORED_OBJECT: // Storage contains an object
|
|
case VT_STREAMED_PROPSET:// Stream contains a propset
|
|
case VT_STORED_PROPSET: // Storage contains a propset
|
|
pValue = ConvertStringProp(pCur, m_dwType, nReps, sizeof(char));
|
|
if (m_dwType & VT_VECTOR)
|
|
pCur = pValue + sizeof(DWORD);
|
|
break;
|
|
}
|
|
#endif // _UNICODE
|
|
|
|
// Figure out how big the data is.
|
|
while (nReps--)
|
|
{
|
|
switch (dwType)
|
|
{
|
|
case VT_EMPTY: // nothing
|
|
cb = 0;
|
|
break;
|
|
|
|
case VT_I2: // 2 byte signed int
|
|
case VT_BOOL: // True=-1, False=0
|
|
cb = 2;
|
|
break;
|
|
|
|
case VT_I4: // 4 byte signed int
|
|
case VT_R4: // 4 byte real
|
|
cb = 4;
|
|
break;
|
|
|
|
case VT_R8: // 8 byte real
|
|
case VT_CY: // currency
|
|
case VT_DATE: // date
|
|
case VT_I8: // signed 64-bit int
|
|
case VT_FILETIME: // FILETIME
|
|
cb = 8;
|
|
break;
|
|
|
|
case VT_LPSTR: // null terminated string
|
|
case VT_BSTR: // binary string
|
|
case VT_STREAM: // Name of the stream follows
|
|
case VT_STORAGE: // Name of the storage follows
|
|
case VT_STREAMED_OBJECT:// Stream contains an object
|
|
case VT_STORED_OBJECT: // Storage contains an object
|
|
case VT_STREAMED_PROPSET:// Stream contains a propset
|
|
case VT_STORED_PROPSET: // Storage contains a propset
|
|
case VT_BLOB: // Length prefixed bytes
|
|
case VT_BLOB_OBJECT: // Blob contains an object
|
|
case VT_BLOB_PROPSET: // Blob contains a propset
|
|
case VT_CF: // Clipboard format
|
|
cb = sizeof(DWORD) + *(LPDWORD)pCur;
|
|
break;
|
|
|
|
case VT_LPWSTR: // UNICODE string
|
|
cb = sizeof(DWORD) + (*(LPDWORD)pCur * sizeof(WCHAR));
|
|
break;
|
|
|
|
case VT_CLSID: // A Class ID
|
|
cb = sizeof(CLSID);
|
|
break;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
pCur += cb;
|
|
cbTotal+= cb;
|
|
}
|
|
|
|
// Write the type
|
|
pIStream->Write((LPVOID)&m_dwType, sizeof(m_dwType), &cb);
|
|
if (cb != sizeof(m_dwType))
|
|
goto Cleanup;
|
|
|
|
// Write the value
|
|
pIStream->Write((LPVOID)pValue, cbTotal, &cb);
|
|
if (cb != cbTotal)
|
|
goto Cleanup;
|
|
|
|
// Make sure we are 32 bit aligned
|
|
cbTotal = (((cbTotal + 3) >> 2) << 2) - cbTotal;
|
|
while (cbTotal--)
|
|
{
|
|
pIStream->Write((LPVOID)&b, 1, &cb);
|
|
if (cb != sizeof(BYTE))
|
|
goto Cleanup;
|
|
}
|
|
|
|
bSuccess = TRUE;
|
|
|
|
Cleanup:
|
|
if (pValue != m_pValue)
|
|
free(pValue);
|
|
|
|
return bSuccess;
|
|
}
|
|
|
|
BOOL CProperty::ReadFromStream(IStream* pIStream)
|
|
{
|
|
ULONG cb;
|
|
ULONG cbItem;
|
|
ULONG cbValue;
|
|
DWORD dwType;
|
|
ULONG nReps;
|
|
ULONG iReps;
|
|
LPSTREAM pIStrItem;
|
|
LARGE_INTEGER li;
|
|
|
|
// All properties are made up of a type/value pair.
|
|
// The obvious first thing to do is to get the type...
|
|
pIStream->Read((LPVOID)&m_dwType, sizeof(m_dwType), &cb);
|
|
if (cb != sizeof(m_dwType))
|
|
return FALSE;
|
|
|
|
dwType = m_dwType;
|
|
nReps = 1;
|
|
cbValue = 0;
|
|
if (m_dwType & VT_VECTOR)
|
|
{
|
|
// The next DWORD in the stream is a count of the
|
|
// elements
|
|
pIStream->Read((LPVOID)&nReps, sizeof(nReps), &cb);
|
|
if (cb != sizeof(nReps))
|
|
return FALSE;
|
|
cbValue += cb;
|
|
dwType &= ~VT_VECTOR;
|
|
}
|
|
|
|
// Since a value can be made up of a vector (VT_VECTOR) of
|
|
// items, we first seek through the value, picking out
|
|
// each item, getting it's size. We use a cloned
|
|
// stream for this (pIStrItem).
|
|
// We then use our pIStream to read the entire 'blob' into
|
|
// the allocated buffer.
|
|
//
|
|
cbItem = 0; // Size of the current item
|
|
pIStream->Clone(&pIStrItem);
|
|
ASSERT(pIStrItem != NULL);
|
|
iReps = nReps;
|
|
while (iReps--)
|
|
{
|
|
switch (dwType)
|
|
{
|
|
case VT_EMPTY: // nothing
|
|
cbItem = 0;
|
|
break;
|
|
|
|
case VT_I2: // 2 byte signed int
|
|
case VT_BOOL: // True=-1, False=0
|
|
cbItem = 2;
|
|
break;
|
|
|
|
case VT_I4: // 4 byte signed int
|
|
case VT_R4: // 4 byte real
|
|
cbItem = 4;
|
|
break;
|
|
|
|
case VT_R8: // 8 byte real
|
|
case VT_CY: // currency
|
|
case VT_DATE: // date
|
|
case VT_I8: // signed 64-bit int
|
|
case VT_FILETIME: // FILETIME
|
|
cbItem = 8;
|
|
break;
|
|
|
|
case VT_LPSTR: // null terminated string
|
|
case VT_BSTR: // binary string
|
|
case VT_STREAM: // Name of the stream follows
|
|
case VT_STORAGE: // Name of the storage follows
|
|
case VT_STREAMED_OBJECT:// Stream contains an object
|
|
case VT_STORED_OBJECT: // Storage contains an object
|
|
case VT_STREAMED_PROPSET:// Stream contains a propset
|
|
case VT_STORED_PROPSET: // Storage contains a propset
|
|
case VT_BLOB: // Length prefixed bytes
|
|
case VT_BLOB_OBJECT: // Blob contains an object
|
|
case VT_BLOB_PROPSET: // Blob contains a propset
|
|
case VT_CF: // Clipboard format
|
|
// Read the DWORD that gives us the size, making
|
|
// sure we increment cbValue.
|
|
pIStream->Read((LPVOID)&cbItem, sizeof(cbItem), &cb);
|
|
if (cb != sizeof(cbItem))
|
|
return FALSE;
|
|
LISet32(li, -(LONG)cb);
|
|
pIStream->Seek(li, STREAM_SEEK_CUR, NULL);
|
|
cbValue += cb;
|
|
break;
|
|
|
|
case VT_LPWSTR: // UNICODE string
|
|
pIStream->Read((LPVOID)&cbItem, sizeof(cbItem), &cb);
|
|
if (cb != sizeof(cbItem))
|
|
return FALSE;
|
|
LISet32(li, -(LONG)cb);
|
|
pIStream->Seek(li, STREAM_SEEK_CUR, NULL);
|
|
cbValue += cb;
|
|
cbItem *= sizeof(WCHAR);
|
|
break;
|
|
|
|
case VT_CLSID: // A Class ID
|
|
cbItem = sizeof(CLSID);
|
|
break;
|
|
|
|
default:
|
|
pIStrItem->Release();
|
|
return FALSE;
|
|
}
|
|
|
|
// Seek to the next item
|
|
LISet32(li, cbItem);
|
|
pIStrItem->Seek(li, STREAM_SEEK_CUR, NULL);
|
|
cbValue += cbItem;
|
|
}
|
|
|
|
pIStrItem->Release();
|
|
|
|
#ifdef _UNICODE
|
|
LPBYTE pTmp;
|
|
|
|
switch (dwType)
|
|
{
|
|
case VT_BSTR: // binary string
|
|
case VT_STREAM: // Name of the stream follows
|
|
case VT_STORAGE: // Name of the storage follows
|
|
case VT_STREAMED_OBJECT:// Stream contains an object
|
|
case VT_STORED_OBJECT: // Storage contains an object
|
|
case VT_STREAMED_PROPSET:// Stream contains a propset
|
|
case VT_STORED_PROPSET: // Storage contains a propset
|
|
pTmp = (LPBYTE)malloc((int)cbValue);
|
|
pIStream->Read(pTmp, cbValue, &cb);
|
|
m_pValue = ConvertStringProp(pTmp, m_dwType, nReps, sizeof(WCHAR));
|
|
free(pTmp);
|
|
break;
|
|
|
|
default:
|
|
#endif // _UNICODE
|
|
// Allocate cbValue bytes
|
|
if (NULL == AllocValue(cbValue))
|
|
return FALSE;
|
|
|
|
// Read the buffer from pIStream
|
|
pIStream->Read(m_pValue, cbValue, &cb);
|
|
if (cb != cbValue)
|
|
return FALSE;
|
|
#ifdef _UNICODE
|
|
break;
|
|
}
|
|
#endif // _UNICODE
|
|
|
|
// Done!
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
LPVOID CProperty::AllocValue(ULONG cb)
|
|
{
|
|
return m_pValue = malloc((int)cb);
|
|
}
|
|
|
|
|
|
void CProperty::FreeValue()
|
|
{
|
|
if (m_pValue != NULL)
|
|
{
|
|
free(m_pValue);
|
|
m_pValue = NULL;
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Implementation of the CPropertySection class
|
|
|
|
CPropertySection::CPropertySection()
|
|
{
|
|
m_FormatID = GUID_NULL;
|
|
m_SH.cbSection = 0;
|
|
m_SH.cProperties = 0;
|
|
}
|
|
|
|
CPropertySection::CPropertySection(CLSID FormatID)
|
|
{
|
|
m_FormatID = FormatID;
|
|
m_SH.cbSection = 0;
|
|
m_SH.cProperties = 0;
|
|
}
|
|
|
|
CPropertySection::~CPropertySection()
|
|
{
|
|
RemoveAll();
|
|
return;
|
|
}
|
|
|
|
CLSID CPropertySection::GetFormatID()
|
|
{ return m_FormatID; }
|
|
|
|
void CPropertySection::SetFormatID(CLSID FormatID)
|
|
{ m_FormatID = FormatID; }
|
|
|
|
BOOL CPropertySection::Set(DWORD dwPropID, LPVOID pValue, DWORD dwType)
|
|
{
|
|
CProperty* pProp = GetProperty(dwPropID);
|
|
if (pProp == NULL)
|
|
{
|
|
if ((pProp = new CProperty(dwPropID, pValue, dwType)) != NULL)
|
|
AddProperty(pProp);
|
|
return (pProp != NULL);
|
|
}
|
|
|
|
pProp->Set(dwPropID, pValue, dwType);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CPropertySection::Set(DWORD dwPropID, LPVOID pValue)
|
|
{
|
|
// Since no dwType was specified, the property is assumed
|
|
// to exist. Fail if it does not.
|
|
CProperty* pProp = GetProperty(dwPropID);
|
|
if (pProp != NULL && pProp->m_dwType)
|
|
{
|
|
pProp->Set(dwPropID, pValue, pProp->m_dwType);
|
|
return TRUE;
|
|
}
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
LPVOID CPropertySection::Get(DWORD dwPropID)
|
|
{ return Get(dwPropID, (DWORD*)NULL); }
|
|
|
|
LPVOID CPropertySection::Get(DWORD dwPropID, DWORD* pcb)
|
|
{
|
|
CProperty* pProp = GetProperty(dwPropID);
|
|
if (pProp)
|
|
return pProp->Get(pcb);
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
void CPropertySection::Remove(DWORD dwID)
|
|
{
|
|
POSITION pos;
|
|
POSITION posRemove = m_PropList.GetHeadPosition();
|
|
CProperty* pProp;
|
|
while (posRemove != NULL)
|
|
{
|
|
pProp = (CProperty*)m_PropList.GetNext(pos);
|
|
if (pProp->m_dwPropID == dwID)
|
|
{
|
|
m_PropList.RemoveAt(posRemove);
|
|
delete pProp;
|
|
m_SH.cProperties--;
|
|
return;
|
|
}
|
|
posRemove = pos;
|
|
}
|
|
}
|
|
|
|
void CPropertySection::RemoveAll()
|
|
{
|
|
POSITION pos = m_PropList.GetHeadPosition();
|
|
while (pos != NULL)
|
|
delete (CProperty*)m_PropList.GetNext(pos);
|
|
m_PropList.RemoveAll();
|
|
m_SH.cProperties = 0;
|
|
}
|
|
|
|
|
|
CProperty* CPropertySection::GetProperty(DWORD dwPropID)
|
|
{
|
|
POSITION pos = m_PropList.GetHeadPosition();
|
|
CProperty* pProp;
|
|
while (pos != NULL)
|
|
{
|
|
pProp= (CProperty*)m_PropList.GetNext(pos);
|
|
if (pProp->m_dwPropID == dwPropID)
|
|
return pProp;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void CPropertySection::AddProperty(CProperty* pProp)
|
|
{
|
|
m_PropList.AddTail(pProp);
|
|
m_SH.cProperties++;
|
|
}
|
|
|
|
DWORD CPropertySection::GetSize()
|
|
{ return m_SH.cbSection; }
|
|
|
|
DWORD CPropertySection::GetCount()
|
|
{ return m_PropList.GetCount(); }
|
|
|
|
CObList* CPropertySection::GetList()
|
|
{ return &m_PropList; }
|
|
|
|
BOOL CPropertySection::WriteToStream(IStream* pIStream)
|
|
{
|
|
// Create a dummy property entry for the name dictionary (ID == 0).
|
|
Set(0, NULL, VT_EMPTY);
|
|
|
|
ULONG cb;
|
|
ULARGE_INTEGER ulSeekOld;
|
|
ULARGE_INTEGER ulSeek;
|
|
LPSTREAM pIStrPIDO;
|
|
PROPERTYIDOFFSET pido;
|
|
LARGE_INTEGER li;
|
|
|
|
// The Section header contains the number of bytes in the
|
|
// section. Thus we need to go back to where we should
|
|
// write the count of bytes
|
|
// after we write all the property sets..
|
|
// We accomplish this by saving the seek pointer to where
|
|
// the size should be written in ulSeekOld
|
|
m_SH.cbSection = 0;
|
|
m_SH.cProperties = m_PropList.GetCount();
|
|
LISet32(li, 0);
|
|
pIStream->Seek(li, STREAM_SEEK_CUR, &ulSeekOld);
|
|
|
|
pIStream->Write((LPVOID)&m_SH, sizeof(m_SH), &cb);
|
|
if (sizeof(m_SH) != cb)
|
|
{
|
|
TRACE0("Write of section header failed (1).\n");
|
|
return FALSE;
|
|
}
|
|
|
|
if (m_PropList.IsEmpty())
|
|
{
|
|
TRACE0("Warning: Wrote empty property section.\n");
|
|
return TRUE;
|
|
}
|
|
|
|
// After the section header is the list of property ID/Offset pairs
|
|
// Since there is an ID/Offset pair for each property and we
|
|
// need to write the ID/Offset pair as we write each property
|
|
// we clone the stream and use the clone to access the
|
|
// table of ID/offset pairs (PIDO)...
|
|
//
|
|
pIStream->Clone(&pIStrPIDO);
|
|
|
|
// Now seek pIStream past the PIDO list
|
|
//
|
|
LISet32(li, m_SH.cProperties * sizeof(PROPERTYIDOFFSET));
|
|
pIStream->Seek(li, STREAM_SEEK_CUR, &ulSeek);
|
|
|
|
// Now write each section to pIStream.
|
|
CProperty* pProp = NULL;
|
|
POSITION pos = m_PropList.GetHeadPosition();
|
|
while (pos != NULL)
|
|
{
|
|
// Get next element (note cast)
|
|
pProp = (CProperty*)m_PropList.GetNext(pos);
|
|
|
|
if (pProp->m_dwPropID != 0)
|
|
{
|
|
// Write it
|
|
if (!pProp->WriteToStream(pIStream))
|
|
{
|
|
pIStrPIDO->Release();
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!WriteNameDictToStream(pIStream))
|
|
{
|
|
pIStrPIDO->Release();
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// Using our cloned stream write the Format ID / Offset pair
|
|
// The offset to this property is the current seek pointer
|
|
// minus the pointer to the beginning of the section
|
|
pido.dwOffset = ulSeek.LowPart - ulSeekOld.LowPart;
|
|
pido.propertyID = pProp->m_dwPropID;
|
|
pIStrPIDO->Write((LPVOID)&pido, sizeof(pido), &cb);
|
|
if (sizeof(pido) != cb)
|
|
{
|
|
TRACE0("Write of 'pido' failed\n");
|
|
pIStrPIDO->Release();
|
|
return FALSE;
|
|
}
|
|
|
|
// Get the seek offset after the write
|
|
LISet32(li, 0);
|
|
pIStream->Seek(li, STREAM_SEEK_CUR, &ulSeek);
|
|
}
|
|
|
|
pIStrPIDO->Release();
|
|
|
|
// Now go back to ulSeekOld and write the section header.
|
|
// Size of section is current seek point minus old seek point
|
|
//
|
|
m_SH.cbSection = ulSeek.LowPart - ulSeekOld.LowPart;
|
|
|
|
// Seek to beginning of this section and write the section header.
|
|
LISet32(li, ulSeekOld.LowPart);
|
|
pIStream->Seek(li, STREAM_SEEK_SET, NULL);
|
|
pIStream->Write((LPVOID)&m_SH, sizeof(m_SH), &cb);
|
|
if (sizeof(m_SH) != cb)
|
|
{
|
|
TRACE0("Write of section header failed (2).\n");
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CPropertySection::ReadFromStream(IStream* pIStream,
|
|
LARGE_INTEGER liPropSet)
|
|
{
|
|
ULONG cb;
|
|
PROPERTYIDOFFSET pido;
|
|
ULONG cProperties;
|
|
LPSTREAM pIStrPIDO;
|
|
ULARGE_INTEGER ulSectionStart;
|
|
LARGE_INTEGER li;
|
|
CProperty* pProp;
|
|
|
|
if (m_SH.cProperties || !m_PropList.IsEmpty())
|
|
RemoveAll();
|
|
|
|
// pIStream is pointing to the beginning of the section we
|
|
// are to read. First there is a DWORD that is the count
|
|
// of bytes in this section, then there is a count
|
|
// of properties, followed by a list of propertyID/offset pairs,
|
|
// followed by type/value pairs.
|
|
//
|
|
LISet32(li, 0);
|
|
pIStream->Seek(li, STREAM_SEEK_CUR, &ulSectionStart);
|
|
pIStream->Read((LPVOID)&m_SH, sizeof(m_SH), &cb);
|
|
if (cb != sizeof(m_SH))
|
|
return FALSE;
|
|
|
|
// Now we're pointing at the first of the PropID/Offset pairs
|
|
// (PIDOs). To get to each property we use a cloned stream
|
|
// to stay back and point at the PIDOs (pIStrPIDO). We seek
|
|
// pIStream to each of the Type/Value pairs, creating CProperites
|
|
// and so forth as we go...
|
|
//
|
|
pIStream->Clone(&pIStrPIDO);
|
|
|
|
cProperties = m_SH.cProperties;
|
|
while (cProperties--)
|
|
{
|
|
pIStrPIDO->Read((LPVOID)&pido, sizeof(pido), &cb);
|
|
if (cb != sizeof(pido))
|
|
{
|
|
pIStrPIDO->Release();
|
|
return FALSE;
|
|
}
|
|
|
|
// Do a seek from the beginning of the property set.
|
|
LISet32(li, ulSectionStart.LowPart + pido.dwOffset);
|
|
pIStream->Seek(liPropSet, STREAM_SEEK_SET, NULL);
|
|
pIStream->Seek(li, STREAM_SEEK_CUR, NULL);
|
|
|
|
// Now pIStream is at the type/value pair
|
|
if (pido.propertyID != 0)
|
|
{
|
|
pProp = new CProperty(pido.propertyID, NULL, 0);
|
|
pProp->ReadFromStream(pIStream);
|
|
m_PropList.AddTail(pProp);
|
|
}
|
|
else
|
|
{
|
|
ReadNameDictFromStream(pIStream);
|
|
}
|
|
}
|
|
|
|
pIStrPIDO->Release();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CPropertySection::GetID(LPCTSTR pszName, DWORD* pdwPropID)
|
|
{
|
|
CString strName(pszName);
|
|
strName.MakeLower(); // Dictionary stores all names in lowercase
|
|
|
|
void* pvID;
|
|
if (m_NameDict.Lookup(strName, pvID))
|
|
{
|
|
*pdwPropID = (DWORD)pvID;
|
|
return TRUE;
|
|
}
|
|
|
|
// Failed to find entry in dictionary
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL CPropertySection::SetName(DWORD dwPropID, LPCTSTR pszName)
|
|
{
|
|
BOOL bSuccess = TRUE;
|
|
CString strName(pszName);
|
|
strName.MakeLower(); // Dictionary stores all names in lowercase
|
|
|
|
TRY
|
|
{
|
|
void* pDummy;
|
|
BOOL bNameExists = m_NameDict.Lookup(strName, pDummy);
|
|
|
|
ASSERT(!bNameExists); // Property names must be unique.
|
|
|
|
if (bNameExists)
|
|
bSuccess = FALSE;
|
|
else
|
|
m_NameDict.SetAt(strName, (void*)dwPropID);
|
|
}
|
|
CATCH (CException, e)
|
|
{
|
|
TRACE0("Failed to add entry to dictionary.\n");
|
|
bSuccess = FALSE;
|
|
}
|
|
END_CATCH
|
|
|
|
return bSuccess;
|
|
}
|
|
|
|
struct DICTENTRYHEADER
|
|
{
|
|
DWORD dwPropID;
|
|
DWORD cb;
|
|
};
|
|
|
|
struct DICTENTRY
|
|
{
|
|
DICTENTRYHEADER hdr;
|
|
char sz[256];
|
|
};
|
|
|
|
BOOL CPropertySection::ReadNameDictFromStream(IStream* pIStream)
|
|
{
|
|
ULONG cb;
|
|
ULONG cbRead = 0;
|
|
|
|
// Read dictionary header (count).
|
|
ULONG cProperties = 0;
|
|
pIStream->Read((LPVOID)&cProperties, sizeof(cProperties), &cb);
|
|
if (sizeof(cProperties) != cb)
|
|
{
|
|
TRACE0("Read of dictionary header failed.\n");
|
|
return FALSE;
|
|
}
|
|
|
|
ULONG iProp;
|
|
DICTENTRY entry;
|
|
|
|
for (iProp = 0; iProp < cProperties; iProp++)
|
|
{
|
|
// Read entry header (dwPropID, cch).
|
|
if (FAILED(pIStream->Read((LPVOID)&entry, sizeof(DICTENTRYHEADER),
|
|
&cbRead)) ||
|
|
(sizeof(DICTENTRYHEADER) != cbRead))
|
|
{
|
|
TRACE0("Read of dictionary entry failed.\n");
|
|
return FALSE;
|
|
}
|
|
|
|
// Read entry data (name).
|
|
|
|
cb = entry.hdr.cb;
|
|
|
|
if (FAILED(pIStream->Read((LPVOID)&entry.sz, cb, &cbRead)) ||
|
|
(cbRead != cb))
|
|
{
|
|
TRACE0("Read of dictionary entry failed.\n");
|
|
return FALSE;
|
|
}
|
|
|
|
LPTSTR pszName;
|
|
|
|
#ifdef _UNICODE
|
|
// Persistent form is always ANSI/DBCS. Convert to Unicode.
|
|
WCHAR wszName[256];
|
|
_mbstowcsz(wszName, entry.sz, 256);
|
|
pszName = wszName;
|
|
#else // _UNICODE
|
|
pszName = entry.sz;
|
|
#endif // _UNICODE
|
|
|
|
// Section's "name" appears first in list and has dwPropID == 0.
|
|
if ((iProp == 0) && (entry.hdr.dwPropID == 0))
|
|
m_strSectionName = pszName; // Section name
|
|
else
|
|
SetName(entry.hdr.dwPropID, pszName); // Some other property
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL WriteNameDictEntry(IStream* pIStream, DWORD dwPropID, CString& strName)
|
|
{
|
|
ULONG cb;
|
|
ULONG cbWritten = 0;
|
|
DICTENTRY entry;
|
|
|
|
entry.hdr.dwPropID = dwPropID;
|
|
entry.hdr.cb = min(strName.GetLength() + 1, 255);
|
|
#ifdef _UNICODE
|
|
// Persistent form is always ANSI/DBCS. Convert from Unicode.
|
|
_wcstombsz(entry.sz, (LPCWSTR)strName, 256);
|
|
#else // _UNICODE
|
|
memcpy(entry.sz, (LPCSTR)strName, (size_t)entry.hdr.cb);
|
|
#endif // _UNICODE
|
|
|
|
cb = sizeof(DICTENTRYHEADER) + entry.hdr.cb;
|
|
|
|
if (FAILED(pIStream->Write((LPVOID)&entry, cb, &cbWritten)) ||
|
|
(cbWritten != cb))
|
|
{
|
|
TRACE0("Write of dictionary entry failed.\n");
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CPropertySection::WriteNameDictToStream(IStream* pIStream)
|
|
{
|
|
ULONG cb;
|
|
|
|
// Write dictionary header (count).
|
|
ULONG cProperties = m_NameDict.GetCount() + 1;
|
|
pIStream->Write((LPVOID)&cProperties, sizeof(cProperties), &cb);
|
|
if (sizeof(cProperties) != cb)
|
|
{
|
|
TRACE0("Write of dictionary header failed.\n");
|
|
return FALSE;
|
|
}
|
|
|
|
POSITION pos;
|
|
CString strName;
|
|
void* pvID;
|
|
|
|
// Write out section's "name" with dwPropID == 0 first
|
|
if (!WriteNameDictEntry(pIStream, 0, m_strSectionName))
|
|
return FALSE;
|
|
|
|
// Enumerate contents of dictionary and write out (dwPropID, cb, name).
|
|
pos = m_NameDict.GetStartPosition();
|
|
while (pos != NULL)
|
|
{
|
|
m_NameDict.GetNextAssoc(pos, strName, pvID);
|
|
if (!WriteNameDictEntry(pIStream, (DWORD)pvID, strName))
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CPropertySection::SetSectionName(LPCTSTR pszName)
|
|
{
|
|
m_strSectionName = pszName;
|
|
return TRUE;
|
|
}
|
|
|
|
LPCTSTR CPropertySection::GetSectionName()
|
|
{
|
|
return (LPCTSTR)m_strSectionName;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Implementation of the CPropertySet class
|
|
|
|
CPropertySet::CPropertySet()
|
|
{
|
|
m_PH.wByteOrder = 0xFFFE;
|
|
m_PH.wFormat = 0;
|
|
m_PH.dwOSVer = (DWORD)MAKELONG(LOWORD(GetVersion()), 2);
|
|
m_PH.clsID = GUID_NULL;
|
|
m_PH.cSections = 0;
|
|
|
|
}
|
|
|
|
CPropertySet::CPropertySet(CLSID clsID)
|
|
{
|
|
m_PH.wByteOrder = 0xFFFE;
|
|
m_PH.wFormat = 0;
|
|
m_PH.dwOSVer = (DWORD)MAKELONG(LOWORD(GetVersion()), 2);
|
|
m_PH.clsID = clsID;
|
|
m_PH.cSections = 0;
|
|
}
|
|
|
|
CPropertySet::~CPropertySet()
|
|
{ RemoveAll(); }
|
|
|
|
BOOL CPropertySet::Set(CLSID FormatID, DWORD dwPropID, LPVOID pValue, DWORD dwType)
|
|
{
|
|
CPropertySection* pSect = GetSection(FormatID);
|
|
if (pSect == NULL)
|
|
{
|
|
if ((pSect = new CPropertySection(FormatID)) != NULL)
|
|
AddSection(pSect);
|
|
}
|
|
pSect->Set(dwPropID, pValue, dwType);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CPropertySet::Set(CLSID FormatID, DWORD dwPropID, LPVOID pValue)
|
|
{
|
|
// Since there is no dwType, we have to assume that the property
|
|
// already exists. If it doesn't, fail.
|
|
CPropertySection* pSect = GetSection(FormatID);
|
|
if (pSect != NULL)
|
|
return pSect->Set(dwPropID, pValue);
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
LPVOID CPropertySet::Get(CLSID FormatID, DWORD dwPropID, DWORD* pcb)
|
|
{
|
|
CPropertySection* pSect = GetSection(FormatID);
|
|
if (pSect)
|
|
return pSect->Get(dwPropID, pcb);
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
LPVOID CPropertySet::Get(CLSID FormatID, DWORD dwPropID)
|
|
{ return Get(FormatID, dwPropID, (DWORD*)NULL); }
|
|
|
|
void CPropertySet::Remove(CLSID FormatID, DWORD dwPropID)
|
|
{
|
|
CPropertySection* pSect = GetSection(FormatID);
|
|
if (pSect)
|
|
pSect->Remove(dwPropID);
|
|
}
|
|
|
|
void CPropertySet::Remove(CLSID FormatID)
|
|
{
|
|
CPropertySection* pSect;
|
|
POSITION pos;
|
|
POSITION posRemove = m_SectionList.GetHeadPosition();
|
|
while (posRemove != NULL)
|
|
{
|
|
pSect = (CPropertySection*)m_SectionList.GetNext(pos);
|
|
if (IsEqualCLSID(pSect->m_FormatID, FormatID))
|
|
{
|
|
m_SectionList.RemoveAt(posRemove);
|
|
delete pSect;
|
|
m_PH.cSections--;
|
|
return;
|
|
}
|
|
posRemove = pos;
|
|
}
|
|
}
|
|
|
|
void CPropertySet::RemoveAll()
|
|
{
|
|
POSITION pos = m_SectionList.GetHeadPosition();
|
|
while (pos != NULL)
|
|
{
|
|
delete (CPropertySection*)m_SectionList.GetNext(pos);
|
|
}
|
|
m_SectionList.RemoveAll();
|
|
m_PH.cSections = 0;
|
|
}
|
|
|
|
CPropertySection* CPropertySet::GetSection(CLSID FormatID)
|
|
{
|
|
POSITION pos = m_SectionList.GetHeadPosition();
|
|
CPropertySection* pSect;
|
|
while (pos != NULL)
|
|
{
|
|
pSect = (CPropertySection*)m_SectionList.GetNext(pos);
|
|
if (IsEqualCLSID(pSect->m_FormatID, FormatID))
|
|
return pSect;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
CPropertySection* CPropertySet::AddSection(CLSID FormatID)
|
|
{
|
|
CPropertySection* pSect = GetSection(FormatID);
|
|
if (pSect)
|
|
return pSect;
|
|
|
|
pSect = new CPropertySection(FormatID);
|
|
if (pSect)
|
|
AddSection(pSect);
|
|
return pSect;
|
|
}
|
|
|
|
void CPropertySet::AddSection(CPropertySection* pSect)
|
|
{
|
|
m_SectionList.AddTail(pSect);
|
|
m_PH.cSections++;
|
|
}
|
|
|
|
CProperty* CPropertySet::GetProperty(CLSID FormatID, DWORD dwPropID)
|
|
{
|
|
CPropertySection* pSect = GetSection(FormatID);
|
|
if (pSect)
|
|
return pSect->GetProperty(dwPropID);
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
void CPropertySet::AddProperty(CLSID FormatID, CProperty* pProp)
|
|
{
|
|
CPropertySection* pSect = GetSection(FormatID);
|
|
if (pSect)
|
|
pSect->AddProperty(pProp);
|
|
}
|
|
|
|
WORD CPropertySet::GetByteOrder()
|
|
{ return m_PH.wByteOrder; }
|
|
|
|
WORD CPropertySet::GetFormatVersion()
|
|
{ return m_PH.wFormat; }
|
|
|
|
void CPropertySet::SetFormatVersion(WORD wFmtVersion)
|
|
{ m_PH.wFormat = wFmtVersion; }
|
|
|
|
DWORD CPropertySet::GetOSVersion()
|
|
{ return m_PH.dwOSVer; }
|
|
|
|
void CPropertySet::SetOSVersion(DWORD dwOSVer)
|
|
{ m_PH.dwOSVer = dwOSVer; }
|
|
|
|
CLSID CPropertySet::GetClassID()
|
|
{ return m_PH.clsID; }
|
|
|
|
void CPropertySet::SetClassID(CLSID clsID)
|
|
{ m_PH.clsID = clsID; }
|
|
|
|
DWORD CPropertySet::GetCount()
|
|
{ return m_SectionList.GetCount(); }
|
|
|
|
CObList* CPropertySet::GetList()
|
|
{ return &m_SectionList; }
|
|
|
|
|
|
BOOL CPropertySet::WriteToStream(IStream* pIStream)
|
|
{
|
|
LPSTREAM pIStrFIDO;
|
|
FORMATIDOFFSET fido;
|
|
ULONG cb;
|
|
ULARGE_INTEGER ulSeek;
|
|
LARGE_INTEGER li;
|
|
|
|
// Write the Property List Header
|
|
m_PH.cSections = m_SectionList.GetCount();
|
|
pIStream->Write((LPVOID)&m_PH, sizeof(m_PH), &cb);
|
|
if (sizeof(m_PH) != cb)
|
|
{
|
|
TRACE0("Write of Property Set Header failed.\n");
|
|
return FALSE;
|
|
}
|
|
|
|
if (m_SectionList.IsEmpty())
|
|
{
|
|
TRACE0("Warning: Wrote empty property set.\n");
|
|
return TRUE;
|
|
}
|
|
|
|
// After the header is the list of Format ID/Offset pairs
|
|
// Since there is an ID/Offset pair for each section and we
|
|
// need to write the ID/Offset pair as we write each section
|
|
// we clone the stream and use the clone to access the
|
|
// table of ID/offset pairs (FIDO)...
|
|
//
|
|
pIStream->Clone(&pIStrFIDO);
|
|
|
|
// Now seek pIStream past the FIDO list
|
|
//
|
|
LISet32(li, m_PH.cSections * sizeof(FORMATIDOFFSET));
|
|
pIStream->Seek(li, STREAM_SEEK_CUR, &ulSeek);
|
|
|
|
// Write each section.
|
|
CPropertySection* pSect = NULL;
|
|
POSITION pos = m_SectionList.GetHeadPosition();
|
|
while (pos != NULL)
|
|
{
|
|
// Get next element (note cast)
|
|
pSect = (CPropertySection*)m_SectionList.GetNext(pos);
|
|
|
|
// Write it
|
|
if (!pSect->WriteToStream(pIStream))
|
|
{
|
|
pIStrFIDO->Release();
|
|
return FALSE;
|
|
}
|
|
|
|
// Using our cloned stream write the Format ID / Offset pair
|
|
fido.formatID = pSect->m_FormatID;
|
|
fido.dwOffset = ulSeek.LowPart;
|
|
pIStrFIDO->Write((LPVOID)&fido, sizeof(fido), &cb);
|
|
if (sizeof(fido) != cb)
|
|
{
|
|
TRACE0("Write of 'fido' failed.\n");
|
|
pIStrFIDO->Release();
|
|
return FALSE;
|
|
}
|
|
|
|
// Get the seek offset (for pIStream) after the write
|
|
LISet32(li, 0);
|
|
pIStream->Seek(li, STREAM_SEEK_CUR, &ulSeek);
|
|
}
|
|
|
|
pIStrFIDO->Release();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CPropertySet::ReadFromStream(IStream* pIStream)
|
|
{
|
|
ULONG cb;
|
|
FORMATIDOFFSET fido;
|
|
ULONG cSections;
|
|
LPSTREAM pIStrFIDO;
|
|
CPropertySection* pSect;
|
|
LARGE_INTEGER li;
|
|
LARGE_INTEGER liPropSet;
|
|
|
|
// Save the stream position at which the property set starts.
|
|
LARGE_INTEGER liZero = {0,0};
|
|
pIStream->Seek(liZero, STREAM_SEEK_CUR, (ULARGE_INTEGER*)&liPropSet);
|
|
|
|
if (m_PH.cSections || !m_SectionList.IsEmpty())
|
|
RemoveAll();
|
|
|
|
// The stream starts like this:
|
|
// wByteOrder wFmtVer dwOSVer clsID cSections
|
|
// Which is nice, because our PROPHEADER is the same!
|
|
pIStream->Read((LPVOID)&m_PH, sizeof(m_PH), &cb);
|
|
if (cb != sizeof(m_PH))
|
|
return FALSE;
|
|
|
|
// Now we're pointing at the first of the FormatID/Offset pairs
|
|
// (FIDOs). To get to each section we use a cloned stream
|
|
// to stay back and point at the FIDOs (pIStrFIDO). We seek
|
|
// pIStream to each of the sections, creating CProperitySection
|
|
// and so forth as we go...
|
|
//
|
|
pIStream->Clone(&pIStrFIDO);
|
|
|
|
cSections = m_PH.cSections;
|
|
while (cSections--)
|
|
{
|
|
pIStrFIDO->Read((LPVOID)&fido, sizeof(fido), &cb);
|
|
if (cb != sizeof(fido))
|
|
{
|
|
pIStrFIDO->Release();
|
|
return FALSE;
|
|
}
|
|
|
|
// Do a seek from the beginning of the property set.
|
|
LISet32(li, fido.dwOffset);
|
|
pIStream->Seek(liPropSet, STREAM_SEEK_SET, NULL);
|
|
pIStream->Seek(li, STREAM_SEEK_CUR, NULL);
|
|
|
|
// Now pIStream is at the type/value pair
|
|
pSect = new CPropertySection;
|
|
pSect->SetFormatID(fido.formatID);
|
|
pSect->ReadFromStream(pIStream, liPropSet);
|
|
m_SectionList.AddTail(pSect);
|
|
}
|
|
|
|
pIStrFIDO->Release();
|
|
return TRUE;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Force any extra compiler-generated code into AFX_INIT_SEG
|
|
|
|
#ifdef AFX_INIT_SEG
|
|
#pragma code_seg(AFX_INIT_SEG)
|
|
#endif
|