Click here to Skip to main content
15,881,559 members
Articles / Desktop Programming / MFC

Professional User Interface Suite

Rate me:
Please Sign up or sign in to vote.
4.99/5 (174 votes)
13 Jan 200423 min read 1.5M   23.5K   641  
MFC extension library enabling software to be provided with a professional UI
// 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 = 0;
	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 = 0;
	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)
		{
#if (defined _DEBUG)
				pProp->AssertValid();
#endif
				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();
#if (defined _DEBUG)
	m_PropList.AssertValid();
#endif
	CProperty* pProp;
	while (pos != NULL)
	{
		pProp= (CProperty*)m_PropList.GetNext( pos );
#if (defined _DEBUG)
		pProp->AssertValid();
#endif
		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 );
#if (defined _DEBUG)
			pProp->AssertValid();
#endif
			m_PropList.AddTail( pProp );
#if (defined _DEBUG)
			m_PropList.AssertValid();
#endif
		}
		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

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Architect Foss Software Inc
Ukraine Ukraine
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions