Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

3222 lines
94 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
//
////////////////////////////////////////////////////////////////////////////////
#include "priv.h"
#pragma hdrstop
#ifndef WINNT
#ifndef INC_OLE2
#define INC_OLE2
#endif
#include "office.h"
#define INITGUID
#include <initguid.h>
#include <shlguid.h>
#undef INITGUID
// REVIEW: Fix the INITGUID stuff to not use pre-compiled headers.....
#include "offcapi.h"
#include "proptype.h"
#include "internal.h"
#include "stmio.h"
#include "propmisc.h"
#include "debug.h"
#else
#define INITGUID
#include <initguid.h>
#endif //!WINNT
#define lpUDData ((LPUDINFO) lpUDObj->m_lpData)
#define lpDSIData ((LPDSINFO) ((LPDOCSUMINFO) lpDSIObj)->m_lpData)
#define lpSIData ((LPSINFO) lpSIObj->m_lpData)
#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
// Constants to indicate which Operating System is used.
#define wWin16 0x0000 /* 16-bit Windows */
#define wMacintosh 0x0001 /* Macintosh OS */
#define wWin32 0x0002 /* 32-bit Windows */
#define FOURKB 4096
#define MAX_STREAM_CB 10240 // How big do we want the stream to be for
// the hGlobal scheme
// Constants for the types.
static const DWORD dwZero = 0;
// The name of the Document Summary Information stream.
const WCHAR SZWDOCPROPSTM[] = L"\005DocumentSummaryInformation";
const WCHAR SZWSUMMARYSTM[] = L"\005SummaryInformation";
const CHAR SZDOCPROPSTM[] = "\005DocumentSummaryInformation";
const CHAR SZSUMMARYSTM[] = "\005SummaryInformation";
DEFINE_GUID (FormatID_SummaryInformation,
0xf29f85e0L,0x4ff9,0x1068,0xab,0x91,0x08,0x00,0x2b,0x27,0xb3,0xd9);
DEFINE_GUID (FormatID_DocumentSummaryInformation,
0xd5cdd502L,0x2e9c,0x101b,0x93,0x97,0x08,0x00,0x2b,0x2c,0xf9,0xae);
DEFINE_GUID (FormatID_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 BOOL PASCAL FGetCodepage (LPSTREAM lpStm, LPPIDOFFSET rgPO, DWORD cElem, DWORD *pdwCodepage);
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);
////////////////////////////////////////////////////////////////////////////////
//
// FOfficeCreateAndInitObjects
//
// Purpose:
// Creates and initializes all non-null args.
//
////////////////////////////////////////////////////////////////////////////////
DLLFUNC BOOL OFC_CALLTYPE
FOfficeCreateAndInitObjects
(LPSIOBJ *lplpSIObj,
LPDSIOBJ *lplpDSIObj,
LPUDOBJ *lplpUDObj,
void *prglpfn[])
{
#ifdef DEBUG
#ifndef WINNT
XOPRO xopro;
int iSI,iDSI;
if(oinfo.pplxopro==NULL)
{
oinfo.pplxopro = PplAlloc(sizeof(XOPRO), 1, 1);
if(oinfo.pplxopro==NULL)
return(fFalse);
}
#endif
#endif
if(lplpSIObj!=NULL)
*lplpSIObj=NULL;
if(lplpDSIObj!=NULL)
*lplpDSIObj=NULL;
if(lplpUDObj!=NULL)
*lplpUDObj=NULL;
if (prglpfn == NULL)
return(FALSE);
if (!FSumInfoCreate (lplpSIObj, prglpfn) ||
!FDocSumCreate (lplpDSIObj, prglpfn) ||
!FUserDefCreate (lplpUDObj, prglpfn))
{
#ifdef DEBUG
Free:
#endif
FOfficeDestroyObjects(lplpSIObj, lplpDSIObj, lplpUDObj);
return(FALSE);
}
#ifdef DEBUG
#ifndef WINNT
if(lplpSIObj!=NULL)
{
xopro.typ=typSI;
xopro.lpSIObj=*lplpSIObj;
if((iSI=IAddPl(&oinfo.pplxopro, &xopro))==-1)
goto Free; //need to free all the objs
}
if(lplpDSIObj!=NULL)
{
xopro.typ=typDSI;
xopro.lpDSIObj=*lplpDSIObj;
if((iDSI=IAddPl(&oinfo.pplxopro, &xopro))==-1)
{
FreeSI:
if(lplpSIObj!=NULL)
{
Assert(iSI!=-1);
RemovePl(oinfo.pplxopro, iSI);
}
goto Free; //need to free all the objs
}
}
if(lplpUDObj!=NULL)
{
xopro.typ=typUD;
xopro.lpUDObj=*lplpUDObj;
if(IAddPl(&oinfo.pplxopro, &xopro)==-1)
{
if(lplpDSIObj!=NULL)
{
Assert(iDSI!=-1);
RemovePl(oinfo.pplxopro, iDSI);
}
goto FreeSI; //need to free all the objs
}
}
#endif
#endif
return TRUE;
} // FOfficeCreateAndInitObjects
////////////////////////////////////////////////////////////////////////////////
//
// FOfficeClearObjects
//
// Purpose:
// Clear any non-null objects
//
////////////////////////////////////////////////////////////////////////////////
DLLFUNC BOOL OFC_CALLTYPE
FOfficeClearObjects
(LPSIOBJ lpSIObj,
LPDSIOBJ lpDSIObj,
LPUDOBJ lpUDObj)
{
FSumInfoClear (lpSIObj);
FDocSumClear (lpDSIObj);
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)
{
#ifdef DEBUG
#ifndef WINNT
XOPRO xopro;
int i;
if((lplpSIObj!=NULL) && (*lplpSIObj != NULL))
{
xopro.typ=typSI;
xopro.lpSIObj=*lplpSIObj;
i=ILookupPl(oinfo.pplxopro, &xopro, CmpXOpro);
Assert(i!=-1);
RemovePl(oinfo.pplxopro, i);
}
if((lplpDSIObj!=NULL) && (*lplpDSIObj != NULL))
{
xopro.typ=typDSI;
xopro.lpDSIObj=*lplpDSIObj;
i=ILookupPl(oinfo.pplxopro, &xopro, CmpXOpro);
Assert(i!=-1);
RemovePl(oinfo.pplxopro, i);
}
if((lplpUDObj!=NULL) && (*lplpUDObj != NULL))
{
xopro.typ=typUD;
xopro.lpUDObj=*lplpUDObj;
i=ILookupPl(oinfo.pplxopro, &xopro, CmpXOpro);
Assert(i!=-1);
RemovePl(oinfo.pplxopro, i);
}
#endif
#endif
FSumInfoDestroy (lplpSIObj); // We don't care what these guys return
FDocSumDestroy (lplpDSIObj);
FUserDefDestroy (lplpUDObj);
return TRUE;
} // FOfficeDestroyObjects
////////////////////////////////////////////////////////////////////////////////
//
// DwOfficeLoadProperties
//
// Purpose:
// Populate the objects with data. lpStg is the root stream.
//
////////////////////////////////////////////////////////////////////////////////
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 dwLoad1 = MSO_IO_ERROR;
DWORD dwLoad2 = MSO_IO_ERROR;
if (lpStg == NULL)
return FALSE;
if (lpSIObj != NULL)
{
dwLoad1 = DwOfficeLoadSumInfo (lpSIObj, lpStg, dwFlags, FALSE);
if (dwLoad1 == MSO_IO_ERROR)
return(MSO_IO_ERROR);
}
if (lpDSIObj != NULL && lpUDObj != NULL)
{
dwLoad2 = DwLoadDocAndUser (lpDSIObj, lpUDObj, lpStg, dwFlags, FALSE);
if (dwLoad2 == MSO_IO_ERROR)
{
if (lpSIObj != NULL)
FSumInfoClear(lpSIObj);
return(MSO_IO_ERROR);
}
}
return(max(dwLoad1, dwLoad2)); // Could be that SumInfo was there, but DocSum was not
} // DwOfficeLoadProperties
////////////////////////////////////////////////////////////////////////////////
//
// DwOfficeLoadIntProperties
//
// Purpose:
// Populate the objects with integer data. lpStg is the root stream.
//
////////////////////////////////////////////////////////////////////////////////
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;
if (lpSIObj != NULL)
{
dwLoad1 = DwOfficeLoadSumInfo (lpSIObj, lpStg, dwFlags, TRUE);
if (dwLoad1 == MSO_IO_ERROR)
return(MSO_IO_ERROR);
}
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);
}
}
return(max(dwLoad1, dwLoad2));
} // DwOfficeLoadIntProperties
////////////////////////////////////////////////////////////////////////////////
//
// 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
{
if (lpStg == NULL)
return FALSE;
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))) ||
!(dwFlags & OIO_SAVEIFCHANGEONLY))
{
if (!FSaveSumInfo (lpSIObj, lpStg, dwFlags))
return(FALSE); // Don't even try to save the 2nd strem
}
}
if ((lpDSIObj != NULL) || (lpUDObj != NULL))
{
if (((dwFlags & OIO_SAVEIFCHANGEONLY) && ((FDocSumShouldSave (lpDSIObj)) || (FUserDefShouldSave (lpUDObj)))) ||
!(dwFlags & OIO_SAVEIFCHANGEONLY))
{
if (!DwSaveDocAndUser (lpDSIObj, lpUDObj, lpStg, dwFlags))
return(FALSE);
}
}
return TRUE;
} // DwOfficeSaveProperties
////////////////////////////////////////////////////////////////////////////////
//
// DwLoadDocAndUser
//
// Purpose:
// Loads document summary and User-defined properties
//
////////////////////////////////////////////////////////////////////////////////
static DWORD PASCAL
DwLoadDocAndUser
(LPDSIOBJ lpDSIObj, // Document summary object
LPUDOBJ lpUDObj, // User-defined object
LPSTORAGE lpStg, // Storage containing the streams
DWORD dwFlags, // Flags
BOOL fIntOnly) // Load Int Properties Only
{
LPSTREAM lpStm;
LARGE_INTEGER liStmPos;
DWORD cbSectOff;
DWORD *pcSect;
DWORD irg;
DWORD irgPO;
DWORD dwLoad;
BOOL fDocSum;
HRESULT hr;
LPIDOFFSET FAR *lprglpFIdOff;
LPVOID **lprglpFIdOffData;
LPPIDOFFSET lprgPO; // pid - offset, pid - offset,.....
LPSECTION FAR *lprglpSect;
DWORD cProps;
#ifdef NOT_IMPL
ULARGE_INTEGER uli;
LARGE_INTEGER li;
#endif
dwLoad = 0;
lpStm = NULL;
lprgPO = NULL;
lprglpSect = NULL;
pcSect = NULL;
// Determine which object should hold unknown data & such
if (lpDSIObj != NULL)
{
pcSect = &(lpDSIData->cSect);
lprglpFIdOffData = &(lpDSIData->rglpFIdOffData);
lprglpFIdOff = &(lpDSIData->rglpFIdOff);
lprglpSect = &(lpDSIData->rglpSect);
}
else if (lpUDObj != NULL)
{
pcSect = &(lpUDData->cSect);
lprglpFIdOffData = &(lpUDData->rglpFIdOffData);
lprglpFIdOff = &(lpUDData->rglpFIdOff);
lprglpSect = &(lpUDData->rglpSect);
}
else
return MSO_IO_ERROR;
Assert (((lpDSIObj != NULL) && (lpUDObj != NULL)));
// Make sure we start with empty objects.
FDocSumClear (lpDSIObj); // This will set the save flag to TRUE
FUserDefClear (lpUDObj); // so reset it to FALSE, so in case of
OfficeDirtyDSIObj (lpDSIObj, FALSE); // failure, we don't show a bogus state.
OfficeDirtyUDObj(lpUDObj, FALSE); // Bug 1068.
// Read the header, allocate tables.
dwLoad = DwLpstmReadHdrAndFID (&lpStg,
&lpStm,
(dwFlags & OIO_ANSI) ? (WCHAR *) SZDOCPROPSTM : (WCHAR *) SZWDOCPROPSTM,
pcSect,
lprglpFIdOff,
lprglpSect);
if (dwLoad != MSO_IO_SUCCESS) // Either an error or stream didn't exist
return dwLoad;
*lprglpFIdOffData = PvMemAlloc(*pcSect*sizeof(LPVOID));
if (*lprglpFIdOffData == NULL)
goto ReadFailed;
FillBuf ((void *) *lprglpFIdOffData, (int)NULL, *pcSect*sizeof(LPVOID));
liStmPos.HighPart = 0;
// Loop through the sections, reading them in.
for (irg = 0; irg < *pcSect; irg++)
{
cProps=0;
// Remember that the section offsets are RELATIVE, so store the
// base offset here.
cbSectOff = liStmPos.LowPart = (*lprglpFIdOff)[irg].dwOffset;
// Go to the section, then read the header.
hr = lpStm->lpVtbl->Seek (lpStm, liStmPos, STREAM_SEEK_SET, NULL);
if (!SUCCEEDED (hr))
{
DebugSz ("Bad section offset");
DebugHr (hr);
goto ReadFailed;
}
hr = lpStm->lpVtbl->Read (lpStm, &((*lprglpSect)[irg]), sizeof (SECTION), NULL);
if (!SUCCEEDED (hr))
{
DebugSz ("Couldn't read section header");
DebugHr (hr);
goto ReadFailed;
}
// See if it is one we understand....
if ((fDocSum = ((MsoIsEqualGuid ((REFGUID) &FormatID_DocumentSummaryInformation, (REFGUID) &((*lprglpFIdOff)[irg].Id))) &&
(lpDSIObj != NULL))) ||
(((MsoIsEqualGuid ((REFGUID) &FormatID_UserDefinedProperties, (REFGUID) &((*lprglpFIdOff)[irg].Id))) &&
(lpUDObj != NULL))))
{
DebugSz ("Found matching FMTID");
cProps= (*lprglpSect)[irg].cProps;
// Allocate the table.
if ((lprgPO = PvMemAlloc(cProps*sizeof(PIDOFFSET))) == NULL)
{
goto ReadFailed;
}
// If read fails, free up the table.
hr = lpStm->lpVtbl->Read (lpStm, lprgPO, cProps*sizeof(PIDOFFSET), NULL);
if (!SUCCEEDED (hr))
{
DebugSz ("Couldn't read offset table");
DebugHr (hr);
LoadFailed:
Assert(lprgPO != NULL);
VFreeMemP(lprgPO, cProps*sizeof(PIDOFFSET));
lprgPO=NULL;
goto ReadFailed;
}
// Adjust all the offsets in the table to be relative to
// the beginning of the stream.
for (irgPO = 0; irgPO < cProps; irgPO++)
{
lprgPO[irgPO].dwOffset += cbSectOff;
}
#ifdef NOT_IMPL
Assert (0);
li.LowPart = 0;
li.HighPart = 0;
hr = lpStm->lpVtbl->Seek (lpStm, li, STREAM_SEEK_CUR, &uli);
#endif
if (fDocSum)
{
Assert ((lpDSIObj != NULL));
if (FLoadDocSum (lpDSIObj, lprgPO, cProps, lpStm, fIntOnly))
dwLoad++;
else
goto LoadFailed;
}
else
{
Assert ((lpUDObj != NULL));
if (FLoadUserDef (lpUDObj, lprgPO, cProps, lpStm, fIntOnly))
dwLoad++;
else
goto LoadFailed;
}
// Deallocate the table
Assert(lprgPO != NULL);
VFreeMemP(lprgPO, cProps*sizeof(PIDOFFSET));
lprgPO=NULL;
}
else // Not the FMTID we understand....
{
DebugSz ("Reading FMTID we don't understand");
DebugGUID ((*lprglpFIdOff)[irg].Id);
// Allocate one big block to hold the entire section. Remember that
// the byte count in the section header is inclusive, so don't
// allocate space for it since we already have it stored elsewhere.
if ((*lprglpSect)[irg].cb > sizeof(SECTION))
{
if (((*lprglpFIdOffData)[irg] = PvMemAlloc((*lprglpSect)[irg].cb-sizeof(SECTION))) == NULL)
goto ReadFailed;
hr = lpStm->lpVtbl->Read (lpStm, ((*lprglpFIdOffData)[irg]), (*lprglpSect)[irg].cb-sizeof(SECTION), NULL);
if (!SUCCEEDED (hr))
{
DebugSz ("Couldn't read unknown section");
DebugHr (hr);
VFreeMemP ((*lprglpFIdOffData)[irg],(*lprglpSect)[irg].cb-sizeof(SECTION));
(*lprglpFIdOffData)[irg]=NULL;
goto ReadFailed;
}
}
} // !FMTID we understand
} // for
if (lpStm != NULL)
lpStm->lpVtbl->Release (lpStm);
return MSO_IO_SUCCESS;
ReadFailed:
DebugSz ("Load failed");
Assert(lprgPO == NULL);
FDocSumClear (lpDSIObj);
FUserDefClear (lpUDObj);
OfficeDirtyDSIObj (lpDSIObj, FALSE);
OfficeDirtyUDObj(lpUDObj, FALSE);
if (lpStm != NULL)
lpStm->lpVtbl->Release (lpStm);
return (MSO_IO_ERROR);
} // DwLoadDocAndUser
////////////////////////////////////////////////////////////////////////////////
//
// DwSaveDocAndUser
//
// Purpose:
// Write the data in the given objects. lpStg is the root stream.
//
// Notes:
// Unknown sections get shuffled around here. We want to try to
// tag all unknown data along with the LPDSIOBJ, since that one is
// more likely to be around than the LPUDOBJ. So, in the odd case
// where a UD was read from the stream, but no DSI, all the unknown
// stuff would have been tagged to the UD. This will move it to
// the DSI, if at all possible.
//
////////////////////////////////////////////////////////////////////////////////
static DWORD PASCAL
DwSaveDocAndUser
(LPDSIOBJ lpDSIObj, // Pointer to Document Summary obj
LPUDOBJ lpUDObj, // Pointer to User-defined Obj
LPSTORAGE lpStg, // Storage to hold the streams
DWORD dwFlags) // Flags
{
LPSTREAM lpStm;
BOOL fSaveUser;
DWORD dwSaved;
LPPIDOFFSET rgPODoc;
LPPIDOFFSET rgPOUser;
LPIDOFFSET rgFIdOff;
DWORD irgPODocMac;
DWORD irgPOUserMac;
DWORD cbDoc;
DWORD cbUser;
DWORD irg;
DWORD dw;
DWORD dwOffset;
DWORD cSects;
DWORD dwDocAlign;
DWORD dwUserAlign;
LPDICT lpdict;
#ifdef NOT_IMPL
ULARGE_INTEGER uli;
LARGE_INTEGER li;
#endif
if (lpStg == NULL)
return FALSE;
irgPODocMac = irgPOUserMac = cbUser = cbDoc = cSects = dwSaved = 0;
dw = dwDocAlign = dwUserAlign = 0;
rgPODoc = rgPOUser = NULL;
rgFIdOff = NULL;
fSaveUser = FALSE;
if (lpDSIObj != NULL)
{
// If we didn't load from disk, fill in the section data
if (lpDSIData->cSect < 1)
{
lpDSIData->cSect = 1;
Assert ((lpDSIData->rglpFIdOff == NULL));
lpDSIData->rglpFIdOff = PvMemAlloc(sizeof(IDOFFSET));
if (lpDSIData->rglpFIdOff == NULL)
{
goto SaveFail;
}
lpDSIData->rglpFIdOff[0].Id = FormatID_DocumentSummaryInformation;
}
// Calculate the Property Id-offset table for the Document Summary object
FCreateDocSumPIdTable (lpDSIObj, &rgPODoc, &irgPODocMac, &cbDoc);
// Count the number of unknown sections in this object.
dw = 0;
if (lpDSIData->rglpFIdOffData != NULL)
{
for (irg = 0; irg < lpDSIData->cSect; irg++)
{
if (!((MsoIsEqualGuid ((REFGUID) &FormatID_DocumentSummaryInformation, (REFGUID) &(lpDSIData->rglpFIdOff[irg].Id))) ||
(MsoIsEqualGuid ((REFGUID) &FormatID_UserDefinedProperties, (REFGUID) &(lpDSIData->rglpFIdOff[irg].Id)))))
dw++;
}
}
// The current number of sections we know about (and can write out)
// are the number of unknowns in this object + the DSI section itself.
cSects += dw + 1;
}
if (lpUDObj != NULL)
{
// Calculate the Property Id-offset table for the Document Summary object
if (fSaveUser = FCreateUserDefPIdTable (lpUDObj, &rgPOUser, &irgPOUserMac, &cbUser, &lpdict))
{
// If we didn't load from disk, fill in the section data
if (lpUDData->cSect < 1)
{
lpUDData->cSect = 1;
Assert ((lpUDData->rglpFIdOff == NULL));
lpUDData->rglpFIdOff = PvMemAlloc(sizeof(IDOFFSET));
if (lpUDData->rglpFIdOff == NULL)
{
goto SaveFail;
}
lpUDData->rglpFIdOff[0].Id = FormatID_UserDefinedProperties;
}
dw = 0;
// If we didn't load from disk, this will be zero.
// Count the number of unknown sections in this object.
if (lpUDData->rglpFIdOffData != NULL)
{
for (irg = 0; irg < lpUDData->cSect; irg++)
{
if (!((MsoIsEqualGuid ((REFGUID) &FormatID_DocumentSummaryInformation, (REFGUID) &(lpUDData->rglpFIdOff[irg].Id))) ||
(MsoIsEqualGuid ((REFGUID) &FormatID_UserDefinedProperties, (REFGUID) &(lpUDData->rglpFIdOff[irg].Id)))))
dw++;
}
}
// The current number of sections we know about (and can write out)
// are the number of unknowns in this object + the UD section itself.
cSects += dw + 1;
}
}
rgFIdOff = PvMemAlloc(cSects*sizeof(IDOFFSET));
if (rgFIdOff == NULL)
goto SaveFail;
// Fill out the offset tables for the sections. Remember that the
// sections must 32-bit align, but the data we wrote might not have,
// so remember how much padding we need to get the data to align.
dwOffset = sizeof(PROPSETHDR) + cSects*sizeof(IDOFFSET);
dw = 0;
if (lpDSIObj != NULL)
{
rgFIdOff[dw].Id = FormatID_DocumentSummaryInformation;
rgFIdOff[dw].dwOffset = dwOffset;
dwOffset += cbDoc + irgPODocMac*sizeof(PIDOFFSET) + sizeof(SECTION);
dwDocAlign = dwOffset;
dwOffset += CBALIGN32 (dwOffset);
dwDocAlign = dwOffset - dwDocAlign;
dw++;
}
if ((lpUDObj != NULL) && (fSaveUser))
{
rgFIdOff[dw].Id = FormatID_UserDefinedProperties;
rgFIdOff[dw].dwOffset = dwOffset;
dwOffset += cbUser + irgPOUserMac*sizeof(PIDOFFSET) + sizeof(SECTION);
dwUserAlign = dwOffset;
dwOffset += CBALIGN32 (dwOffset);
dwUserAlign = dwOffset - dwUserAlign;
dw++;
}
// Add any remaining sections from the two lists into the
// list.
for (irg = 0; irg < lpDSIData->cSect; irg++)
{
if (!((MsoIsEqualGuid ((REFGUID) &FormatID_DocumentSummaryInformation, (REFGUID) &(lpDSIData->rglpFIdOff[irg].Id))) ||
(MsoIsEqualGuid ((REFGUID) &FormatID_UserDefinedProperties, (REFGUID) &(lpDSIData->rglpFIdOff[irg].Id)))))
{
rgFIdOff[dw].Id = lpDSIData->rglpFIdOff[irg].Id;
rgFIdOff[dw].dwOffset = dwOffset;
dwOffset += lpDSIData->rglpSect[irg].cb;
dw++;
}
}
if (fSaveUser)
{
Assert ((lpUDObj != NULL));
for (irg = 0; irg < lpUDData->cSect; irg++)
{
if (!((MsoIsEqualGuid ((REFGUID) &FormatID_DocumentSummaryInformation, (REFGUID) &(lpUDData->rglpFIdOff[irg].Id))) ||
(MsoIsEqualGuid ((REFGUID) &FormatID_UserDefinedProperties, (REFGUID) &(lpUDData->rglpFIdOff[irg].Id)))))
{
rgFIdOff[dw].Id = lpUDData->rglpFIdOff[irg].Id;
rgFIdOff[dw].dwOffset = dwOffset;
dwOffset += lpUDData->rglpSect[irg].cb;
dw++;
}
}
}
// Init the save buffer.
VAllocWriteBuf();
lpStm = NULL;
// Finally, the property id-offset table is filled out. Now
// write it, and be sure to write the data in the same order!
if (!FLpstmWriteHdr (lpStg,
(dwFlags & OIO_ANSI) ? (WCHAR *) SZDOCPROPSTM : (WCHAR *) SZWDOCPROPSTM,
&lpStm, cSects, (dwFlags & OIO_SAVESIMPLEDOCFILE) ? TRUE : FALSE))
goto SaveFail;
dw = 0;
// Write out the format headers.
if (!FLpstmWrite (lpStm, rgFIdOff, cSects*sizeof(IDOFFSET)))
goto SaveFail;
// Now do the stuff for the different sections. Write the Document Summary
// section first if it exists. Also write out padding to 32-bit align
// if needed.
if (lpDSIObj != NULL)
{
// Write the pid-offset table. Also, adjust the offsets to start
// at the end of the table before writing the data.
if (!FLpstmWritePropOffTable (lpStm, rgPODoc, irgPODocMac, cbDoc))
goto SaveFail;
// Write out the Document summary data
if (!FLpstmSaveDocSum (lpDSIObj, lpStm))
goto SaveFail;
if (dwDocAlign > 0)
{
if (!FLpstmWrite (lpStm, &dw, dwDocAlign))
goto SaveFail;
}
dwSaved++;
}
#ifdef NOT_IMPL
lpStm = FLpstmWrite (lpStm, NULL, 1, fFalse, fFalse);
li.LowPart = 0;
li.HighPart = 0;
lpStm->lpVtbl->Seek (lpStm, li, STREAM_SEEK_CUR, &uli);
#endif
// Next do the User-defined section, if possible.
if ((lpUDObj != NULL) && (fSaveUser))
{
Assert ((lpUDObj != NULL));
// Write the pid-offset table. Also, adjust the offsets to start
// at the end of the table before writing the data.
if (!FLpstmWritePropOffTable (lpStm, rgPOUser, irgPOUserMac, cbUser))
goto SaveFail;
// Write out the User-defined data
if (!FLpstmSaveUserDef (lpUDObj, lpStm, &lpdict))
goto SaveFail;
if (dwUserAlign > 0)
{
if (!FLpstmWrite (lpStm, &dw, dwUserAlign))
goto SaveFail;
}
dwSaved++;
}
// Now do all the stuff we didn't understand.
if (lpDSIObj != NULL)
{
if (!FLpstmWriteOtherSections (lpStm, lpDSIData->rglpSect,
lpDSIData->rglpFIdOff, lpDSIData->rglpFIdOffData,
lpDSIData->cSect))
goto SaveFail;
}
if ((lpUDObj != NULL) && (fSaveUser))
{
if (!FLpstmWriteOtherSections (lpStm, lpUDData->rglpSect,
lpUDData->rglpFIdOff, lpUDData->rglpFIdOffData,
lpUDData->cSect))
goto SaveFail;
}
// Flush the save buffer & free it
if (!FFlushWriteBuf(lpStm))
goto SaveFail;
VFreeWriteBuf();
if (!(dwFlags & OIO_SAVESIMPLEDOCFILE))
VSetRealStmSize(lpStm);
// We're done!
if (lpStm != NULL)
lpStm->lpVtbl->Release (lpStm);
if (rgPODoc != NULL)
VFreeMemP(rgPODoc,sizeof(PIDOFFSET)*(cDSIPIDS+lpDSIData->cbUnkMac+1));
if (rgPOUser != NULL)
VFreeMemP(rgPOUser,sizeof(PIDOFFSET)*(irgPOUserMac));
if (rgFIdOff != NULL)
VFreeMemP(rgFIdOff, cSects * sizeof(IDOFFSET));
return dwSaved;
SaveFail:
if (rgPODoc != NULL)
VFreeMemP(rgPODoc,sizeof(PIDOFFSET)*(cDSIPIDS+lpDSIData->cbUnkMac+1));
if (rgPOUser != NULL)
VFreeMemP(rgPOUser,sizeof(PIDOFFSET)*(irgPOUserMac));
if (rgFIdOff != NULL)
VFreeMemP(rgFIdOff, cSects * sizeof(IDOFFSET));
VFreeWriteBuf();
lpStg->lpVtbl->Revert (lpStg);
if (lpStm != NULL)
lpStm->lpVtbl->Release (lpStm);
return (0);
} // DwSaveDocAndUser
////////////////////////////////////////////////////////////////////////////////
//
// DwOfficeLoadSumInfo
//
// Purpose:
// Populate the object with data.
//
////////////////////////////////////////////////////////////////////////////////
DWORD
DwOfficeLoadSumInfo
(LPSIOBJ lpSIObj, // Pointer to object
LPSTORAGE lpStg, // Pointer to root storage
DWORD dwFlags, // Flags
BOOL fIntOnly) // Load only Int Properties?
{
LPSTREAM lpStm;
LARGE_INTEGER liStmPos;
DWORD dwType, dwLoad;
LPPIDOFFSET rgPO; // pid - offset, pid - offset,.....
DWORD irg;
DWORD irgPO;
DWORD irgPOMac;
DWORD irglpUnk;
DWORD cbSectOff;
DWORD cProps;
char *lpstz;
BOOL fRead;
HRESULT hr;
if ((lpSIObj == NULL) ||
(lpSIData == NULL) ||
(lpStg == NULL))
return MSO_IO_ERROR;
// Make sure we start with an empty object.
FSumInfoClear (lpSIObj); // This will set the save flag to true
OfficeDirtySIObj(lpSIObj, FALSE); // so clear it. Bug 1068
rgPO = NULL;
// Uggh.
// Read the header, allocate tables. Then,
// load the property id-offset table for the section we
// want, copy all the other sections we don't understand into
// our data.
dwLoad = DwLpstmReadHdrAndFID(&lpStg,
&lpStm,
(dwFlags & OIO_ANSI) ? (WCHAR *) SZSUMMARYSTM : (WCHAR *) SZWSUMMARYSTM,
&(lpSIData->cSect),
&(lpSIData->rglpFIdOff),
&(lpSIData->rglpSect));
if (dwLoad != MSO_IO_SUCCESS) // either stream didn't exist or an error happened
return dwLoad;
lpSIData->rglpFIdOffData = PvMemAlloc(lpSIData->cSect*sizeof(LPVOID));
if (lpSIData->rglpFIdOffData == NULL)
goto LoadFail;
FillBuf ((void *) lpSIData->rglpFIdOffData, (int)NULL, lpSIData->cSect*sizeof(LPVOID));
irgPOMac = 0;
liStmPos.HighPart = 0;
// Loop through the sections, reading them in.
for (irg = 0; irg < lpSIData->cSect; irg++)
{
cProps = 0;
// Remember that the section offsets are RELATIVE, so store the
// base offset here.
cbSectOff = liStmPos.LowPart = (lpSIData->rglpFIdOff)[irg].dwOffset;
// Go to the section, then read the header.
hr = lpStm->lpVtbl->Seek (lpStm, liStmPos, STREAM_SEEK_SET, NULL);
if (!SUCCEEDED (hr))
{
DebugSz ("Bad section offset");
DebugHr (hr);
goto LoadFail;
}
hr = lpStm->lpVtbl->Read (lpStm, &(lpSIData->rglpSect[irg]), sizeof (SECTION), NULL);
if (!SUCCEEDED (hr))
{
DebugSz ("Couldn't read section header");
DebugHr (hr);
goto LoadFail;
}
// See if it is one we understand....
if (MsoIsEqualGuid ((REFGUID) &FormatID_SummaryInformation, (REFGUID) &(lpSIData->rglpFIdOff[irg].Id)))
{
DebugSz ("Found matching FMTID");
// It's OK to have no property Id's.
if ((cProps = lpSIData->rglpSect[irg].cProps) == 0)
continue;
// Allocate the table.
if ((rgPO = PvMemAlloc(cProps*sizeof(PIDOFFSET))) == NULL)
goto LoadFail;
// If read fails, free up the table.
hr = lpStm->lpVtbl->Read (lpStm, rgPO, cProps*sizeof(PIDOFFSET), NULL);
if (!SUCCEEDED (hr))
{
DebugSz ("Couldn't read offset table");
DebugHr (hr);
FreeUp:
VFreeMemP(rgPO,sizeof(PIDOFFSET)*cProps);
goto LoadFail;
}
// Adjust all the offsets in the table to be relative to
// the beginning of the stream.
for (irgPO = 0; irgPO < cProps; irgPO++)
rgPO[irgPO].dwOffset += cbSectOff;
irgPOMac = cProps;
Assert(irgPOMac != 0);
// See how many PId's we don't understand in the table.
lpSIData->cbUnkMac = 0;
for (irgPO = 0; irgPO < irgPOMac; irgPO++)
if ((rgPO[irgPO].Id > PID_SILAST) ||
(rgPO[irgPO].Id < PID_SIFIRST))
lpSIData->cbUnkMac++;
// Read the codepage from the stream. If it's not there,
// assume it is the default.
if (!FGetCodepage (lpStm, rgPO, irgPOMac, &gdwFileCP))
gdwFileCP = GetACP();
else
{
// This property would have shown up as an unknown one.
if (lpSIData->cbUnkMac > 0)
lpSIData->cbUnkMac--;
}
if (lpSIData->cbUnkMac > 0)
{
lpSIData->rglpUnk = PvMemAlloc(lpSIData->cbUnkMac*sizeof(PROPIDTYPELP));
if (lpSIData->rglpUnk == NULL)
goto FreeUp;
irglpUnk = 0;
}
gdwCurrentCP = GetACP();
// Read in each property value.
for (irgPO = 0; irgPO < irgPOMac; irgPO++)
{
// Skip the codepage, since we already read it.
if (rgPO[irgPO].Id == PID_CODEPAGE)
continue;
fRead = FALSE;
liStmPos.LowPart = rgPO[irgPO].dwOffset;
if (!SUCCEEDED (lpStm->lpVtbl->Seek (lpStm, liStmPos, STREAM_SEEK_SET, NULL)))
goto FreeUp;
if (!SUCCEEDED (lpStm->lpVtbl->Read (lpStm, &dwType, sizeof (DWORD), NULL)))
goto FreeUp;
if (fIntOnly && dwType != VT_I4)
continue;
// Read in the right data.
switch (dwType)
{
case VT_LPSTR :
if (rgPO[irgPO].Id < cSIStringsMax)
{
if (!FLpstmReadVT_LPSTR (lpStm, &lpstz, lpSIData->lpfnFCPConvert, FALSE))
goto FreeUp;
lpSIData->rglpstz[rgPO[irgPO].Id] = lpstz;
fRead = TRUE;
}
break;
case VT_FILETIME :
if (rgPO[irgPO].Id-cSIFTOffset < cSIFTMax)
{
if (!SUCCEEDED
(lpStm->lpVtbl->Read (lpStm,
&(lpSIData->rgft[rgPO[irgPO].Id-cSIFTOffset]),
sizeof (FILETIME), NULL)))
goto FreeUp;
fRead = TRUE;
// HACK ALERT!!!
// WinWord 6.0c was stupid and set LASTPRINTED to some
// completely bogus value. Don't read it.
if (rgPO[irgPO].Id == PID_LASTPRINTED)
{
if (((lpSIData->rgft[rgPO[irgPO].Id-cSIFTOffset].dwLowDateTime == 0x59a4c000) &&
(lpSIData->rgft[rgPO[irgPO].Id-cSIFTOffset].dwHighDateTime == 0x01deb78b)) ||
((lpSIData->rgft[rgPO[irgPO].Id-cSIFTOffset].dwLowDateTime == 0x000164c0) &&
(lpSIData->rgft[rgPO[irgPO].Id-cSIFTOffset].dwHighDateTime == 0x77c656d3)))
{
break; // We read it, but pretend we didn't.
}
}
VSumInfoSetPropBit(rgPO[irgPO].Id, &lpSIData->bPropSet);
}
break;
case VT_I4 :
if (rgPO[irgPO].Id - cdwSIOffset < cdwSIMax)
{
if (!SUCCEEDED
(lpStm->lpVtbl->Read (lpStm,
&(lpSIData->rgdw[rgPO[irgPO].Id-cdwSIOffset]),
sizeof (DWORD), NULL)))
goto FreeUp;
VSumInfoSetPropBit(rgPO[irgPO].Id, &lpSIData->bPropSet);
fRead = TRUE;
}
break;
case VT_CF :
if (rgPO[irgPO].Id == PID_THUMBNAIL)
{
if (!FLpstmReadVT_CF (lpStm, &(lpSIData->SINail)))
goto FreeUp;
fRead = TRUE;
lpSIData->fSINail = TRUE;
lpSIData->fSaveSINail = TRUE;
}
break;
case VT_EMPTY :
// If VT_EMPTY is specified for any PId we understand, simply
// skip it, since it is not Unknown data.
if ((rgPO[irgPO].Id >= PID_SIFIRST) && (rgPO[irgPO].Id <= PID_SILAST))
fRead = TRUE;
break;
default:
DebugSzdw ("Doh! Unknown type %x in stream, punt for now!", dwType);
} // switch (dwType)
// If we didn't read it above, it is unknown data, so read it here.
if (!fRead)
{
// If this is true, it means that the PID is legal, but somehow the type is not.
// So we didn't catch the PID as an unknown above. Blow this one off.
if ((rgPO[irgPO].Id >= PID_SIFIRST) && (rgPO[irgPO].Id <= PID_SILAST))
continue;
AssertSz ((irglpUnk < lpSIData->cbUnkMac), "irglUnk data count out of range!");
if (!FLpstmReadUnknown (lpStm, dwType, rgPO[irgPO].Id, &irglpUnk, lpSIData->rglpUnk))
goto FreeUp;
}
} // for
VFreeMemP(rgPO,sizeof(PIDOFFSET)*cProps);
rgPO = NULL;
}
else // Not the FMTID we understand....
{
DebugSz ("Reading FMTID we don't understand");
DebugGUID (lpSIData->rglpFIdOff[irg].Id);
// Allocate one big block to hold the entire section. Remember that
// the byte count in the section header is inclusive, so don't
// allocate space for it since we already have it stored elsewhere.
if (lpSIData->rglpSect[irg].cb > sizeof(SECTION))
{
if ((lpSIData->rglpFIdOffData[irg] = PvMemAlloc(lpSIData->rglpSect[irg].cb-sizeof(SECTION))) == NULL)
goto LoadFail;
hr = lpStm->lpVtbl->Read (lpStm, (lpSIData->rglpFIdOffData[irg]), lpSIData->rglpSect[irg].cb-sizeof(SECTION), NULL);
if (!SUCCEEDED (hr))
{
DebugSz ("Couldn't read unknown section");
DebugHr (hr);
VFreeMemP (lpSIData->rglpFIdOffData[irg],lpSIData->rglpSect[irg].cb-sizeof(SECTION) );
goto LoadFail;
}
}
} // !FMTID we understand
} // for
OfficeDirtySIObj (lpSIObj, FALSE);
if (lpStm != NULL)
lpStm->lpVtbl->Release (lpStm);
return MSO_IO_SUCCESS;
LoadFail:
FSumInfoClear (lpSIObj);
OfficeDirtySIObj (lpSIObj, FALSE);
if (lpStm != NULL)
lpStm->lpVtbl->Release (lpStm);
return MSO_IO_ERROR;
} // DwOfficeLoadSumInfo
////////////////////////////////////////////////////////////////////////////////
//
// FSaveSumInfo
//
// Purpose:
// Write the data in the given object.
//
////////////////////////////////////////////////////////////////////////////////
BOOL
FSaveSumInfo
(LPSIOBJ lpSIObj, // Pointer to the object
LPSTORAGE lpStg, // Pointer to root storage.
DWORD dwFlags) // Flags
{
LPSTREAM lpStm;
LPPIDOFFSET rgPO; // pid - offset, pid - offset,.....
DWORD irgPO;
DWORD dwOffset;
DWORD irg;
if ((lpSIObj == NULL) ||
(lpSIData == NULL) ||
(lpStg == NULL))
return FALSE;
// Create the biggest PropId-offset table we might need.
rgPO = PvMemAlloc(sizeof(PIDOFFSET)*(cSIPIDS+lpSIData->cbUnkMac+1));
if (rgPO == NULL)
{
// REVIEW: add alert
return FALSE;
}
// If we didn't load from disk, this will be zero.
if (lpSIData->cSect < 1)
{
lpSIData->cSect = 1;
}
// If this is empty, just make one big enough to hold our stuff.
if (lpSIData->rglpFIdOff == NULL)
{
lpSIData->rglpFIdOff = PvMemAlloc(sizeof(IDOFFSET));
if (lpSIData->rglpFIdOff == NULL)
{
goto SaveFail;
}
// Don't forget to fill out the format id in it (offset is meaningless here)
lpSIData->rglpFIdOff[0].Id = FormatID_SummaryInformation;
}
// Zip through all of our properties and put the valid ones in the
// property id-offset table, filling in the offsets as we go. Remember
// that all offsets for type-value pairs must be aligned on 32-bit
// boundaries, so adjust offsets accordingly....
// All offsets need have a DWORD added to them to account for
// the space that the property type field will have.
// Start the offsets at 0. They need to be adjusted after
// the table is completed. This is done when the table is
// written out.
// Add in the codepage for this file
rgPO[0].Id = PID_CODEPAGE;
rgPO[0].dwOffset = 0;
irgPO = 1;
dwOffset = 2*sizeof(DWORD);
// VT_LPSTR
for (irg = 0; irg < cSIStringsMax; irg++)
{
if (lpSIData->rglpstz[irg] != NULL)
{
rgPO[irgPO].Id = irg;
rgPO[irgPO].dwOffset = dwOffset;
dwOffset += 2*sizeof(DWORD) + CBSTR (lpSIData->rglpstz[irg]);
dwOffset += CBALIGN32 (dwOffset);
irgPO++;
}
}
// VT_FT
for (irg = 0; irg < cSIFTMax; irg++)
{
if (FSumInfoPropBitIsSet(PID_EDITTIME+irg, lpSIData->bPropSet))
{
rgPO[irgPO].Id = irg+cSIFTOffset;
rgPO[irgPO].dwOffset = dwOffset;
// Should naturally align on 32-bit bound
dwOffset += sizeof (FILETIME) + sizeof(DWORD);
irgPO++;
}
}
AssertSz (((sizeof(FILETIME)%4) == 0), "Huh? FILETIME size no longer multiple of 4");
// VT_I4
for (irg = 0; irg < cdwSIMax; irg++)
{
// Hack because these two PIds fall in the middle of this array.
if ((irg+cdwSIOffset != PID_THUMBNAIL) &&
(irg+cdwSIOffset != PID_APPNAME))
{
if (FSumInfoPropBitIsSet(PID_PAGECOUNT+irg, lpSIData->bPropSet))
{
rgPO[irgPO].Id = irg+cdwSIOffset;
rgPO[irgPO].dwOffset = dwOffset;
dwOffset += 2*sizeof(DWORD); // Should naturally align on 32-bit bound
irgPO++;
}
}
}
if ((lpSIData->fSINail) && (lpSIData->fSaveSINail))
{
rgPO[irgPO].Id = PID_THUMBNAIL;
rgPO[irgPO].dwOffset = dwOffset;
dwOffset += 3*sizeof(DWORD) + lpSIData->SINail.cbData + CbThumbNailFMTID(lpSIData->SINail.cftag);
dwOffset += CBALIGN32 (dwOffset);
irgPO++;
}
// Now do all the ones we didn't understand.
for (irg = 0; irg < lpSIData->cbUnkMac; irg++)
{
rgPO[irgPO].Id = lpSIData->rglpUnk[irg].dwId;
rgPO[irgPO].dwOffset = dwOffset;
dwOffset += lpSIData->rglpUnk[irg].dwSize + sizeof(DWORD);
dwOffset += CBALIGN32 (dwOffset);
irgPO++;
}
// We need to have a section header too....
if (lpSIData->rglpSect == NULL)
{
lpSIData->rglpSect = PvMemAlloc(sizeof(SECTION));
if (lpSIData->rglpSect == NULL)
goto SaveFail;
}
// Let's set the new cb and cProps since this might have changed since we loaded.
lpSIData->rglpSect[0].cb = dwOffset + sizeof(SECTION) + irgPO*sizeof(PIDOFFSET);
lpSIData->rglpSect[0].cProps = irgPO;
// Init the save buffer.
VAllocWriteBuf();
lpStm = NULL;
// Finally, the property id-offset table is filled out. Now
// write it, and be sure to write the data in the same order!
if (!FLpstmWriteHdr (lpStg,
(dwFlags & OIO_ANSI) ? (WCHAR *) SZSUMMARYSTM : (WCHAR *) SZWSUMMARYSTM,
&lpStm, lpSIData->cSect, (dwFlags & OIO_SAVESIMPLEDOCFILE) ? TRUE : FALSE))
goto SaveFail;
// Write out the format headers.
if (!FLpstmWriteFmtIdSection (lpStm, lpSIData->rglpSect, lpSIData->rglpFIdOff,
FormatID_SummaryInformation, lpSIData->cSect))
goto SaveFail;
// Write the pid-offset table. Also, adjust the offsets to start
// at the end of the table before writing the data.
if (!FLpstmWritePropOffTable (lpStm, rgPO, irgPO, dwOffset))
goto SaveFail;
// Codepage
gdwCurrentCP = GetACP();
if (!FLpstmWriteVT_I2 (lpStm, (WORD) gdwCurrentCP))
goto SaveFail;
// VT_LPSTR
for (irg = 0; irg < cSIStringsMax; irg++)
{
if (lpSIData->rglpstz[irg] != NULL)
{
if (!FLpstmWriteVT_LPSTR (lpStm, &(lpSIData->rglpstz[irg][0]), TRUE, VT_LPSTR))
goto SaveFail;
}
}
// VT_FILETIME
for (irg = 0; irg < cSIFTMax; irg++)
{
if (FSumInfoPropBitIsSet(PID_EDITTIME+irg, lpSIData->bPropSet))
{
if (!FLpstmWriteVT_FILETIME (lpStm, &(lpSIData->rgft[irg])))
goto SaveFail;
}
}
// VT_I4
for (irg = 0; irg < cdwSIMax; irg++)
{
// Hack because these two PIds fall in the middle of this array.
if ((irg+cdwSIOffset != PID_THUMBNAIL) &&
(irg+cdwSIOffset != PID_APPNAME))
{
if (FSumInfoPropBitIsSet(PID_PAGECOUNT+irg, lpSIData->bPropSet))
{
if (!FLpstmWriteVT_I4 (lpStm, lpSIData->rgdw[irg]))
goto SaveFail;
}
}
}
if ((lpSIData->fSINail) && (lpSIData->fSaveSINail))
{
if (!FLpstmWriteVT_CF (lpStm, &(lpSIData->SINail)))
goto SaveFail;
}
// Now do all the data we didn't understand.
if (!FLpstmWriteUnknowns (lpStm, lpSIData->cbUnkMac, lpSIData->rglpUnk))
goto SaveFail;
// Write out the sections we didn't understand.
if (!FLpstmWriteOtherSections (lpStm, lpSIData->rglpSect, lpSIData->rglpFIdOff,
lpSIData->rglpFIdOffData, lpSIData->cSect))
goto SaveFail;
OfficeDirtySIObj (lpSIObj, FALSE);
// Flush the save buffer & free it
if (!FFlushWriteBuf(lpStm))
goto SaveFail;
if (!(dwFlags & OIO_SAVESIMPLEDOCFILE))
VSetRealStmSize(lpStm);
VFreeWriteBuf();
if (lpStm != NULL)
lpStm->lpVtbl->Release (lpStm);
VFreeMemP(rgPO,sizeof(PIDOFFSET)*(cSIPIDS+lpSIData->cbUnkMac+1));
return TRUE;
SaveFail:
// Free the save buffer.
VFreeWriteBuf();
if (rgPO != NULL)
VFreeMemP(rgPO,sizeof(PIDOFFSET)*(cSIPIDS+lpSIData->cbUnkMac+1));
if (lpStm != NULL)
lpStm->lpVtbl->Release (lpStm);
return FALSE;
} // FSaveSumInfo
////////////////////////////////////////////////////////////////////////////////
//
// FLoadDocSum
//
// Purpose:
// Populate the object with data.
//
////////////////////////////////////////////////////////////////////////////////
BOOL
FLoadDocSum
(LPDSIOBJ lpDSIObj, // Pointer to object
LPPIDOFFSET lprgPO, // PId - offset table
DWORD irgPOMac, // Size of lprgPO
LPSTREAM lpStm, // Pointer to the stream
BOOL fIntOnly) // Load Int Properties Only?
{
LARGE_INTEGER liStmPos;
DWORD dwType;
DWORD irgPO;
DWORD irglpUnk;
char *lpstz;
BOOL fRead;
HRESULT hr;
if ((lpDSIObj == NULL) ||
(lpDSIData == NULL))
return FALSE;
// It's OK to have no property Id's.
if (irgPOMac == 0)
{
DebugSz ("No property Id's");
return TRUE;
}
// See how many PId's we don't understand in the table.
lpDSIData->cbUnkMac = 0;
for (irgPO = 0; irgPO < irgPOMac; irgPO++)
if ((lprgPO[irgPO].Id > PID_DSILAST) ||
(lprgPO[irgPO].Id < PID_DSIFIRST))
lpDSIData->cbUnkMac++;
liStmPos.HighPart = 0;
// Read the codepage from the stream. If it's not there,
// assume it is the default.
if (!FGetCodepage (lpStm, lprgPO, irgPOMac, &gdwFileCP))
gdwFileCP = GetACP();
else
{
// This property would have shown up as an unknown one.
if (lpDSIData->cbUnkMac > 0)
lpDSIData->cbUnkMac--;
}
gdwCurrentCP = GetACP();
if (lpDSIData->cbUnkMac > 0)
{
lpDSIData->rglpUnk = PvMemAlloc(lpDSIData->cbUnkMac*sizeof(PROPIDTYPELP));
if (lpDSIData->rglpUnk == NULL)
goto LoadFail;
irglpUnk = 0;
}
// Read in each property value.
for (irgPO = 0; irgPO < irgPOMac; irgPO++)
{
// Skip the codepage, since we already read it.
if (lprgPO[irgPO].Id == PID_CODEPAGE)
continue;
fRead = FALSE;
liStmPos.LowPart = lprgPO[irgPO].dwOffset;
if (!SUCCEEDED (lpStm->lpVtbl->Seek (lpStm, liStmPos, STREAM_SEEK_SET, NULL)))
goto LoadFail;
if (!SUCCEEDED (lpStm->lpVtbl->Read (lpStm, &dwType, sizeof (DWORD), NULL)))
goto LoadFail;
if (fIntOnly && dwType!=VT_I4)
continue;
// Read in the right data.
switch (dwType)
{
case VT_LPSTR :
if (lprgPO[irgPO].Id < cDSIStringsMax)
{
if (!FLpstmReadVT_LPSTR (lpStm, &lpstz, lpDSIData->lpfnFCPConvert, FALSE))
goto LoadFail;
lpDSIData->rglpstz[lprgPO[irgPO].Id] = lpstz;
fRead = TRUE;
}
break;
case VT_I4 :
if (lprgPO[irgPO].Id < cdwDSIMax)
{
hr = lpStm->lpVtbl->Read (lpStm, &(lpDSIData->rgdw[lprgPO[irgPO].Id]), sizeof (DWORD), NULL);
if (!SUCCEEDED (hr))
{
DebugHr (hr);
goto LoadFail;
}
VDocSumInfoSetPropBit(lprgPO[irgPO].Id, &lpDSIData->bPropSet);
fRead = TRUE;
}
break;
case VT_BOOL :
if (lprgPO[irgPO].Id == PID_SCALE || lprgPO[irgPO].Id == PID_LINKSDIRTY)
{
if (!FLpstmReadVT_BOOL (lpStm,
(lprgPO[irgPO].Id == PID_SCALE) ?
&(lpDSIData->fScale) :
&(lpDSIData->fLinksChanged)))
goto LoadFail;
fRead = TRUE;
}
break;
case (VT_VECTOR | VT_LPSTR) :
if (lprgPO[irgPO].Id == PID_DOCPARTS)
{
HRESULT hr;
// Read the number of elements in the vector
hr = lpStm->lpVtbl->Read (lpStm, &(lpDSIData->dwcTotParts), sizeof(DWORD), NULL);
if (!SUCCEEDED (hr))
{
DebugHr (hr);
goto LoadFail;
}
// Read that darn data
if (lpDSIData->lpplxheadpart == NULL) // Document Parts came first
{
if (!FReadDocParts(lpStm, lpDSIObj))
goto LoadFail;
}
else if (!FReadAndInsertDocParts(lpStm, lpDSIObj)) // Headings came first
goto LoadFail;
fRead = TRUE;
}
break;
case (VT_VECTOR | VT_VARIANT) :
if (lprgPO[irgPO].Id == PID_HEADINGPAIR)
{
HRESULT hr;
// Read the number of elements in the vector
hr = lpStm->lpVtbl->Read (lpStm, &(lpDSIData->dwcTotHead), sizeof(DWORD), NULL);
if (!SUCCEEDED (hr))
{
DebugHr (hr);
goto LoadFail;
}
// Remember that the number of elements is twice the total number
// of heading parts, since each part has 2 pieces.
lpDSIData->dwcTotHead /= 2;
// Read that darn data
if (lpDSIData->lpplxheadpart == NULL) // Heading Pairs came first
{
if (!FReadHeadingPairs(lpStm, lpDSIObj))
goto LoadFail;
}
else if (!FReadAndInsertHeadingPairs(lpStm, lpDSIObj)) // Document Parts came first
goto LoadFail;
fRead = TRUE;
}
break;
case VT_EMPTY :
// If VT_EMPTY is specified for any PId we understand, simply
// skip it, since it is not Unknown data.
if ((lprgPO[irgPO].Id >= PID_DSIFIRST) && (lprgPO[irgPO].Id <= PID_DSILAST))
fRead = TRUE;
break;
} // switch (dwType)
// If we didn't read it above, it is unknown data, so read it here.
if (!fRead)
{
// If this is true, it means that the PID is legal, but somehow the type is not.
// So we didn't catch the PID as an unknown above.
if ((lprgPO[irgPO].Id >= PID_DSIFIRST) && (lprgPO[irgPO].Id <= PID_DSILAST))
continue;
AssertSz ((irglpUnk < lpDSIData->cbUnkMac), "irglUnk data count out of range!");
if (!FLpstmReadUnknown (lpStm, dwType, lprgPO[irgPO].Id, &irglpUnk,
lpDSIData->rglpUnk))
goto LoadFail;
}
} // for
OfficeDirtyDSIObj (lpDSIObj, FALSE);
return TRUE;
LoadFail:
if (lpDSIData->rglpUnk != NULL)
{
VFreeMemP (lpDSIData->rglpUnk, lpDSIData->cbUnkMac*sizeof(PROPIDTYPELP));
lpDSIData->rglpUnk=NULL;
}
return FALSE;
} // FLoadDocSum
////////////////////////////////////////////////////////////////////////////////
//
// FReadDocParts
//
// Read all the document parts from the stream and just add'em to the plex.
// The document parts were the first ones in the stream
//
////////////////////////////////////////////////////////////////////////////////
BOOL PASCAL FReadDocParts(lpStm, lpDSIObj)
LPSTREAM lpStm; // Pointer to the stream
LPDSIOBJ lpDSIObj; // DSI object
{
XHEADPART xheadpart;
DWORD i,cParts;
cParts = lpDSIData->dwcTotParts;
i = 0;
while (i < cParts)
{
xheadpart.fHeading = FALSE;
xheadpart.dwParts = 0;
xheadpart.iHeading = 0; // This will be set when we read the headings
if (!FLpstmReadVT_LPSTR (lpStm, &(xheadpart.lpstz),lpDSIData->lpfnFCPConvert, FALSE))
goto Fail;
if (IAddNewPlPos(&(lpDSIData->lpplxheadpart), &xheadpart, sizeof(XHEADPART), i) == -1)
goto Fail;
++i;
}
return(TRUE);
Fail:
if (i > 0) // If we couldn't add the first one, there's nothing to free
FreeHeadPartPlex(lpDSIObj);
return(FALSE);
}
////////////////////////////////////////////////////////////////////////////////
//
// FReadAndInsertDocParts
//
// Read all the document parts from the stream and insert'em under the
// appropriate heading.
// The headings were the first ones in the stream.
//
////////////////////////////////////////////////////////////////////////////////
BOOL PASCAL FReadAndInsertDocParts(lpStm, lpDSIObj)
LPSTREAM lpStm; // Pointer to the stream
LPDSIOBJ lpDSIObj; // DSI object
{
XHEADPART xheadpart;
DWORD cT,i, cTotParts, cParts, iHeading;
cTotParts = lpDSIData->dwcTotParts;
cT = 0;
iHeading = 0;
while (cT < cTotParts)
{
cParts = lpDSIData->lpplxheadpart->rgxheadpart[iHeading].dwParts;
for (i = 1; i <= cParts; ++i)
{
xheadpart.fHeading = FALSE;
xheadpart.iHeading = iHeading;
if (!FLpstmReadVT_LPSTR (lpStm, &(xheadpart.lpstz),lpDSIData->lpfnFCPConvert, FALSE))
goto Fail;
// Insert the document part, iHeading is the base, i the offset
if (IAddNewPlPos(&(lpDSIData->lpplxheadpart), &xheadpart, sizeof(XHEADPART), iHeading+i) == -1)
goto Fail;
}
iHeading += cParts+1; // Let's go to the next heading
cT += cParts;
}
return TRUE;
Fail:
FreeHeadPartPlex(lpDSIObj);
return(FALSE);
}
////////////////////////////////////////////////////////////////////////////////
//
// FReadHeadingPairs
//
// Read all the Heading Pairs from the stream and just add'em to the plex.
// The heading pairs were the first ones in the stream
//
////////////////////////////////////////////////////////////////////////////////
BOOL PASCAL FReadHeadingPairs(lpStm, lpDSIObj)
LPSTREAM lpStm; // Pointer to the stream
LPDSIOBJ lpDSIObj; // DSI object
{
XHEADPART xheadpart;
DWORD i,cHead, dwType;
cHead = lpDSIData->dwcTotHead;
i = 0;
while (i < cHead)
{
// Read the type
if ((!SUCCEEDED(lpStm->lpVtbl->Read (lpStm, &dwType, sizeof (DWORD), NULL)))
|| (dwType != VT_LPSTR))
goto Fail;
// Read the VT_LPSTR
if (!FLpstmReadVT_LPSTR (lpStm, &(xheadpart.lpstz), lpDSIData->lpfnFCPConvert, FALSE))
goto Fail;
// Read in the type for the VT_I4
if ((!SUCCEEDED(lpStm->lpVtbl->Read (lpStm, &dwType, sizeof (DWORD), NULL)))
|| (dwType != VT_I4))
goto Fail;
// Read in the VT_I4
if (!SUCCEEDED(lpStm->lpVtbl->Read (lpStm, &(xheadpart.dwParts), sizeof (DWORD), NULL)))
goto Fail;
xheadpart.fHeading = TRUE;
xheadpart.iHeading = 0;
if (IAddNewPlPos(&(lpDSIData->lpplxheadpart), &xheadpart, sizeof(XHEADPART), i) == -1)
goto Fail;
++i;
}
return(TRUE);
Fail:
if (i > 0)
FreeHeadPartPlex(lpDSIObj);
return(FALSE);
}
////////////////////////////////////////////////////////////////////////////////
//
// FReadAndInsertHeadingPairs
//
// Read all the Heading Pairs from the stream and insert'em above the
// appropriate document parts.
// The document parts were the first ones in the stream.
//
////////////////////////////////////////////////////////////////////////////////
BOOL PASCAL FReadAndInsertHeadingPairs(lpStm, lpDSIObj)
LPSTREAM lpStm; // Pointer to the stream
LPDSIOBJ lpDSIObj; // DSI object
{
XHEADPART xheadpart;
DWORD cT,cTotHead,dwType,i;
DWORD iOffset; // Used to insert the heading in the right place
cTotHead = lpDSIData->dwcTotHead; // save indirection
cT = 0;
iOffset = 0;
while (cT < cTotHead)
{
// Read the type
if ((!SUCCEEDED(lpStm->lpVtbl->Read (lpStm, &dwType, sizeof (DWORD), NULL)))
|| (dwType != VT_LPSTR))
goto Fail;
// Read the VT_LPSTR
if (!FLpstmReadVT_LPSTR (lpStm, &(xheadpart.lpstz), lpDSIData->lpfnFCPConvert, FALSE))
goto Fail;
// Read in the type for the VT_I4
if ((!SUCCEEDED(lpStm->lpVtbl->Read (lpStm, &dwType, sizeof (DWORD), NULL)))
|| (dwType != VT_I4))
goto Fail;
// Read in the VT_I4
if (!SUCCEEDED(lpStm->lpVtbl->Read (lpStm, &(xheadpart.dwParts), sizeof (DWORD), NULL)))
goto Fail;
xheadpart.fHeading = TRUE;
xheadpart.iHeading = 0;
if (IAddNewPlPos(&(lpDSIData->lpplxheadpart), &xheadpart, sizeof(XHEADPART), iOffset) == -1)
goto Fail;
// Make sure the document parts point to the correct heading
i = 1;
while (i <= xheadpart.dwParts)
lpDSIData->lpplxheadpart->rgxheadpart[iOffset + i++].iHeading = iOffset;
iOffset += xheadpart.dwParts+1;
++cT;
}
return TRUE;
Fail:
FreeHeadPartPlex(lpDSIObj);
return(FALSE);
}
////////////////////////////////////////////////////////////////////////////////
//
// FLpstmSaveDocSum
//
// Purpose:
// Write the data in the given object.
//
////////////////////////////////////////////////////////////////////////////////
BOOL PASCAL
FLpstmSaveDocSum
(LPDSIOBJ lpDSIObj, // Pointer to the object
LPSTREAM lpStm) // Pointer to the root storage
{
DWORD irg;
if ((lpDSIObj == NULL) ||
(lpDSIData == NULL) ||
(lpStm == NULL))
return FALSE;
// Codepage
gdwCurrentCP = GetACP();
if (!FLpstmWriteVT_I2 (lpStm, (WORD) gdwCurrentCP))
goto SaveFail;
// VT_LPSTR
for (irg = 0; irg < cDSIStringsMax; irg++)
{
if (lpDSIData->rglpstz[irg] != NULL)
{
if (!FLpstmWriteVT_LPSTR (lpStm, &(lpDSIData->rglpstz[irg][0]), TRUE, VT_LPSTR))
goto SaveFail;
}
}
// VT_I4
for (irg = PID_BYTECOUNT; irg < cdwDSIMax; irg++)
{
// Only actually save int stats that are not 0
if (FDocSumInfoPropBitIsSet(irg, lpDSIData->bPropSet))
{
if (!FLpstmWriteVT_I4 (lpStm, lpDSIData->rgdw[irg]))
goto SaveFail;
}
}
// VT_BOOL
if (!FLpstmWriteVT_BOOL (lpStm, lpDSIData->fScale))
goto SaveFail;
if (!FLpstmWriteVT_BOOL(lpStm, lpDSIData->fLinksChanged))
goto SaveFail;
// VT_LPSTR | VT_VECTOR
if (lpDSIData->dwcTotParts)
{
DWORD rgdw[2];
rgdw[0] = (VT_VECTOR | VT_LPSTR);
rgdw[1] = lpDSIData->dwcTotParts;
// The type & size of the vector
if (!FLpstmWrite (lpStm, rgdw, sizeof(rgdw)))
goto SaveFail;
// The vector entries - do NOT write out using FLpstmWriteVT_LPSTR -
// that puts the type id in too! Also remember the parts array is 1-based
irg = 0;
while (irg < lpDSIData->dwcTotParts+lpDSIData->dwcTotHead)
{
if (!(lpDSIData->lpplxheadpart->rgxheadpart[irg].fHeading))
{
if (!FLpstmWrite (lpStm, PSTZ (lpDSIData->lpplxheadpart->rgxheadpart[irg].lpstz),
CBSTR (lpDSIData->lpplxheadpart->rgxheadpart[irg].lpstz)+sizeof(DWORD)))
goto SaveFail;
}
++irg;
} // while
}
// VT_VECTOR | VT_VARIANT (PID_HEADINGPAIR)
// This is written out as: (VT_LPSTR, VT_I4), (VT_LPSTR, VT_I4), ......
if (lpDSIData->dwcTotHead)
{
DWORD rgdw[2];
rgdw[0] = (VT_VECTOR | VT_VARIANT);
rgdw[1] = 2*lpDSIData->dwcTotHead; // Since the string and count are written
// separately, double the vector size
// The type & size of the vector
if (!FLpstmWrite (lpStm, rgdw, sizeof(rgdw)))
goto SaveFail;
irg = 0;
while (irg < lpDSIData->dwcTotHead+lpDSIData->dwcTotParts)
{
if (lpDSIData->lpplxheadpart->rgxheadpart[irg].fHeading)
{
// The heading string
if (!FLpstmWriteVT_LPSTR (lpStm, lpDSIData->lpplxheadpart->rgxheadpart[irg].lpstz, FALSE, VT_LPSTR))
goto SaveFail;
// The number of parts for the heading
if (!FLpstmWriteVT_I4 (lpStm, lpDSIData->lpplxheadpart->rgxheadpart[irg].dwParts))
goto SaveFail;
}
++irg;
} // while
}
// Now do all the data we didn't understand.
if (!FLpstmWriteUnknowns (lpStm, lpDSIData->cbUnkMac, lpDSIData->rglpUnk))
goto SaveFail;
OfficeDirtyDSIObj (lpDSIObj, FALSE);
return TRUE;
SaveFail:
return FALSE;
} // FLpstmSaveDocSum
////////////////////////////////////////////////////////////////////////////////
//
// FLoadUserDef
//
// Purpose:
// Read the user-defined properties from the root stream and populate the
// object.
//
////////////////////////////////////////////////////////////////////////////////
BOOL
FLoadUserDef
(LPUDOBJ lpUDObj, // Pointer to object
LPPIDOFFSET lprgPO, // PId-offset table
DWORD irgPOMac, // Number of entries in lprgPO
LPSTREAM lpStm, // Stream data is in
BOOL fIntOnly) // Load Int Properties only?
{
LARGE_INTEGER liStmPos;
DWORD dwType;
DWORD irgPO;
LPUDPROP lpudprop;
DWORD dwcDict; // Number of dictionary entries
LPDICT rglpdict[DICTHASHMAX]; // Hash table for dictionary entries
BOOL fRead;
HRESULT hr;
BOOL fAdded; // Was the property added or updated. Here's why:
// 1) First we read the link value into lpudprop
// and add that to the list -- lpudprop should
// not be freed.
// 2) Then we read the link name and update the node
// we just added in 1). lpudprop should be
// freed.
if ((lpUDObj == NULL) ||
(lpUDData == NULL))
return FALSE;
// It's OK to have no property Id's.
if (irgPOMac == 0)
{
DebugSz ("No property Id's");
goto LoadDone;
}
liStmPos.HighPart = 0;
liStmPos.LowPart = 0;
// Read the dictionary first
for (irgPO = 0; irgPO < irgPOMac; irgPO++)
{
if (lprgPO[irgPO].Id == PID_DICT)
{
liStmPos.LowPart = lprgPO[irgPO].dwOffset;
break;
}
} // for
AssertSz ((liStmPos.LowPart != 0), "User Defined Property stream must have a dictionary");
if (liStmPos.LowPart == 0)
{
// REVIEW: Figure out what to do if there is no dictionary!
goto LoadFail;
}
// Read in the dictionary
dwcDict = DICTHASHMAX;
FillBuf (rglpdict, 0, sizeof(rglpdict));
if (!FLpstmLoadDict (lpStm, &dwcDict, (LPDICT *) &rglpdict,
lpUDData->lpfnFCPConvert))
goto LoadFail;
// Read the codepage from the stream. If it's not there,
// assume it is the default.
if (!FGetCodepage (lpStm, lprgPO, irgPOMac, &gdwFileCP))
gdwFileCP = GetACP();
else
{
// This property would have shown up as an unknown one.
if (lpUDData->cbUnkMac > 0)
lpUDData->cbUnkMac--;
}
gdwCurrentCP = GetACP();
// Read in each property value.
lpudprop = NULL;
for (irgPO = 0; irgPO < irgPOMac; irgPO++)
{
// We already read the codepage, so skip it.
if ((lprgPO[irgPO].Id != PID_CODEPAGE) && (lprgPO[irgPO].Id != PID_DICT))
{
fRead = FALSE;
liStmPos.LowPart = lprgPO[irgPO].dwOffset;
hr = lpStm->lpVtbl->Seek (lpStm, liStmPos, STREAM_SEEK_SET, NULL);
if (!SUCCEEDED (hr))
{
DebugHr (hr);
goto LoadFail;
}
hr = lpStm->lpVtbl->Read (lpStm, &dwType, sizeof (DWORD), NULL);
if (!SUCCEEDED (hr))
{
DebugHr (hr);
goto LoadFail;
}
// Create an object to hold the new data
if (lpudprop == NULL)
{
lpudprop = PvMemAlloc(sizeof(UDPROP));
if (lpudprop == NULL)
goto LoadFail;
FillBuf (lpudprop, 0, sizeof(UDPROP));
lpudprop->udtype = wUDinvalid;
}
if (fIntOnly && dwType!=VT_I4)
continue;
// Read in the right data.
switch (dwType)
{
case VT_LPSTR :
if (!FLpstmReadVT_LPSTR (lpStm, (LPSTR *) &(lpudprop->lpvValue), lpUDData->lpfnFCPConvert, FALSE))
goto LoadFail;
lpudprop->udtype = wUDlpsz;
fRead = TRUE;
break;
case VT_I4 :
hr = lpStm->lpVtbl->Read (lpStm, &(lpudprop->lpvValue), sizeof (DWORD), NULL);
if (!SUCCEEDED (hr))
{
DebugHr (hr);
goto LoadFail;
}
lpudprop->udtype = wUDdw;
fRead = TRUE;
break;
case VT_BOOL :
if (!FLpstmReadVT_BOOL (lpStm, (WORD *) &(lpudprop->lpvValue)))
goto LoadFail;
lpudprop->udtype = wUDbool;
fRead = TRUE;
break;
case VT_FILETIME:
case VT_R8 :
Assert(sizeof(NUM) == sizeof(FILETIME));
lpudprop->lpvValue = PvMemAlloc(sizeof(NUM));
if (lpudprop->lpvValue == NULL)
goto LoadFail;
if (dwType == VT_FILETIME)
{
if (!SUCCEEDED
(lpStm->lpVtbl->Read (lpStm, lpudprop->lpvValue,
sizeof(FILETIME), NULL)))
goto LoadFail;
}
else if (!FLpstmReadVT_R8_DATE(lpStm, (NUM *)lpudprop->lpvValue))
goto LoadFail;
lpudprop->udtype = (dwType == VT_R8) ? wUDfloat : wUDdate;
fRead = TRUE;
break;
} // switch (dwType)
if (!fRead)
goto NextOne;
else
{
// Add this property to the map.
if (!FAddPropToList (lpUDObj, rglpdict, lprgPO[irgPO].Id, lpudprop, &fAdded))
// REVIEW: What do we do with PID's not in the dictionary?
goto LoadFail;
if (!fAdded)
{
DeallocValue(&(lpudprop->lpvValue), lpudprop->udtype);
NextOne:
VFreeMemP(lpudprop, sizeof(UDPROP));
}
lpudprop = NULL;
}
} // !PID_DICT !PID_CODEPAGE
} // for
LoadDone:
FreeRgDictionary (lpUDObj, rglpdict);
OfficeDirtyUDObj (lpUDObj, FALSE);
return TRUE;
LoadFail:
if (lpUDData->rglpUnk != NULL)
VFreeMemP(lpUDData->rglpUnk, lpUDData->cbUnkMac*sizeof(PROPIDTYPELP));
// REVIEW: do we need to free the value??
if (lpudprop != NULL)
VFreeMemP(lpudprop, sizeof(UDPROP));
FreeRgDictionary (lpUDObj, rglpdict);
return FALSE;
} // FLoadUserDef
////////////////////////////////////////////////////////////////////////////////
//
// FLpstmSaveUserDef
//
// Purpose:
// Write the data stored in the user-defined property object to the stream.
//
////////////////////////////////////////////////////////////////////////////////
BOOL PASCAL
FLpstmSaveUserDef
(LPUDOBJ lpUDObj, // Pointer to object
LPSTREAM lpStm, // Pointer to stream
LPDICT *lplpdict) // Pointer to dictionary.
{
LPUDPROP lpudp;
BOOL f;
if ((lpUDObj == NULL) ||
(lpUDData == NULL) ||
(lplpdict == NULL) ||
(lpStm == NULL))
return FALSE;
// Write out the dictionary.
f = FLpstmWriteDict (lpStm, lpUDData->dwcProps, *lplpdict);
// There is only 1 dictionary here i.e. a linked list, so we shouldn't call
// FreeRgDictionary
FreeDictionaryAlone (lpUDObj, *lplpdict);
if (!f)
goto SaveFail;
// Codepage
gdwCurrentCP = GetACP();
if (!FLpstmWriteVT_I2 (lpStm, (WORD) gdwCurrentCP))
goto SaveFail;
// Traverse the list, writing out the data.
lpudp = lpUDData->lpudpHead;
while (lpudp != NULL)
{
if (!FLpstmWriteUDdata (lpStm, lpudp->udtype, lpudp->lpvValue))
goto SaveFail;
// If there's link data, write it out....
if (lpudp->lpstzLink != NULL)
{
if (!FLpstmWriteVT_LPSTR (lpStm, lpudp->lpstzLink, TRUE, VT_LPSTR))
goto SaveFail;
}
// Same goes for IMoniker data
if (lpudp->lpstzIMoniker != NULL)
{
if (!FLpstmWriteVT_LPSTR (lpStm, lpudp->lpstzIMoniker, TRUE, VT_LPSTR))
goto SaveFail;
}
lpudp = (LPUDPROP) lpudp->llist.lpllistNext;
} // while
// Now do all the data we didn't understand.
if (!FLpstmWriteUnknowns (lpStm, lpUDData->cbUnkMac, lpUDData->rglpUnk))
goto SaveFail;
OfficeDirtyUDObj (lpUDObj, FALSE);
return TRUE;
SaveFail:
return FALSE;
} // FLpstmSaveUserDef
////////////////////////////////////////////////////////////////////////////////
//
// DwLpstmReadHdrAndFID
//
// Purpose:
// Reads the header, fmtid-offset table, and allocates space
// to hold them all.
//
////////////////////////////////////////////////////////////////////////////////
DWORD PASCAL
DwLpstmReadHdrAndFID
(LPSTORAGE FAR *lplpStg, // Storage the stream is in
LPSTREAM *lplpStm, // Pointer to stream
WCHAR FAR *lpszdw, // Name of stream to open
DWORD *pcSect, // Number of sections
LPIDOFFSET FAR *lprglpFIdOff, // Fmtid-offset table
LPSECTION FAR *lprglpSect) // Section headers.
{
DWORD dwT;
dwT = DwLpstmReadHdr (*lplpStg, lpszdw, lplpStm, pcSect);
if (dwT != MSO_IO_SUCCESS)
return(dwT);
// Create space to hold all of the sections we don't understand. At
// this point, we don't know if we understand any of them.
*lprglpFIdOff = PvMemAlloc(*pcSect*sizeof(IDOFFSET));
if (*lprglpFIdOff == NULL)
goto ReadFailed;
*lprglpSect = PvMemAlloc(*pcSect*sizeof(SECTION));
if (*lprglpSect == NULL)
goto ReadFailed;
// Read in the format id/offset section of data
if (!FLpstmLoadFmtIdSection (*lplpStm, *lprglpFIdOff, *pcSect))
goto ReadFailed;
return MSO_IO_SUCCESS;
ReadFailed:
if (*lprglpFIdOff != NULL)
VFreeMemP (*lprglpFIdOff,*pcSect*sizeof(IDOFFSET));
if (*lprglpSect != NULL)
VFreeMemP (*lprglpSect,*pcSect*sizeof(SECTION));
return MSO_IO_ERROR;
} // DwLpstmReadHdrAndFID
////////////////////////////////////////////////////////////////////////////////
//
// DwLpstmReadHdr
//
// Purpose:
// Open the stream and read the header.
////////////////////////////////////////////////////////////////////////////////
DWORD PASCAL
DwLpstmReadHdr
(LPSTORAGE lpStg, // Storage to open stream in
WCHAR FAR *lpstzwName, // Name of stream
LPSTREAM *lplpStm, // Place to stuff stream pointer
ULONG *pcSect) // Number of sections in stream
{
HRESULT hr;
PROPSETHDR hdr;
STATSTG statstg;
HGLOBAL hglobal;
Assert (lplpStm != NULL);
Assert (lpStg != NULL);
Assert (pcSect != NULL);
hr = lpStg->lpVtbl->OpenStream (lpStg, lpstzwName, NULL,
STGM_SHARE_EXCLUSIVE | STGM_READ, 0, lplpStm);
if (!SUCCEEDED(hr))
{
if (hr == STG_E_FILENOTFOUND) // The stream just wasn't there, no biggie
return(MSO_IO_NOSTM);
return(MSO_IO_ERROR);
}
if ((lplpStm == NULL) || (*lplpStm == NULL))
return MSO_IO_ERROR;
if (!SUCCEEDED((*lplpStm)->lpVtbl->Stat(*lplpStm, &statstg, STATFLAG_NONAME)))
goto Error;
// If stream is small enough, cache it in memory. Faster loading
if ((statstg.cbSize.HighPart == 0) && (statstg.cbSize.LowPart <= MAX_STREAM_CB))
{
hglobal = GlobalAlloc(GMEM_MOVEABLE, statstg.cbSize.LowPart);
(*lplpStm)->lpVtbl->Read(*lplpStm,GlobalLock(hglobal), statstg.cbSize.LowPart, NULL);
GlobalUnlock(hglobal);
(*lplpStm)->lpVtbl->Release(*lplpStm);
if (!SUCCEEDED(CreateStreamOnHGlobal(hglobal, fTrue, lplpStm)))
{
GlobalFree(hglobal);
*lplpStm = NULL;
goto Error;
}
}
hr = (*lplpStm)->lpVtbl->Read (*lplpStm, &hdr, sizeof (PROPSETHDR), NULL);
// Bail if we don't understand the header.
if ((!SUCCEEDED(hr)) ||
(hdr.wByteOrder != wIntelByteOrder) ||
(hdr.wFormat != 0) ||
((*pcSect = hdr.cSections) == 0)) // Note that *pc is assigned here...
{
Error:
if ((lplpStm != NULL) && (*lplpStm != NULL))
{
(*lplpStm)->lpVtbl->Release(*lplpStm);
*lplpStm = NULL;
}
if (lpStg != NULL)
lpStg->lpVtbl->Revert (lpStg);
return MSO_IO_ERROR;
}
gfMacintosh = (HIWORD (hdr.dwOSVersion) == wMacintosh);
return MSO_IO_SUCCESS;
} // DwLpstmReadHdr
////////////////////////////////////////////////////////////////////////////////
//
// FLpstmWriteHdr
//
// Purpose:
// Opens the stream and writes out the header.
//
////////////////////////////////////////////////////////////////////////////////
BOOL PASCAL
FLpstmWriteHdr
(LPSTORAGE lpStg, // Storage to open stream in
WCHAR FAR *lpstzwName, // Name of stream
LPSTREAM *lplpStm, // Place to stuff stream pointer
ULONG cSect, // Number of sections in stream
BOOL fSimpleDocFile) // Is the file a simple docfile
{
HRESULT hr;
PROPSETHDR hdr;
ULARGE_INTEGER uli;
DWORD grfMode;
uli.LowPart = FOURKB;
uli.HighPart = 0;
// First try to open an existing stream. If it fails, try creating it.
hr = lpStg->lpVtbl->OpenStream (lpStg, lpstzwName, NULL,
STGM_SHARE_EXCLUSIVE | STGM_WRITE,
0, lplpStm);
if (!SUCCEEDED (hr))
{
if (fSimpleDocFile)
grfMode = STGM_SHARE_EXCLUSIVE | STGM_READWRITE;
else
grfMode = STGM_SHARE_EXCLUSIVE | STGM_CREATE | STGM_WRITE;
hr = lpStg->lpVtbl->CreateStream (lpStg, lpstzwName,grfMode,0, 0, lplpStm);
if ((!SUCCEEDED(hr)) ||
(lplpStm == NULL) ||
(*lplpStm == NULL))
{
DebugHr (hr);
return FALSE;
}
}
(*lplpStm)->lpVtbl->SetSize (*lplpStm, uli);
hdr.wByteOrder = wIntelByteOrder;
hdr.wFormat = 0;
hdr.dwOSVersion = (DWORD) MAKELONG (LOWORD (GetVersion()), wWin32);
hdr.clsid = CLSID_NULL;
hdr.cSections = cSect;
if (!FLpstmWrite (*lplpStm, &hdr, sizeof (PROPSETHDR)))
{
AssertSz (0, "Couldn't write header");
if (lpStg != NULL)
lpStg->lpVtbl->Revert (lpStg);
*lplpStm = NULL;
return FALSE;
}
return TRUE;
} // FLpstmWriteHdr
////////////////////////////////////////////////////////////////////////////////
//
// FLpstmLoadFmtIdSection
//
// Purpose:
// Read the format id/offset pairs
//
////////////////////////////////////////////////////////////////////////////////
BOOL PASCAL
FLpstmLoadFmtIdSection
(LPSTREAM lpStm, // Pointer to stream
LPIDOFFSET rglpFIdOff, // Array of format ids
DWORD cSect) // Number of sections
{
HRESULT hr;
hr = lpStm->lpVtbl->Read (lpStm, rglpFIdOff, cSect*sizeof(IDOFFSET), NULL);
if (!SUCCEEDED (hr))
{
DebugHr (hr);
return FALSE;
}
return TRUE;
} // FLpstmLoadFmtIdSection
////////////////////////////////////////////////////////////////////////////////
//
// FLpstmWriteFmtIdSection
//
// Purpose:
// Write the format id/offset pairs.
//
// Notes: This will write the given FmtId first, then any other
// sections afterwards. This is done mostly to save older browsers
// when the Summary Info stream is stream is written.
//
////////////////////////////////////////////////////////////////////////////////
BOOL PASCAL
FLpstmWriteFmtIdSection
(LPSTREAM lpStm, // Pointer to stream
LPSECTION rglpSect, // The section headers
LPIDOFFSET rglpFIdOff, // Array of fmtid's
GUID fmtid, // The FMTID to read the table for
DWORD cSect) // Number of sections
{
DWORD irg;
DWORD dwOffset;
// First offset will start after the header and fmtid-offset table
// is written.
dwOffset = sizeof (PROPSETHDR) + cSect*sizeof(IDOFFSET);
// Loop through the sections, put the fmtid-offset pair for
// the given fmtid first to save other browsers.
for (irg = 0; irg < cSect; irg++)
{
// See if it is the one we understand....
if (MsoIsEqualGuid ((REFGUID) &fmtid, (REFGUID) &(rglpFIdOff[irg].Id)))
{
rglpFIdOff[irg].dwOffset = dwOffset;
if (!FLpstmWrite (lpStm, &(rglpFIdOff[irg]), sizeof(IDOFFSET)))
return FALSE;
dwOffset += rglpSect[irg].cb;
}
}
// Now do the ones that we didn't understand.
for (irg = 0; irg < cSect; irg++)
{
// See if it is one we don't understand....
if (!(MsoIsEqualGuid ((REFGUID) &fmtid, (REFGUID) &(rglpFIdOff[irg].Id))))
{
rglpFIdOff[irg].dwOffset = dwOffset;
if (!FLpstmWrite (lpStm, &(rglpFIdOff[irg]), sizeof(IDOFFSET)))
return FALSE;
dwOffset += rglpSect[irg].cb;
}
}
return TRUE;
} // FLpstmWriteFmtIdSection
////////////////////////////////////////////////////////////////////////////////
//
// FLpstmWritePropOffTable
//
// Purpose:
// Write the Property Id & Offset table.
// Also, adjust the offsets to start after the table.
//
////////////////////////////////////////////////////////////////////////////////
BOOL PASCAL
FLpstmWritePropOffTable
(LPSTREAM lpStm, // Pointer to stream
PIDOFFSET rgPO[], // Pointer to Prop-offset table
DWORD cTable, // Size of table
DWORD cbData) // Size of the data
{
SECTION sect;
DWORD irgPO;
// Fill out section header and write it
sect.cb = sizeof(PIDOFFSET)*cTable + sizeof(SECTION);
sect.cProps = cTable;
// Adjust the offsets to start at the end of the table.
for (irgPO = 0; irgPO < cTable; irgPO++)
rgPO[irgPO].dwOffset += sect.cb + CBALIGN32 (sect.cb);
sect.cb += cbData;
sect.cb += CBALIGN32 (sect.cb);
if (!FLpstmWrite (lpStm, &sect, sizeof(SECTION)))
return FALSE;
#ifdef DEBUG
#ifdef LOTS_O_DEBUG
{
char tbuf[100];
char tbuf2[1000];
DWORD i;
tbuf2[0] = '\0';
for (i = 0; i < irgPO; i++)
{
sprintf (tbuf, "Offset: %ld\tPID: %lx\n", rgPO[i].dwOffset, rgPO[i].Id);
strcat (tbuf2, tbuf);
}
MessageBox (GetFocus(), tbuf2, NULL, MB_OK);
}
#endif //lots_o_debug
#endif //debug
// Write the table.
return(FLpstmWrite (lpStm, rgPO, sizeof(PIDOFFSET)*cTable));
} // FLpstmWritePropOffTable
////////////////////////////////////////////////////////////////////////////////
//
// FLpstmLoadDict
//
// Purpose:
// Read in the dictionary
//
////////////////////////////////////////////////////////////////////////////////
BOOL PASCAL
FLpstmLoadDict
(LPSTREAM lpStm, // Pointer to stream
DWORD *dwc, // Number of entries in the hash table
LPDICT *rglpdict, // Dictionary hash table
BOOL (*lpfnFCPConvert)(LPSTR, DWORD, DWORD, BOOL)) // Code page converter
{
DWORD dwcEntries;
DWORD irg;
DWORD dwHash;
HRESULT hr;
LPDICT lpdict;
// Get the number of entries in the dictionary
hr = lpStm->lpVtbl->Read (lpStm, &dwcEntries, sizeof(DWORD), NULL);
if (!SUCCEEDED (hr))
{
DebugHr (hr);
return FALSE;
}
// Read in each entry and add it to the hash table
for (irg = 0; irg < dwcEntries; irg++)
{
lpdict = PvMemAlloc(sizeof(DICT));
if (lpdict == NULL)
return FALSE;
hr = lpStm->lpVtbl->Read (lpStm, &(lpdict->dwPId), sizeof(DWORD), NULL);
if (!SUCCEEDED (hr))
{
DebugHr (hr);
goto LoadFail;
}
if (!FLpstmReadVT_LPSTR (lpStm, &(lpdict->lpstz), lpfnFCPConvert, FALSE))
goto LoadFail;
// Always insert the node at the beginning of the bucket
lpdict->llist.lpllistPrev = NULL;
dwHash = lpdict->dwPId % *dwc;
if (rglpdict[dwHash] == NULL)
{
rglpdict[dwHash] = lpdict;
lpdict->llist.lpllistNext = NULL;
}
else
{
lpdict->llist.lpllistNext = (LLIST *) rglpdict[dwHash];
rglpdict[dwHash]->llist.lpllistPrev = (LLIST *) lpdict;
rglpdict[dwHash] = lpdict;
}
} // for
// Don't forget to give back the number of dictionary entries.
*dwc = dwcEntries;
return TRUE;
LoadFail:
if (lpdict != NULL)
VFreeMemP(lpdict, sizeof(DICT));
return FALSE;
} // FLpstmLoadDict
////////////////////////////////////////////////////////////////////////////////
//
// FLpstmWriteDict
//
// Purpose:
// Write the dictionary to the stream.
//
////////////////////////////////////////////////////////////////////////////////
BOOL PASCAL
FLpstmWriteDict
(LPSTREAM lpStm, // Pointer to stream
DWORD dwc, // Number of dictionary entries
LPDICT lpdict) // Pointer to head of dictionary list
{
DWORD cb;
// Write out the number of dictionary entries
if (!FLpstmWrite (lpStm, &dwc, sizeof(DWORD)))
return FALSE;
// Go through the list of dictionary entries and write each one out.
while (lpdict != NULL)
{
cb = CBBUF (lpdict->lpstz);
CBBUF (lpdict->lpstz) = lpdict->dwPId;
// Write out the Property Id & Name
if (!FLpstmWrite (lpStm, &(lpdict->lpstz[0]), CBSTR(lpdict->lpstz)+2*sizeof(DWORD)))
return FALSE;
CBBUF (lpdict->lpstz) = cb;
lpdict = (LPDICT) lpdict->llist.lpllistNext;
} // while
return TRUE;
} // FLpstmWriteDict
// Function: FOFCValidFmtID
//
// Purpose: To check if a guid is one we understand
//
// Returns: TRUE if we do, FALSE if we don't
//
// Author: martinth, 09/18/94
//
BOOL PASCAL FOFCValidFmtID(REFGUID rguid)
{
return (MsoIsEqualGuid (rguid , (REFGUID) &FormatID_SummaryInformation) ||
MsoIsEqualGuid (rguid , (REFGUID) &FormatID_DocumentSummaryInformation) ||
MsoIsEqualGuid (rguid , (REFGUID) &FormatID_UserDefinedProperties));
}
////////////////////////////////////////////////////////////////////////////////
//
// FLpstmWriteOtherSections
//
// Purpose:
// Write out the sections we didn't understand.
//
////////////////////////////////////////////////////////////////////////////////
BOOL PASCAL
FLpstmWriteOtherSections
(LPSTREAM lpStm, // Pointer to stream
LPSECTION rglpSect, // Section headers
LPIDOFFSET rglpFIdOff, // Fmtid-offset pairs
LPVOID rglpFIdOffData[], // Section data
DWORD cSect) // Number of sections
{
DWORD irg;
// Loop through the sections, writing out all but the one specified
// by fmtid
for (irg = 0; irg < cSect; irg++)
{
// See if it is the one we don't want to write
if (!FOFCValidFmtID((REFGUID) &(rglpFIdOff[irg].Id)))
{
// Write the section header
if (!FLpstmWrite (lpStm, &(rglpSect[irg]), sizeof(SECTION)))
return FALSE;
// And the data for it
if ((rglpSect[irg].cb > sizeof(SECTION)))
{
if (!FLpstmWrite (lpStm, rglpFIdOffData[irg], rglpSect[irg].cb-sizeof(SECTION)))
return FALSE;
}
}
} // for
return TRUE;
} // FLpstmWriteOtherSections
////////////////////////////////////////////////////////////////////////////////
//
// FLpstmWriteUDdata
//
// Purpose:
// Writes out the given data, based on the type.
//
////////////////////////////////////////////////////////////////////////////////
BOOL PASCAL
FLpstmWriteUDdata
(LPSTREAM lpStm, // Stream to write to
UDTYPES udtype, // The type of the data
LPVOID lpv) // The data
{
BOOL f;
switch (udtype)
{
case wUDlpsz :
f = FLpstmWriteVT_LPSTR (lpStm, (LPSTR) lpv, TRUE, VT_LPSTR);
break;
case wUDdate :
f = FLpstmWriteVT_FILETIME(lpStm, (LPFILETIME)lpv);
break;
case wUDfloat :
f = FLpstmWriteVT_R8_DATE(lpStm, (NUM *) lpv, FALSE);
break;
case wUDdw :
f = FLpstmWriteVT_I4 (lpStm, (DWORD) lpv);
break;
case wUDbool :
f = FLpstmWriteVT_BOOL (lpStm, (WORD) lpv);
break;
default :
AssertSz (0, "Bogus type!");
f = FALSE;
break;
} // switch
return (f);
} // FLpstmWriteUDdata
////////////////////////////////////////////////////////////////////////////////
//
// FGetCodepage
//
// Purpose:
// Reads the codepage property from the stream. If there is no
// codepage in the stream, this returns FALSE.
//
////////////////////////////////////////////////////////////////////////////////
static BOOL PASCAL
FGetCodepage
(LPSTREAM lpStm, // Stream to look in
LPPIDOFFSET rgPO, // Table of Property Id's & offsets
DWORD cElem, // Number of entries in the table
DWORD *pdwCodepage) // Pointer to put codepage in
{
DWORD irg;
LARGE_INTEGER liStmPos;
HRESULT hr;
DWORD rgdw[2];
if ((lpStm == NULL) ||
(rgPO == NULL) ||
(pdwCodepage == NULL))
return FALSE;
for (irg = 0; irg < cElem; irg++)
{
if (rgPO[irg].Id == PID_CODEPAGE)
{
liStmPos.HighPart = 0;
liStmPos.LowPart = rgPO[irg].dwOffset;
hr = lpStm->lpVtbl->Seek (lpStm, liStmPos, STREAM_SEEK_SET, NULL);
if (!SUCCEEDED (hr))
{
DebugHr (hr);
return FALSE;
}
hr = lpStm->lpVtbl->Read (lpStm, rgdw, sizeof(rgdw), NULL);
if (!SUCCEEDED (hr))
{
DebugHr (hr);
return FALSE;
}
if (rgdw[0] != VT_I2)
{
DebugSz ("PID One has the wrong type.");
return FALSE;
}
*pdwCodepage = rgdw[1];
return TRUE;
}
}
return FALSE;
} // FGetCodepage
#ifdef DEBUG
void DoSIChkMem(LPSIOBJ lpSIObj, void(OFC_CALLBACK *PfnOfficeMem)(void *pv, int cb))
{
#define lpData ((LPSINFO) ((LPOFFICESUMINFO) lpSIObj)->m_lpData)
int irglpstz;
if ((lpSIObj==NULL))
return;
if (lpData != NULL)
{
(*PfnOfficeMem)(lpData, sizeof(SINFO));
if (lpData->ppldocprop != NULL)
(*PfnOfficeMem)(lpData->ppldocprop, CbPlAlloc(lpData->ppldocprop));
for (irglpstz = 0; irglpstz < cSIStringsMax; irglpstz++)
{
if (lpData->rglpstz[irglpstz] != NULL)
(*PfnOfficeMem)(lpData->rglpstz[irglpstz], CBBUF(lpData->rglpstz[irglpstz]));
}
if (lpData->fSINail)
{
if (lpData->SINail.pbFMTID != NULL)
(*PfnOfficeMem)(lpData->SINail.pbFMTID, CbThumbNailFMTID(lpData->SINail.cftag));
if (lpData->SINail.pbData != NULL)
(*PfnOfficeMem)(lpData->SINail.pbData, lpData->SINail.cbData);
}
if (lpData->rglpUnk != NULL)
(*PfnOfficeMem)(lpData->rglpUnk, lpData->cbUnkMac*sizeof(PROPIDTYPELP));
if (lpData->rglpFIdOffData != NULL)
(*PfnOfficeMem)(lpData->rglpFIdOffData, lpData->cSect*sizeof(LPVOID));
if (lpData->rglpFIdOff != NULL)
(*PfnOfficeMem)(lpData->rglpFIdOff, lpData->cSect*sizeof(IDOFFSET));
if (lpData->rglpSect != NULL)
(*PfnOfficeMem)(lpData->rglpSect, lpData->cSect*sizeof(SECTION));
}
(*PfnOfficeMem)(lpSIObj, sizeof(OFFICESUMINFO));
#undef lpData
}
void DoDSIChkMem(LPDSIOBJ lpDSIObj, void(OFC_CALLBACK *PfnOfficeMem)(void *pv, int cb))
{
int irg;
#define lpData ((LPDSINFO) ((LPDOCSUMINFO) lpDSIObj)->m_lpData)
if ((lpDSIObj == NULL))
return;
if (lpData != NULL)
{
(*PfnOfficeMem)(lpData, sizeof(DSINFO));
if (lpData->ppldocprop != NULL)
(*PfnOfficeMem)(lpData->ppldocprop, CbPlAlloc(lpData->ppldocprop));
for (irg = 0; irg < cDSIStringsMax; irg++)
{
if (lpData->rglpstz[irg] != NULL)
(*PfnOfficeMem)((void *)lpData->rglpstz[irg],CBBUF(lpData->rglpstz[irg]));
}
if (lpData->rglpFIdOffData != NULL)
(*PfnOfficeMem)(lpData->rglpFIdOffData,lpData->cSect*sizeof(LPVOID));
if (lpData->rglpFIdOff != NULL)
(*PfnOfficeMem)(lpData->rglpFIdOff, lpData->cSect*sizeof(IDOFFSET));
if (lpData->rglpSect != NULL)
(*PfnOfficeMem)(lpData->rglpSect, lpData->cSect*sizeof(SECTION));
if(lpData->cbUnkMac != 0)
(*PfnOfficeMem)(lpData->rglpUnk,lpData->cbUnkMac*sizeof(PROPIDTYPELP));
if (lpData->lpplxheadpart != NULL)
{
SHORT irg;
for (irg = 0; irg < lpData->lpplxheadpart->ixheadpartMac; ++irg)
(*PfnOfficeMem)(lpData->lpplxheadpart->rgxheadpart[irg].lpstz,
CBBUF(lpData->lpplxheadpart->rgxheadpart[irg].lpstz));
(*PfnOfficeMem)(lpData->lpplxheadpart, CbPlAlloc(lpData->lpplxheadpart));
}
}
(*PfnOfficeMem)(lpDSIObj, sizeof(DOCSUMINFO));
#undef lpData
}
void DoUDChkMem(LPUDOBJ lpUDObj, void(OFC_CALLBACK *PfnOfficeMem)(void *pv, int cb))
{
#define lpUDObjData ((LPUDINFO)(((LPUDOBJ) lpUDObj)->m_lpData))
LPUDPROP lpudp;
DWORD cb=0;
if (lpUDObj==NULL)
return;
if (lpUDObjData != NULL)
{
(*PfnOfficeMem)(lpUDObjData, sizeof(UDINFO));
if (lpUDObjData->ppldocprop != NULL)
(*PfnOfficeMem)(lpUDObjData->ppldocprop, CbPlAlloc(lpUDObjData->ppldocprop));
lpudp = lpUDObjData->lpudpHead;
while (lpudp != NULL)
{
if (lpudp->lpstzName != NULL)
(*PfnOfficeMem)(lpudp->lpstzName,CBBUF(lpudp->lpstzName));
if (lpudp->lpstzLink != NULL)
(*PfnOfficeMem)(lpudp->lpstzLink,CBBUF(lpudp->lpstzLink));
if (lpudp->lpstzIMoniker != NULL)
(*PfnOfficeMem)(lpudp->lpstzIMoniker,CBBUF(lpudp->lpstzIMoniker));
// Separate by type because we'll probably need size data later....
switch (lpudp->udtype)
{
case wUDdate :
case wUDfloat :
cb = sizeof(NUM);
break;
case wUDlpsz :
cb = CBBUF((LPSTR)(lpudp->lpvValue));
break;
case wUDbool:
case wUDdw:
cb=0;
break;
default:
Assert(fFalse);
cb=0;
break;
}
if(cb>0)
(*PfnOfficeMem)(lpudp->lpvValue,cb);
(*PfnOfficeMem)(lpudp, sizeof(UDPROP));
lpudp = (LPUDPROP) lpudp->llist.lpllistNext;
}
lpudp = lpUDObjData->lpudpTmpHead;
while (lpudp != NULL)
{
if (lpudp->lpstzName != NULL)
(*PfnOfficeMem)(lpudp->lpstzName,CBBUF(lpudp->lpstzName));
if (lpudp->lpstzLink != NULL)
(*PfnOfficeMem)(lpudp->lpstzLink,CBBUF(lpudp->lpstzLink));
if (lpudp->lpstzIMoniker != NULL)
(*PfnOfficeMem)(lpudp->lpstzIMoniker,CBBUF(lpudp->lpstzIMoniker));
// Separate by type because we'll probably need size data later....
switch (lpudp->udtype)
{
case wUDdate :
case wUDfloat :
cb = sizeof(NUM);
break;
case wUDlpsz :
cb = CBBUF((LPSTR)(lpudp->lpvValue));
break;
case wUDbool:
case wUDdw:
cb=0;
break;
default:
Assert(fFalse);
cb=0;
break;
}
if(cb>0)
(*PfnOfficeMem)(lpudp->lpvValue,cb);
(*PfnOfficeMem)(lpudp, sizeof(UDPROP));
lpudp = (LPUDPROP) lpudp->llist.lpllistNext;
}
if(lpUDObjData->rglpUnk != NULL)
(*PfnOfficeMem)(lpUDObjData->rglpUnk, lpUDObjData->cbUnkMac*sizeof(PROPIDTYPELP));
if (lpUDObjData->rglpFIdOffData != NULL)
(*PfnOfficeMem)(lpUDObjData->rglpFIdOffData, lpUDObjData->cSect*sizeof(LPVOID));
if (lpUDObjData->rglpFIdOff != NULL)
(*PfnOfficeMem)(lpUDObjData->rglpFIdOff, lpUDObjData->cSect*sizeof(IDOFFSET));
if (lpUDObjData->rglpSect != NULL)
(*PfnOfficeMem)(lpUDObjData->rglpSect, lpUDObjData->cSect*sizeof(SECTION));
}//if
(*PfnOfficeMem)(lpUDObj, sizeof(USERPROP));
#undef lpUDObjData
}
#ifndef WINNT
void DoDocPropChkMem(void(OFC_CALLBACK *PfnOfficeMem)(void *pv, int cb))
{
int ixopro;
PLXOPRO *pplxopro;
if((pplxopro=oinfo.pplxopro) != NULL)
{
(*PfnOfficeMem)(pplxopro, CbPlAlloc(pplxopro));
for(ixopro=0;ixopro<pplxopro->ixoproMac; ixopro++)
{
if(pplxopro->rgxopro[ixopro].typ==typSI)
DoSIChkMem(pplxopro->rgxopro[ixopro].lpSIObj, PfnOfficeMem);
else if(pplxopro->rgxopro[ixopro].typ==typDSI)
DoDSIChkMem(pplxopro->rgxopro[ixopro].lpDSIObj, PfnOfficeMem);
else{
Assert(pplxopro->rgxopro[ixopro].typ==typUD);
DoUDChkMem(pplxopro->rgxopro[ixopro].lpUDObj, PfnOfficeMem);
}
}
}
}
#endif
#endif // DEBUG