Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version
Go to top

Professional User Interface Suite

, 13 Jan 2004
MFC extension library enabling software to be provided with a professional UI
prof-uis-223-freeware_src.zip
Prof-UIS
Bin_600
Bin_700
Bin_710
Include
profuisdll
idd_ext_color_dlg.ico
profuisdll.def
profuisdll_600.dsp
profuisdll_600.dsw
res
black_arrow_bottom.cur
black_arrow_left.cur
black_arrow_right.cur
black_arrow_top.cur
cmd00001.cur
cmd00002.cur
cmd00003.cur
cur_arrow_invert.cur
cur_black_finger.cur
cur_black_hand.cur
cur_hand_like.cur
cur_pan_all.cur
cur_pan_bottom.cur
cur_pan_bottom_left.cur
cur_pan_bottom_right.cur
cur_pan_horz.cur
cur_pan_left.cur
cur_pan_right.cur
cur_pan_top.cur
cur_pan_top_left.cur
cur_pan_top_right.cur
cur_pan_vert.cur
cur_resize_h1.cur
cur_resize_h2.cur
cur_resize_v1.cur
cur_resize_v2.cur
cur00001.cur
cur00002.cur
cur00003.cur
cur00004.cur
hollow_cross_normal.cur
hollow_cross_small.cur
ico00001.ico
id_view_.bmp
idd_ext_.ico
idd_icon.ico
ied_cp.cur
ied_el.cur
ied_fill.cur
ied_line.cur
ied_pen.cur
ied_rect.cur
ied_tool.cur
suppress.cur
suppress_arrow.cur
toolbar_.bmp
zoom_hollow.cur
zoom_minus.cur
zoom_plus.cur
profuislib
profuislib_600.dsp
profuislib_600.dsw
Samples
AviFrames
AviFrames_600.dsp
res
AviFrames.ico
CINEAPK.AVI
IDR_TOOLBAR_PLAYER.bmp
IDR_TOOLBAR_UISTYLE.bmp
Toolbar.bmp
toolbar2.bmp
xptheme.bin
DRAWCLI
DRAWCLI_600.dsp
L.JPN
RES
res
DRAWCLI.ICO
DRAWDOC.ICO
ico00001.ico
ico00002.ico
ico00003.ico
ico00004.ico
ico00005.ico
ico00006.ico
ico00007.ico
ico00008.ico
icon1.ico
id_objec.ico
id_scppv.ico
idr_abou.ico
PENCIL.CUR
TOOLBAR_1_16bit.bmp
TOOLBAR_1_4bit.bmp
TOOLBAR_2_16bit.bmp
TOOLBAR_2_4bit.bmp
FixedSizePanels
FixedSizePanels_600.dsp
res
bitmap_t.bmp
FixedSizePanels.ico
Toolbar.bmp
toolbar2.bmp
FullScreenState
FullScreenState_600.dsp
res
FullScreenState.ico
idr_mdit.ico
Toolbar.bmp
toolbar1.bmp
toolbar2.bmp
toolbar4.bmp
FunnyBars
FunnyBars_600.dsp
res
FunnyBars.ico
Toolbar.bmp
toolbar_16_16_8.bmp
toolbar_44_40_32.bmp
winXP.manifest
GLViews
GLViews_600.dsp
res
bitmap_i.bmp
bitmap_s.bmp
bitmap_t.bmp
bmp00001.bmp
btpnlane.bmp
CINEAPK.AVI
cur00001.cur
cur00002.cur
cursor_g.cur
earth.bmp
GLViews.ico
moon.bmp
ProfUisCubeSideOrange.bmp
ProfUisCubeSideWhite.bmp
ring.bmp
Toolbar.bmp
toolbar_.bmp
toolbar2.bmp
wndavipl.bmp
wndmirror.bmp
res_html
bodytop.gif
book_close.gif
book_open.gif
MDI
MDI_600.dsp
res
idr_mdit.ico
MDI.ico
Toolbar.bmp
toolbar2.bmp
MDI_InnerOuterBars
MDI_InnerOuterBars_600.dsp
res
bmp00001.bmp
idr_mdit.ico
MDI_InnerOuterBars.ico
Toolbar.bmp
toolbar_.bmp
toolbar2.bmp
MDIDOCVIEW
MDIDOCVIEW_600.dsp
res
MDIDOCVIEW.ico
MDIDOCVIEWDoc.ico
Toolbar.bmp
MthOutput
MthOutput_600.dsp
res
idr_mdit.ico
MthOutput.ico
Toolbar.bmp
toolbar1.bmp
toolbar2.bmp
ProfUIS_Controls
ProfUIS_Controls_600.dsp
res
bitmap1.bmp
bitmap2.bmp
icon1.ico
ProfUIS_Controls.ico
tab_imag.bmp
toolbar1.bmp
ResizableChildSheet
res
idr_chil.ico
ResizableChildSheet.ico
Toolbar.bmp
ResizableChildSheet_600.dsp
ResizablePropertySheet
res
bitmap1.bmp
bitmap2.bmp
ResizablePropertySheet.ico
ResizablePropertySheet_600.dsp
SDI
res
SDI.ico
Toolbar.bmp
toolbar2.bmp
SDI_600.dsp
SDIDOCVIEW
res
SDIDOCVIEW.ico
SDIDOCVIEWDoc.ico
Toolbar.bmp
SDIDOCVIEW_600.dsp
StateInFile
res
StateInFile.ico
Toolbar.bmp
StateInFile_600.dsp
StatusPanes
res
download.avi
StatusPanes.ico
Toolbar.bmp
toolbar2.bmp
StatusPanes_600.dsp
Src
Workspace
ProfUIS_600.dsw
profuis-v2.20_freeware.zip
Include
profuisdll
idd_ext_color_dlg.ico
profuisdll.def
profuisdll.dsp
res
profuislib
profuislib.dsp
Samples
DRAWCLI
MDI
MDIDOCVIEW
MDI_InnerOuterBars
ProfUIS_Controls
ResizablePropertySheet
SDI
SDIDOCVIEW
StateInFile
DRAWCLI.dsp
L.JPN
res
RES
DRAWCLI.ICO
DRAWDOC.ICO
ico00001.ico
ico00002.ico
icon1.ico
id_objec.ico
PENCIL.CUR
TOOLBAR.BMP
MDI.dsp
res
idr_mdit.ico
MDI.ico
Toolbar.bmp
toolbar2.bmp
MDIDOCVIEW.dsp
res
MDIDOCVIEW.ico
MDIDOCVIEWDoc.ico
Toolbar.bmp
MDI_InnerOuterBars.dsp
res
bmp00001.bmp
idr_mdit.ico
MDI_InnerOuterBars.ico
Toolbar.bmp
toolbar2.bmp
toolbar_.bmp
ProfUIS_Controls.dsp
res
bitmap1.bmp
bitmap2.bmp
icon1.ico
ProfUIS_Controls.ico
tab_imag.bmp
toolbar1.bmp
res
ResizablePropertySheet.dsp
ResizablePropertySheet.ico
res
SDI.dsp
SDI.ico
Toolbar.bmp
toolbar2.bmp
res
SDIDOCVIEW.dsp
SDIDOCVIEW.ico
SDIDOCVIEWDoc.ico
Toolbar.bmp
res
StateInFile.dsp
StateInFile.ico
Toolbar.bmp
Src
Workspace
ProfUIS.dsw
ProfUIS.suo
profuis21_bin.zip
Bin
MDI.exe
ProfUIS21.dll
ProfUIS_Controls.exe
SDI.exe
StateInFile.exe
profuis21_src.zip
idd_ext_color_dlg.ico
profuisdll.aps
profuisdll.clw
profuisdll.def
profuisdll.dep
profuisdll.dsp
profuisdll.mak
profuisdll.plg
MDI.APS
MDI.clw
MDI.dep
MDI.dsp
MDI.mak
MDI.plg
idr_mdit.ico
MDI.ico
Toolbar.bmp
toolbar2.bmp
ProfUIS_Controls.aps
ProfUIS_Controls.clw
ProfUIS_Controls.dep
ProfUIS_Controls.dsp
ProfUIS_Controls.mak
ProfUIS_Controls.ico
toolbar1.bmp
SDI.APS
SDI.clw
SDI.dep
SDI.dsp
SDI.mak
SDI.plg
SDI.ico
Toolbar.bmp
toolbar2.bmp
StateInFile.aps
StateInFile.clw
StateInFile.dep
StateInFile.dsp
StateInFile.mak
StateInFile.plg
StateInFile.ico
Toolbar.bmp
ProfUIS.dsw
ProfUIS.ncb
ProfUIS.opt
// 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

Share

About the Author

Sergiy Lavrynenko of Foss Software, Inc.
Architect Foss Software Inc
Ukraine Ukraine
No Biography provided

| Advertise | Privacy | Mobile
Web03 | 2.8.140922.1 | Last Updated 14 Jan 2004
Article Copyright 2002 by Sergiy Lavrynenko of Foss Software, Inc.
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid