home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tricks of the Windows Gam…ming Gurus (2nd Edition)
/
Disc2.iso
/
msdn_vcb
/
samples
/
vc98
/
mfc
/
ole
/
drawcli
/
propset.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
1998-03-26
|
40KB
|
1,574 lines
// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992-1998 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>
#include "propset.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
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( void )
{
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;
case VT_VARIANT: // VARIANT*
break;
default:
if (pValueOrig)
free( pValue );
return FALSE;
}
// Add 'cb' to cbItem before seeking...
//
// 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( void )
{ 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;
case VT_VARIANT: // VARIANT*
break;
default:
return NULL;
}
if (pcb != NULL)
*pcb = cb;
return p;
}
DWORD CProperty::GetType( void )
{ return m_dwType; }
void CProperty::SetType( DWORD dwType )
{ m_dwType = dwType; }
DWORD CProperty::GetID( void )
{ return m_dwPropID; }
void CProperty::SetID( DWORD dwPropID )
{ m_dwPropID = dwPropID; }
LPVOID CProperty::GetRawValue( void )
{ 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;
case VT_VARIANT: // VARIANT*
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;
case VT_VARIANT: // VARIANT*
break;
default:
pIStrItem->Release();
return FALSE;
}
// Add 'cb' to cbItem before seeking...
//
// 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( void )
{
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( void )
{
RemoveAll();
return;
}
CLSID CPropertySection::GetFormatID( void )
{ 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)
{
pProp->AssertValid();
return pProp->Get( pcb );
}
else
return NULL;
}
void CPropertySection::Remove( DWORD dwID )
{
POSITION pos = m_PropList.GetHeadPosition();
CProperty* pProp;
while( pos != NULL )
{
POSITION posRemove = pos;
pProp = (CProperty*)m_PropList.GetNext( pos );
if (pProp->m_dwPropID == dwID)
{
m_PropList.RemoveAt( posRemove );
delete pProp;
m_SH.cProperties--;
return;
}
}
}
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();
m_PropList.AssertValid();
CProperty* pProp;
while (pos != NULL)
{
pProp= (CProperty*)m_PropList.GetNext( pos );
pProp->AssertValid();
if (pProp->m_dwPropID == dwPropID)
return pProp;
}
return NULL;
}
void CPropertySection::AddProperty( CProperty* pProp )
{
m_PropList.AddTail( pProp );
m_SH.cProperties++;
}
DWORD CPropertySection::GetSize( void )
{ return m_SH.cbSection; }
DWORD CPropertySection::GetCount( void )
{ return m_PropList.GetCount(); }
CObList* CPropertySection::GetList( void )
{ 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;
}
// Now seek to end of of the now written section
LISet32(li, ulSeek.LowPart);
pIStream->Seek(li, STREAM_SEEK_SET, NULL);
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 );
pProp->AssertValid();
m_PropList.AddTail( pProp );
m_PropList.AssertValid();
}
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( void )
{
return (LPCTSTR)m_strSectionName;
}
/////////////////////////////////////////////////////////////////////////////
// Implementation of the CPropertySet class
CPropertySet::CPropertySet( void )
{
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 posRemove = m_SectionList.GetHeadPosition();
POSITION pos = posRemove;
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( void )
{ return m_PH.wByteOrder; }
WORD CPropertySet::GetFormatVersion( void )
{ return m_PH.wFormat; }
void CPropertySet::SetFormatVersion( WORD wFmtVersion )
{ m_PH.wFormat = wFmtVersion; }
DWORD CPropertySet::GetOSVersion( void )
{ return m_PH.dwOSVer; }
void CPropertySet::SetOSVersion( DWORD dwOSVer )
{ m_PH.dwOSVer = dwOSVer; }
CLSID CPropertySet::GetClassID( void )
{ return m_PH.clsID; }
void CPropertySet::SetClassID( CLSID clsID )
{ m_PH.clsID = clsID; }
DWORD CPropertySet::GetCount( void )
{ return m_SectionList.GetCount(); }
CObList* CPropertySet::GetList( void )
{ 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