mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2255 lines
74 KiB
2255 lines
74 KiB
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Propio.c
|
|
//
|
|
// MS Office Properties IO
|
|
//
|
|
// Notes:
|
|
// Because the Document Summary and User-defined objects both store
|
|
// their data in one stream (different sections though), one of these
|
|
// needs to also be responsible for saving any other sections that
|
|
// we don't understand at this time. The rule used here is that
|
|
// if the Document Summary object exists, it will store the
|
|
// unknown data, otherwise the User-defined object will.
|
|
//
|
|
// Change history:
|
|
//
|
|
// Date Who What
|
|
// --------------------------------------------------------------------------
|
|
// 07/26/94 B. Wentz Created file
|
|
// 07/08/96 MikeHill Add all properties to the UDProp list
|
|
// (not just props that are UDTYPEs).
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "priv.h"
|
|
#pragma hdrstop
|
|
|
|
#include <stdio.h> // for sprintf
|
|
#include <shlwapi.h>
|
|
|
|
#ifdef DEBUG
|
|
#define typSI 0
|
|
#define typDSI 1
|
|
#define typUD 2
|
|
typedef struct _xopro
|
|
{
|
|
int typ;
|
|
union{
|
|
LPSIOBJ lpSIObj;
|
|
LPDSIOBJ lpDSIObj;
|
|
LPUDOBJ lpUDObj;
|
|
};
|
|
} XOPRO;
|
|
// Plex of xopros
|
|
DEFPL (PLXOPRO, XOPRO, ixoproMax, ixoproMac, rgxopro);
|
|
#endif
|
|
|
|
// The constant indicating that the object uses Intel byte-ordering.
|
|
#define wIntelByteOrder 0xFFFE
|
|
|
|
#ifndef CP_WINUNICODE
|
|
#define CP_WINUNICODE 1200
|
|
#endif
|
|
|
|
// The name of the Document Summary Information stream.
|
|
|
|
const GUID FMTID_SummaryInformation = {0xf29f85e0L,0x4ff9,0x1068,0xab,0x91,0x08,0x00,0x2b,0x27,0xb3,0xd9};
|
|
const GUID FMTID_DocumentSummaryInformation = {0xd5cdd502L,0x2e9c,0x101b,0x93,0x97,0x08,0x00,0x2b,0x2c,0xf9,0xae};
|
|
const GUID FMTID_UserDefinedProperties = {0xd5cdd505L,0x2e9c,0x101b,0x93,0x97,0x08,0x00,0x2b,0x2c,0xf9,0xae};
|
|
|
|
// Internal prototypes
|
|
static DWORD PASCAL DwLoadDocAndUser (LPDSIOBJ lpDSIObj, LPUDOBJ lpUDObj, LPSTORAGE lpStg, DWORD dwFlags, BOOL fIntOnly);
|
|
static DWORD PASCAL DwSaveDocAndUser (LPDSIOBJ lpDSIObj, LPUDOBJ lpUDObj, LPSTORAGE lpStg, DWORD dwFlags);
|
|
static DWORD PASCAL DwLoadPropSetRange (LPPROPERTYSETSTORAGE lpPropertySetStorage, REFFMTID pfmtid, UINT FAR * lpuCodePage, PROPID propidFirst, PROPID propidLast, PROPVARIANT rgpropvar[], DWORD grfStgMode);
|
|
static DWORD PASCAL DwSavePropSetRange (LPPROPERTYSETSTORAGE lpPropertySetStorage, UINT uCodePage, REFFMTID pfmtid, PROPID propidFirst, PROPID propidLast, PROPVARIANT rgpropvarOriginal[], PROPID propidSkip, DWORD grfStgMode);
|
|
static BOOL PASCAL FReadDocParts(LPSTREAM lpStm, LPDSIOBJ lpDSIObj);
|
|
static BOOL PASCAL FReadAndInsertDocParts(LPSTREAM lpStm, LPDSIOBJ lpDSIObj);
|
|
static BOOL PASCAL FReadHeadingPairs(LPSTREAM lpStm, LPDSIOBJ lpDSIObj);
|
|
static BOOL PASCAL FReadAndInsertHeadingPairs(LPSTREAM lpStm, LPDSIOBJ lpDSIObj);
|
|
static BOOL PASCAL FLoadUserDef(LPUDOBJ lpUDObj, LPPROPERTYSETSTORAGE lpPropertySetStorage, UINT *puCodePage, BOOL fIntOnly, DWORD grfStgMode);
|
|
static BOOL PASCAL FSaveUserDef(LPUDOBJ lpUDObj, LPPROPERTYSETSTORAGE lpPropertySetStorage, UINT uCodePage, DWORD grfStgMode );
|
|
|
|
|
|
BOOL OFC_CALLBACK FCPConvert( LPTSTR lpsz, DWORD dwFrom, DWORD dwTo, BOOL fMacintosh )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL OFC_CALLBACK FSzToNum(double *lpdbl, LPTSTR lpsz)
|
|
{
|
|
LPTSTR lpDec;
|
|
LPTSTR lpTmp;
|
|
double mult;
|
|
|
|
//
|
|
// First, find decimal point
|
|
//
|
|
|
|
for (lpDec = lpsz; *lpDec && *lpDec!=TEXT('.'); lpDec++)
|
|
{
|
|
;
|
|
}
|
|
|
|
*lpdbl = 0.0;
|
|
mult = 1.0;
|
|
|
|
//
|
|
// Do integer part
|
|
//
|
|
|
|
for (lpTmp = lpDec - 1; lpTmp >= lpsz; lpTmp--)
|
|
{
|
|
//
|
|
// check for negative sign
|
|
//
|
|
|
|
if (*lpTmp == TEXT('-'))
|
|
{
|
|
//
|
|
// '-' sign should only be at beginning of string
|
|
//
|
|
|
|
if (lpTmp == lpsz)
|
|
{
|
|
if (*lpdbl > 0.0)
|
|
{
|
|
*lpdbl *= -1.0;
|
|
}
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
*lpdbl = 0.0;
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// check for positive sign
|
|
//
|
|
|
|
if (*lpTmp == TEXT('+'))
|
|
{
|
|
//
|
|
// '+' sign should only be at beginning of string
|
|
//
|
|
|
|
if (lpTmp == lpsz)
|
|
{
|
|
if (*lpdbl < 0.0)
|
|
{
|
|
*lpdbl *= -1.0;
|
|
}
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
*lpdbl = 0.0;
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
if ( (*lpTmp < TEXT('0')) || (*lpTmp > TEXT('9')) )
|
|
{
|
|
*lpdbl = 0.0;
|
|
return FALSE;
|
|
}
|
|
|
|
*lpdbl += (mult * (double)(*lpTmp - TEXT('0')));
|
|
mult *= 10.0;
|
|
}
|
|
|
|
//
|
|
// Do decimal part
|
|
//
|
|
|
|
mult = 0.1;
|
|
if (*lpDec)
|
|
{
|
|
for (lpTmp = lpDec + 1; *lpTmp; lpTmp++)
|
|
{
|
|
if ((*lpTmp < TEXT('0')) || (*lpTmp > TEXT('9')))
|
|
{
|
|
*lpdbl = 0.0;
|
|
return FALSE;
|
|
}
|
|
|
|
*lpdbl += (mult * (double)(*lpTmp - TEXT('0')));
|
|
mult *= 0.1;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL OFC_CALLBACK FNumToSz(double *lpdbl, LPTSTR lpsz, DWORD cbMax)
|
|
{
|
|
#ifdef UNICODE
|
|
swprintf(lpsz, TEXT("%g"), *(double *) lpdbl);
|
|
#else
|
|
sprintf(lpsz, TEXT("%g"), *(double *) lpdbl);
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL OFC_CALLBACK FUpdateStats(HWND hwndParent, LPSIOBJ lpSIObj, LPDSIOBJ lpDSIObj)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
const void *rglpfnProp[] = {
|
|
(void *) FCPConvert,
|
|
(void *) FSzToNum,
|
|
(void *) FNumToSz,
|
|
(void *) FUpdateStats
|
|
};
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// FOfficeCreateAndInitObjects
|
|
//
|
|
// Purpose:
|
|
// Creates and initializes all non-null args.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
DLLFUNC BOOL OFC_CALLTYPE FOfficeCreateAndInitObjects(LPSIOBJ *lplpSIObj, LPDSIOBJ *lplpDSIObj, LPUDOBJ *lplpUDObj)
|
|
{
|
|
#ifdef _ABBREVIATED_DOCPROP_
|
|
if (!FUserDefCreate (lplpUDObj, rglpfnProp))
|
|
#else //_ABBREVIATED_DOCPROP_
|
|
if (!FSumInfoCreate (lplpSIObj, rglpfnProp) ||
|
|
!FDocSumCreate (lplpDSIObj, rglpfnProp) ||
|
|
!FUserDefCreate (lplpUDObj, rglpfnProp))
|
|
#endif //_ABBREVIATED_DOCPROP_
|
|
|
|
{
|
|
FOfficeDestroyObjects(lplpSIObj, lplpDSIObj, lplpUDObj);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
} // FOfficeCreateAndInitObjects
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// FOfficeClearObjects
|
|
//
|
|
// Purpose:
|
|
// Clear any non-null objects
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
DLLFUNC BOOL OFC_CALLTYPE FOfficeClearObjects (
|
|
LPSIOBJ lpSIObj,
|
|
LPDSIOBJ lpDSIObj,
|
|
LPUDOBJ lpUDObj)
|
|
{
|
|
#ifndef _ABBREVIATED_DOCPROP_
|
|
FSumInfoClear (lpSIObj);
|
|
FDocSumClear (lpDSIObj);
|
|
#endif //_ABBREVIATED_DOCPROP_
|
|
FUserDefClear (lpUDObj);
|
|
|
|
return TRUE;
|
|
|
|
} // FOfficeClearObjects
|
|
|
|
#ifdef DEBUG
|
|
int CmpXOpro(XOPRO *pxopro1, XOPRO *pxopro2)
|
|
{
|
|
if(pxopro1->typ==pxopro2->typ)
|
|
{
|
|
switch(pxopro1->typ)
|
|
{
|
|
case typSI:
|
|
if(pxopro1->lpSIObj==pxopro2->lpSIObj)
|
|
return(sgnEQ);
|
|
break;
|
|
case typDSI:
|
|
if(pxopro1->lpDSIObj==pxopro2->lpDSIObj)
|
|
return(sgnEQ);
|
|
break;
|
|
case typUD:
|
|
if(pxopro1->lpUDObj==pxopro2->lpUDObj)
|
|
return(sgnEQ);
|
|
break;
|
|
default:
|
|
Assert(fFalse);
|
|
break;
|
|
}
|
|
}
|
|
return(sgnNE);
|
|
}
|
|
#endif
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// FOfficeDestroyObjects
|
|
//
|
|
// Purpose:
|
|
// Destroy any non-null objects
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
DLLFUNC BOOL OFC_CALLTYPE FOfficeDestroyObjects (
|
|
LPSIOBJ *lplpSIObj,
|
|
LPDSIOBJ *lplpDSIObj,
|
|
LPUDOBJ *lplpUDObj)
|
|
{
|
|
#ifndef _ABBREVIATED_DOCPROP_
|
|
FSumInfoDestroy (lplpSIObj); // We don't care what these guys return
|
|
FDocSumDestroy (lplpDSIObj);
|
|
#endif //_ABBREVIATED_DOCPROP_
|
|
FUserDefDestroy (lplpUDObj);
|
|
return TRUE;
|
|
|
|
} // FOfficeDestroyObjects
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// DwOfficeLoadProperties
|
|
//
|
|
// Purpose:
|
|
// Populate the objects with data. lpStg is the root stream.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
UINT gdwFileCP = CP_ACP;
|
|
|
|
DLLFUNC DWORD OFC_CALLTYPE DwOfficeLoadProperties (
|
|
LPSTORAGE lpStg, // Pointer to root storage
|
|
LPSIOBJ lpSIObj, // Pointer to Summary Obj
|
|
LPDSIOBJ lpDSIObj, // Pointer to Document Summary obj
|
|
LPUDOBJ lpUDObj, // Pointer to User-defined Obj
|
|
DWORD dwFlags, // Flags
|
|
DWORD grfStgMode) // STGM flags with which to open the property set
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
BOOL fSuccess = FALSE;
|
|
|
|
LPPROPERTYSETSTORAGE lpPropertySetStorage = NULL;
|
|
|
|
// Validate the inputs.
|
|
|
|
if (lpStg == NULL)
|
|
goto Exit;
|
|
|
|
|
|
// Get the IPropertySetStorage from the IStorage.
|
|
|
|
hr = lpStg->lpVtbl->QueryInterface( lpStg,
|
|
&IID_IPropertySetStorage,
|
|
&lpPropertySetStorage );
|
|
if (FAILED (hr))
|
|
{
|
|
AssertSz (0, TEXT("Couldn't query for IPropertySetStorage"));
|
|
goto Exit;
|
|
}
|
|
|
|
#ifndef _ABBREVIATED_DOCPROP_
|
|
if (lpSIObj != NULL)
|
|
{
|
|
// Make sure we start with an empty object.
|
|
|
|
FSumInfoClear (lpSIObj); // This will set the save flag
|
|
OfficeDirtySIObj(lpSIObj, FALSE); // so clear it. Bug 1068
|
|
|
|
// Load the properties into an array of PropVariants
|
|
|
|
if (MSO_IO_SUCCESS != DwLoadPropSetRange (lpPropertySetStorage,
|
|
&FMTID_SummaryInformation,
|
|
&gdwFileCP,
|
|
PID_SIFIRST,
|
|
PID_SILAST,
|
|
GETSINFO(lpSIObj)->rgpropvar,
|
|
grfStgMode))
|
|
{
|
|
AssertSz (0, TEXT("Could not load SummaryInformation property set"));
|
|
goto Exit;
|
|
}
|
|
|
|
if (GETSINFO(lpSIObj)->rgpropvar[ PVSI_THUMBNAIL ].vt == VT_CF)
|
|
{
|
|
GETSINFO(lpSIObj)->fSaveSINail = TRUE;
|
|
#ifdef SHELL
|
|
// We don't need the thumbnail, we just want to know if it exists.
|
|
// So, we can free the memory.
|
|
|
|
PropVariantClear( &GETSINFO(lpSIObj)->rgpropvar[ PVSI_THUMBNAIL ] );
|
|
#endif
|
|
}
|
|
|
|
OfficeDirtySIObj (lpSIObj, FALSE);
|
|
}
|
|
#endif //_ABBREVIATED_DOCPROP_
|
|
|
|
|
|
#ifndef _ABBREVIATED_DOCPROP_
|
|
if (lpDSIObj != NULL)
|
|
{
|
|
// Make sure we start with an empty object.
|
|
|
|
FDocSumClear (lpDSIObj);
|
|
OfficeDirtyDSIObj(lpDSIObj, FALSE);
|
|
|
|
if (MSO_IO_SUCCESS != DwLoadPropSetRange (lpPropertySetStorage,
|
|
&FMTID_DocumentSummaryInformation,
|
|
&gdwFileCP,
|
|
PID_DSIFIRST,
|
|
PID_DSILAST,
|
|
GETDSINFO(lpDSIObj)->rgpropvar,
|
|
grfStgMode))
|
|
{
|
|
AssertSz (0, TEXT("Could not load DocSumInfo"));
|
|
goto Exit;
|
|
}
|
|
|
|
OfficeDirtyDSIObj (lpDSIObj, FALSE);
|
|
}
|
|
#endif // _ABBREVIATED_DOCPROP_
|
|
|
|
if (lpUDObj != NULL)
|
|
{
|
|
// Make sure we start with an empty object.
|
|
|
|
FUserDefClear (lpUDObj);
|
|
OfficeDirtyUDObj(lpUDObj, FALSE);
|
|
|
|
// Load the properties into a linked-list.
|
|
|
|
if (!FLoadUserDef (lpUDObj,
|
|
lpPropertySetStorage,
|
|
&gdwFileCP,
|
|
FALSE, // Not integers only.
|
|
grfStgMode))
|
|
{
|
|
MESSAGE (TEXT("Could not load User-Defined properties"));
|
|
goto Exit;
|
|
}
|
|
|
|
OfficeDirtyUDObj (lpUDObj, FALSE);
|
|
}
|
|
|
|
// If none of the property sets had a code-page property, set it to
|
|
// the current system default.
|
|
|
|
if (gdwFileCP == CP_ACP)
|
|
gdwFileCP = GetACP();
|
|
|
|
fSuccess = TRUE;
|
|
|
|
Exit:
|
|
|
|
RELEASEINTERFACE( lpPropertySetStorage );
|
|
|
|
if (fSuccess)
|
|
{
|
|
return (MSO_IO_SUCCESS);
|
|
}
|
|
else
|
|
{
|
|
DebugHr (hr);
|
|
FOfficeClearObjects (lpSIObj, lpDSIObj, lpUDObj);
|
|
|
|
#ifndef _ABBREVIATED_DOCPROP_
|
|
OfficeDirtySIObj (lpSIObj, FALSE);
|
|
OfficeDirtyDSIObj (lpDSIObj, FALSE);
|
|
#endif //_ABBREVIATED_DOCPROP_
|
|
|
|
OfficeDirtyUDObj (lpUDObj, FALSE);
|
|
|
|
return (MSO_IO_ERROR);
|
|
}
|
|
|
|
} // DwOfficeLoadProperties
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// DwOfficeLoadIntProperties
|
|
//
|
|
// Purpose:
|
|
// Populate the objects with integer data. lpStg is the root stream.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#ifdef UNUSED
|
|
|
|
DLLFUNC DWORD OFC_CALLTYPE DwOfficeLoadIntProperties (
|
|
LPSTORAGE lpStg, // Pointer to root storage
|
|
LPSIOBJ lpSIObj, // Pointer to Summary Obj
|
|
LPDSIOBJ lpDSIObj, // Pointer to Document Summary obj
|
|
LPUDOBJ lpUDObj, // Pointer to User-defined Obj
|
|
DWORD dwFlags) // Flags
|
|
{
|
|
DWORD dwLoad1 = MSO_IO_ERROR;
|
|
DWORD dwLoad2 = MSO_IO_ERROR;
|
|
|
|
if (lpStg == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
#ifndef _ABBREVIATED_DOCPROP_
|
|
if (lpSIObj != NULL)
|
|
{
|
|
dwLoad1 = DwOfficeLoadSumInfo (lpSIObj, lpStg, dwFlags, TRUE);
|
|
if (dwLoad1 == MSO_IO_ERROR)
|
|
{
|
|
return(MSO_IO_ERROR);
|
|
}
|
|
}
|
|
#endif //_ABBREVIATED_DOCPROP_
|
|
|
|
#ifndef _ABBREVIATED_DOCPROP_
|
|
if (lpDSIObj != NULL && lpUDObj != NULL)
|
|
{
|
|
dwLoad2 = DwLoadDocAndUser (lpDSIObj, lpUDObj, lpStg, dwFlags, TRUE);
|
|
if (dwLoad2 == MSO_IO_ERROR)
|
|
{
|
|
if (lpSIObj != NULL)
|
|
{
|
|
FSumInfoClear(lpSIObj);
|
|
}
|
|
return(MSO_IO_ERROR);
|
|
}
|
|
}
|
|
#endif //_ABBREVIATED_DOCPROP_
|
|
|
|
return(max(dwLoad1, dwLoad2));
|
|
|
|
} // DwOfficeLoadIntProperties
|
|
|
|
#endif
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// DwOfficeSaveProperties
|
|
//
|
|
// Purpose:
|
|
// Write the data in the given objects. lpStg is the root stream.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DLLFUNC DWORD OFC_CALLTYPE DwOfficeSaveProperties (
|
|
LPSTORAGE lpStg, // Pointer to root storage
|
|
LPSIOBJ lpSIObj, // Pointer to Summary Obj
|
|
LPDSIOBJ lpDSIObj, // Pointer to Document Summary obj
|
|
LPUDOBJ lpUDObj, // Pointer to User-defined Obj
|
|
DWORD dwFlags, // Flags
|
|
DWORD grfStgMode) // STGM flags with which to open the property set
|
|
{
|
|
// ------
|
|
// Locals
|
|
// ------
|
|
|
|
HRESULT hr = E_FAIL;
|
|
BOOL fSuccess = FALSE;
|
|
LPPROPERTYSETSTORAGE lpPropertySetStorage = NULL;
|
|
|
|
// Validate the inputs.
|
|
|
|
if (lpStg == NULL)
|
|
{
|
|
AssertSz (0, TEXT("Invalid inputs to DwOfficeSaveProperties"));
|
|
goto Exit;
|
|
}
|
|
|
|
// Get the IPropertySetStorage from the IStorage.
|
|
|
|
hr = lpStg->lpVtbl->QueryInterface( lpStg,
|
|
&IID_IPropertySetStorage,
|
|
&lpPropertySetStorage );
|
|
if (FAILED (hr))
|
|
{
|
|
AssertSz (0, TEXT("Couldn't query for IPropertySetStorage"));
|
|
goto Exit;
|
|
}
|
|
|
|
#ifndef _ABBREVIATED_DOCPROP_
|
|
// ---------------------------------------
|
|
// Save the SummaryInformation properties.
|
|
// ---------------------------------------
|
|
|
|
if (lpSIObj != NULL)
|
|
{
|
|
// Only save if the user didn't specify the change only flag,
|
|
// or if they did, only save if we need to.
|
|
|
|
if ( !(dwFlags & OIO_SAVEIFCHANGEONLY) || FSumInfoShouldSave(lpSIObj) )
|
|
{
|
|
if (MSO_IO_SUCCESS != DwSavePropSetRange (
|
|
lpPropertySetStorage, // The property set
|
|
GetACP(), // The code page
|
|
&FMTID_SummaryInformation, // The format ID
|
|
PID_SIFIRST, // The first PID to use
|
|
PID_SILAST, // The last PID to use
|
|
GETSINFO(lpSIObj)->rgpropvar, // The properties
|
|
// Skip the thumbnail if not saving
|
|
GETSINFO(lpSIObj)->fSaveSINail ? 0 : PID_THUMBNAIL,
|
|
grfStgMode)) // STGM flags
|
|
{
|
|
AssertSz (0, TEXT("Could not save SummaryInformation property set"));
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
#endif //_ABBREVIATED_DOCPROP_
|
|
|
|
|
|
#ifndef _ABBREVIATED_DOCPROP_
|
|
// -----------------------------------------------
|
|
// Save the DocumentSummaryInformation properties.
|
|
// -----------------------------------------------
|
|
|
|
if (lpDSIObj != NULL)
|
|
{
|
|
if (((dwFlags & OIO_SAVEIFCHANGEONLY) && (FDocSumShouldSave (lpDSIObj))) ||
|
|
!(dwFlags & OIO_SAVEIFCHANGEONLY))
|
|
{
|
|
if (MSO_IO_SUCCESS != DwSavePropSetRange (lpPropertySetStorage, // The property set
|
|
GetACP(), // The code page
|
|
// The format ID
|
|
&FMTID_DocumentSummaryInformation,
|
|
PID_DSIFIRST, // The first PID to use
|
|
PID_DSILAST, // The last PID to use
|
|
// The properties
|
|
GETDSINFO(lpDSIObj)->rgpropvar,
|
|
0, // Don't skip any properties
|
|
grfStgMode)) // STGM flags
|
|
{
|
|
AssertSz (0, TEXT("Could not save DocSumInfo"));
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
#endif _ABBREVIATED_DOCPROP_
|
|
|
|
// ---------------------------------
|
|
// Save the User-Defined properties.
|
|
// ---------------------------------
|
|
|
|
if (lpUDObj != NULL)
|
|
{
|
|
if (((dwFlags & OIO_SAVEIFCHANGEONLY) && (FUserDefShouldSave (lpUDObj))) ||
|
|
!(dwFlags & OIO_SAVEIFCHANGEONLY))
|
|
{
|
|
if (!FSaveUserDef (lpUDObj,
|
|
lpPropertySetStorage,
|
|
GetACP(),
|
|
grfStgMode))
|
|
{
|
|
AssertSz (0, TEXT("Could not save UserDefined properties"));
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Exit
|
|
//
|
|
|
|
fSuccess = TRUE;
|
|
|
|
Exit:
|
|
|
|
RELEASEINTERFACE( lpPropertySetStorage );
|
|
|
|
if (fSuccess)
|
|
{
|
|
#ifndef _ABBREVIATED_DOCPROP_
|
|
OfficeDirtySIObj (lpSIObj, FALSE);
|
|
OfficeDirtyDSIObj (lpDSIObj, FALSE);
|
|
#endif //_ABBREVIATED_DOCPROP_
|
|
|
|
OfficeDirtyUDObj (lpUDObj, FALSE);
|
|
|
|
return (TRUE);
|
|
}
|
|
else
|
|
{
|
|
DebugHr (hr);
|
|
return (FALSE);
|
|
}
|
|
|
|
} // DwOfficeSaveProperties
|
|
|
|
|
|
///////////////////////////////////////////////////////
|
|
//
|
|
// DwLoadPropSetRange
|
|
//
|
|
// Purpose:
|
|
// Load a range of properties (specified by the first and
|
|
// last property ID) from a given PropertySetStorage. All
|
|
// strings are converted to the appropriate system format
|
|
// (LPTSTRs).
|
|
//
|
|
// Inputs:
|
|
// LPPROPERTYSETSTORAGE - The set of property storage objects.
|
|
// REFFMTID - The Format ID of the desired property set
|
|
// UINT * - A location to put the PID_CODEPAGE. This
|
|
// should be initialized by the caller to a
|
|
// valid default, in case the PID_CODEPAGE
|
|
// does not exist.
|
|
// PROPID - The first property in the range.
|
|
// PROPID - The last property in the range.
|
|
// PROPVARIANT[] - An array of PropVariants, large enough
|
|
// for the (pidLast-pidFirst+1) properties.
|
|
// DWORD - Flags from the STGM enumeration to use when
|
|
// opening the property storage.
|
|
//
|
|
// Output:
|
|
// An MSO error code.
|
|
//
|
|
// Note:
|
|
// When strings are converted to the system format, their
|
|
// VarTypes are converted too. E.g., if an ANSI VT_LPSTR is
|
|
// read from a property set, the string will be converted
|
|
// to Unicode, and the VarType will be changed to VT_LPWSTR.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static DWORD PASCAL DwLoadPropSetRange (
|
|
LPPROPERTYSETSTORAGE lpPropertySetStorage,
|
|
REFFMTID pfmtid,
|
|
UINT FAR * lpuCodePage,
|
|
PROPID propidFirst,
|
|
PROPID propidLast,
|
|
PROPVARIANT rgpropvar[],
|
|
DWORD grfStgMode)
|
|
{
|
|
// ------
|
|
// Locals
|
|
// ------
|
|
|
|
DWORD dwResult = MSO_IO_ERROR; // The return code.
|
|
HRESULT hr; // OLE errors
|
|
ULONG ulIndex; // Index into the rgpropvar
|
|
// The requested IPropertyStorage
|
|
LPPROPERTYSTORAGE lpPropertyStorage;
|
|
PROPSPEC FAR * rgpropspec; // The PropSpecs for the ReadMultiple
|
|
PROPVARIANT propvarCodePage; // A PropVariant with which to read the PID_CODEPAGE
|
|
|
|
// The total number of properties to read.
|
|
ULONG cProps = propidLast - propidFirst + 1;
|
|
|
|
// ----------
|
|
// Initialize
|
|
// ----------
|
|
|
|
Assert (lpPropertySetStorage != NULL);
|
|
Assert (lpPropertySetStorage->lpVtbl != NULL);
|
|
Assert (propidLast >= propidFirst);
|
|
|
|
lpPropertyStorage = NULL;
|
|
PropVariantInit( &propvarCodePage );
|
|
|
|
// Initialize the PropVariants, so that if we
|
|
// early-exit, we'll return VT_EMPTY for all the properties.
|
|
|
|
for (ulIndex = 0; ulIndex < cProps; ulIndex++)
|
|
PropVariantInit (&rgpropvar[ulIndex]);
|
|
|
|
// Allocate an array of PropSpecs.
|
|
|
|
rgpropspec = PvMemAlloc ( cProps * sizeof (*rgpropspec));
|
|
if (rgpropspec == NULL)
|
|
{
|
|
AssertSz (0, TEXT("Couldn't alloc rgpropspec"));
|
|
goto Exit;
|
|
}
|
|
|
|
// ----------------------
|
|
// Open the property set.
|
|
// ----------------------
|
|
|
|
hr = lpPropertySetStorage->lpVtbl->Open(
|
|
lpPropertySetStorage, // this pointer
|
|
pfmtid, // Identifies propset
|
|
grfStgMode, // STGM_ flags
|
|
&lpPropertyStorage ); // Result
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
// We couldn't open the property set.
|
|
if( hr == STG_E_FILENOTFOUND )
|
|
{
|
|
// No problem, it just didn't exist.
|
|
dwResult = MSO_IO_SUCCESS;
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
AssertSz (0, TEXT("Couldn't open property set"));
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// -------------------
|
|
// Read the properties
|
|
// -------------------
|
|
|
|
// Initialize the local PropSpec array in preparation for a ReadMultiple.
|
|
// The PROPIDs range from propidFirst to propidLast.
|
|
|
|
for (ulIndex = 0; ulIndex < cProps; ulIndex++)
|
|
{
|
|
rgpropspec[ ulIndex ].ulKind = PRSPEC_PROPID;
|
|
rgpropspec[ ulIndex ].propid = ulIndex + propidFirst;
|
|
}
|
|
|
|
|
|
// Read in the properties
|
|
|
|
hr = lpPropertyStorage->lpVtbl->ReadMultiple (
|
|
lpPropertyStorage, // 'this' pointer
|
|
cProps, // count
|
|
rgpropspec, // Props to read
|
|
rgpropvar); // Buffers for props
|
|
|
|
// Did we fail to read anything?
|
|
|
|
if (hr != S_OK)
|
|
{
|
|
// If S_FALSE, no problem; none of the properties existed.
|
|
if (hr == S_FALSE)
|
|
{
|
|
dwResult = MSO_IO_SUCCESS;
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
// Otherwise, we have a problem.
|
|
AssertSz (0, TEXT("Couldn't read from property set"));
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// -----------------
|
|
// Get the Code-Page
|
|
// -----------------
|
|
|
|
rgpropspec[0].ulKind = PRSPEC_PROPID;
|
|
rgpropspec[0].propid = PID_CODEPAGE;
|
|
|
|
hr = lpPropertyStorage->lpVtbl->ReadMultiple (
|
|
lpPropertyStorage, // 'this' pointer
|
|
1, // count
|
|
rgpropspec, // Props to read
|
|
&propvarCodePage); // Buffer for prop
|
|
|
|
// We only set the code page if we actually read it.
|
|
|
|
if (hr == S_OK
|
|
&&
|
|
propvarCodePage.vt == VT_I2)
|
|
{
|
|
*lpuCodePage = propvarCodePage.iVal;
|
|
}
|
|
//*lpuCodePage = GetACP() ;
|
|
|
|
|
|
// ---------------------------
|
|
// Correct the string formats.
|
|
// ---------------------------
|
|
|
|
// E.g., if this is a Unicode system, convert LPSTRs to LPWSTRs.
|
|
|
|
for (ulIndex = 0; ulIndex < cProps; ulIndex++)
|
|
{
|
|
// Is this is vector of Variants?
|
|
|
|
if (rgpropvar[ ulIndex ].vt == (VT_VARIANT | VT_VECTOR))
|
|
{
|
|
// Loop through each element of the vector, converting
|
|
// any elements which are strings.
|
|
|
|
ULONG ulVectorIndex;
|
|
|
|
for (ulVectorIndex = 0;
|
|
ulVectorIndex < rgpropvar[ ulIndex ].capropvar.cElems;
|
|
ulVectorIndex++)
|
|
{
|
|
if (PROPVAR_STRING_CONVERSION_REQUIRED (
|
|
&rgpropvar[ulIndex].capropvar.pElems[ulVectorIndex],
|
|
*lpuCodePage
|
|
))
|
|
{
|
|
// Convert the PropVariant string, putting it in a new
|
|
// PropVariant.
|
|
|
|
PROPVARIANT propvarConvert;
|
|
PropVariantInit (&propvarConvert);
|
|
|
|
if (!FPropVarConvertString (&propvarConvert,
|
|
&rgpropvar[ulIndex].capropvar.pElems[ulVectorIndex],
|
|
*lpuCodePage ))
|
|
{
|
|
AssertSz (0, TEXT("Couldn't convert string"));
|
|
goto Exit;
|
|
}
|
|
|
|
// Clear the old PropVar, and copy in the new one.
|
|
|
|
PropVariantClear (&rgpropvar[ulIndex].capropvar.pElems[ulVectorIndex]);
|
|
rgpropvar[ulIndex].capropvar.pElems[ulVectorIndex] = propvarConvert;
|
|
}
|
|
} // for (ulVectorIndex = 0; ...
|
|
} // if ((rgpropvar[ ulIndex ].vt == (VT_VARIANT | VT_VECTOR))
|
|
|
|
// This isn't a Variant Vector, but is it a string
|
|
// of some kind which requires conversion?
|
|
|
|
else if (PROPVAR_STRING_CONVERSION_REQUIRED (
|
|
&rgpropvar[ ulIndex ],
|
|
*lpuCodePage))
|
|
{
|
|
// Convert the PropVariant string into a new PropVariant
|
|
// buffer. The string may be a singleton, or a vector.
|
|
|
|
PROPVARIANT propvarConvert;
|
|
PropVariantInit (&propvarConvert);
|
|
|
|
if (!FPropVarConvertString (&propvarConvert,
|
|
&rgpropvar[ ulIndex ],
|
|
*lpuCodePage ))
|
|
{
|
|
AssertSz (0, TEXT("Couldn't convert string"));
|
|
goto Exit;
|
|
}
|
|
|
|
// Free the old PropVar and load the new one.
|
|
|
|
PropVariantClear (&rgpropvar[ ulIndex ]);
|
|
rgpropvar[ ulIndex ] = propvarConvert;
|
|
|
|
} // else if (PROPVAR_STRING_CONVERSION_REQUIRED ( ...
|
|
} // for (ulIndex = 0; ulIndex < cProps; ulIndex++)
|
|
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
dwResult = MSO_IO_SUCCESS;
|
|
|
|
Exit:
|
|
|
|
// Release the code-page just in case somebody put the wrong type
|
|
// there (like a blob).
|
|
|
|
PropVariantClear (&propvarCodePage);
|
|
|
|
// Release the PropSpecs and the IPropertyStorage
|
|
|
|
if (rgpropspec != NULL)
|
|
{
|
|
VFreeMemP (rgpropspec, cProps * sizeof (*rgpropspec));
|
|
}
|
|
|
|
RELEASEINTERFACE (lpPropertyStorage);
|
|
|
|
// If we failed, free the PropVariants.
|
|
|
|
if (dwResult != MSO_IO_SUCCESS)
|
|
{
|
|
FreePropVariantArray( cProps, rgpropvar );
|
|
DebugHr( hr );
|
|
}
|
|
|
|
return (dwResult);
|
|
|
|
|
|
} // DwLoadPropSetRange
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Wrap of IPropertySetStorage::Create
|
|
//
|
|
// Each new ANSI property set created by docprop must set PID_CODEPAGE to CP_UTF8
|
|
// to avoid ansi<->unicode roundtripping issues.
|
|
//
|
|
HRESULT _CreatePropertyStorage(
|
|
LPPROPERTYSETSTORAGE psetstg,
|
|
REFFMTID rfmtid,
|
|
CLSID* pclsid,
|
|
DWORD grfMode,
|
|
UINT* /*IN OUT*/ puCodePage,
|
|
IPropertyStorage** ppstg )
|
|
{
|
|
|
|
|
|
DWORD grfFlags = (CP_WINUNICODE == (*puCodePage)) ?
|
|
PROPSETFLAG_DEFAULT : PROPSETFLAG_ANSI;
|
|
|
|
HRESULT hr = psetstg->lpVtbl->Create( psetstg, rfmtid, pclsid, grfFlags, grfMode, ppstg );
|
|
if( SUCCEEDED( hr ) )
|
|
{
|
|
if( PROPSETFLAG_ANSI == grfFlags )
|
|
{
|
|
PROPSPEC propspec = { PRSPEC_PROPID, PID_CODEPAGE };
|
|
PROPVARIANT varCP;
|
|
varCP.vt = VT_I2;
|
|
varCP.iVal = (SHORT)CP_UTF8;
|
|
if( SUCCEEDED( (*ppstg)->lpVtbl->WriteMultiple( *ppstg, 1, &propspec, &varCP, PID_UDFIRST ) ) )
|
|
*puCodePage = (UINT)MAKELONG(varCP.iVal, 0);
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////
|
|
//
|
|
// DwSavePropSetRange
|
|
//
|
|
// Purpose:
|
|
// Save a range of properties to a Property Set Storage.
|
|
// The properties to be saved are provided in an
|
|
// array of PropVariants, and their property IDs are
|
|
// specified by the first and last PID for the range.
|
|
// The caller may also specify that a property be
|
|
// "skipped", i.e., not written.
|
|
//
|
|
// Inputs:
|
|
// LPPROPERTYSETSTORAGE - The Property Set Storage
|
|
// UINT - The code page with which the strings
|
|
// should be written.
|
|
// REFFMTID - The GUID identifying the Property Storage
|
|
// within the Property Set Storage.
|
|
// PROPID - The PID to assign to the first property.
|
|
// PROPID - The PID to assign to the last property
|
|
// PROPVARIANT [] - The propeties to write. All strings
|
|
// are assumed to be in the system format
|
|
// (e.g. VT_LPWSTRs for NT). This array
|
|
// is returned un-modified to the caller.
|
|
// PROPID - If non-zero, identifies a property
|
|
// which should not be written, even if
|
|
// it is non-empty. If the property exists
|
|
// in the property set, it will be deleted.
|
|
// (This was added to provide a way to skip
|
|
// the PID_THUMBNAIL.)
|
|
// DWORD - Flags from the STGM enumeration to use when
|
|
// opening the property storage.
|
|
//
|
|
// Output:
|
|
// An MSO error code.
|
|
//
|
|
// Notes:
|
|
// - If the code page is Unicode, all strings are written as LPWSTRs,
|
|
// otherwise, they are written as LPSTRs.
|
|
// - Only non-empty properties are written.
|
|
//
|
|
// Implementation:
|
|
// This routine creates a new PropVariant array which is the
|
|
// subset of the caller's PropVariant array which must actually
|
|
// be written (i.e, it doesn't include the VT_EMPTY properties
|
|
// or the 'propidSkip').
|
|
//
|
|
// We allocate as little extra memory as possible. For example,
|
|
// if we have to write a string, we'll copy the pointer to the
|
|
// string into the subset PropVariant array. Thus we'll have
|
|
// two pointers to the string.
|
|
//
|
|
// If a string to be written must be converted first (to another
|
|
// code-page), then the original PropVariant array will continue
|
|
// pointing to the original string, and the subset PropVariant
|
|
// array will point to the converted string (and must consequently
|
|
// be freed).
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static DWORD PASCAL DwSavePropSetRange (
|
|
LPPROPERTYSETSTORAGE lpPropertySetStorage,
|
|
UINT uCodePage,
|
|
REFFMTID pfmtid,
|
|
PROPID propidFirst,
|
|
PROPID propidLast,
|
|
PROPVARIANT rgpropvarOriginal[],
|
|
PROPID propidSkip,
|
|
DWORD grfStgMode)
|
|
{
|
|
// ------
|
|
// Locals
|
|
// ------
|
|
|
|
DWORD dwResult = MSO_IO_ERROR; // The functions return code.
|
|
HRESULT hr; // OLE results.
|
|
// The Property Storage to write to
|
|
LPPROPERTYSTORAGE lpPropertyStorage = NULL;
|
|
|
|
ULONG cOriginal; // The size of rgpropvarOriginal,
|
|
ULONG cNew; // and the number which must actually be written.
|
|
ULONG ulIndex; // Index into rgpropvarOriginal
|
|
|
|
PROPSPEC FAR * rgpropspecNew = NULL;// PropSpecs for the WriteMultiple
|
|
LPPROPVARIANT rgpropvarNew = NULL; // The sub-set of rgpropvarOrigianl we must write.
|
|
|
|
// The following array has an entry for each entry in rgpropvarNew.
|
|
// Each entry identifies the corresponding entry in rgpropvarOriginal.
|
|
// E.g. rgMapNewToOriginal[0] is the index in rgpropvarOriginal of
|
|
// the first property to be written.
|
|
|
|
ULONG *rgMapNewToOriginal = NULL;
|
|
|
|
// ----------
|
|
// Initialize
|
|
// ----------
|
|
|
|
cOriginal = propidLast - propidFirst + 1;
|
|
cNew = 0;
|
|
|
|
Assert (cOriginal <= max(NUM_SI_PROPERTIES, NUM_DSI_PROPERTIES));
|
|
|
|
Assert (lpPropertySetStorage != NULL);
|
|
Assert (lpPropertySetStorage->lpVtbl != NULL);
|
|
Assert (propidLast >= propidFirst);
|
|
Assert (rgpropvarOriginal != NULL);
|
|
|
|
// Allocate an array of PropSpecs for the WriteMultiple.
|
|
|
|
rgpropspecNew = PvMemAlloc ( cOriginal * sizeof (*rgpropspecNew));
|
|
if (rgpropspecNew == NULL)
|
|
{
|
|
AssertSz (0, TEXT("Couldn't alloc rgpropspecNew"));
|
|
goto Exit;
|
|
}
|
|
|
|
// Allocate an array of PropVariants which will hold the subset
|
|
// of the caller's properties which must be written.
|
|
// Initialize to zeros so that we don't think we have memory
|
|
// to free in the error path.
|
|
|
|
rgpropvarNew = PvMemAlloc ( cOriginal * sizeof (*rgpropvarNew));
|
|
if (rgpropvarNew == NULL)
|
|
{
|
|
AssertSz (0, TEXT("Couldn't alloc rgpropvarNew"));
|
|
goto Exit;
|
|
}
|
|
FillBuf (rgpropvarNew, 0, cOriginal * sizeof (*rgpropvarNew));
|
|
|
|
// Allocate the look-up-table which maps entries in rgpropvarNew
|
|
// to rgpropvarOriginal
|
|
|
|
rgMapNewToOriginal = PvMemAlloc (cOriginal * sizeof(*rgMapNewToOriginal));
|
|
if (rgMapNewToOriginal == NULL)
|
|
{
|
|
AssertSz (0, TEXT("Couldn't alloc rgMapNewToOriginal"));
|
|
goto Exit;
|
|
}
|
|
|
|
// -------------------------
|
|
// Open the Property Storage
|
|
// -------------------------
|
|
|
|
hr = lpPropertySetStorage->lpVtbl->Open(
|
|
lpPropertySetStorage, // this pointer
|
|
pfmtid,
|
|
grfStgMode,
|
|
&lpPropertyStorage );
|
|
|
|
|
|
// If it didn't exist, create it.
|
|
|
|
if( hr == STG_E_FILENOTFOUND )
|
|
{
|
|
hr = _CreatePropertyStorage( lpPropertySetStorage,
|
|
pfmtid,
|
|
NULL,
|
|
STGM_DIRECT | STGM_SHARE_EXCLUSIVE | STGM_READWRITE,
|
|
&uCodePage,
|
|
&lpPropertyStorage );
|
|
}
|
|
|
|
// Check the result of the open/create.
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
AssertSz (0, TEXT("Couldn't open property set"));
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------
|
|
// Copy the properties to be written into rgpropvarNew
|
|
// ---------------------------------------------------
|
|
|
|
// Loop through all the properties in rgpropvarOriginal
|
|
|
|
for (ulIndex = 0; ulIndex < cOriginal; ulIndex++)
|
|
{
|
|
// Is this property extant and not the one to skip?
|
|
|
|
if (rgpropvarOriginal[ ulIndex ].vt != VT_EMPTY
|
|
&&
|
|
( propidSkip == 0
|
|
||
|
|
propidSkip != propidFirst + ulIndex )
|
|
)
|
|
{
|
|
// We have a property which must be written.
|
|
|
|
BOOL fVector;
|
|
VARTYPE vt;
|
|
|
|
// Record a mapping from the new index to the original.
|
|
|
|
rgMapNewToOriginal[ cNew ] = ulIndex;
|
|
|
|
// Add an entry to the PropSpec array.
|
|
|
|
rgpropspecNew[ cNew ].ulKind = PRSPEC_PROPID;
|
|
rgpropspecNew[ cNew ].propid = propidFirst + ulIndex;
|
|
|
|
// Get the underlying VarType.
|
|
|
|
fVector = (rgpropvarOriginal[ ulIndex ].vt & VT_VECTOR) ? TRUE : FALSE;
|
|
vt = rgpropvarOriginal[ ulIndex ].vt & ~VT_VECTOR;
|
|
|
|
// If this property is a vector of variants, some of those
|
|
// elements may be strings which need to be converted.
|
|
|
|
if ((vt == VT_VARIANT) && fVector)
|
|
{
|
|
ULONG ulVectorIndex;
|
|
|
|
// We'll inintialize the capropvar.pElems in rgpropvarNew
|
|
// so that it points to the one in rgpropvarOriginal. We'll
|
|
// only allocate if a conversion is necessary. I.e., we handle
|
|
// pElems as a copy-on-write.
|
|
|
|
rgpropvarNew[ cNew ] = rgpropvarOriginal[ ulIndex ];
|
|
|
|
// Loop through the elements of the vector.
|
|
|
|
for (ulVectorIndex = 0;
|
|
ulVectorIndex < rgpropvarNew[ cNew ].capropvar.cElems;
|
|
ulVectorIndex++)
|
|
{
|
|
// Is this a string requiring a code-page conversion?
|
|
|
|
if (PROPVAR_STRING_CONVERSION_REQUIRED(
|
|
&rgpropvarOriginal[ulIndex].capropvar.pElems[ulVectorIndex],
|
|
uCodePage ))
|
|
{
|
|
// We must convert this string. Have we allocated a pElems yet?
|
|
|
|
if (rgpropvarNew[cNew].capropvar.pElems
|
|
== rgpropvarOriginal[ulIndex].capropvar.pElems)
|
|
{
|
|
// Allocate a new pElems for rgpropvarNew
|
|
|
|
rgpropvarNew[cNew].capropvar.pElems
|
|
= CoTaskMemAlloc (rgpropvarNew[cNew].capropvar.cElems
|
|
* sizeof(*rgpropvarNew[cNew].capropvar.pElems));
|
|
if (rgpropvarNew[cNew].capropvar.pElems == NULL)
|
|
{
|
|
AssertSz (0, TEXT("Couldn't allocate pElems"));
|
|
goto Exit;
|
|
}
|
|
|
|
// Initialize it to match that in rgpropvarOriginal
|
|
|
|
PbMemCopy (rgpropvarNew[cNew].capropvar.pElems,
|
|
rgpropvarOriginal[ulIndex].capropvar.pElems,
|
|
rgpropvarNew[cNew].capropvar.cElems
|
|
* sizeof(*rgpropvarNew[cNew].capropvar.pElems));
|
|
}
|
|
|
|
// Now, we can convert this string from rgpropvarOriginal into
|
|
// rgpropvarNew.
|
|
|
|
PropVariantInit (&rgpropvarNew[cNew].capropvar.pElems[ulVectorIndex]);
|
|
if (!FPropVarConvertString(&rgpropvarNew[cNew].capropvar.pElems[ulVectorIndex],
|
|
&rgpropvarOriginal[ulIndex].capropvar.pElems[ulVectorIndex],
|
|
uCodePage))
|
|
{
|
|
AssertSz(0, TEXT("Couldn't convert code page of string"));
|
|
goto Exit;
|
|
}
|
|
|
|
} // if (PROPVAR_STRING_CONVERSION_REQUIRED( ...
|
|
} // for (ulVectorIndex = 0; ...
|
|
} // if (vt == VT_VARIANT && fVector)
|
|
|
|
// This isn't a variant vector, but is it some type of string
|
|
// property for which we must make a conversion?
|
|
|
|
else if (PROPVAR_STRING_CONVERSION_REQUIRED (
|
|
&rgpropvarOriginal[ ulIndex ],
|
|
uCodePage))
|
|
{
|
|
PropVariantInit (&rgpropvarNew[cNew]);
|
|
if (!FPropVarConvertString (&rgpropvarNew[cNew],
|
|
&rgpropvarOriginal[ulIndex],
|
|
uCodePage))
|
|
{
|
|
AssertSz (0, TEXT("Couldn't convert string"));
|
|
goto Exit;
|
|
}
|
|
|
|
} // else if (PROPVAR_STRING_CONVERSION_REQUIRED ( ...
|
|
|
|
// If neither of the above special-cases were triggered,
|
|
// then simply copy the PropVariant structure (but not
|
|
// any referred-to data). We save memory by not duplicating
|
|
// the referred-to data, but we must be careful in the exit
|
|
// not to free it.
|
|
|
|
else
|
|
{
|
|
rgpropvarNew[cNew] = rgpropvarOriginal[ulIndex];
|
|
|
|
} // if ((vt == VT_VARIANT) && fVector) ... else
|
|
|
|
|
|
// We're done copying/converting this property from rgpropvarOriginal
|
|
// into rgpropvarNew.
|
|
|
|
cNew++;
|
|
|
|
} // if (rgpropvarOriginal[ ulIndex ].vt != VT_EMPTY ...
|
|
} // for (ulIndex = 0; ulIndex < cProps; ulIndex++)
|
|
|
|
|
|
// ------------------------
|
|
// Write out the properties
|
|
// ------------------------
|
|
|
|
|
|
// Write out properties if we found any.
|
|
|
|
if (cNew > 0)
|
|
{
|
|
hr = lpPropertyStorage->lpVtbl->WriteMultiple (
|
|
lpPropertyStorage, // 'this' pointer
|
|
cNew, // Count
|
|
rgpropspecNew, // Props to write
|
|
rgpropvarNew, // The props
|
|
PID_UDFIRST);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
AssertSz (0, TEXT("Couldn't write properties"));
|
|
goto Exit;
|
|
}
|
|
} // if (cNew > 0)
|
|
|
|
// ---------------------
|
|
// Delete the propidSkip
|
|
// ---------------------
|
|
|
|
// If the caller specified a PID to skip, then it should
|
|
// be deleted from the property set as well.
|
|
|
|
if (propidSkip != 0)
|
|
{
|
|
rgpropspecNew[0].ulKind = PRSPEC_PROPID;
|
|
rgpropspecNew[0].propid = propidSkip;
|
|
|
|
hr = lpPropertyStorage->lpVtbl->DeleteMultiple (
|
|
lpPropertyStorage, // this pointer
|
|
1, // Delete one property
|
|
rgpropspecNew ); // The prop to delete
|
|
if (FAILED(hr))
|
|
{
|
|
AssertSz (0, TEXT("Couldn't delete the propidSkip"));
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
dwResult = MSO_IO_SUCCESS;
|
|
|
|
Exit:
|
|
|
|
// Clear any of the properties in rgpropvarNew for which new
|
|
// buffers were allocated. Then free the rgpropvarNew array itself.
|
|
// We know that buffers were allocated for rgpropvarNew if it's contents
|
|
// don't match rgpropvarOriginal.
|
|
|
|
if (rgpropvarNew != NULL)
|
|
{
|
|
// Loop through rgpropvarNew
|
|
|
|
for (ulIndex = 0; ulIndex < cNew; ulIndex++)
|
|
{
|
|
// Was memory allocated for this rgpropvarNew?
|
|
|
|
if (memcmp (&rgpropvarNew[ ulIndex ],
|
|
&rgpropvarOriginal[ rgMapNewToOriginal[ulIndex] ],
|
|
sizeof(rgpropvarNew[ ulIndex ])))
|
|
{
|
|
// Is this a variant vector?
|
|
|
|
if (rgpropvarNew[ulIndex].vt == (VT_VECTOR | VT_VARIANT))
|
|
{
|
|
ULONG ulVectIndex;
|
|
|
|
// Loop through the variant vector and free any PropVariants
|
|
// that were allocated. We follow the same principle, if the
|
|
// entry in rgpropvarNew doesn't match the entry in
|
|
// rgpropvarOriginal, we must have allocated new memory.
|
|
|
|
for (ulVectIndex = 0;
|
|
ulVectIndex < rgpropvarNew[ulIndex].capropvar.cElems;
|
|
ulVectIndex++)
|
|
{
|
|
if (memcmp(&rgpropvarNew[ ulIndex ].capropvar.pElems[ ulVectIndex ],
|
|
&rgpropvarOriginal[ rgMapNewToOriginal[ulIndex] ].capropvar.pElems[ ulVectIndex ],
|
|
sizeof(rgpropvarNew[ ulIndex ].capropvar.pElems[ ulVectIndex ])))
|
|
{
|
|
PropVariantClear (&rgpropvarNew[ulIndex].capropvar.pElems[ulVectIndex]);
|
|
}
|
|
}
|
|
|
|
// Unconditionally free the pElems buffer.
|
|
|
|
CoTaskMemFree (rgpropvarNew[ulIndex].capropvar.pElems);
|
|
|
|
} // if (rgpropvarNew[ulIndex].vt == (VT_VECTOR | VT_VARIANT))
|
|
|
|
// This isn't a variant vector
|
|
|
|
else
|
|
{
|
|
// But does the rgpropvarNew have private memory (i.e.
|
|
// a converted string buffer)?
|
|
|
|
if (memcmp (&rgpropvarNew[ ulIndex ],
|
|
&rgpropvarOriginal[ rgMapNewToOriginal[ulIndex] ],
|
|
sizeof(rgpropvarNew[ ulIndex ])))
|
|
{
|
|
PropVariantClear (&rgpropvarNew[ulIndex]);
|
|
}
|
|
} // if (rgpropvarNew[ulIndex].vt == (VT_VECTOR | VT_VARIANT)) ... else
|
|
} // if (rgpropvarNew[ulIndex] ...
|
|
} // for (ulIndex = 0; ulIndex < cNew; ulIndex++)
|
|
|
|
// Free the rgpropvarNew array itself.
|
|
|
|
VFreeMemP (rgpropvarNew, cOriginal * sizeof (*rgpropvarNew));
|
|
|
|
} // if (rgpropvarNew != NULL)
|
|
|
|
// Free the remaining arrays and release the Property Storage interface.
|
|
|
|
if (rgpropspecNew != NULL)
|
|
{
|
|
VFreeMemP (rgpropspecNew, cOriginal * sizeof (*rgpropspecNew));
|
|
}
|
|
|
|
if (rgMapNewToOriginal != NULL)
|
|
{
|
|
VFreeMemP (rgMapNewToOriginal, cOriginal * sizeof(*rgMapNewToOriginal));
|
|
}
|
|
|
|
RELEASEINTERFACE (lpPropertyStorage);
|
|
|
|
|
|
// And we're done.
|
|
|
|
return (dwResult);
|
|
|
|
} // DwSavePropSetRange
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// FLoadUserDef
|
|
//
|
|
// Purpose:
|
|
// Load the User-Defined properties (those in the second section of
|
|
// the DocumentSummaryInformation property set). There can be any number
|
|
// of these properties, and the user specifies they're name, value, and
|
|
// type (from a limited subset of the VarTypes). Since this is
|
|
// variable-sized, the properties are loaded into a linked-list.
|
|
//
|
|
// Inputs:
|
|
// LPUDOBJ - All User-Defined data (including the properties).
|
|
// Its m_lpData must point to a valid UDINFO structure.
|
|
// LPPROPERTYSETSTORAGE - The Property Set Storage in which we'll find the
|
|
// UD property storage.
|
|
// UINT* - The PID_CODEPAGE, if it exists. Left unmodified
|
|
// if it doesn't exist. All string properties will
|
|
// converted to this format. This must be intialized
|
|
// by the caller to a valid default.
|
|
// BOOL - Only load integer values.
|
|
// DWORD - Flags from the STGM enumeration to use when opening
|
|
// the property storage.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static BOOL PASCAL FLoadUserDef (
|
|
LPUDOBJ lpUDObj,
|
|
LPPROPERTYSETSTORAGE lpPropertySetStorage,
|
|
UINT *puCodePage,
|
|
BOOL fIntOnly, // Load Int Properties only?
|
|
DWORD grfStgMode)
|
|
{
|
|
|
|
// ------
|
|
// Locals
|
|
// ------
|
|
|
|
BOOL fSuccess = FALSE; // Return code to the caller.
|
|
HRESULT hr; // Error codes for OLE calls.
|
|
|
|
LPPROPERTYSTORAGE lpPropertyStorage = NULL; // The UD property storage
|
|
LPENUMSTATPROPSTG lpEnum = NULL; // Enumerates the UD property storage
|
|
STATPROPSETSTG statpropsetstg; // Holds the ClassID from the property storage
|
|
|
|
// Used in ReadMultiple call.
|
|
PROPSPEC rgpropspec[ DEFAULT_IPROPERTY_COUNT ];
|
|
// A subset of the UD properties
|
|
PROPVARIANT rgpropvar[ DEFAULT_IPROPERTY_COUNT ];
|
|
// Stats on a subset of the UD properties
|
|
STATPROPSTG rgstatpropstg[ DEFAULT_IPROPERTY_COUNT ];
|
|
ULONG ulIndex; // Index into the above arrays.
|
|
|
|
PROPSPEC propspec; // PropSpec for reading the code-page
|
|
LPUDPROP lpudprop = NULL; // A single UD property (points to the PropVariant)
|
|
ULONG cEnumerated = 0; // Number of properties found in an enumeration
|
|
|
|
|
|
// --------------
|
|
// Initialization
|
|
// --------------
|
|
|
|
Assert (!fIntOnly); // No longer used.
|
|
Assert (lpUDObj != NULL && GETUDINFO(lpUDObj) != NULL);
|
|
Assert (puCodePage != NULL);
|
|
|
|
// We need to zero-out the PropVariant and StatPropStg
|
|
// arrays so that we don't think they need to be freed
|
|
// in the Exit block.
|
|
|
|
FillBuf (rgpropvar, 0, sizeof (rgpropvar));
|
|
FillBuf (rgstatpropstg, 0, sizeof (rgstatpropstg));
|
|
|
|
|
|
// -----------------------------------------
|
|
// Get the PropertyStorage and an Enumerator
|
|
// -----------------------------------------
|
|
|
|
// Open the IPropertyStorage and check for errors.
|
|
|
|
hr = lpPropertySetStorage->lpVtbl->Open(
|
|
lpPropertySetStorage, // this pointer
|
|
&FMTID_UserDefinedProperties,
|
|
grfStgMode,
|
|
&lpPropertyStorage );
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
// We couldn't open the property set.
|
|
if( hr == STG_E_FILENOTFOUND )
|
|
{
|
|
// No problem, it just didn't exist.
|
|
fSuccess = TRUE;
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
AssertSz (0, TEXT("Couldn't open property set"));
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Save the property storage's class ID (identifying the application
|
|
// which is primarily responsible for it). We do this because
|
|
// we may later delete the existing property set.
|
|
|
|
hr = lpPropertyStorage->lpVtbl->Stat (lpPropertyStorage, &statpropsetstg);
|
|
if (FAILED(hr))
|
|
{
|
|
AssertSz (0, TEXT("Couldn't Stat the Property Storage"));
|
|
goto Exit;
|
|
}
|
|
|
|
GETUDINFO(lpUDObj)->clsid = statpropsetstg.clsid;
|
|
|
|
|
|
// Get the IEnum interface and check for errors.
|
|
|
|
hr = lpPropertyStorage->lpVtbl->Enum(
|
|
lpPropertyStorage,
|
|
&lpEnum );
|
|
if (FAILED(hr))
|
|
{
|
|
AssertSz (0, TEXT("Couldn't enumerate the PropertyStorage"));
|
|
goto Exit;
|
|
}
|
|
|
|
// ------------------
|
|
// Read the Code Page
|
|
// ------------------
|
|
|
|
propspec.ulKind = PRSPEC_PROPID;
|
|
propspec.propid = PID_CODEPAGE;
|
|
|
|
hr = lpPropertyStorage->lpVtbl->ReadMultiple (lpPropertyStorage, 1, &propspec, &rgpropvar[0]);
|
|
if (FAILED(hr))
|
|
{
|
|
AssertSz (0, TEXT("Couldn't get property set"));
|
|
}
|
|
|
|
// If this is a valid PID_CODEPAGE, give it to the caller.
|
|
|
|
if (hr == S_OK && rgpropvar[0].vt == VT_I2)
|
|
{
|
|
*puCodePage = (UINT)MAKELONG(rgpropvar[0].iVal, 0);
|
|
}
|
|
PropVariantClear (&rgpropvar[0]);
|
|
|
|
|
|
// -------------------------------------------------------------
|
|
// Loop through the properties and add to the UDPROPS structure.
|
|
// -------------------------------------------------------------
|
|
|
|
// This loop executes once for each enumeration. Each enumeration
|
|
// gets multiple STATPROPSTGs, so within this loop an inner loop
|
|
// will process each property. This two-level looping mechanism is
|
|
// used in order to reduce the number of ReadMultiples.
|
|
|
|
// Use the IEnum to load the first set of STATPROPSTGs.
|
|
|
|
hr = lpEnum->lpVtbl->Next (lpEnum, DEFAULT_IPROPERTY_COUNT, rgstatpropstg, &cEnumerated);
|
|
if (FAILED(hr))
|
|
{
|
|
AssertSz (0, TEXT("Couldn't get next StatPropStg"));
|
|
goto Exit;
|
|
}
|
|
Assert (cEnumerated <= DEFAULT_IPROPERTY_COUNT);
|
|
|
|
// If the last IEnum returned properties, process them here.
|
|
// At the end of this while loop, we re-call the IEnum, thus continuing
|
|
// until no properties are left to be enumerated.
|
|
|
|
while (cEnumerated)
|
|
{
|
|
// ------------------------------
|
|
// Read this batch of properties.
|
|
// ------------------------------
|
|
|
|
for (ulIndex = 0; ulIndex < cEnumerated; ulIndex++)
|
|
{
|
|
rgpropspec[ ulIndex ].ulKind = PRSPEC_PROPID;
|
|
rgpropspec[ ulIndex ].propid = rgstatpropstg[ ulIndex ].propid;
|
|
|
|
}
|
|
|
|
|
|
// Read the properties.
|
|
|
|
hr = lpPropertyStorage->lpVtbl->ReadMultiple(
|
|
lpPropertyStorage,
|
|
cEnumerated,
|
|
rgpropspec,
|
|
rgpropvar );
|
|
if (FAILED(hr))
|
|
{
|
|
AssertSz (0, TEXT("Couldn't read from property set"));
|
|
goto Exit;
|
|
}
|
|
|
|
// ------------------------------------------------------
|
|
// Loop through the properties, adding them to the UDOBJ.
|
|
// ------------------------------------------------------
|
|
|
|
for (ulIndex = 0; ulIndex < cEnumerated; ulIndex++)
|
|
{
|
|
// Convert string PropVariants to the right code page.
|
|
// We won't worry about Variants which are strings, because
|
|
// this is not a legal type for the UD properties.
|
|
|
|
if (PROPVAR_STRING_CONVERSION_REQUIRED (
|
|
&rgpropvar[ ulIndex ],
|
|
*puCodePage))
|
|
{
|
|
// Convert the string in the PropVariant, putting the
|
|
// result in a temporary PropVariant.
|
|
|
|
PROPVARIANT propvarConvert;
|
|
PropVariantInit (&propvarConvert);
|
|
|
|
if (!FPropVarConvertString (&propvarConvert,
|
|
&rgpropvar[ulIndex],
|
|
*puCodePage))
|
|
{
|
|
AssertSz (0, TEXT("Couldn't convert string"));
|
|
goto Exit;
|
|
}
|
|
|
|
// Free the old PropVariant, and load in the converted
|
|
// one.
|
|
|
|
PropVariantClear (&rgpropvar[ ulIndex ]);
|
|
rgpropvar[ ulIndex ] = propvarConvert;
|
|
}
|
|
|
|
#ifndef UNICODE
|
|
// Convert the property name to the right code page.
|
|
|
|
if (rgstatpropstg[ ulIndex ].lpwstrName != NULL)
|
|
{
|
|
LPSTR lpsz;
|
|
|
|
if (!FCoWStrToStr (&lpsz, rgstatpropstg[ ulIndex ].lpwstrName,
|
|
*puCodePage))
|
|
{
|
|
goto Exit;
|
|
}
|
|
CoTaskMemFree (rgstatpropstg[ ulIndex ].lpwstrName);
|
|
|
|
// [scotthan] HACK ALERT: watch this wacky cast;
|
|
// it'll bite you if you don't reciprocate when reading
|
|
// the string back out! (see userdef.c, FAddPropToList() impl).
|
|
rgstatpropstg[ ulIndex ].lpwstrName =(LPWSTR)lpsz;
|
|
}
|
|
#endif
|
|
|
|
// Allocate a new UDPROP structure, which will be added to the
|
|
// linked-list.
|
|
|
|
lpudprop = LpudpropCreate();
|
|
if (lpudprop == NULL)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Add this UDPROP to the linked-list. On success, this will assume
|
|
// responsibility for the PropVariant and STATPROPSTG buffers, and
|
|
// will NULL out our pointers accordingly.
|
|
|
|
if (!FAddPropToList (lpUDObj,
|
|
&rgpropvar[ ulIndex ],
|
|
&rgstatpropstg[ ulIndex ],
|
|
lpudprop))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
lpudprop = NULL;
|
|
|
|
} // for (ulIndex = 0; ulIndex < cEnumerated; ulIndex++)
|
|
|
|
|
|
// ---------------------
|
|
// Get a new enumeration
|
|
// ---------------------
|
|
|
|
// We've processed all the properties in the last enumeration, let's get
|
|
// a new set (if there are any). If there are no more, cEnumerated, will be
|
|
// zero, and we'll break out of the outer while loop.
|
|
|
|
FreePropVariantArray( cEnumerated, rgpropvar );
|
|
|
|
hr = lpEnum->lpVtbl->Next (lpEnum, DEFAULT_IPROPERTY_COUNT, rgstatpropstg, &cEnumerated);
|
|
if (FAILED(hr))
|
|
{
|
|
AssertSz (0, TEXT("Couldn't get next StatPropStg"));
|
|
goto Exit;
|
|
}
|
|
|
|
} // while (cEnumerated)
|
|
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
fSuccess = TRUE;
|
|
|
|
Exit:
|
|
|
|
// Free any properties with buffers. This will only happen
|
|
// if there was an error.
|
|
|
|
if (cEnumerated > 0)
|
|
{
|
|
FreePropVariantArray (cEnumerated, rgpropvar);
|
|
}
|
|
|
|
// Again if there was an error, we must free the UDPROP object.
|
|
|
|
if (lpudprop)
|
|
{
|
|
VUdpropFree (&lpudprop);
|
|
}
|
|
|
|
// Free any name buffers we still have from the enumerations.
|
|
// Once again, this is only necessary if there was an error.
|
|
|
|
for (ulIndex = 0; ulIndex < cEnumerated; ulIndex++)
|
|
{
|
|
if (rgstatpropstg[ ulIndex ].lpwstrName != NULL)
|
|
{
|
|
CoTaskMemFree (rgstatpropstg[ ulIndex ].lpwstrName);
|
|
}
|
|
}
|
|
|
|
// Release the Property Storage and Enumeration interfaces.
|
|
|
|
RELEASEINTERFACE (lpEnum);
|
|
RELEASEINTERFACE (lpPropertyStorage);
|
|
|
|
|
|
return fSuccess;
|
|
|
|
} // FLoadUserDef
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// FSaveUserDef
|
|
//
|
|
// Purpose:
|
|
// Save the User-Defined properties to the second section of
|
|
// the DocumentSummaryInformation property set.
|
|
//
|
|
// Inputs:
|
|
// LPUDOBJ - All UD data (including the properties)
|
|
// It's m_lpData must point to a valid UDINFO structure.
|
|
// LPPROPERTYSETSTORAGE - The Property Set Storage
|
|
// UINT - The code page in which strings should be
|
|
// written. If Unicode, all strings are
|
|
// written as LPWSTRs, otherwise all strings
|
|
// are written as LPSTRs.
|
|
// DWORD - Flags from the STGM enumeration to use when
|
|
// opening the property storage.
|
|
//
|
|
// Outputs:
|
|
// TRUE if successful.
|
|
//
|
|
// Pre-conditions:
|
|
// The properties to be written are all from the UDTYPES
|
|
// enumeration.
|
|
//
|
|
// Implementation:
|
|
// Properties which are links to application data require special
|
|
// handling. First, the property value is written (along with its
|
|
// name). Then, the application-defined link name is
|
|
// written (e.g. the Bookmark name in Word). The link name
|
|
// is written using the same PID as was the link value, except that
|
|
// the PID_LINKMASK is ORed in. The link name property has no name
|
|
// in the property set dictionary.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static
|
|
BOOL PASCAL FSaveUserDef (
|
|
LPUDOBJ lpUDObj,
|
|
LPPROPERTYSETSTORAGE lpPropertySetStorage,
|
|
UINT uCodePage,
|
|
DWORD grfStgMode)
|
|
{
|
|
// ------
|
|
// Locals
|
|
// ------
|
|
|
|
BOOL fSuccess = FALSE; // What to return to the caller.
|
|
HRESULT hr; // OLE result codes.
|
|
|
|
BOOL fLink, fLinkInvalid;
|
|
|
|
// The UD Property Storage
|
|
LPPROPERTYSTORAGE lpPropertyStorage = NULL;
|
|
LPUDITER lpudi = NULL; // Iterates the linked-list of UDPROPs
|
|
LPPROPVARIANT lppropvar = NULL; // A property from the linked-list
|
|
ULONG ulIndex; // Generic index into arrays
|
|
PROPID propid; // The PID to assign to the next property
|
|
|
|
// Arrays to be used in the WriteMultiple. The array of BOOLs
|
|
// indicate which elements of the PropVariant array must be freed.
|
|
|
|
ULONG ulPropIndex = 0;
|
|
PROPSPEC rgpropspec[ DEFAULT_IPROPERTY_COUNT ];
|
|
PROPVARIANT rgpropvar[ DEFAULT_IPROPERTY_COUNT ];
|
|
BOOL rgfFreePropVar[ DEFAULT_IPROPERTY_COUNT ];
|
|
|
|
// Arrays to be used in the WritePropertyNames.
|
|
|
|
ULONG ulNameIndex = 0;
|
|
PROPID rgpropidName[ DEFAULT_IPROPERTY_COUNT ];
|
|
LPWSTR rglpwstrName[ DEFAULT_IPROPERTY_COUNT ];
|
|
|
|
// ----------
|
|
// Initialize
|
|
// ----------
|
|
|
|
Assert (lpUDObj != NULL && GETUDINFO(lpUDObj) != NULL);
|
|
Assert (lpPropertySetStorage != NULL && lpPropertySetStorage->lpVtbl != NULL);
|
|
|
|
// Initialize the necessary arrays, so that we don't unnecessarily
|
|
// free something in the Error path.
|
|
|
|
FillBuf (rgpropvar, 0, sizeof(rgpropvar));
|
|
FillBuf (rgfFreePropVar, 0, sizeof(rgfFreePropVar));
|
|
FillBuf (rglpwstrName, 0, sizeof(rglpwstrName));
|
|
|
|
// Delete the existing property set and create a new empty one.
|
|
// We must do this because we don't know which of the
|
|
// existing properties need to be deleted, we only know what
|
|
// the current set of properties should be.
|
|
|
|
hr = lpPropertySetStorage->lpVtbl->Delete(
|
|
lpPropertySetStorage,
|
|
&FMTID_UserDefinedProperties );
|
|
if (FAILED(hr))
|
|
{
|
|
if (hr != STG_E_FILENOTFOUND)
|
|
{
|
|
AssertSz (0, TEXT("Couldn't remove old properties"));
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
hr = _CreatePropertyStorage( lpPropertySetStorage,
|
|
&FMTID_UserDefinedProperties,
|
|
&GETUDINFO(lpUDObj)->clsid,
|
|
grfStgMode,
|
|
&uCodePage,
|
|
&lpPropertyStorage );
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
AssertSz (0, TEXT("Couldn't open User-Defined property set"));
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
// Create an iterator which we use to enumerate the properties
|
|
// (UDPROPs) in the linked-list.
|
|
|
|
lpudi = LpudiUserDefCreateIterator (lpUDObj);
|
|
|
|
// ------------------------------------------------------------------
|
|
// Loop through the properties and write them to the UD property set.
|
|
// ------------------------------------------------------------------
|
|
|
|
// We use a two-layer loop. The inner loop batches a group of properties
|
|
// in a PropVariant array, and then writes them to the Property Storage.
|
|
// The outer loop repeats this process until there are no more properties.
|
|
// This two-layer mechanism is desirable so that we reduce the number
|
|
// of WriteMultiple calls.
|
|
|
|
propid = PID_UDFIRST;
|
|
fLink = FALSE;
|
|
|
|
while (TRUE)
|
|
{
|
|
|
|
// ------------------------------------------
|
|
// Batch up a set of properties to be written
|
|
// ------------------------------------------
|
|
|
|
ulPropIndex = ulNameIndex = 0;
|
|
|
|
// We will break out of this loop when we have no more properties
|
|
// or if we have enough for a WriteMultiple.
|
|
|
|
while (FUserDefIteratorValid (lpudi))
|
|
{
|
|
Assert (lpudi->lpudp != NULL);
|
|
|
|
// ----------------------------------------------------------------------
|
|
// Create entries in the arrays for WriteMultiple and WritePropertyNames.
|
|
// ----------------------------------------------------------------------
|
|
|
|
// If fLink is TRUE, it means that we've written out the
|
|
// property, and now we need to write out the link name
|
|
// (with the PID_LINKMASK ORed into the propid).
|
|
|
|
if (!fLink)
|
|
{
|
|
// We aren't writing a link. So let's get the
|
|
// property from the linked-list (we know it exists because
|
|
// FUserDefIteratorValid was true).
|
|
|
|
lppropvar
|
|
= LppropvarUserDefGetIteratorVal (lpudi, NULL, NULL);
|
|
if (lppropvar == NULL)
|
|
{
|
|
AssertSz (0, TEXT("Invalid PropVariant in iterator"));
|
|
goto Exit;
|
|
}
|
|
|
|
// Copy this propvariant into the array which will be used for
|
|
// the WriteMultiple. Note that we do not copy any referenced
|
|
// buffer (e.g. we don't copy the string buffer if this is a string).
|
|
|
|
rgpropvar[ ulPropIndex ] = *lppropvar;
|
|
|
|
// If this property has a name, prepare to write it.
|
|
|
|
if (lpudi->lpudp->lpstzName != NULL)
|
|
{
|
|
// Add this name to rglpwstrName & rgpropidName.
|
|
|
|
#ifndef UNICODE
|
|
{
|
|
// Convert the ANSI name to Unicode (all OLE calls require
|
|
// Unicode strings).
|
|
|
|
if (!FCoStrToWStr (&rglpwstrName[ ulNameIndex ],
|
|
lpudi->lpudp->lpstzName,
|
|
uCodePage ))
|
|
{
|
|
AssertSz (0, TEXT("Couldn't convert name to Unicode"));
|
|
goto Exit;
|
|
}
|
|
}
|
|
#else
|
|
// Add this name to the list of those to be written.
|
|
|
|
rglpwstrName[ ulNameIndex ] = lpudi->lpudp->lpstzName;
|
|
|
|
#endif // UNICODE
|
|
|
|
// Add this propid to the list of those with names.
|
|
|
|
rgpropidName[ ulNameIndex ] = propid;
|
|
|
|
} // if (lpudi->lpudp->lpstzName != NULL)
|
|
} // if (!fLink)
|
|
|
|
else
|
|
{
|
|
// We are processing a link name. I.e., we've written the
|
|
// property value, now we need to write the name of the link,
|
|
// as a property, with the PID_LINKSMASK bit set in the PID.
|
|
|
|
Assert (lpudi->lpudp->lpstzLink != NULL);
|
|
|
|
// Create a entry in the PropVariant.
|
|
|
|
rgpropvar[ ulPropIndex ].vt = VT_LPTSTR;
|
|
(LPTSTR) rgpropvar[ ulPropIndex ].pszVal = lpudi->lpudp->lpstzLink;
|
|
}
|
|
|
|
// rgpropvar[ulPropIndex] now holds the property to be written,
|
|
// whether it is a real property or a link name.
|
|
|
|
// ------------------------------------
|
|
// Convert strings to the proper format.
|
|
// -------------------------------------
|
|
|
|
// (This could also convert the type from LPWSTR to LPSTR, or vice-versa).
|
|
|
|
// We don't have to worry about strings in vectors or in
|
|
// variant vectors, because these are illegal types for this
|
|
// property set.
|
|
|
|
if (rgpropvar[ ulPropIndex ].vt == VT_LPTSTR)
|
|
{
|
|
// If this string needs to be converted do so, putting the converted
|
|
// string in a new buffer. So,
|
|
// the caller's PropVariant still points to the old buffer,
|
|
// and our rgpropvar points to the new buffer.
|
|
|
|
if (PROPVAR_STRING_CONVERSION_REQUIRED (
|
|
&rgpropvar[ ulPropIndex ],
|
|
uCodePage))
|
|
{
|
|
// Convert the string into a temporary PropVariant.
|
|
|
|
PROPVARIANT propvarConvert;
|
|
PropVariantInit (&propvarConvert);
|
|
|
|
if (!FPropVarConvertString (&propvarConvert,
|
|
&rgpropvar[ ulPropIndex ],
|
|
uCodePage ))
|
|
{
|
|
AssertSz (0, TEXT("Couldn't convert string"));
|
|
goto Exit;
|
|
}
|
|
|
|
// Load this new PropVariant into rgpropvar, but don't
|
|
// delete the old buffer (so that we leave the linked-list
|
|
// of UDPROPs intact).
|
|
|
|
rgpropvar[ ulPropIndex ] = propvarConvert;
|
|
|
|
// Since we just created a new buffer, we must remember to free it.
|
|
rgfFreePropVar[ ulPropIndex ] = TRUE;
|
|
|
|
} // if (PROPVAR_STRING_CONVERSION_REQUIRED ( ...
|
|
} // if (rgpropvar[ ulPropIndex ].vt == VT_LPTSTR)
|
|
|
|
|
|
// --------------------------
|
|
// Finish this loop iteration
|
|
// --------------------------
|
|
|
|
// Set up the PropSpec.
|
|
|
|
rgpropspec[ ulPropIndex ].ulKind = PRSPEC_PROPID;
|
|
rgpropspec[ ulPropIndex ].propid = propid;
|
|
|
|
// If this is a link name, set the bit in the PID.
|
|
|
|
if (fLink)
|
|
{
|
|
rgpropspec[ ulPropIndex ].propid |= PID_LINKMASK;
|
|
}
|
|
|
|
// Advance the property index. And if we set a name, advance
|
|
// the name index.
|
|
|
|
ulPropIndex++;
|
|
if (rglpwstrName[ ulNameIndex ] != NULL)
|
|
{
|
|
ulNameIndex++;
|
|
}
|
|
|
|
// If we've just processed a link, or this is a property
|
|
// which is not linked to application content, then move on to the next property
|
|
// in the iterator. If we've just processed a property value that
|
|
// is linked, set fLink so that on the next pass through
|
|
// this loop, we'll write out the link name.
|
|
|
|
if (fLink || !FUserDefIteratorIsLink (lpudi))
|
|
{
|
|
fLink = FALSE;
|
|
propid++;
|
|
FUserDefIteratorNext (lpudi);
|
|
}
|
|
else
|
|
{
|
|
fLink = TRUE;
|
|
}
|
|
|
|
// If there's no more room in the WriteMultiple arrays,
|
|
// then write out the properties. We'll return to this
|
|
// inner loop when that's complete.
|
|
|
|
if (ulPropIndex >= DEFAULT_IPROPERTY_COUNT)
|
|
{
|
|
break;
|
|
}
|
|
} // while (FUserDefIteratorValid (lpudi))
|
|
|
|
// If broke out of the previous loop becuase there were no
|
|
// more properties, then we can break out of the outer loop
|
|
// as well -- we're done.
|
|
|
|
if (ulPropIndex == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// ---------------------
|
|
// Write the properties.
|
|
// ---------------------
|
|
|
|
hr = lpPropertyStorage->lpVtbl->WriteMultiple (
|
|
lpPropertyStorage, // 'this' pointer
|
|
ulPropIndex, // Number of properties
|
|
rgpropspec, // Property specifiers
|
|
rgpropvar, // The properties
|
|
PID_UDFIRST); // Not used.
|
|
if (FAILED(hr))
|
|
{
|
|
AssertSz (0, TEXT("Couldn't write properties"));
|
|
goto Exit;
|
|
}
|
|
|
|
// If we created any new buffers during string conversion,
|
|
// free them now.
|
|
|
|
for (ulIndex = 0; ulIndex < ulPropIndex; ulIndex++)
|
|
{
|
|
if (rgfFreePropVar[ ulIndex ])
|
|
{
|
|
PropVariantClear (&rgpropvar[ ulIndex ]);
|
|
rgfFreePropVar[ ulIndex ] = FALSE;
|
|
}
|
|
}
|
|
|
|
// ----------------
|
|
// Write the Names.
|
|
// ----------------
|
|
|
|
if (ulNameIndex != 0)
|
|
{
|
|
|
|
hr = lpPropertyStorage->lpVtbl->WritePropertyNames (
|
|
lpPropertyStorage, // 'this' pointer
|
|
ulNameIndex, // Number of names
|
|
rgpropidName, // PIDs for these names
|
|
rglpwstrName ); // The names
|
|
if (FAILED(hr))
|
|
{
|
|
AssertSz (0, TEXT("Couldn't write property names"));
|
|
goto Exit;
|
|
}
|
|
} // if (ulNameIndex != 0)
|
|
|
|
// Clear the names array.
|
|
|
|
for (ulIndex = 0; ulIndex < ulNameIndex; ulIndex++)
|
|
{
|
|
|
|
#ifndef UNICODE
|
|
// Free the memory which was allocated for this name.
|
|
|
|
CoTaskMemFree (rglpwstrName[ ulIndex ]);
|
|
#endif
|
|
|
|
rglpwstrName[ ulIndex ] = NULL;
|
|
|
|
} // for (ulIndex = 0; ulIndex < ulNameIndex; ulIndex++)
|
|
} // while (TRUE)
|
|
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
fSuccess = TRUE;
|
|
|
|
Exit:
|
|
|
|
// Free the iterator
|
|
|
|
if (lpudi)
|
|
{
|
|
FUserDefDestroyIterator (&lpudi);
|
|
}
|
|
|
|
// Free any memory that was allocated for PropVariants.
|
|
|
|
for (ulIndex = 0; ulIndex < ulPropIndex; ulIndex++)
|
|
{
|
|
if (rgfFreePropVar[ ulIndex ])
|
|
{
|
|
PropVariantClear (&rgpropvar[ ulIndex ]);
|
|
}
|
|
}
|
|
|
|
|
|
#ifndef UNICODE
|
|
// Free any memory that was allocated for name.
|
|
|
|
for (ulIndex = 0; ulIndex < ulNameIndex; ulIndex++)
|
|
{
|
|
CoTaskMemFree (rglpwstrName[ ulIndex ]);
|
|
}
|
|
#endif
|
|
|
|
// Release the UD Property Storage.
|
|
|
|
RELEASEINTERFACE (lpPropertyStorage);
|
|
|
|
return (fSuccess);
|
|
|
|
} // FSaveUserDef
|
|
|