Leaked source code of windows server 2003
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.
 
 
 
 
 
 

5917 lines
172 KiB

//+--------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1996.
//
// File: proptest.cxx
//
// Description: This is the main file for all the tests
// of Property set interfaces.
//
//---------------------------------------------------------------
//
// IPropertyStorage tests
//
// NOTE: we require more headers here just for convenience of
// the test program, for other programs only "props.h" should be
// needed.
#ifndef _UNIX
#define INITGUID
#endif // _UNIX
#include "../../props/h/windef.h"
#include "../../h/props.h"
#include "../../props/h/propapi.h"
#include "../../props/h/propset.hxx"
#include "cpropvar.hxx"
#include "../../props/h/propmac.hxx"
#include "../../time.hxx"
#include "proptest.hxx"
#include <sys/types.h>
#include <sys/stat.h>
#ifdef _WIN32
#include <direct.h> // _mkdir is stored here in win32
#endif
// -------
// Globals
// -------
#ifndef _UNIX
// again for static linkage, these symbols will be duplicated.
OLECHAR aocMap[33], oszSummary[19], oszDocumentSummary[27];
GUID guidSummary =
{ 0xf29f85e0,
0x4ff9, 0x1068,
{ 0xab, 0x91, 0x08, 0x00, 0x2b, 0x27, 0xb3, 0xd9 } };
GUID guidDocumentSummary =
{ 0xd5cdd502,
0x2e9c, 0x101b,
{ 0x93, 0x97, 0x08, 0x00, 0x2b, 0x2c, 0xf9, 0xae } };
GUID guidDocumentSummary2 =
{ 0xd5cdd505,
0x2e9c, 0x101b,
{ 0x93, 0x97, 0x08, 0x00, 0x2b, 0x2c, 0xf9, 0xae } };
#else
extern LPOLESTR aocMap, oszSummary, oszDocumentSummary;
extern GUID guidSummary, guidDocumentSummary, guidDocumentSummary2;
#endif // _UNIX
LARGE_INTEGER g_li0;
CPropVariant g_rgcpropvarAll[ CPROPERTIES_ALL ];
CPropSpec g_rgcpropspecAll[ CPROPERTIES_ALL ];
char g_szPropHeader[] = " propid/name propid cb type value\n";
char g_szEmpty[] = "";
BOOL g_fVerbose = FALSE;
/* just a uuid that we use to create new uuids */
GUID g_curUuid =
{ /* e4ecf7f0-e587-11cf-b10d-00aa005749e9 */
0xe4ecf7f0,
0xe587,
0x11cf,
{0xb1, 0x0d, 0x00, 0xaa, 0x00, 0x57, 0x49, 0xe9}
};
#define TEST_STANDARD 0x1
#define TEST_WORD6 0x2
#define TEST_INTEROP_W 0x4
#define TEST_INTEROP_R 0x8
__inline OLECHAR
MapChar(IN ULONG i)
{
return((OLECHAR) aocMap[i & CHARMASK]);
}
//
// We simulate uuidcreate by incrementing a global uuid
// each time the function is called.
//
void UuidCreate (
OUT GUID * pUuid
)
{
g_curUuid.Data1++;
*pUuid = g_curUuid; // member to member copy
}
#ifndef _UNIX
//+--------------------------------------------------------------------------
// Function: RtlGuidToPropertySetName
//
// Synopsis: Map property set GUID to null-terminated OLECODE name string.
//
// The aocname parameter is assumed to be a buffer with room for
// CCH_PROPSETSZ (28) OLECHARs. The first character
// is always OC_PROPSET0 (0x05), as specified by the OLE Appendix
// B documentation. The colon character normally used as an NT
// stream name separator is not written to the caller's buffer.
//
// No error is possible.
// Arguments: IN GUID *pguid -- pointer to GUID to convert
// OUT OLECHAR aocname[] -- output string buffer
//
// Returns: count of non-NULL characters in the output string buffer
//---------------------------------------------------------------------------
ULONG PROPSYSAPI PROPAPI
RtlGuidToPropertySetName(
IN GUID const *pguid,
OUT OLECHAR aocname[])
{
BYTE *pb = (BYTE *) pguid;
BYTE *pbEnd = pb + sizeof(*pguid);
ULONG cbitRemain = CBIT_BYTE;
OLECHAR *poc = aocname;
*poc++ = OC_PROPSET0;
// Note: CCH_PROPSET includes the OC_PROPSET0, and sizeof(osz...)
// includes the trailing L'\0', so sizeof(osz...) is ok because the
// OC_PROPSET0 character compensates for the trailing NULL character.
ASSERT(CCH_PROPSET >= sizeof(oszSummary)/sizeof(OLECHAR));
if (*pguid == guidSummary)
{
RtlCopyMemory(poc, oszSummary, sizeof(oszSummary));
return(sizeof(oszSummary)/sizeof(OLECHAR));
}
ASSERT(CCH_PROPSET >= sizeof(oszDocumentSummary)/sizeof(OLECHAR));
if (*pguid == guidDocumentSummary || *pguid == guidDocumentSummary2)
{
RtlCopyMemory(poc, oszDocumentSummary, sizeof(oszDocumentSummary));
return(sizeof(oszDocumentSummary)/sizeof(OLECHAR));
}
while (pb < pbEnd)
{
ULONG i = *pb >> (CBIT_BYTE - cbitRemain);
if (cbitRemain >= CBIT_CHARMASK)
{
*poc = MapChar(i);
if (cbitRemain == CBIT_BYTE &&
*poc >= ((OLECHAR)'a') &&
*poc <= ((OLECHAR)'z') )
{
*poc += (OLECHAR) ((OLECHAR)('A') - (OLECHAR)('a'));
}
poc++;
cbitRemain -= CBIT_CHARMASK;
if (cbitRemain == 0)
{
pb++;
cbitRemain = CBIT_BYTE;
}
}
else
{
if (++pb < pbEnd)
{
i |= *pb << cbitRemain;
}
*poc++ = MapChar(i);
cbitRemain += CBIT_BYTE - CBIT_CHARMASK;
}
}
*poc = (OLECHAR)'\0';
return(CCH_PROPSET);
}
#endif // #ifndef _UNIX
#define Check(x,y) _Check(x,y, __LINE__)
void Cleanup(); // forward declaration
//+=================================================================
//
// Function: _Check
//
// Synopsis: Verify that the actual HR is the expected
// value. If not, report an error and exit.
//
// Inputs: [HRESULT] hrExpected
// What we expected
// [HRESULT] hrActual
// The actual HR of the previous operation.
// [int] line
// The line number of the operation.
//
// Outputs: None.
//
//+=================================================================
void _Check(HRESULT hrExpected, HRESULT hrActual, int line)
{
if (hrExpected != hrActual)
{
printf("\nFailed with hr=%08x at line %d (expected hr=%08x in proptest.cxx)\n",
hrActual, line, hrExpected );
Cleanup();
}
}
OLECHAR * GetNextTest()
{
static int nTest;
static CHAR pchBuf[10];
static OLECHAR pocs[10];
sprintf(pchBuf, "%d", nTest++);
STOT(pchBuf, pocs, strlen(pchBuf)+1);
return(pocs);
}
void Now(FILETIME *pftNow)
{
DfGetTOD(pftNow);
}
enum CreateOpen
{
Create, Open
};
IStorage *_pstgTemp=NULL;
IStorage *_pstgTempCopyTo=NULL; // _pstgTemp is copied to _pstgTempCopyTo
class CTempStorage
{
public:
CTempStorage(DWORD grfMode = STGM_DIRECT | STGM_CREATE)
{
Check(S_OK, (_pstgTemp->CreateStorage(GetNextTest(), grfMode |
STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0,
&_pstg)));
}
CTempStorage(CreateOpen co, IStorage *pstgParent,
OLECHAR *pocsChild, DWORD grfMode = STGM_DIRECT)
{
if (co == Create)
Check(S_OK, pstgParent->CreateStorage(pocsChild,
grfMode | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0,
&_pstg));
else
Check(S_OK, pstgParent->OpenStorage(pocsChild, NULL,
grfMode | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, NULL, 0,
&_pstg));
}
~CTempStorage()
{
if (_pstg != NULL)
_pstg->Release();
}
IStorage * operator -> ()
{
return(_pstg);
}
operator IStorage * ()
{
return(_pstg);
}
void Release()
{
if (_pstg != NULL)
{
_pstg->Release();
_pstg = NULL;
}
}
private:
static unsigned int _iName;
IStorage * _pstg;
};
unsigned int CTempStorage::_iName;
class CGenProps
{
public:
CGenProps() : _vt((VARENUM)2) {}
PROPVARIANT * GetNext(int HowMany, int *pActual, BOOL fWrapOk = FALSE, BOOL fNoNonSimple = TRUE);
private:
BOOL _GetNext(PROPVARIANT *pVar, BOOL fWrapOk, BOOL fNoNonSimple);
VARENUM _vt;
};
PROPVARIANT * CGenProps::GetNext(int HowMany, int *pActual, BOOL fWrapOk, BOOL fNoNonSimple)
{
PROPVARIANT *pVar = new PROPVARIANT[HowMany];
if (pVar == NULL)
return(NULL);
int l;
for (l=0; l<HowMany && _GetNext(pVar + l, fWrapOk, fNoNonSimple); l++) { };
if (pActual)
*pActual = l;
if (l == 0)
{
delete pVar;
return(NULL);
}
return(pVar);
}
BOOL CGenProps::_GetNext(PROPVARIANT *pVar, BOOL fWrapOk, BOOL fNoNonSimple)
{
if (_vt == (VT_VECTOR | VT_CLSID)+1)
{
if (!fWrapOk)
return(FALSE);
else
_vt = (VARENUM)2;
}
PROPVARIANT Var;
BOOL fFirst = TRUE;
do
{
GUID *pg;
if (!fFirst)
{
PropVariantClear(&Var);
}
fFirst = FALSE;
memset(&Var, 0, sizeof(Var));
Var.vt = _vt;
(*((int*)&_vt))++;
switch (Var.vt)
{
case VT_LPSTR:
Var.pszVal = (LPSTR)CoTaskMemAlloc(6);
strcpy(Var.pszVal, "lpstr");
break;
case VT_LPWSTR:
DECLARE_WIDESTR(wcsTemp, "lpwstr");
Var.pwszVal = (LPWSTR)CoTaskMemAlloc(14);
wcscpy(Var.pwszVal, wcsTemp);
break;
case VT_CLSID:
pg = (GUID*)CoTaskMemAlloc(sizeof(GUID));
UuidCreate(pg);
Var.puuid = pg;
break;
case VT_CF:
Var.pclipdata = (CLIPDATA*)CoTaskMemAlloc(sizeof(CLIPDATA));
Var.pclipdata->cbSize = 10;
Var.pclipdata->pClipData = (BYTE*)CoTaskMemAlloc(10);
Var.pclipdata->ulClipFmt = 0;
break;
}
} while ( (fNoNonSimple &&
(Var.vt == VT_STREAM || Var.vt == VT_STREAMED_OBJECT ||
Var.vt == VT_STORAGE || Var.vt == VT_STORED_OBJECT)) ||
Var.vt == (VT_VECTOR | VT_VARIANT) ||
Var.vt == (VT_VECTOR | VT_CF) ||
Var.vt == (VT_VECTOR | VT_BSTR) ||
S_OK != PropVariantCopy(pVar, &Var) ); // Is valid propvariant ?
PropVariantClear(&Var);
return(TRUE);
}
VOID
CleanStat(ULONG celt, STATPROPSTG *psps)
{
while (celt--)
{
CoTaskMemFree(psps->lpwstrName);
psps++;
}
}
HRESULT
PopulateRGPropVar( CPropVariant rgcpropvar[],
CPropSpec rgcpropspec[] )
{
HRESULT hr = E_FAIL;
int i;
ULONG ulPropIndex = 0;
CLIPDATA clipdataNull, clipdataNonNull;
// Initialize the PropVariants
for( i = 0; i < CPROPERTIES_ALL; i++ )
{
rgcpropvar[i].Clear();
}
// Create a UI1 property
DECLARE_OLESTR(ocsUI1, "UI1 Property" );
rgcpropspec[ulPropIndex] = ocsUI1;
rgcpropvar[ulPropIndex] = (UCHAR) 39; // 0x27
ASSERT( rgcpropvar[ulPropIndex].VarType() == VT_UI1 );
ulPropIndex++;
// Create an I2 property
DECLARE_OLESTR(ocsI2, "I2 Property" );
rgcpropspec[ulPropIndex] = ocsI2;
rgcpropvar[ulPropIndex] = (SHORT) -502; // 0xfe0a
ASSERT( rgcpropvar[ulPropIndex].VarType() == VT_I2 );
ulPropIndex++;
// Create a UI2 property
DECLARE_OLESTR(ocsUI2, "UI2 Property" );
rgcpropspec[ulPropIndex] = ocsUI2;
rgcpropvar[ulPropIndex] = (USHORT) 502; // 01f6
ASSERT( rgcpropvar[ulPropIndex].VarType() == VT_UI2 );
ulPropIndex++;
// Create a BOOL property
DECLARE_OLESTR(ocsBool, "Bool Property" );
rgcpropspec[ulPropIndex] = ocsBool;
rgcpropvar[ulPropIndex].SetBOOL( VARIANT_TRUE ); // 0xFFFF
ASSERT( rgcpropvar[ulPropIndex].VarType() == VT_BOOL );
ulPropIndex++;
// Create a I4 property
DECLARE_OLESTR(ocsI4, "I4 Property" );
rgcpropspec[ulPropIndex] = ocsI4;
rgcpropvar[ulPropIndex] = (long) -523; // 0xFFFFFDF5
ASSERT( rgcpropvar[ulPropIndex].VarType() == VT_I4 );
ulPropIndex++;
// Create a UI4 property
DECLARE_OLESTR(ocsUI4, "UI4 Property" );
rgcpropspec[ulPropIndex] = ocsUI4;
rgcpropvar[ulPropIndex] = (ULONG) 530; // 0x212
ASSERT( rgcpropvar[ulPropIndex].VarType() == VT_UI4 );
ulPropIndex++;
// Create a R4 property
DECLARE_OLESTR(ocsR4, "R4 Property" );
rgcpropspec[ulPropIndex] = ocsR4;
rgcpropvar[ulPropIndex] = (float) 5.37; // 0x40abd70a ?
ASSERT( rgcpropvar[ulPropIndex].VarType() == VT_R4 );
ulPropIndex++;
// Create an ERROR property
DECLARE_OLESTR(ocsErr, "ERROR Property" );
rgcpropspec[ulPropIndex] = ocsErr;
// 0x800030002
rgcpropvar[ulPropIndex].SetERROR( STG_E_FILENOTFOUND );
ASSERT( rgcpropvar[ulPropIndex].VarType() == VT_ERROR );
ulPropIndex++;
// Create an I8 property
LARGE_INTEGER large_integer;
large_integer.LowPart = 551; // 0x227
large_integer.HighPart = 30; // 0x1E
DECLARE_OLESTR(ocsI8, "I8 Property" );
rgcpropspec[ulPropIndex] = ocsI8;
rgcpropvar[ulPropIndex] = large_integer;
ASSERT( rgcpropvar[ulPropIndex].VarType() == VT_I8 );
ulPropIndex++;
// Create a UI8 property
ULARGE_INTEGER ularge_integer;
ularge_integer.LowPart = 561; // 0x231
ularge_integer.HighPart = 30; // 0x1E
DECLARE_OLESTR(ocsUI8, "UI8 Property" );
rgcpropspec[ulPropIndex] = ocsUI8;
rgcpropvar[ulPropIndex] = ularge_integer;
ASSERT( rgcpropvar[ulPropIndex].VarType() == VT_UI8 );
ulPropIndex++;
// Create an R8 property
DECLARE_OLESTR( ocsR8, "R8 Property" );
rgcpropspec[ulPropIndex] = ocsR8;
rgcpropvar[ulPropIndex] = (double) 571.36; // 4081dae1:47ae147b
ASSERT( rgcpropvar[ulPropIndex].VarType() == VT_R8 );
ulPropIndex++;
// Create a CY property
CY cy;
cy.int64 = 578; // 0x242
DECLARE_OLESTR(ocsCY, "Cy Property" );
rgcpropspec[ulPropIndex] = ocsCY;
rgcpropvar[ulPropIndex] = cy;
ASSERT( rgcpropvar[ulPropIndex].VarType() == VT_CY );
ulPropIndex++;
// Create a DATE property
DECLARE_OLESTR(ocsDate, "DATE Property" );
rgcpropspec[ulPropIndex] = ocsDate;
rgcpropvar[ulPropIndex].SetDATE( 587 );
ASSERT( rgcpropvar[ulPropIndex].VarType() == VT_DATE );
ulPropIndex++;
// Create a FILETIME property
FILETIME filetime;
filetime.dwLowDateTime = 0x767c0570;
filetime.dwHighDateTime = 0x1bb7ecf;
DECLARE_OLESTR(ocsFT, "FILETIME Property" );
rgcpropspec[ulPropIndex] = ocsFT;
rgcpropvar[ulPropIndex] = filetime;
ASSERT( rgcpropvar[ulPropIndex].VarType() == VT_FILETIME );
ulPropIndex++;
// Create a CLSID property
DECLARE_OLESTR(ocsCLSID, "CLSID Property" );
rgcpropspec[ulPropIndex] = ocsCLSID;
// f29f85e0-4ff9-1068-ab91-08002b27b3d9
rgcpropvar[ulPropIndex] = FMTID_SummaryInformation;
ASSERT( rgcpropvar[ulPropIndex].VarType() == VT_CLSID );
ulPropIndex++;
// Create a vector of CLSIDs
DECLARE_OLESTR(ocsVectCLSID, "CLSID Vector Property" );
rgcpropspec[ulPropIndex] = ocsVectCLSID;
// f29f85e0-4ff9-1068-ab91-08002b27b3d9
rgcpropvar[ulPropIndex][0] = FMTID_SummaryInformation;
rgcpropvar[ulPropIndex][1] = FMTID_DocSummaryInformation;
rgcpropvar[ulPropIndex][2] = FMTID_UserDefinedProperties;
ASSERT( rgcpropvar[ulPropIndex].VarType() == VT_CLSID | VT_VECTOR );
ulPropIndex++;
// Create a BSTR property
DECLARE_OLESTR(ocsBSTR, "BSTR");
DECLARE_OLESTR(ocsBSTRVal, "BSTR Value");
rgcpropspec[ulPropIndex] = ocsBSTR;
rgcpropvar[ulPropIndex].SetBSTR( ocsBSTRVal );
ASSERT( rgcpropvar[ulPropIndex].VarType() == VT_BSTR );
ulPropIndex++;
// Create a BSTR Vector property
DECLARE_OLESTR(ocsBSTRVect, "BSTR Vector");
rgcpropspec[ulPropIndex] = ocsBSTRVect;
DECLARE_OLESTR(ocsBSTRVectElt, "# - BSTR Vector Element");
for( i = 0; i < 3; i++ )
{
OLECHAR *olestrElement = ocsBSTRVectElt;
olestrElement[0] = (OLECHAR) i%10 + (OLECHAR)'0';
rgcpropvar[ulPropIndex].SetBSTR( olestrElement, i );
}
ASSERT( rgcpropvar[ulPropIndex].VarType() == (VT_BSTR | VT_VECTOR) );
ulPropIndex++;
// Create a variant vector BSTR property.
DECLARE_OLESTR(ocsBSTRVariantVect, "BSTR Variant Vector");
DECLARE_OLESTR(ocsBSTRVariantVectElt, "# - Vector Variant Vector");
rgcpropspec[ulPropIndex ] = ocsBSTRVariantVect;
for( i = 0; i < 3; i++ )
{
if( i == 0 )
{
rgcpropvar[ulPropIndex][0] =
(PROPVARIANT*) CPropVariant((long) 0x1234);
}
else
{
CPropVariant cpropvarBSTR;
cpropvarBSTR.SetBSTR( ocsBSTRVariantVectElt );
(cpropvarBSTR.GetBSTR())[0] = (OLECHAR) i%10 + (OLECHAR)'0';
rgcpropvar[ulPropIndex][i] = (PROPVARIANT*) cpropvarBSTR;
}
}
ASSERT( rgcpropvar[ulPropIndex].VarType() == (VT_VARIANT | VT_VECTOR) );
ulPropIndex++;
// Create an LPSTR property
DECLARE_OLESTR(ocsLPSTRP, "LPSTR Property");
rgcpropspec[ulPropIndex] = ocsLPSTRP;
rgcpropvar[ulPropIndex] = "LPSTR Value";
ASSERT( rgcpropvar[ulPropIndex].VarType() == VT_LPSTR );
ulPropIndex++;
// Create some ClipFormat properties
DECLARE_OLESTR(ocsClipName, "ClipFormat property");
rgcpropspec[ ulPropIndex ] = ocsClipName;
DECLARE_WIDESTR(wcsClipData, "Clipboard Data");
rgcpropvar[ ulPropIndex ] = CClipData( wcsClipData );
ASSERT( rgcpropvar[ ulPropIndex ].VarType() == VT_CF );
ulPropIndex++;
DECLARE_OLESTR(ocsEmptyClipName,
"Empty ClipFormat property (NULL pointer)");
rgcpropspec[ ulPropIndex ] = ocsEmptyClipName;
clipdataNull.cbSize = 4;
clipdataNull.ulClipFmt = (ULONG) -1; // 0xffff
clipdataNull.pClipData = NULL;
rgcpropvar[ ulPropIndex ] = clipdataNull;
ASSERT( rgcpropvar[ ulPropIndex ].VarType() == VT_CF );
ulPropIndex++;
DECLARE_OLESTR(ocsEmptyClipNameNotNull,
"Empty ClipFormat property (non-NULL pointer)");
rgcpropspec[ ulPropIndex ] = ocsEmptyClipNameNotNull;
clipdataNonNull.cbSize = 4;
clipdataNonNull.ulClipFmt = (ULONG) -1; // 0xffff
clipdataNonNull.pClipData = (BYTE*) CoTaskMemAlloc(0);
rgcpropvar[ ulPropIndex ] = clipdataNonNull;
ASSERT( rgcpropvar[ ulPropIndex ].VarType() == VT_CF );
ulPropIndex++;
// Create a vector of ClipFormat properties
CClipData cclipdataEmpty;
cclipdataEmpty.Set( (ULONG) -1, "", 0 );
DECLARE_OLESTR(ocsClipArr, "ClipFormat Array Property");
rgcpropspec[ ulPropIndex ] = ocsClipArr;
DECLARE_OLESTR(ocsElt1, "Clipboard Date element 1");
DECLARE_OLESTR(ocsElt2, "Clipboard Date element 2");
rgcpropvar[ ulPropIndex ][0] = CClipData( ocsElt1 );
rgcpropvar[ ulPropIndex ][1] = cclipdataEmpty;
rgcpropvar[ ulPropIndex ][2] = clipdataNull;
rgcpropvar[ ulPropIndex ][3] = clipdataNonNull;
rgcpropvar[ ulPropIndex ][4] = CClipData( ocsElt2 );
ASSERT( rgcpropvar[ulPropIndex].VarType() == (VT_CF | VT_VECTOR) );
ASSERT( rgcpropvar[ulPropIndex].Count() == 5 );
ulPropIndex++;
// Create an LPSTR|Vector property (e.g., the DocSumInfo
// Document Parts array).
DECLARE_OLESTR(ocsLPSTRorVect, "LPSTR|Vector property");
rgcpropspec[ ulPropIndex ] = ocsLPSTRorVect;
rgcpropvar[ ulPropIndex ][0] = "LPSTR Element 0";
rgcpropvar[ ulPropIndex ][1] = "LPSTR Element 1";
ASSERT( rgcpropvar[ulPropIndex].VarType() == (VT_LPSTR | VT_VECTOR) );
ulPropIndex++;
// Create an LPWSTR|Vector property
DECLARE_OLESTR(ocsLPWSTRVect, "LPWSTR|Vector property");
DECLARE_WIDESTR(ocslpwvElt1, "LPWSTR Element 0");
DECLARE_WIDESTR(ocslpwvElt2, "LPWSTR Element 1");
rgcpropspec[ ulPropIndex ] = ocsLPWSTRVect;
rgcpropvar[ ulPropIndex ][0] = ocslpwvElt1;
rgcpropvar[ ulPropIndex ][1] = ocslpwvElt2;
ASSERT( rgcpropvar[ulPropIndex].VarType() == (VT_LPWSTR | VT_VECTOR) );
ulPropIndex++;
// Create a DocSumInfo HeadingPairs array.
DECLARE_OLESTR(ocsPairArr, "HeadingPair array");
rgcpropspec[ ulPropIndex ] = ocsPairArr;
rgcpropvar[ ulPropIndex ][0] = (PROPVARIANT*) CPropVariant( "Heading 0" );
rgcpropvar[ ulPropIndex ][1] = (PROPVARIANT*) CPropVariant( (long) 1 );
rgcpropvar[ ulPropIndex ][2] = (PROPVARIANT*) CPropVariant( "Heading 1" );
rgcpropvar[ ulPropIndex ][3] = (PROPVARIANT*) CPropVariant( (long) 1 );
ASSERT( rgcpropvar[ulPropIndex].VarType() == (VT_VARIANT | VT_VECTOR) );
ulPropIndex++;
// Create some NULL (but extant) properties
DECLARE_OLESTR(ocsEmptyLPSTR, "Empty LPSTR");
rgcpropspec[ulPropIndex] = ocsEmptyLPSTR;
rgcpropvar[ulPropIndex] = "";
ASSERT( rgcpropvar[ulPropIndex].VarType() == VT_LPSTR );
ulPropIndex++;
DECLARE_OLESTR(ocsEmptyLPWSTR, "Empty LPWSTR");
DECLARE_WIDESTR(wcsEmpty, "");
rgcpropspec[ulPropIndex] = ocsEmptyLPWSTR;
rgcpropvar[ulPropIndex] = wcsEmpty;
ASSERT( rgcpropvar[ulPropIndex].VarType() == VT_LPWSTR );
ulPropIndex++;
DECLARE_OLESTR(ocsEmptyBLOB, "Empty BLOB");
rgcpropspec[ulPropIndex] = ocsEmptyBLOB;
rgcpropvar[ulPropIndex] = CBlob(0);
ASSERT( rgcpropvar[ulPropIndex].VarType() == VT_BLOB );
ulPropIndex++;
DECLARE_OLESTR(ocsEmptyBSTR, "Empty BSTR");
DECLARE_OLESTR(ocsEmpty, "");
rgcpropspec[ulPropIndex] = ocsEmptyBSTR;
rgcpropvar[ulPropIndex].SetBSTR( ocsEmpty );
ASSERT( rgcpropvar[ulPropIndex].VarType() == VT_BSTR );
ulPropIndex++;
// ----
// Exit
// ----
CoTaskMemFree( clipdataNonNull.pClipData );
memset( &clipdataNonNull, 0, sizeof(clipdataNonNull) );
ASSERT( CPROPERTIES_ALL == ulPropIndex );
hr = S_OK;
return(hr);
}
//+---------------------------------------------------------------
//
// Function: test_WriteReadAllProperties
//
// Synopsis: This test simply creates two new property
// sets in a new file (one Ansi and one Unicode),
// writes all the properties in g_rgcpropvarAll,
// reads them back, and verifies that it reads what
// it wrote.
//
// Outputs: None.
//
//+---------------------------------------------------------------
void
test_WriteReadAllProperties(ULONG ulTestOptions )
{
FMTID fmtidAnsi, fmtidUnicode;
TSafeStorage< IStorage > pstg;
TSafeStorage< IPropertySetStorage > ppropsetstg;
TSafeStorage< IPropertyStorage > ppropstgAnsi;
TSafeStorage< IPropertyStorage > ppropstgUnicode;
CPropVariant rgcpropvarAnsi[ CPROPERTIES_ALL ];
CPropVariant rgcpropvarUnicode[ CPROPERTIES_ALL ];
printf( " Simple Write/Read Test\n" );
// -----------------------
// Create the Property Set
// -----------------------
// Generate FMTIDs.
fmtidAnsi = IID_IPropertyStorage;
fmtidUnicode = IID_IPropertySetStorage;
// Generate a filename from the directory name.
DECLARE_OLESTR(ocsFile, "AllProps.stg" );
if (! (ulTestOptions & TEST_INTEROP_R))
{
// Create the Docfile.
printf(" Writing into file ... \n");
Check( S_OK, StgCreateDocfile( ocsFile,
STGM_CREATE |
STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
0L,
&pstg ));
// Get the IPropertySetStorage
Check( S_OK, pstg->QueryInterface( IID_IPropertySetStorage,
(void**)&ppropsetstg ));
// Create a Property Storage
Check( S_OK, ppropsetstg->Create( fmtidAnsi,
&CLSID_NULL,
PROPSETFLAG_DEFAULT,
STGM_READWRITE |
STGM_SHARE_EXCLUSIVE,
&ppropstgAnsi ));
Check( S_OK, ppropsetstg->Create( fmtidUnicode,
&CLSID_NULL,
PROPSETFLAG_ANSI,
STGM_READWRITE |
STGM_SHARE_EXCLUSIVE,
&ppropstgUnicode ));
Check( S_OK, ppropstgAnsi->WriteMultiple( CPROPERTIES_ALL,
(PROPSPEC*)g_rgcpropspecAll,
(PROPVARIANT*)g_rgcpropvarAll,
PID_FIRST_USABLE ));
Check( S_OK, ppropstgUnicode->WriteMultiple( CPROPERTIES_ALL,
(PROPSPEC*)g_rgcpropspecAll,
(PROPVARIANT*)g_rgcpropvarAll,
PID_FIRST_USABLE ));
}
else
{
// open it
printf(" opening file ... \n");
Check(S_OK, StgOpenStorage( ocsFile,
(IStorage*) NULL,
STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
(SNB) 0,
(DWORD) 0,
&pstg ));
// Get the IPropertySetStorage
Check( S_OK, pstg->QueryInterface( IID_IPropertySetStorage,
(void**)&ppropsetstg ));
// Open the Property Storage
Check(S_OK, ppropsetstg->Open(fmtidUnicode,
STGM_SHARE_EXCLUSIVE |
STGM_DIRECT | STGM_READWRITE,
&ppropstgUnicode));
Check(S_OK, ppropsetstg->Open(fmtidAnsi,
STGM_SHARE_EXCLUSIVE |
STGM_DIRECT | STGM_READWRITE,
&ppropstgAnsi));
}
printf(" Verifying file ... \n");
// either way, read it and make sure it is right
Check( S_OK, ppropstgAnsi->ReadMultiple( CPROPERTIES_ALL,
(PROPSPEC*)g_rgcpropspecAll,
(PROPVARIANT*)rgcpropvarAnsi ));
Check( S_OK, ppropstgUnicode->ReadMultiple( CPROPERTIES_ALL,
(PROPSPEC*)g_rgcpropspecAll,
(PROPVARIANT*)rgcpropvarUnicode ));
// ----------------------
// Compare the properties
// ----------------------
for( int i = 0; i < (int)CPROPERTIES_ALL; i++ )
{
Check( TRUE, rgcpropvarAnsi[i] == g_rgcpropvarAll[i]
&&
rgcpropvarUnicode[i] == g_rgcpropvarAll[i] );
}
} // test_WriteReadProperties
//
// DOCFILE -- run all tests on DocFile
//
// IPropertySetStorage tests
//
void
test_IPropertySetStorage_IUnknown(IStorage *pStorage)
{
printf( " IPropertySetStorage::IUnknown\n" );
// Check ref counting through different interfaces on object
//
// QI to IPropertySetStorage
// QI to IUnknown on IStorage
// QI to IUnknown on IPropertySetStorage
// QI back to IPropertySetStorage from IUnknown
// QI back to IStorage from IPropertySetStorage
//
// Release all.
//
IStorage *pStorage2;
IPropertySetStorage *ppss1, *ppss2, *ppss3;
IUnknown *punk1,*punk2;
Check(S_OK, pStorage->QueryInterface(IID_IPropertySetStorage, (void**)&ppss1));
Check(S_OK, pStorage->QueryInterface(IID_IUnknown, (void **)&punk1));
Check(S_OK, ppss1->QueryInterface(IID_IUnknown, (void **)&punk2));
Check(S_OK, ppss1->QueryInterface(IID_IStorage, (void **)&pStorage2));
Check(S_OK, ppss1->QueryInterface(IID_IPropertySetStorage, (void **)&ppss2));
Check(S_OK, punk1->QueryInterface(IID_IPropertySetStorage, (void **)&ppss3));
ppss1->AddRef();
ppss1->Release();
//pStorage.Release();
ppss1->Release();
punk1->Release();
punk2->Release();
pStorage2->Release();
ppss2->Release();
ppss3->Release();
}
#define INVALID_POINTER ( (void *) 0xFFFFFFFF )
#define VTABLE_MEMBER_FN(pObj,entry) ( (*(ULONG ***)(pObj))[ (entry) ] )
//+---------------------------------------------------------
//
// Template: Alloc2PageVector
//
// Purpose: This function template allocates two pages
// of memory, and then sets a vector pointer
// so that its first element is wholy within
// the first page, and the second element is
// wholy within the second. Then, the protection
// of the second page is set according to the
// caller-provided parameter.
//
//
// Inputs: [TYPE**] ppBase
// Points to the beginning of the two pages.
// [TYPE**] ppVector
// Points to the beginning of the vector of TYPEs.
// [DWORD] dwProtect
// The desired protection on the second page
// (from the PAGE_* enumeration).
// [LPWSTR] lpwstr (optional)
// If not NULL, used to initialize the vector
// elements.
//
// Output: TRUE iff successful.
//
//+---------------------------------------------------------
template< class TYPE > BOOL Alloc2PageVector( TYPE** ppBase,
TYPE** ppVector,
DWORD dwProtect,
TYPE* pInit )
{
DWORD dwOldProtect;
SYSTEM_INFO si;
GetSystemInfo( &si );
*ppBase = (TYPE*) VirtualAlloc( NULL, 2 * si.dwPageSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
if( NULL == *ppBase )
return( FALSE );
*ppVector = (TYPE*) ( (BYTE*) *ppBase + si.dwPageSize - sizeof(TYPE) );
if( NULL != pInit )
{
memcpy( &((LPWSTR*)*ppVector)[0], pInit, sizeof(TYPE) );
memcpy( &((LPWSTR*)*ppVector)[1], pInit, sizeof(TYPE) );
}
if( !VirtualProtect( (BYTE*) *ppBase + si.dwPageSize, si.dwPageSize, dwProtect, &dwOldProtect ) )
return( FALSE );
return( TRUE );
}
void
test_PropVariantValidation( IStorage *pStg )
{
printf( " PropVariant Validation\n" );
TSafeStorage< IPropertySetStorage > pPSStg( pStg );
TSafeStorage< IPropertyStorage > pPStg;
CPropVariant cpropvar;
CLIPDATA clipdata;
PROPSPEC propspec;
DECLARE_WIDESTR(wszText, "Unicode Text String");
FMTID fmtid;
UuidCreate( &fmtid );
Check(S_OK, pPSStg->Create( fmtid,
NULL,
PROPSETFLAG_DEFAULT,
STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
&pPStg ));
propspec.ulKind = PRSPEC_PROPID;
propspec.propid = 2;
// -------------------------------
// Test invalid VT_CF Propvariants
// -------------------------------
// NULL clip format.
clipdata.cbSize = 4;
clipdata.ulClipFmt = (ULONG) -1;
clipdata.pClipData = NULL;
cpropvar = clipdata;
Check(S_OK, pPStg->WriteMultiple( 1, &propspec, cpropvar, PID_FIRST_USABLE ));
// Too short cbSize.
((PROPVARIANT*)cpropvar)->pclipdata->cbSize = 3;
Check(STG_E_INVALIDPARAMETER, pPStg->WriteMultiple( 1, &propspec, cpropvar, PID_FIRST_USABLE ));
// Too short pClipData (it should be 1 byte, but the pClipData is NULL).
((PROPVARIANT*)cpropvar)->pclipdata->cbSize = 5;
Check(STG_E_INVALIDPARAMETER, pPStg->WriteMultiple( 1, &propspec, cpropvar, PID_FIRST_USABLE ));
}
// Check creation/open/deletion of property sets (check fmtid and predefined names)
// Create a property set
// Try recreate of same
// Try delete
// Close the property set
// Try recreate of same
// Reopen the property set
// Try recreate of same
// Try delete
// Close the property set
// Delete the property set
// Repeat the test once more
void
test_IPropertySetStorage_CreateOpenDelete(IStorage *pStorage)
{
printf( " IPropertySetStorage::Create/Open/Delete\n" );
FMTID fmtid;
PROPSPEC propspec;
UuidCreate(&fmtid);
int i;
for (i=0; i<4; i++)
{
{
TSafeStorage< IPropertySetStorage > pPropSetStg(pStorage);
IPropertyStorage *PropStg, *PropStg2;
Check(S_OK, pPropSetStg->Create(fmtid,
NULL,
PROPSETFLAG_DEFAULT,
STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_DIRECT | STGM_READWRITE,
&PropStg));
Check(S_OK, pPropSetStg->Create(
fmtid,
NULL,
PROPSETFLAG_DEFAULT,
STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_DIRECT | STGM_READWRITE,
&PropStg2));
Check(STG_E_REVERTED, PropStg->Commit(0));
PropStg->Release();
PropStg2->Release();
}
{
TSafeStorage< IPropertySetStorage > pPropSetStg(pStorage);
IPropertyStorage *PropStg, *PropStg2;
// use STGM_FAILIFTHERE
Check(STG_E_FILEALREADYEXISTS,
pPropSetStg->Create(fmtid,
NULL,
PROPSETFLAG_DEFAULT,
STGM_SHARE_EXCLUSIVE | STGM_DIRECT |
STGM_READWRITE,
&PropStg));
Check(S_OK,
pPropSetStg->Open(fmtid,
STGM_SHARE_EXCLUSIVE | STGM_DIRECT |
STGM_READWRITE,
&PropStg));
Check(STG_E_ACCESSDENIED,
pPropSetStg->Create(fmtid,
NULL,
PROPSETFLAG_DEFAULT,
STGM_SHARE_EXCLUSIVE | STGM_DIRECT |
STGM_READWRITE,
&PropStg2));
Check(S_OK,
pPropSetStg->Delete(fmtid));
propspec.ulKind = PRSPEC_PROPID;
propspec.propid = 1000;
PROPVARIANT propvar;
propvar.vt = VT_I4;
propvar.lVal = 12345;
Check(STG_E_REVERTED,
PropStg->WriteMultiple(1, &propspec, &propvar,
2)); // force dirty
PropStg->Release();
}
}
// --------------------------------------------------------
// Test the Create/Delete of the DocumentSummaryInformation
// property set (this requires special code because it
// has two sections).
// --------------------------------------------------------
TSafeStorage< IPropertySetStorage > pPropSetStg(pStorage);
TSafeStorage< IPropertyStorage> pPropStg1, pPropStg2;
// Create & Delete a DSI propset with just the first section.
Check(S_OK, pPropSetStg->Create(FMTID_DocSummaryInformation,
NULL,
PROPSETFLAG_DEFAULT,
STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_DIRECT | STGM_READWRITE,
&pPropStg1));
pPropStg1->Release(); pPropStg1 = NULL;
Check(S_OK, pPropSetStg->Delete( FMTID_DocSummaryInformation ));
// Create & Delete a DSI propset with just the second section
Check(S_OK, pPropSetStg->Create(FMTID_UserDefinedProperties,
NULL,
PROPSETFLAG_DEFAULT,
STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_DIRECT | STGM_READWRITE,
&pPropStg1 ));
pPropStg1->Release(); pPropStg1 = NULL;
Check(S_OK, pPropSetStg->Delete( FMTID_UserDefinedProperties ));
// Create & Delete a DocumentSummaryInformation propset with both sections.
// If you delete the DSI propset first, it should delete both sections.
// If you delete the UD propset first, the DSI propset should still
// remain. We'll loop twice, trying both combinations.
for( i = 0; i < 2; i++ )
{
// Create the first section, which implicitely creates
// the second section.
Check(S_OK, pPropSetStg->Create(FMTID_DocSummaryInformation,
NULL,
PROPSETFLAG_DEFAULT,
STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_DIRECT | STGM_READWRITE,
&pPropStg1));
pPropStg1->Release(); pPropStg1 = NULL;
if( i == 0 )
{
Check(S_OK, pPropSetStg->Delete( FMTID_UserDefinedProperties ));
Check(S_OK, pPropSetStg->Delete( FMTID_DocSummaryInformation ));
}
else
{
Check(S_OK, pPropSetStg->Delete( FMTID_DocSummaryInformation ));
Check(STG_E_FILENOTFOUND, pPropSetStg->Delete( FMTID_UserDefinedProperties ));
}
} // for( i = 0; i < 2; i++ )
// -------------------------------------
// Test special properties in DocSumInfo
// -------------------------------------
// This verifies that when we Create a DocSumInfo
// property set, and write a Vector or LPSTRs,
// we can read it again. We test this because
// Vectors of LPSTRs are a special case in the DocSumInfo,
// and the Create & Open path are slightly different
// in CPropertySetStream::_LoadHeader.
// Create a new property set.
Check(S_OK, pPropSetStg->Create(FMTID_DocSummaryInformation,
NULL,
PROPSETFLAG_DEFAULT,
STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_DIRECT | STGM_READWRITE,
&pPropStg1));
// Create a vector of LPSTRs. Make the strings
// varying lengths to ensure we get plenty of
// opportunity for alignment problems.
CPropVariant cpropvarWrite, cpropvarRead;
cpropvarWrite[3] = "12345678";
cpropvarWrite[2] = "1234567";
cpropvarWrite[1] = "123456";
cpropvarWrite[0] = "12345";
ASSERT( cpropvarWrite.Count() == 4 );
// Write the property
DECLARE_OLESTR(ocsVect, "A Vector of LPSTRs");
propspec.ulKind = PRSPEC_LPWSTR;
propspec.lpwstr = ocsVect;
Check(S_OK, pPropStg1->WriteMultiple( 1, &propspec, cpropvarWrite, 2 ));
// Read the property back.
Check(S_OK, pPropStg1->ReadMultiple( 1, &propspec, cpropvarRead ));
// Verify that we read what we wrote.
for( i = 0; i < (int) cpropvarWrite.Count(); i++ )
{
Check(0, strcmp( (LPSTR) cpropvarWrite[i], (LPSTR) cpropvarRead[i] ));
}
}
void
test_IPropertySetStorage_SummaryInformation(IStorage *pStorage)
{
printf( " SummaryInformation\n" );
TSafeStorage< IPropertySetStorage > pPropSetStg(pStorage);
IPropertyStorage *PropStg;
IStream *pstm;
Check(S_OK, pPropSetStg->Create(FMTID_SummaryInformation,
NULL,
PROPSETFLAG_DEFAULT, // simple, wide
STGM_SHARE_EXCLUSIVE | STGM_DIRECT | STGM_READWRITE,
&PropStg));
PropStg->Release();
DECLARE_OLESTR(ocsSummary, "\005SummaryInformation");
Check(S_OK, pStorage->OpenStream(ocsSummary,
NULL,
STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
0,
&pstm));
pstm->Release();
}
//
// Check STGM_FAILIFTHERE and ~STGM_FAILIFTHERE in following cases
// Check overwriting simple with simple
void
test_IPropertySetStorage_FailIfThere(IStorage *pStorage)
{
// (Use "fale" instead of "fail" in this printf so the output won't
// alarm anyone with the word "fail" uncessarily).
printf( " IPropertySetStorage, FaleIfThere\n" );
TSafeStorage< IPropertySetStorage > pPropSetStg(pStorage);
// Iter 0 1 2 3 4 5 6 7
// Create simple nonsimple simple nonsimple simple nonsimple simple nonsimple
// ReCreate simple simple nonsimple nonsimple simple simple nonsimple nonsimple
// failif failif failif failif overw overw overw overw
//
// expected exists exists exists exists ok ok ok ok
for (int i=0; i<8; i++)
{
FMTID fmtid;
IPropertyStorage *PropStg;
UuidCreate(&fmtid);
Check(S_OK, pPropSetStg->Create(fmtid,
NULL,
0,
STGM_SHARE_EXCLUSIVE | STGM_DIRECT | STGM_READWRITE,
&PropStg));
PropStg->Release();
Check((i&4) == 4 ? S_OK : STG_E_FILEALREADYEXISTS,
pPropSetStg->Create(fmtid,
NULL,
0,
( (i & 4) == 4 ? STGM_CREATE : STGM_FAILIFTHERE) |
STGM_SHARE_EXCLUSIVE | STGM_DIRECT | STGM_READWRITE,
&PropStg));
if (PropStg)
{
PropStg->Release();
}
}
}
void
test_IPropertySetStorage(IStorage *pStorage)
{
// Check ref counting through different interfaces on object
test_IPropertySetStorage_IUnknown(pStorage);
test_IPropertySetStorage_CreateOpenDelete(pStorage);
test_IPropertySetStorage_SummaryInformation(pStorage);
test_IPropertySetStorage_FailIfThere(pStorage);
}
// IEnumSTATPROPSETSTG
//
// Check enumeration of property sets
//
// Check refcounting and IUnknown
//
// Create some property sets, predefined and not, simple and not, one through IStorage
// Enumerate them and check
// (check fmtid, grfFlags)
// (check when asking for more than there is: S_FALSE, S_OK)
// Delete one
// Reset the enumerator
// Enumerate them and check
// Delete one
//
// Reset the enumeratorA
// Read one from enumeratorA
// Clone enumerator -> enumeratorB
// Loop comparing rest of enumerator contents
//
// Reset the enumerator
// Skip all
// Check none left
//
// Reset the enumerator
// Skip all but one
// Check one left
//
void CheckTime(const FILETIME &ftStart, const FILETIME &ftPropSet)
{
FILETIME ftNow;
Now(&ftNow);
if (ftPropSet.dwLowDateTime == 0 && ftPropSet.dwHighDateTime == 0)
{
return;
}
// if ftPropSet < ftStart || ftNow < ftPropSet, error
ASSERT (!(CompareFileTime(&ftPropSet, &ftStart) == -1 ||
CompareFileTime(&ftNow, &ftPropSet) == -1));
}
void test_IEnumSTATPROPSETSTG(IStorage *pStorage)
{
printf( " IEnumSTATPROPSETSTG\n" );
FMTID afmtid[8];
CLSID aclsid[8];
IPropertyStorage *pPropSet;
TSafeStorage< IPropertySetStorage > pPropSetStg(pStorage);
FILETIME ftStart;
Now(&ftStart);
pPropSetStg->Delete(FMTID_SummaryInformation);
for (int i=0; i<5; i++)
{
if (i & 4)
afmtid[i] = FMTID_SummaryInformation;
else
UuidCreate(&afmtid[i]);
UuidCreate(&aclsid[i]);
Check(S_OK, pPropSetStg->Create(afmtid[i], aclsid+i,
((i & 2) ? PROPSETFLAG_ANSI : 0),
STGM_READWRITE | STGM_DIRECT | STGM_SHARE_EXCLUSIVE,
&pPropSet));
pPropSet->Release();
}
STATPROPSETSTG StatBuffer[6];
ULONG celt;
IEnumSTATPROPSETSTG *penum, *penum2;
Check(S_OK, pPropSetStg->Enum(&penum));
IUnknown *punk, *punk2;
IEnumSTATPROPSETSTG *penum3;
Check(S_OK, penum->QueryInterface(IID_IUnknown, (void**)&punk));
Check(S_OK, punk->QueryInterface(IID_IEnumSTATPROPSETSTG, (void**)&penum3));
Check(S_OK, penum->QueryInterface(IID_IEnumSTATPROPSETSTG, (void**)&punk2));
ASSERT(punk == punk2);
punk->Release();
penum3->Release();
punk2->Release();
// test S_FALSE
Check(S_FALSE, penum->Next(6, StatBuffer, &celt));
ASSERT(celt == 5);
penum->Reset();
// test reading half out, then cloning, then comparing
// rest of enumeration with other clone.
Check(S_OK, penum->Next(3, StatBuffer, &celt));
ASSERT(celt == 3);
celt = 0;
Check(S_OK, penum->Clone(&penum2));
Check(S_OK, penum->Next(2, StatBuffer, &celt));
ASSERT(celt == 2);
// check the clone
for (int c=0; c<2; c++)
{
STATPROPSETSTG CloneStat;
Check(S_OK, penum2->Next(1, &CloneStat, NULL));
Check(TRUE, 0 == memcmp(&CloneStat, StatBuffer+c, sizeof(CloneStat)));
Check(TRUE, CloneStat.dwOSVersion == PROPSETHDR_OSVERSION_UNKNOWN);
}
// check both empty
celt = 0;
Check(S_FALSE, penum->Next(1, StatBuffer, &celt));
ASSERT(celt == 0);
Check(S_FALSE, penum2->Next(1, StatBuffer, &celt));
ASSERT(celt == 0);
penum->Reset();
//
// loop deleting one propset at a time
// enumerate the propsets checking that correct ones appear.
//
for (ULONG d = 0; d<5; d++)
{
// d is for delete
BOOL afFound[5];
Check(S_OK, penum->Next(5-d, StatBuffer, &celt));
ASSERT(celt == 5-d);
penum->Reset();
memset(afFound, 0, sizeof(afFound));
for (ULONG iPropSet=0; iPropSet<5; iPropSet++)
{
ULONG iSearch;
for (iSearch=0; iSearch<5-d; iSearch++)
{
if (0 == memcmp(&StatBuffer[iSearch].fmtid,
&afmtid[iPropSet],
sizeof(StatBuffer[0].fmtid)))
{
ASSERT (!afFound[iPropSet]);
afFound[iPropSet] = TRUE;
break;
}
}
if (iPropSet < d)
{
ASSERT(!afFound[iPropSet]);
}
if (iSearch == 5-d)
{
ASSERT(iPropSet < d);
continue;
}
ASSERT( ( (StatBuffer[iSearch].grfFlags
& PROPSETFLAG_NONSIMPLE) == 0 ) );
ASSERT((StatBuffer[iSearch].grfFlags & PROPSETFLAG_ANSI) == 0);
if (StatBuffer[iSearch].grfFlags & PROPSETFLAG_NONSIMPLE)
{
ASSERT(StatBuffer[iSearch].clsid == aclsid[iPropSet]);
}
else
{
ASSERT(StatBuffer[iSearch].clsid == CLSID_NULL);
}
CheckTime(ftStart, StatBuffer[iSearch].mtime);
CheckTime(ftStart, StatBuffer[iSearch].atime);
CheckTime(ftStart, StatBuffer[iSearch].ctime);
}
Check(S_OK, pPropSetStg->Delete(afmtid[d]));
Check(S_OK, penum->Reset());
}
penum->Release();
penum2->Release();
}
// Creation tests
//
// Access flags/Valid parameters/Permissions
// Check readonly cannot be written -
// WriteProperties, WritePropertyNames
void
test_IPropertyStorage_Access(IStorage *pStorage)
{
printf( " IPropertyStorage creation (access) tests\n" );
TSafeStorage< IPropertySetStorage > pPropSetStg(pStorage);
FMTID fmtid;
UuidCreate(&fmtid);
// check by name
IPropertyStorage *pPropStg;
Check(S_OK, pPropSetStg->Create(fmtid, NULL, 0,
STGM_SHARE_EXCLUSIVE | STGM_READWRITE, &pPropStg));
// QueryInterface tests
// QI to IPropertyStorage
// QI to IUnknown on IPropertyStorage
// QI back to IPropertyStorage from IUnknown
//
// Release all.
IPropertyStorage *pPropStg2,*pPropStg3;
IUnknown *punk;
Check(S_OK, pPropStg->QueryInterface(IID_IPropertyStorage,
(void**)&pPropStg2));
Check(S_OK, pPropStg->QueryInterface(IID_IUnknown,
(void**)&punk));
Check(S_OK, punk->QueryInterface(IID_IPropertyStorage,
(void**)&pPropStg3));
pPropStg3->Release();
pPropStg2->Release();
punk->Release();
PROPSPEC ps;
ps.ulKind = PRSPEC_LPWSTR;
DECLARE_OLESTR(ocsTestProp, "testprop");
ps.lpwstr = ocsTestProp;
PROPVARIANT pv;
pv.vt = VT_LPWSTR;
DECLARE_CONST_OLESTR(ocsTestVal, "testval");
LPOLESTR ocsTest = ocsTestVal;
DECLARE_WIDESTR(wcsTestVal, "testval");
pv.pwszVal = wcsTestVal;
Check(S_OK, pPropStg->WriteMultiple(1, &ps, &pv, 2));
pPropStg->Release();
Check(S_OK, pPropSetStg->Open(fmtid, STGM_SHARE_EXCLUSIVE | STGM_READ,
&pPropStg));
Check(STG_E_ACCESSDENIED, pPropStg->WriteMultiple(1, &ps, &pv, 2));
Check(STG_E_ACCESSDENIED, pPropStg->DeleteMultiple(1, &ps));
PROPID propid=3;
Check(STG_E_ACCESSDENIED, pPropStg->WritePropertyNames(1, &propid,
&ocsTestVal));
Check(STG_E_ACCESSDENIED, pPropStg->DeletePropertyNames(1, &propid));
FILETIME ft;
Check(STG_E_ACCESSDENIED, pPropStg->SetTimes(&ft, &ft, &ft));
CLSID clsid;
Check(STG_E_ACCESSDENIED, pPropStg->SetClass(clsid));
pPropStg->Release();
}
// Creation tests
// Check VT_STREAM etc not usable with simple.
void
test_IPropertyStorage_Create(IStorage *pStorage)
{
printf( " IPropertyStorage creation tests\n" );
TSafeStorage< IPropertySetStorage > pPropSetStg(pStorage);
FMTID fmtid;
UuidCreate(&fmtid);
// check by name
IPropertyStorage *pPropStg;
Check(S_OK, pPropSetStg->Create(fmtid, NULL, 0 /* simple */,
STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, &pPropStg));
PROPSPEC ps;
ps.ulKind = PRSPEC_PROPID;
ps.propid = 2;
PROPVARIANT pv;
pv.vt = VT_STREAM;
pv.pStream = NULL;
// the ref impl. does not recognize VT_STREAM, and will thus
// treat it as a invalid type.
Check(STG_E_INVALIDPARAMETER,
pPropStg->WriteMultiple(1, &ps, &pv, 2000));
//
pPropStg->Release();
}
void
CheckStat( IPropertyStorage *pPropSet, REFFMTID fmtid,
REFCLSID clsid, ULONG PropSetFlag,
const FILETIME & ftStart, DWORD dwOSVersion )
{
STATPROPSETSTG StatPropSetStg;
Check(S_OK, pPropSet->Stat(&StatPropSetStg));
Check(TRUE, StatPropSetStg.fmtid == fmtid);
Check(TRUE, StatPropSetStg.clsid == clsid);
Check(TRUE, StatPropSetStg.grfFlags == PropSetFlag);
Check(TRUE, StatPropSetStg.dwOSVersion == dwOSVersion);
CheckTime(ftStart, StatPropSetStg.mtime);
CheckTime(ftStart, StatPropSetStg.ctime);
CheckTime(ftStart, StatPropSetStg.atime);
}
//
//
// Stat (Create four combinations)
// Check ansi/wide fflag
// Also test clsid on propset
void test_IPropertyStorage_Stat(IStorage *pStorage)
{
printf( " IPropertyStorage::Stat\n" );
DWORD dwOSVersion = 0;
TSafeStorage< IPropertySetStorage > pPropSetStg(pStorage);
FMTID fmtid;
UuidCreate(&fmtid);
IPropertyStorage *pPropSet;
STATPROPSETSTG StatPropSetStg;
// Calculate the OS Version
#ifdef _MAC_
#error Do not know how to calculate the OS Version for the macintosh.
#endif
dwOSVersion = MAKELONG( 0x0100, OSKIND_REF );
for (ULONG i=0; i<4; i++)
{
FILETIME ftStart;
DfGetTOD(&ftStart);
memset(&StatPropSetStg, 0, sizeof(StatPropSetStg));
CLSID clsid;
UuidCreate(&clsid);
Check(S_OK, pPropSetStg->Create(fmtid, &clsid,
((i & 2) ? PROPSETFLAG_ANSI : 0),
STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE,
&pPropSet));
CheckStat(pPropSet, fmtid, clsid,
((i & 2) ? PROPSETFLAG_ANSI : 0), ftStart, dwOSVersion );
pPropSet->Release();
Check(S_OK, pPropSetStg->Open(fmtid,
STGM_SHARE_EXCLUSIVE | STGM_READWRITE,
&pPropSet));
CheckStat(pPropSet, fmtid, clsid,
((i & 2) ? PROPSETFLAG_ANSI : 0), ftStart, dwOSVersion );
pPropSet->Release();
Check(S_OK, pPropSetStg->Open(fmtid,
STGM_SHARE_EXCLUSIVE | STGM_READWRITE,
&pPropSet));
CheckStat(pPropSet, fmtid, clsid,
((i & 2) ? PROPSETFLAG_ANSI : 0), ftStart, dwOSVersion );
pPropSet->Release();
}
}
//
// test using IStorage::Commit to commit the changes in a nested
// property set
//
void
test_IPropertyStorage_Commit(IStorage *pStorage)
{
printf( " IPropertyStorage::Commit\n" );
// create another level of storage
SCODE sc;
// 8 scenarios: simple * direct * (release only + commit storage + commit
// propset)
// note: some scenarios might repeat since there is no
// non-simple/transacted cases
for (int i=0; i<32; i++)
{
CTempStorage pDeeper(Create, pStorage, GetNextTest(),
STGM_DIRECT);
TSafeStorage< IPropertySetStorage > pPropSetStg(pDeeper);
FMTID fmtid;
UuidCreate(&fmtid);
IPropertyStorage *pPropSet;
if (S_OK != (sc = pPropSetStg->Create(
fmtid, NULL,
PROPSETFLAG_DEFAULT,
STGM_SHARE_EXCLUSIVE | STGM_READWRITE |
STGM_DIRECT,
&pPropSet)))
{
Check(S_OK, sc);
}
PROPSPEC ps;
ps.ulKind = PRSPEC_PROPID;
ps.propid = 100;
PROPVARIANT pv;
pv.vt = VT_I4;
pv.lVal = 1234;
Check(S_OK, pPropSet->WriteMultiple(1, &ps, &pv, 1000));
PropVariantClear(&pv);
Check(S_OK, pPropSet->ReadMultiple(1, &ps, &pv));
ASSERT(pv.vt==VT_I4);
ASSERT(pv.lVal == 1234);
pv.lVal = 2345; // no size changes
Check(S_OK, pPropSet->WriteMultiple(1, &ps, &pv, 1000));
if (i & 4)
Check(S_OK, pPropSet->Commit(0));
if (i & 2)
Check(S_OK, pStorage->Commit(0));
Check(0, pPropSet->Release()); // implicit commit if i&2 is false
if (S_OK == pPropSetStg->Open(fmtid, STGM_SHARE_EXCLUSIVE | STGM_READWRITE,
&pPropSet))
{
PropVariantClear(&pv);
Check( S_OK, pPropSet->ReadMultiple(1, &ps, &pv));
ASSERT(pv.vt == VT_I4);
ASSERT(pv.lVal == 2345);
pPropSet->Release();
}
}
}
void
test_IPropertyStorage_WriteMultiple(IStorage *pStorage)
{
test_IPropertyStorage_Commit(pStorage);
}
// this serves as a test for WritePropertyNames, ReadPropertyNames, DeletePropertyNames
// DeleteMultiple, PropVariantCopy, FreePropVariantArray.
void
test_IPropertyStorage_DeleteMultiple(IStorage *pStorage)
{
printf( " IPropertyStorage::DeleteMultiple\n" );
TSafeStorage< IPropertySetStorage > pPropSetStg(pStorage);
FMTID fmtid;
UuidCreate(&fmtid);
IPropertyStorage *pPropSet;
int PropId = 3;
UuidCreate(&fmtid);
Check(S_OK, pPropSetStg->Create(
fmtid,
NULL, PROPSETFLAG_DEFAULT,
STGM_SHARE_EXCLUSIVE | STGM_READWRITE,
&pPropSet));
// create and delete each type.
PROPVARIANT *pVar;
for (int AtOnce=1; AtOnce <3; AtOnce++)
{
CGenProps gp;
int Actual;
while (pVar = gp.GetNext(AtOnce, &Actual, FALSE, TRUE))
{
PROPSPEC ps[3];
PROPID rgpropid[3];
LPOLESTR rglpostrName[3];
OLECHAR aosz[3][16];
char pchTemp [16];
for (int s=0; s<3; s++)
{
sprintf(pchTemp, "prop%d", PropId );
STOT(pchTemp, aosz[s], strlen(pchTemp)+1);
rgpropid[s] = PropId++;
rglpostrName[s] = &aosz[s][0];
ps[s].ulKind = PRSPEC_LPWSTR;
ps[s].lpwstr = &aosz[s][0];
}
for (int l=1; l<Actual; l++)
{
PROPVARIANT VarRead[3];
Check(S_FALSE, pPropSet->ReadMultiple(l, ps, VarRead));
Check(S_OK, pPropSet->
WritePropertyNames(l, rgpropid, rglpostrName));
Check(S_FALSE, pPropSet->ReadMultiple(l, ps, VarRead));
Check(S_OK, pPropSet->WriteMultiple(l, ps, pVar, 1000));
Check(S_OK, pPropSet->ReadMultiple(l, ps, VarRead));
Check(S_OK, FreePropVariantArray(l, VarRead));
Check(S_OK, pPropSet->DeleteMultiple(l, ps));
Check(S_FALSE, pPropSet->ReadMultiple(l, ps, VarRead));
Check(S_OK, FreePropVariantArray(l, VarRead));
LPOLESTR rglpostrNameCheck[3];
Check(S_OK, pPropSet->
ReadPropertyNames(l, rgpropid, rglpostrNameCheck));
for (int c=0; c<l; c++)
{
ASSERT(ocscmp(rglpostrNameCheck[c], rglpostrName[c])==0);
CoTaskMemFree(rglpostrNameCheck[c]);
}
Check(S_OK, pPropSet->DeletePropertyNames(l, rgpropid));
Check(S_FALSE, pPropSet->ReadPropertyNames(l, rgpropid,
rglpostrNameCheck));
}
FreePropVariantArray(Actual, pVar);
delete pVar;
}
}
pPropSet->Release();
}
void
test_IPropertyStorage(IStorage *pStorage)
{
test_IPropertyStorage_Access(pStorage);
test_IPropertyStorage_Create(pStorage);
test_IPropertyStorage_Stat(pStorage);
test_IPropertyStorage_WriteMultiple(pStorage);
test_IPropertyStorage_DeleteMultiple(pStorage);
}
//
// Word6.0 summary information
// Open
// Read fields
// Stat
//
#define W6TEST "w6test.doc"
void test_Word6(IStorage *pStorage)
{
printf( " Word 6.0 compatibility test\n" );
extern unsigned char g_achTestDoc[];
extern unsigned g_cbTestDoc;
OLECHAR ocsTempFile[MAX_PATH+1];
FILE *f = fopen(W6TEST, "w+b");
int nWritten = fwrite(g_achTestDoc, 1, g_cbTestDoc, f);
ASSERT(nWritten == (int)g_cbTestDoc);
fclose(f);
STOT(W6TEST, ocsTempFile, strlen(W6TEST)+1);
IStorage *pStg;
Check(S_OK, StgOpenStorage(ocsTempFile,
(IStorage*)NULL,
(DWORD) STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
0,
0,
&pStg));
TSafeStorage< IPropertySetStorage > pPropSetStg(pStg);
IPropertyStorage *pPropStg;
Check(S_OK, pPropSetStg->Open(FMTID_SummaryInformation,
STGM_SHARE_EXCLUSIVE | STGM_DIRECT | STGM_READ,
&pPropStg));
#define WORDPROPS 18
static struct tagWordTest {
VARENUM vt;
void *pv;
} avt[WORDPROPS] = {
VT_LPSTR, "Title of the document.", // PID_TITLE
VT_LPSTR, "Subject of the document.", // PID_SUBJECT
VT_LPSTR, "Author of the document.", // PID_AUTHOR
VT_LPSTR, "Keywords of the document.", // PID_KEYWORDS
VT_LPSTR, "Comments of the document.", // PID_COMMENTS
VT_LPSTR, "Normal.dot", // PID_TEMPLATE -- Normal.dot
VT_LPSTR, "Bill Morel", // PID_LASTAUTHOR --
VT_LPSTR, "3", // PID_REVNUMBER -- '3'
VT_EMPTY, 0, // PID_EDITTIME -- 3 Minutes FILETIME
VT_EMPTY, 0, // PID_LASTPRINTED -- 04/07/95 12:04 FILETIME
VT_EMPTY, 0, // PID_CREATE_DTM
VT_EMPTY, 0, // PID_LASTSAVE_DTM
VT_I4, (void*) 1, // PID_PAGECOUNT
VT_I4, (void*) 7, // PID_WORDCOUNT
VT_I4, (void*) 65, // PID_CHARCOUNT
VT_EMPTY, 0, // PID_THUMBNAIL
VT_LPSTR, "Microsoft Word 6.0", // PID_APPNAME
VT_I4, 0 }; // PID_SECURITY
PROPSPEC propspec[WORDPROPS+2];
int i;
for (i=2; i<WORDPROPS+2; i++)
{
propspec[i].ulKind = PRSPEC_PROPID;
propspec[i].propid = (PROPID)i;
}
PROPVARIANT propvar[WORDPROPS+2];
Check(S_OK, pPropStg->ReadMultiple(WORDPROPS, propspec+2, propvar+2));
for (i=2; i<WORDPROPS+2; i++)
{
if ( propvar[i].vt != avt[i-2].vt )
{
printf( " PROPTEST: 0x%x retrieved type 0x%x, expected type 0x%x\n",
i, propvar[i].vt, avt[i-2].vt );
ASSERT(propvar[i].vt == avt[i-2].vt);
}
switch (propvar[i].vt)
{
case VT_LPSTR:
ASSERT(strcmp(propvar[i].pszVal, (char*)avt[i-2].pv)==0);
break;
case VT_I4:
ASSERT(propvar[i].lVal == (int)avt[i-2].pv);
break;
}
}
FreePropVariantArray( WORDPROPS, propvar+2 );
pPropStg->Release();
pStg->Release();
//_unlink("w6test");
}
BOOL
IsEqualSTATPROPSTG(const STATPROPSTG *p1, const STATPROPSTG *p2)
{
BOOL f1 = p1->propid == p2->propid;
BOOL f2 = p1->vt == p2->vt;
BOOL f3 = (p1->lpwstrName == NULL && p2->lpwstrName == NULL) ||
((p1->lpwstrName != NULL && p2->lpwstrName != NULL) &&
ocscmp(p1->lpwstrName, p2->lpwstrName) == 0);
return(f1 && f2 && f3);
}
void
test_IEnumSTATPROPSTG(IStorage *pstgTemp)
{
printf( " IEnumSTATPROPSTG\n" );
PROPID apropid[8];
LPOLESTR alpostrName[8];
OLECHAR aosz[8][32];
PROPID PropId=2;
PROPSPEC ps[8];
char pchTemp[32];
FMTID fmtid;
IPropertyStorage *pPropStg;
TSafeStorage< IPropertySetStorage > pPropSetStg(pstgTemp);
UuidCreate(&fmtid);
for (int setup=0; setup<8; setup++)
{
alpostrName[setup] = &aosz[setup][0];
}
CGenProps gp;
// simple/simple, ansi/wide, named/not named
for (int outer=0; outer<8; outer++)
{
Check(S_OK, pPropSetStg->Create(fmtid, NULL,
((outer&2) ? PROPSETFLAG_ANSI : 0),
STGM_CREATE | STGM_READWRITE | STGM_DIRECT | STGM_SHARE_EXCLUSIVE,
&pPropStg));
for (int i=0; i<CPROPERTIES; i++)
{
apropid[i] = PropId++;
if (outer & 1)
{
ps[i].ulKind = PRSPEC_LPWSTR;
sprintf(pchTemp, "prop%d\0", apropid[i]);
STOT(pchTemp, aosz[i], strlen(pchTemp)+1);
ps[i].lpwstr = aosz[i];
}
else
{
ps[i].ulKind = PRSPEC_PROPID;
ps[i].propid = apropid[i];
}
}
if (outer & 1)
{
Check(S_OK, pPropStg->WritePropertyNames(CPROPERTIES, apropid, alpostrName));
}
PROPVARIANT *pVar = gp.GetNext(CPROPERTIES, NULL, TRUE, TRUE);
ASSERT(pVar != NULL);
Check(S_OK, pPropStg->WriteMultiple(CPROPERTIES, ps, pVar, 1000));
FreePropVariantArray(CPROPERTIES, pVar);
delete pVar;
// Allocate enough STATPROPSTGs for one more than the actual properties
// in the set.
STATPROPSTG StatBuffer[CPROPERTIES+1];
ULONG celt;
IEnumSTATPROPSTG *penum, *penum2;
Check(S_OK, pPropStg->Enum(&penum));
IUnknown *punk, *punk2;
IEnumSTATPROPSTG *penum3;
Check(S_OK, penum->QueryInterface(IID_IUnknown, (void**)&punk));
Check(S_OK, punk->QueryInterface(IID_IEnumSTATPROPSTG, (void**)&penum3));
Check(S_OK, penum->QueryInterface(IID_IEnumSTATPROPSTG, (void**)&punk2));
ASSERT(punk == punk2);
punk->Release();
penum3->Release();
punk2->Release();
// test S_FALSE
Check(S_FALSE, penum->Next( CPROPERTIES+1, StatBuffer, &celt));
ASSERT(celt == CPROPERTIES);
CleanStat(celt, StatBuffer);
penum->Reset();
// test reading half out, then cloning, then comparing
// rest of enumeration with other clone.
Check(S_OK, penum->Next(CPROPERTIES/2, StatBuffer, &celt));
ASSERT(celt == CPROPERTIES/2);
CleanStat(celt, StatBuffer);
celt = 0;
Check(S_OK, penum->Clone(&penum2));
Check(S_OK, penum->Next(CPROPERTIES - CPROPERTIES/2, StatBuffer, &celt));
ASSERT(celt == CPROPERTIES - CPROPERTIES/2);
// check the clone
for (int c=0; c<CPROPERTIES - CPROPERTIES/2; c++)
{
STATPROPSTG CloneStat;
Check(S_OK, penum2->Next(1, &CloneStat, NULL));
ASSERT(IsEqualSTATPROPSTG(&CloneStat, StatBuffer+c));
CleanStat(1, &CloneStat);
}
CleanStat(celt, StatBuffer);
// check both empty
celt = 0;
Check(S_FALSE, penum->Next(1, StatBuffer, &celt));
ASSERT(celt == 0);
Check(S_FALSE, penum2->Next(1, StatBuffer, &celt));
ASSERT(celt == 0);
penum->Reset();
//
// loop deleting one property at a time
// enumerate the propertys checking that correct ones appear.
//
for (ULONG d = 0; d<CPROPERTIES; d++)
{
// d is for delete
BOOL afFound[CPROPERTIES];
ULONG cTotal = 0;
Check(S_OK, penum->Next(CPROPERTIES-d, StatBuffer, &celt));
ASSERT(celt == CPROPERTIES-d);
penum->Reset();
memset(afFound, 0, sizeof(afFound));
for (ULONG iProperty=0; iProperty<CPROPERTIES; iProperty++)
{
// Search the StatBuffer for this property.
for (ULONG iSearch=0; iSearch<CPROPERTIES-d; iSearch++)
{
// Compare this entry in the StatBuffer to the property for which we're searching.
// Use the lpstrName or propid, whichever is appropriate for this pass (indicated
// by 'outer').
if ( ( (outer & 1) == 1 && 0 == ocscmp(StatBuffer[iSearch].lpwstrName, ps[iProperty].lpwstr) )
||
( (outer & 1) == 0 && StatBuffer[iSearch].propid == apropid[iProperty] )
)
{
ASSERT (!afFound[iSearch]);
afFound[iSearch] = TRUE;
cTotal++;
break;
}
}
}
CleanStat(celt, StatBuffer);
ASSERT(cTotal == CPROPERTIES-d);
Check(S_OK, pPropStg->DeleteMultiple(1, ps+d));
Check(S_OK, penum->Reset());
}
penum->Release();
penum2->Release();
pPropStg->Release();
}
}
void
test_MaxPropertyName(IStorage *pstgTemp)
{
printf( " Max Property Name length\n" );
// ----------
// Initialize
// ----------
CPropVariant cpropvar;
// Create a new storage, because we're going to create
// well-known property sets, and this way we can be sure
// that they don't already exist.
TSafeStorage< IStorage > pstg;
DECLARE_OLESTR(ocsMaxProp, "MaxPropNameTest");
Check(S_OK, pstgTemp->CreateStorage( ocsMaxProp,
STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
0L, 0L,
&pstg ));
// Generate a new Format ID.
FMTID fmtid;
UuidCreate(&fmtid);
// Get a IPropertySetStorage from the IStorage.
TSafeStorage< IPropertySetStorage > pPropSetStg(pstg);
TSafeStorage< IPropertyStorage > pPropStg;
// ----------------------------------
// Test the non-SumInfo property set.
// ----------------------------------
// Create a new PropertyStorage.
Check(S_OK, pPropSetStg->Create(fmtid,
NULL,
PROPSETFLAG_DEFAULT,
STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_DIRECT | STGM_READWRITE,
&pPropStg));
// Generate a property name which is max+1 characters.
OLECHAR *poszPropertyName;
poszPropertyName = (OLECHAR*)
CoTaskMemAlloc( (CCH_MAXPROPNAMESZ+1) * sizeof(OLECHAR) );
Check(TRUE, poszPropertyName != NULL );
for( ULONG ulIndex = 0; ulIndex < CCH_MAXPROPNAMESZ; ulIndex++ )
poszPropertyName[ ulIndex ] = (OLECHAR)'a' + (OLECHAR) ( ulIndex % 26 );
poszPropertyName[ CCH_MAXPROPNAMESZ ] = (OLECHAR)0; // terminating null
// Write out a property with this max+1 name.
PROPSPEC propspec;
propspec.ulKind = PRSPEC_LPWSTR;
propspec.lpwstr = poszPropertyName;
cpropvar = (long) 0x1234;
Check(STG_E_INVALIDPARAMETER, pPropStg->WriteMultiple( 1, &propspec, cpropvar, PID_FIRST_USABLE ));
// Write out a property with a max-character name (we create a max-
// char name by terminating the previously-used string one character
// earlier).
poszPropertyName[ CWC_MAXPROPNAME ] = 0;
Check(S_OK, pPropStg->WriteMultiple( 1, &propspec, cpropvar, PID_FIRST_USABLE ));
// Write out a property with a minimum-character name.
DECLARE_OLESTR(ocsX, "X");
propspec.lpwstr = ocsX;
Check(S_OK, pPropStg->WriteMultiple( 1, &propspec, cpropvar, PID_FIRST_USABLE ));
// Write out a property with a below-minimum-character name.
DECLARE_OLESTR(ocsEmpty, "");
propspec.lpwstr = ocsEmpty;
Check(STG_E_INVALIDPARAMETER,
pPropStg->WriteMultiple( 1, &propspec,
cpropvar,
PID_FIRST_USABLE));
CoTaskMemFree( poszPropertyName );
}
#define CODEPAGE_TEST_NAMED_PROPERTY "Named Property"
#define CODEPAGE_TEST_UNNAMED_BSTR_PROPID 3
#define CODEPAGE_TEST_UNNAMED_I4_PROPID 4
#define CODEPAGE_TEST_VBSTR_PROPID 7
#define CODEPAGE_TEST_VPROPVAR_BSTR_PROPID 9
void
CreateCodePageTestFile( LPOLESTR poszFileName,
IStorage **ppStg,
BOOL fUseUnicode)
{
ASSERT( poszFileName != NULL );
// --------------
// Initialization
// --------------
TSafeStorage< IPropertySetStorage > pPSStg;
TSafeStorage< IPropertyStorage > pPStg;
PROPSPEC propspec;
CPropVariant cpropvar;
*ppStg = NULL;
OLECHAR poszActualFile[MAX_PATH];
ocscpy(poszActualFile, poszFileName);
// assume the path name has enough space, change the last character
// of the filename
int len = ocslen(poszActualFile);
if (!fUseUnicode)
{
poszActualFile[len]='a';
poszActualFile[len+1]=0;
}
else
{
poszActualFile[len]='w';
poszActualFile[len+1]=0;
}
Check(S_OK, StgCreateDocfile( poszActualFile,
STGM_CREATE | STGM_READWRITE |
STGM_SHARE_EXCLUSIVE,
0,
ppStg ));
// Get an IPropertySetStorage
Check(S_OK, (*ppStg)->QueryInterface( IID_IPropertySetStorage, (void**)&pPSStg ));
// Create an IPropertyStorage (ANSI or UNICODE)
DWORD psFlag= (fUseUnicode) ? 0 : PROPSETFLAG_ANSI ;
Check(S_OK, pPSStg->Create( FMTID_NULL,
NULL,
psFlag,
STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
&pPStg ));
// ----------------------
// Write a named property
// ----------------------
// Write a named I4 property
propspec.ulKind = PRSPEC_LPWSTR;
DECLARE_OLESTR(ocsNamed, CODEPAGE_TEST_NAMED_PROPERTY);
propspec.lpwstr = ocsNamed;
cpropvar = (LONG) 0x12345678;
Check(S_OK, pPStg->WriteMultiple( 1, &propspec, cpropvar, PID_FIRST_USABLE ));
// --------------------------
// Write singleton properties
// --------------------------
// Write an un-named BSTR.
propspec.ulKind = PRSPEC_PROPID;
propspec.propid = CODEPAGE_TEST_UNNAMED_BSTR_PROPID;
DECLARE_OLESTR(ocsBSTR, "BSTR Property");
cpropvar.SetBSTR( ocsBSTR );
Check(S_OK, pPStg->WriteMultiple( 1, &propspec, cpropvar, PID_FIRST_USABLE ));
// Write an un-named I4
propspec.ulKind = PRSPEC_PROPID;
propspec.propid = CODEPAGE_TEST_UNNAMED_I4_PROPID;
cpropvar = (LONG) 0x76543210;
Check(S_OK, pPStg->WriteMultiple( 1, &propspec, cpropvar, PID_FIRST_USABLE ));
// -----------------------
// Write vector properties
// -----------------------
// Write a vector of BSTRs.
propspec.ulKind = PRSPEC_PROPID;
propspec.propid = CODEPAGE_TEST_VBSTR_PROPID;
DECLARE_OLESTR(ocsElt0, "BSTR Element 0");
DECLARE_OLESTR(ocsElt1, "BSTR Element 1");
cpropvar.SetBSTR( ocsElt1, 1 );
cpropvar.SetBSTR( ocsElt0, 0 );
ASSERT( (VT_VECTOR | VT_BSTR) == cpropvar.VarType() );
Check(S_OK, pPStg->WriteMultiple( 1, &propspec, cpropvar, PID_FIRST_USABLE ));
// -------------------------------
// Write Variant Vector Properties
// -------------------------------
// Write a variant vector that has a BSTR
propspec.ulKind = PRSPEC_PROPID;
propspec.propid = CODEPAGE_TEST_VPROPVAR_BSTR_PROPID;
CPropVariant cpropvarT;
DECLARE_OLESTR(ocsPropVect, "PropVar Vector BSTR");
cpropvarT.SetBSTR( ocsPropVect );
cpropvar[1] = (LPPROPVARIANT) cpropvarT;
cpropvar[0] = (LPPROPVARIANT) CPropVariant((long) 44);
ASSERT( (VT_VARIANT | VT_VECTOR) == cpropvar.VarType() );
Check(S_OK, pPStg->WriteMultiple( 1, &propspec, cpropvar, PID_FIRST_USABLE ));
} // CreateCodePageTestFile()
void
OpenCodePageTestFile( LPOLESTR poszFileName,
IStorage **ppStg,
BOOL fUseUnicode)
{
OLECHAR poszActualFile[MAX_PATH];
ocscpy(poszActualFile, poszFileName);
// assume the path name has enough space, change the last character
// of the filename
int len = ocslen(poszActualFile);
if (!fUseUnicode)
{
poszActualFile[len]='a';
poszActualFile[len+1]=0;
}
else
{
poszActualFile[len]='w';
poszActualFile[len+1]=0;
}
Check(S_OK, StgOpenStorage( poszActualFile,
(IStorage*) NULL,
STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
(SNB) 0,
(DWORD) 0,
ppStg ));
}
void
ModifyPropSetCodePage( IStorage *pStg, ULONG ulCodePage )
{
ASSERT( pStg != NULL );
// --------------
// Initialization
// --------------
OLECHAR aocPropSetName[ 32 ];
DWORD dwOffset = 0;
DWORD dwcbSection = 0;
DWORD dwcProperties = 0;
ULONG ulcbWritten = 0;
LARGE_INTEGER liSectionOffset, liCodePageOffset;
TSafeStorage< IStream > pStm;
CPropVariant cpropvar;
// Open the Stream
RtlGuidToPropertySetName( &FMTID_NULL, aocPropSetName );
Check(S_OK, pStg->OpenStream( aocPropSetName,
(VOID*)NULL,
STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
(DWORD)NULL,
&pStm ));
// Seek past the propset header and the format ID.
liSectionOffset.HighPart = 0;
liSectionOffset.LowPart = sizeof(PROPERTYSETHEADER) + sizeof(FMTID);
Check(S_OK, pStm->Seek(liSectionOffset, STREAM_SEEK_SET, NULL ));
// Move to the beginning of the property set.
liSectionOffset.HighPart = 0;
Check(S_OK, pStm->Read( &liSectionOffset.LowPart, sizeof(DWORD), NULL ));
PropByteSwap(&liSectionOffset.LowPart);
Check(S_OK, pStm->Seek( liSectionOffset, STREAM_SEEK_SET, NULL ));
// Get the section size & property count.
Check(S_OK, pStm->Read( &dwcbSection, sizeof(DWORD), NULL ));
PropByteSwap( &dwcbSection );
Check(S_OK, pStm->Read( &dwcProperties, sizeof(DWORD), NULL ));
PropByteSwap( &dwcProperties );
// Scan for the PID_CODEPAGE property.
ULONG ulIndex;
for(ulIndex = 0; ulIndex < dwcProperties; ulIndex++ )
{
PROPID propid;
DWORD dwOffset;
// Read in the PROPID
Check(S_OK, pStm->Read( &propid, sizeof(PROPID), NULL ));
// Is it the codepage?
if( PropByteSwap(propid) == PID_CODEPAGE )
break;
// Read in this PROPIDs offset (we don't need it, but we want
// to seek past it.
Check(S_OK, pStm->Read( &dwOffset, sizeof(dwOffset), NULL ));
}
// Verify that the above for loop terminated because we found
// the codepage.
Check( TRUE, ulIndex < dwcProperties );
// Move to the code page.
liCodePageOffset.HighPart = 0;
Check(S_OK, pStm->Read( &liCodePageOffset.LowPart, sizeof(DWORD), NULL ));
PropByteSwap( &liCodePageOffset.LowPart );
liCodePageOffset.LowPart += liSectionOffset.LowPart + sizeof(ULONG); // Move past VT too.
ASSERT( liSectionOffset.HighPart == 0 );
Check(S_OK, pStm->Seek( liCodePageOffset, STREAM_SEEK_SET, NULL ));
// this is so that you can manually verify what was read
// i.e. that it is the code page
WORD wCodePage;
Check(S_OK, pStm->Read( &wCodePage, sizeof(WORD), NULL));
Check(S_OK, pStm->Seek( liCodePageOffset, STREAM_SEEK_SET, NULL ));
// Write the new code page.
wCodePage = PropByteSwap( (WORD) ((ulCodePage << 16) >> 16) );
Check(S_OK, pStm->Write( &wCodePage, sizeof(wCodePage), &ulcbWritten ));
Check(TRUE, ulcbWritten == sizeof(wCodePage) );
//pStm->Commit(0);
} // ModifyPropSetCodePage()
void
ModifyOSVersion( IStorage* pStg, DWORD dwOSVersion )
{
ASSERT( pStg != NULL );
// --------------
// Initialization
// --------------
OLECHAR aocPropSetName[ 32 ];
ULONG ulcbWritten = 0;
LARGE_INTEGER liOffset;
PROPERTYSETHEADER propsetheader;
TSafeStorage< IStream > pStm;
// Open the Stream
RtlGuidToPropertySetName( &FMTID_NULL, aocPropSetName );
Check(S_OK, pStg->OpenStream( aocPropSetName,
(VOID*)NULL,
STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
(DWORD)NULL,
&pStm ));
// Seek to the OS Version field in the header.
liOffset.HighPart = 0;
propsetheader; // avoid compiler warning of unref'd var.
liOffset.LowPart = sizeof(propsetheader.wByteOrder) + sizeof(propsetheader.wFormat);
Check(S_OK, pStm->Seek( liOffset, STREAM_SEEK_SET, NULL ));
// Set the new OS Version
PropByteSwap( &dwOSVersion );
Check(S_OK, pStm->Write( &dwOSVersion, sizeof(dwOSVersion), &ulcbWritten ));
Check(TRUE, ulcbWritten == sizeof(dwOSVersion) );
} // ModifyOSVersion()
#define CODEPAGE_DEFAULT 0x04e4 // US English
#define CODEPAGE_GOOD 0x0000 // Present on a US English machine
#define CODEPAGE_BAD 0x9999 // Non-existent code page
void
test_CodePages( LPOLESTR poszDirectory,
ULONG ulTestOptions,
BOOL fTestUnicode)
{
printf( " Code Page compatibility -- " );
if (ulTestOptions & TEST_INTEROP_R)
printf("Verify Read, ");
else
printf("Write & Read, ");
if (fTestUnicode)
printf("UNICODE files\n");
else
printf("ASCII files\n");
// --------------
// Initialization
// --------------
OLECHAR oszBadFile[ MAX_PATH ];
OLECHAR oszGoodFile[ MAX_PATH ];
OLECHAR oszUnicodeFile[ MAX_PATH ];
OLECHAR oszMacFile[ MAX_PATH ];
HRESULT hr = S_OK;
TSafeStorage< IStorage > pStgBad, pStgGood, pStgUnicode, pStgMac;
CPropVariant cpropvarWrite, cpropvarRead;
Check( TRUE, GetACP() == CODEPAGE_DEFAULT );
// ------------------------------
// Create test property sets
// ------------------------------
// Create a property set with a bad codepage.
#ifdef _WIN32
DECLARE_OLESTR(ocsBad, "\\badcp.sg");
ocscpy( oszBadFile, poszDirectory );
ocscat( oszBadFile, ocsBad );
#else
DECLARE_OLESTR(ocsBad, "badcp.sg");
ocscpy( oszBadFile, ocsBad );
#endif
if (! (ulTestOptions & TEST_INTEROP_R))
{
CreateCodePageTestFile( oszBadFile, &pStgBad, fTestUnicode );
if (!fTestUnicode)
{
// modification of code page is only
// interesting for ansi property sets,
// otherwise it will result in in error
ModifyPropSetCodePage( pStgBad, CODEPAGE_BAD );
}
}
else
OpenCodePageTestFile( oszBadFile, &pStgBad, fTestUnicode );
// Create a property set with a good codepage.
#ifdef _WIN32
ocscpy( oszGoodFile, poszDirectory );
DECLARE_OLESTR(ocsGood, "\\goodcp.sg");
ocscat( oszGoodFile, ocsGood );
#else
DECLARE_OLESTR(ocsGood, "goodcp.sg");
ocscpy( oszGoodFile, ocsGood );
#endif
if (! (ulTestOptions & TEST_INTEROP_R))
{
CreateCodePageTestFile( oszGoodFile, &pStgGood, fTestUnicode );
// We can only modify code page of ansi property sets; Modfiying
// code page of UNICODE pages will result in a INVALID_HEADER
// 'cos it will treat strings as wide chars instead of single-byte
if (!fTestUnicode)
{
ModifyPropSetCodePage( pStgGood, CODEPAGE_GOOD );
}
}
else
OpenCodePageTestFile( oszGoodFile, &pStgGood, fTestUnicode );
// Create a property set that has the OS Kind (in the
// header) set to "Mac".
#ifdef _WIN32
DECLARE_OLESTR(ocsMac, "\\mackind.sg");
ocscpy( oszMacFile, poszDirectory );
ocscat( oszMacFile, ocsMac );
#else
DECLARE_OLESTR(ocsMac, "mackind.sg");
ocscpy( oszMacFile, ocsMac );
#endif
if (! (ulTestOptions & TEST_INTEROP_R))
{
CreateCodePageTestFile( oszMacFile, &pStgMac, fTestUnicode );
ModifyOSVersion( pStgMac, 0x00010904 );
}
else
OpenCodePageTestFile( oszMacFile, &pStgMac, fTestUnicode );
// ---------------------------
// Open the Ansi property sets
// ---------------------------
TSafeStorage< IPropertySetStorage > pPropSetStgBad(pStgBad);
TSafeStorage< IPropertySetStorage > pPropSetStgGood(pStgGood);
TSafeStorage< IPropertySetStorage > pPropSetStgMac(pStgMac);
TSafeStorage< IPropertyStorage > pPropStgBad, pPropStgGood, pPropStgMac;
Check(S_OK, pPropSetStgBad->Open(FMTID_NULL,
STGM_SHARE_EXCLUSIVE | STGM_DIRECT | STGM_READWRITE,
&pPropStgBad));
Check(S_OK, pPropSetStgGood->Open(FMTID_NULL,
STGM_SHARE_EXCLUSIVE | STGM_DIRECT | STGM_READWRITE,
&pPropStgGood));
Check(S_OK, pPropSetStgMac->Open(FMTID_NULL,
STGM_SHARE_EXCLUSIVE | STGM_DIRECT | STGM_READWRITE,
&pPropStgMac));
PROPSPEC propspec;
PROPVARIANT propvar;
PropVariantInit( &propvar );
// ASSUMPTION: all three files are created all together by
// proptest, they will all be ansi or they will all be
// unicode.
//
// For UNICODE APIs, when the code page is not Unicode, it will
// try and translate it to unicode and thus result in an error
propspec.ulKind = PRSPEC_PROPID;
propspec.propid = 1; // propid for code page indicator
Check(S_OK, pPropStgGood->ReadMultiple( 1, &propspec, &propvar));
HRESULT pStgBadHr = S_OK; // default
if (propvar.iVal == (SHORT) CODEPAGE_GOOD)
{ // files created in Ansi
#ifdef _UNICODE
pStgBadHr = HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION);
#endif
}
// Since we are doing interoperability testing and pStgBad with
// a wrong codepage number will result in a invalid_header error,
// we change it back to normal to get rid of the error.
//
// Also, we only change it when the current code page is bad,
// otherwise, we leave it as it is.
//
// We know that the correct number to put in is 1252 'cos we only
// change the code page for ansi property sets.
if (propvar.iVal == (SHORT) CODEPAGE_BAD)
ModifyPropSetCodePage( pStgBad, 1252 );
// ------------------------------------------
// Test BSTRs in the three property sets
// ------------------------------------------
// Attempt to read by name.
propspec.ulKind = PRSPEC_LPWSTR;
DECLARE_OLESTR(ocsCP_test, CODEPAGE_TEST_NAMED_PROPERTY);
propspec.lpwstr = ocsCP_test;
CPropVariant cpropvar=(LONG) 0x12345678;
Check(S_OK,
pPropStgGood->ReadMultiple( 1, &propspec, &propvar ));
Check(S_OK, CPropVariant::Compare( &propvar, &cpropvar ));
PropVariantClear( &propvar );
Check(pStgBadHr,
pPropStgBad->ReadMultiple( 1, &propspec, &propvar ));
PropVariantClear( &propvar );
Check(S_OK,
pPropStgMac->ReadMultiple( 1, &propspec, &propvar ));
Check(S_OK, CPropVariant::Compare( &propvar, &cpropvar ));
// Attempt to write by name.
Check(S_OK,
pPropStgMac->WriteMultiple( 1, &propspec, &propvar, PID_FIRST_USABLE ));
Check(pStgBadHr,
pPropStgBad->WriteMultiple( 1, &propspec, &propvar, PID_FIRST_USABLE ));
Check(S_OK,
pPropStgGood->WriteMultiple( 1, &propspec, &propvar, PID_FIRST_USABLE ));
PropVariantClear( &propvar );
// Attempt to read the BSTR property
propspec.ulKind = PRSPEC_PROPID;
propspec.propid = CODEPAGE_TEST_UNNAMED_BSTR_PROPID;
DECLARE_OLESTR(ocsBSTR, "BSTR Property");
cpropvar.SetBSTR( ocsBSTR );
Check(S_OK,
pPropStgMac->ReadMultiple( 1, &propspec, &propvar ));
Check(S_OK, CPropVariant::Compare( &propvar, &cpropvar ));
PropVariantClear( &propvar );
Check(pStgBadHr,
pPropStgBad->ReadMultiple( 1, &propspec, &propvar ));
PropVariantClear( &propvar );
Check(S_OK,
pPropStgGood->ReadMultiple( 1, &propspec, &propvar ));
Check(S_OK, CPropVariant::Compare( &propvar, &cpropvar ));
// Attempt to write the BSTR property
Check(S_OK,
pPropStgMac->WriteMultiple( 1, &propspec, &propvar, PID_FIRST_USABLE ));
Check(pStgBadHr,
pPropStgBad->WriteMultiple( 1, &propspec, &propvar, PID_FIRST_USABLE ));
Check(S_OK,
pPropStgGood->WriteMultiple( 1, &propspec, &propvar, PID_FIRST_USABLE ));
PropVariantClear( &propvar );
// Attempt to read the BSTR Vector property
propspec.ulKind = PRSPEC_PROPID;
propspec.propid = CODEPAGE_TEST_VBSTR_PROPID;
DECLARE_OLESTR(ocsElt0, "BSTR Element 0");
DECLARE_OLESTR(ocsElt1, "BSTR Element 1");
cpropvar.SetBSTR( ocsElt1, 1 );
cpropvar.SetBSTR( ocsElt0, 0 );
ASSERT( (VT_VECTOR | VT_BSTR) == cpropvar.VarType() );
Check(S_OK,
pPropStgMac->ReadMultiple( 1, &propspec, &propvar ));
Check(S_OK, CPropVariant::Compare( &propvar, &cpropvar ));
PropVariantClear( &propvar );
Check(pStgBadHr,
pPropStgBad->ReadMultiple( 1, &propspec, &propvar ));
PropVariantClear(&propvar);
Check(S_OK,
pPropStgGood->ReadMultiple( 1, &propspec, &propvar ));
Check(S_OK, CPropVariant::Compare( &propvar, &cpropvar ));
// Attempt to write the BSTR Vector property
Check(S_OK,
pPropStgMac->WriteMultiple( 1, &propspec, &propvar, PID_FIRST_USABLE ));
Check(pStgBadHr,
pPropStgBad->WriteMultiple( 1, &propspec, &propvar, PID_FIRST_USABLE ));
Check(S_OK,
pPropStgGood->WriteMultiple( 1, &propspec, &propvar, PID_FIRST_USABLE ));
PropVariantClear( &propvar );
// Attempt to read the Variant Vector which has a BSTR
CPropVariant cpropvarT;
DECLARE_OLESTR(ocsPropVect, "PropVar Vector BSTR");
cpropvarT.SetBSTR( ocsPropVect );
cpropvar[1] = (LPPROPVARIANT) cpropvarT;
cpropvar[0] = (LPPROPVARIANT) CPropVariant((long) 44);
ASSERT( (VT_VARIANT | VT_VECTOR) == cpropvar.VarType() );
propspec.ulKind = PRSPEC_PROPID;
propspec.propid = CODEPAGE_TEST_VPROPVAR_BSTR_PROPID;
Check(S_OK,
pPropStgMac->ReadMultiple( 1, &propspec, &propvar ));
Check(S_OK, CPropVariant::Compare( &propvar, &cpropvar ));
PropVariantClear( &propvar );
Check(pStgBadHr,
pPropStgBad->ReadMultiple( 1, &propspec, &propvar ));
PropVariantClear(&propvar);
Check(S_OK,
pPropStgGood->ReadMultiple( 1, &propspec, &propvar ));
Check(S_OK, CPropVariant::Compare( &propvar, &cpropvar ));
// Attempt to write the Variant Vector which has a BSTR
Check(S_OK,
pPropStgMac->WriteMultiple( 1, &propspec, &propvar, PID_FIRST_USABLE ));
Check(pStgBadHr,
pPropStgBad->WriteMultiple( 1, &propspec, &propvar, PID_FIRST_USABLE ));
Check(S_OK,
pPropStgGood->WriteMultiple( 1, &propspec, &propvar, PID_FIRST_USABLE ));
PropVariantClear( &propvar );
// Attempt to read the I4 property.
propspec.ulKind = PRSPEC_PROPID;
propspec.propid = CODEPAGE_TEST_UNNAMED_I4_PROPID;
Check(S_OK,
pPropStgMac->ReadMultiple( 1, &propspec, &propvar ));
cpropvar = (LONG) 0x76543210;
Check(S_OK, CPropVariant::Compare( &propvar, &cpropvar ));
PropVariantClear( &propvar );
hr = pPropStgBad->ReadMultiple( 1, &propspec, &propvar );
Check(TRUE, S_OK == hr || pStgBadHr == hr );
PropVariantClear( &propvar );
Check(S_OK,
pPropStgGood->ReadMultiple( 1, &propspec, &propvar ));
Check(S_OK, CPropVariant::Compare( &propvar, &cpropvar ));
// Attempt to write the I4 property
Check(S_OK,
pPropStgMac->WriteMultiple( 1, &propspec, &propvar, PID_FIRST_USABLE ));
hr = pPropStgBad->WriteMultiple( 1, &propspec, &propvar, PID_FIRST_USABLE );
Check(TRUE, S_OK == hr || pStgBadHr == hr );
Check(S_OK,
pPropStgGood->WriteMultiple( 1, &propspec, &propvar, PID_FIRST_USABLE ));
PropVariantClear( &propvar );
// ---------------------------------------
// Test LPSTRs in the Unicode property set
// ---------------------------------------
// This test doesn't verify that the LPSTRs are actually
// written in Unicode. A manual test is required for that.
// Create a Unicode property set. We'll make it
// non-simple so that we can test a VT_STREAM (which
// is stored like an LPSTR).
#ifdef _WIN32
DECLARE_OLESTR(ocsUni, "\\UnicodCP.stg");
ocscpy( oszUnicodeFile, poszDirectory );
ocscat( oszUnicodeFile, ocsUni);
#else
DECLARE_OLESTR(ocsUni, "UnicodCP.stg");
ocscpy( oszUnicodeFile, ocsUni );
#endif
Check(S_OK, StgCreateDocfile(oszUnicodeFile,
STGM_CREATE | STGM_READWRITE |
STGM_SHARE_EXCLUSIVE,
(DWORD)NULL,
&pStgUnicode));
TSafeStorage< IPropertySetStorage > pPropSetStgUnicode(pStgUnicode);
TSafeStorage< IPropertyStorage > pPropStgUnicode;
Check(S_OK, pPropSetStgUnicode->Create(FMTID_NULL,
&CLSID_NULL,
PROPSETFLAG_DEFAULT,
STGM_CREATE | STGM_SHARE_EXCLUSIVE
| STGM_READWRITE,
&pPropStgUnicode));
// Write/verify an LPSTR property.
propspec.ulKind = PRSPEC_LPWSTR;
DECLARE_OLESTR(ocsLPSTR, "LPSTR Property");
propspec.lpwstr = ocsLPSTR;
cpropvarWrite = "An LPSTR Property";
Check(S_OK, pPropStgUnicode->WriteMultiple( 1, &propspec, cpropvarWrite, PID_FIRST_USABLE ));
Check(S_OK, pPropStgUnicode->ReadMultiple( 1, &propspec, cpropvarRead ));
Check(0, strcmp( (LPSTR) cpropvarWrite, (LPSTR) cpropvarRead ));
cpropvarRead.Clear();
// Write/verify a vector of LPSTR properties
DECLARE_OLESTR(ocsVectLPSTR, "Vector of LPSTR properties");
propspec.lpwstr = ocsVectLPSTR;
cpropvarWrite[1] = "LPSTR Property #1";
cpropvarWrite[0] = "LPSTR Property #0";
Check(S_OK, pPropStgUnicode->WriteMultiple( 1, &propspec, cpropvarWrite, PID_FIRST_USABLE ));
Check(S_OK, pPropStgUnicode->ReadMultiple( 1, &propspec, cpropvarRead ));
Check(0, strcmp( (LPSTR) cpropvarWrite[1], (LPSTR) cpropvarRead[1] ));
Check(0, strcmp( (LPSTR) cpropvarWrite[0], (LPSTR) cpropvarRead[0] ));
cpropvarRead.Clear();
// Write/verify a vector of variants which has an LPSTR
DECLARE_OLESTR(ocsVariantWithLPSTR, "Variant Vector with an LPSTR");
propspec.lpwstr = ocsVariantWithLPSTR;
cpropvarWrite[1] = (LPPROPVARIANT) CPropVariant("LPSTR in a Variant Vector");
cpropvarWrite[0] = (LPPROPVARIANT) CPropVariant((long) 22); // an I4
ASSERT( (VT_VECTOR | VT_VARIANT) == cpropvarWrite.VarType() );
Check(S_OK, pPropStgUnicode->WriteMultiple( 1, &propspec, cpropvarWrite, PID_FIRST_USABLE ));
Check(S_OK, pPropStgUnicode->ReadMultiple( 1, &propspec, cpropvarRead ));
Check(0, strcmp( (LPSTR) cpropvarWrite[1], (LPSTR) cpropvarRead[1] ));
cpropvarRead.Clear();
}
void
test_PropertyInterfaces(IStorage *pstgTemp)
{
// this test depends on being first for enumerator
test_IEnumSTATPROPSETSTG(pstgTemp);
test_MaxPropertyName(pstgTemp);
test_IPropertyStorage(pstgTemp);
test_IPropertySetStorage(pstgTemp);
test_IEnumSTATPROPSTG(pstgTemp);
}
//===================================================================
//
// Function: test_CopyTo
//
// Synopsis: Verify that IStorage::CopyTo copies an
// un-flushed property set.
//
// This test creates and writes to a simple property set,
// a non-simple property set, and a new Storage & Stream,
// all within the source (caller-provided) Storage.
//
// It then copies the entire source Storage to the
// destination Storage, and verifies that all commited
// data in the Source is also in the destination.
//
// All new Storages and property sets are created
// under a new base storage. The caller can specify
// if this base Storage is direct or transacted, and
// can specify if the property sets are direct or
// transacted.
//
//===================================================================
void test_CopyTo(IStorage *pstgSource, // Source of the CopyTo
IStorage *pstgDestination, // Destination of the CopyTo
ULONG ulBaseStgTransaction, // Transaction bit for the base storage.
ULONG ulPropSetTransaction, // Transaction bit for the property sets.
LPOLESTR oszBaseStorageName )
{
printf( " IStorage::CopyTo (Base Storage is %s, PropSets are %s)\n",
ulBaseStgTransaction & STGM_TRANSACTED ? "transacted" : "directed",
ulPropSetTransaction & STGM_TRANSACTED ? "transacted" : "directed" );
// ---------------
// Local Variables
// ---------------
DECLARE_OLESTR(poszTestSubStorage ,"TestStorage" );
DECLARE_OLESTR(poszTestSubStream ,"TestStream" );
DECLARE_OLESTR(poszTestDataPreCommit ,"Test Data (pre-commit)" );
DECLARE_OLESTR(poszTestDataPostCommit ,"Test Data (post-commit)");
long lSimplePreCommit = 0x0123;
long lSimplePostCommit = 0x4567;
long lNonSimplePreCommit = 0x89AB;
long lNonSimplePostCommit = 0xCDEF;
BYTE acReadBuffer[ 80 ];
ULONG cbRead;
FMTID fmtidSimple, fmtidNonSimple;
// Base Storages for the Source & Destination. All
// new Streams/Storages/PropSets will be created below here.
TSafeStorage< IStorage > pstgBaseSource;
TSafeStorage< IStorage > pstgBaseDestination;
TSafeStorage< IStorage > pstgSub; // A sub-storage of the base.
TSafeStorage< IStream > pstmSub; // A Stream in the sub-storage (pstgSub)
PROPSPEC propspec;
PROPVARIANT propvarSourceSimple,
propvarDestination;
// -----
// Begin
// -----
// Create new format IDs
UuidCreate(&fmtidSimple);
UuidCreate(&fmtidNonSimple);
// -----------------------
// Create the base Storage
// -----------------------
// Create a base Storage for the Source. All of this test will be under
// that Storage.
// In the source Storage.
Check( S_OK, pstgSource->CreateStorage(
oszBaseStorageName,
STGM_CREATE | ulBaseStgTransaction | STGM_SHARE_EXCLUSIVE | STGM_READWRITE,
0L, 0L,
&pstgBaseSource ));
// And in the destination Storage.
Check( S_OK, pstgDestination->CreateStorage(
oszBaseStorageName,
STGM_CREATE | STGM_DIRECT | STGM_SHARE_EXCLUSIVE | STGM_READWRITE,
0L, 0L,
&pstgBaseDestination ));
// -------------------------------------------
// Write data to a new Stream in a new Storage
// -------------------------------------------
// We'll partially verify the CopyTo by checking that this data
// makes it into the destination Storage.
// Create a Storage, and then a Stream within it.
Check( S_OK, pstgBaseSource->CreateStorage(
poszTestSubStorage,
STGM_CREATE | ulPropSetTransaction | STGM_SHARE_EXCLUSIVE | STGM_READWRITE,
0L, 0L,
&pstgSub ));
Check( S_OK, pstgSub->CreateStream(
poszTestSubStream,
STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
0L, 0L,
&pstmSub ));
// Write data to the Stream.
Check( S_OK, pstmSub->Write(
poszTestDataPreCommit,
( sizeof(OLECHAR)
*
( ocslen(poszTestDataPreCommit) + sizeof( OLECHAR ))
),
NULL ));
// ---------------------------------------------------------
// Write to a new simple property set in the Source storage.
// ---------------------------------------------------------
TSafeStorage< IPropertySetStorage > pPropSetStgSource(pstgBaseSource);
TSafeStorage< IPropertyStorage > pPropStgSource1,
pPropStgSource2,
pPropStgDestination;
// Create a property set mode.
Check(S_OK, pPropSetStgSource->Create(fmtidSimple,
NULL,
PROPSETFLAG_DEFAULT,
STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE,
&pPropStgSource1));
// Write the property set name (just to test this functionality).
PROPID pidDictionary = 0;
DECLARE_CONST_OLESTR(cposzPropSetName, "Property Set for CopyTo Test");
ASSERT( CWC_MAXPROPNAMESZ >= ocslen(cposzPropSetName) + sizeof(OLECHAR) );
Check(S_OK, pPropStgSource1->WritePropertyNames( 1, &pidDictionary,
&cposzPropSetName ));
// Create a PROPSPEC. We'll use this throughout the rest of the routine.
propspec.ulKind = PRSPEC_PROPID;
propspec.propid = 1000;
// Create a PROPVARIANT for this test of the Simple case.
propvarSourceSimple.vt = VT_I4;
propvarSourceSimple.lVal = lSimplePreCommit;
// Write the PROPVARIANT to the property set.
Check(S_OK, pPropStgSource1->WriteMultiple(1, &propspec, &propvarSourceSimple, 2));
// -------------------------
// Commit everything so far.
// -------------------------
// Commit the sub-Storage.
Check(S_OK, pstgSub->Commit( STGC_DEFAULT ));
// Commit the simple property set.
Check(S_OK, pPropStgSource1->Commit( STGC_DEFAULT ));
// Commit the base Storage which holds all of the above.
Check(S_OK, pstgBaseSource->Commit( STGC_DEFAULT ));
// -------------------------------------------------
// Write new data to everything but don't commit it.
// -------------------------------------------------
// Write to the sub-storage.
Check(S_OK, pstmSub->Seek(g_li0, STREAM_SEEK_SET, NULL));
Check( S_OK, pstmSub->Write(
poszTestDataPostCommit,
( sizeof(OLECHAR)
*
(ocslen( poszTestDataPostCommit ) + sizeof(OLECHAR))
),
NULL ));
// Write to the simple property set.
propvarSourceSimple.lVal = lSimplePostCommit;
Check(S_OK, pPropStgSource1->WriteMultiple(1, &propspec, &propvarSourceSimple, 2));
// -------------------------------------------
// Copy the source Storage to the destination.
// -------------------------------------------
// Release the sub-Storage (which is below the base Storage, and has
// a Stream with data in it), just to test that the CopyTo can
// handle it.
pstgSub->Release();
pstgSub = NULL;
Check(S_OK, pstgBaseSource->CopyTo( 0, NULL, NULL, pstgBaseDestination ));
// ----------------------------------------------------------
// Verify the simple property set in the destination Storage.
// ----------------------------------------------------------
TSafeStorage< IPropertySetStorage > pPropSetStgDestination(pstgBaseDestination);
// Open the simple property set.
Check(S_OK, pPropSetStgDestination->Open(fmtidSimple,
STGM_SHARE_EXCLUSIVE | STGM_READWRITE,
&pPropStgDestination));
// Verify the property set name.
OLECHAR *poszPropSetNameDestination;
BOOL bReadPropertyNamePassed = FALSE;
Check(S_OK, pPropStgDestination->
ReadPropertyNames( 1, &pidDictionary, &poszPropSetNameDestination ));
if( poszPropSetNameDestination // Did we get a name back?
&& // If so, was it the correct name?
!ocscmp( cposzPropSetName, poszPropSetNameDestination )
)
{
bReadPropertyNamePassed = TRUE;
}
CoTaskMemFree( poszPropSetNameDestination );
poszPropSetNameDestination = NULL;
Check( TRUE, bReadPropertyNamePassed );
// Read the PROPVARIANT that we wrote earlier.
Check(S_OK, pPropStgDestination->ReadMultiple(1, &propspec, &propvarDestination));
// Verify that it's correct.
Check(TRUE, propvarDestination.vt == propvarSourceSimple.vt );
Check(TRUE, propvarDestination.lVal == lSimplePostCommit);
Check(S_OK, pPropStgDestination->Commit( STGC_DEFAULT ));
Check(S_OK, pPropStgDestination->Release());
pPropStgDestination = NULL;
// ------------------------------------------------
// Verify the test data in the destination Storage.
// ------------------------------------------------
// Now we can release and re-use the Stream pointer that
// currently points to the sub-Stream in the source docfile.
Check(STG_E_REVERTED, pstmSub->Commit( STGC_DEFAULT ));
Check(S_OK, pstmSub->Release());
pstmSub = NULL;
// Get the Storage then the Stream.
Check( S_OK, pstgBaseDestination->OpenStorage(
poszTestSubStorage,
NULL,
STGM_READWRITE | STGM_DIRECT | STGM_SHARE_EXCLUSIVE,
NULL,
0L,
&pstgSub ));
Check( S_OK, pstgSub->OpenStream(
poszTestSubStream,
NULL,
STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
0L,
&pstmSub ));
// Read the data and compare it against what we wrote.
Check( S_OK, pstmSub->Read(
acReadBuffer,
sizeof( acReadBuffer ),
&cbRead ));
OLECHAR const *poszTestData = ( STGM_TRANSACTED & ulPropSetTransaction )
?
poszTestDataPreCommit
:
poszTestDataPostCommit;
Check( TRUE, cbRead == sizeof(OLECHAR)
*
(ocslen( poszTestData ) + sizeof(OLECHAR))
);
Check( FALSE, ocscmp( poszTestData, (OLECHAR *) acReadBuffer ));
// ----
// Exit
// ----
// We're done. Don't bother to release anything;
// they'll release themselves in their destructors.
return;
} // test_CopyTo()
//--------------------------------------------------------
//
// Function: test_OLESpecTickerExample
//
// Synopsis: This function generates the ticker property set
// example that's used in the OLE Programmer's Reference
// (when describing property ID 0 - the dictionary).
//
//--------------------------------------------------------
#define PID_SYMBOL 0x7
#define PID_OPEN 0x3
#define PID_CLOSE 0x4
#define PID_HIGH 0x5
#define PID_LOW 0x6
#define PID_LAST 0x8
#define PID_VOLUME 0x9
void test_OLESpecTickerExample( IStorage* pstg )
{
printf( " Generate the Stock Ticker property set example from the OLE Programmer's Ref\n" );
// ------
// Locals
// ------
FMTID fmtid;
PROPSPEC propspec;
DECLARE_CONST_OLESTR(coszPropSetName, "Stock Quote" );
DECLARE_CONST_OLESTR(coszTickerSymbolName, "Ticker Symbol" );
DECLARE_CONST_OLESTR(coszOpenName, "Opening Price" );
DECLARE_CONST_OLESTR(coszCloseName, "Last Closing Price" );
DECLARE_CONST_OLESTR(coszHighName, "High Price" );
DECLARE_CONST_OLESTR(coszLowName, "Low Price" );
DECLARE_CONST_OLESTR(coszLastName, "Last Price" );
DECLARE_CONST_OLESTR(coszVolumeName, "Volume" );
// ---------------------------------
// Create a new simple property set.
// ---------------------------------
TSafeStorage< IPropertySetStorage > pPropSetStg(pstg);
IPropertyStorage *pPropStg;
UuidCreate( &fmtid );
Check(S_OK, pPropSetStg->Create(fmtid,
NULL,
PROPSETFLAG_DEFAULT, // Unicode
STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_DIRECT | STGM_READWRITE,
&pPropStg));
// ---------------------------------------------
// Fill in the simply property set's dictionary.
// ---------------------------------------------
// Write the property set's name.
PROPID pidDictionary = 0;
Check(S_OK, pPropStg->WritePropertyNames(1, &pidDictionary, &coszPropSetName ));
// Write the High price, forcing the dictionary to pad.
propspec.ulKind = PRSPEC_PROPID;
propspec.propid = PID_HIGH;
Check(S_OK, pPropStg->WritePropertyNames(1, &propspec.propid, &coszHighName ));
// Write the ticker symbol.
propspec.propid = PID_SYMBOL;
Check(S_OK, pPropStg->WritePropertyNames(1, &propspec.propid, &coszTickerSymbolName));
// Write the rest of the dictionary.
propspec.propid = PID_LOW;
Check(S_OK, pPropStg->WritePropertyNames(1, &propspec.propid, &coszLowName));
propspec.propid = PID_OPEN;
Check(S_OK, pPropStg->WritePropertyNames(1, &propspec.propid, &coszOpenName));
propspec.propid = PID_CLOSE;
Check(S_OK, pPropStg->WritePropertyNames(1, &propspec.propid, &coszCloseName));
propspec.propid = PID_LAST;
Check(S_OK, pPropStg->WritePropertyNames(1, &propspec.propid, &coszLastName));
propspec.propid = PID_VOLUME;
Check(S_OK, pPropStg->WritePropertyNames(1, &propspec.propid, &coszVolumeName));
// Write out the ticker symbol.
propspec.propid = PID_SYMBOL;
PROPVARIANT propvar;
propvar.vt = VT_LPWSTR;
DECLARE_WIDESTR(wszMSFT, "MSFT");
propvar.pwszVal = wszMSFT;
Check(S_OK, pPropStg->WriteMultiple(1, &propspec, &propvar, 2));
// ----
// Exit
// ----
Check(S_OK, pPropStg->Commit( STGC_DEFAULT ));
Check(S_OK, pPropStg->Release());
Check(S_OK, pstg->Commit( STGC_DEFAULT ));
return;
} // test_OLESpecTickerExample()
void
test_Office( LPOLESTR wszTestFile )
{
printf( " Generate Office Property Sets\n" );
TSafeStorage<IStorage> pStg;
TSafeStorage<IPropertyStorage> pPStgSumInfo, pPStgDocSumInfo, pPStgUserDefined;
PROPVARIANT propvarWrite, propvarRead;
PROPSPEC propspec;
PropVariantInit( &propvarWrite );
PropVariantInit( &propvarRead );
// Create the DocFile
Check(S_OK, StgCreateDocfile( wszTestFile,
STGM_DIRECT | STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
0,
&pStg));
// Create the SummaryInformation property set.
TSafeStorage<IPropertySetStorage> pPSStg( pStg );
Check(S_OK, pPSStg->Create( FMTID_SummaryInformation,
NULL,
PROPSETFLAG_ANSI,
STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_DIRECT | STGM_READWRITE,
&pPStgSumInfo ));
// Write a Title to the SumInfo property set.
PropVariantInit( &propvarWrite );
propvarWrite.vt = VT_LPSTR;
propvarWrite.pszVal = "Title from PropTest";
propspec.ulKind = PRSPEC_PROPID;
propspec.propid = PID_TITLE;
Check( S_OK, pPStgSumInfo->WriteMultiple( 1, &propspec, &propvarWrite, PID_FIRST_USABLE ));
Check( S_OK, pPStgSumInfo->ReadMultiple( 1, &propspec, &propvarRead ));
Check( TRUE, propvarWrite.vt == propvarRead.vt );
Check( FALSE, strcmp( propvarWrite.pszVal, propvarRead.pszVal ));
PropVariantClear( &propvarRead );
PropVariantInit( &propvarRead );
pPStgSumInfo->Release();
pPStgSumInfo = NULL;
// Create the DocumentSummaryInformation property set.
Check(S_OK, pPSStg->Create( FMTID_DocSummaryInformation,
NULL,
PROPSETFLAG_ANSI,
STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_DIRECT | STGM_READWRITE,
&pPStgDocSumInfo ));
// Write a word-count to the DocSumInfo property set.
PropVariantInit( &propvarWrite );
propvarWrite.vt = VT_I4;
propvarWrite.lVal = 100;
propspec.ulKind = PRSPEC_PROPID;
propspec.propid = PID_WORDCOUNT;
Check( S_OK, pPStgDocSumInfo->WriteMultiple( 1, &propspec, &propvarWrite, PID_FIRST_USABLE ));
Check( S_OK, pPStgDocSumInfo->ReadMultiple( 1, &propspec, &propvarRead ));
Check( TRUE, propvarWrite.vt == propvarRead.vt );
Check( TRUE, propvarWrite.lVal == propvarRead.lVal );
PropVariantClear( &propvarRead );
PropVariantInit( &propvarRead );
pPStgDocSumInfo->Release();
pPStgDocSumInfo = NULL;
// Create the UserDefined property set.
Check(S_OK, pPSStg->Create( FMTID_UserDefinedProperties,
NULL,
PROPSETFLAG_ANSI,
STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_DIRECT | STGM_READWRITE,
&pPStgUserDefined ));
// Write named string to the UserDefined property set.
PropVariantInit( &propvarWrite );
propvarWrite.vt = VT_LPSTR;
propvarWrite.pszVal = "User-Defined string from PropTest";
propspec.ulKind = PRSPEC_LPWSTR;
DECLARE_OLESTR(ocsTest, "PropTest String");
propspec.lpwstr = ocsTest;
Check( S_OK, pPStgUserDefined->WriteMultiple( 1, &propspec, &propvarWrite, PID_FIRST_USABLE ));
Check( S_OK, pPStgUserDefined->ReadMultiple( 1, &propspec, &propvarRead ));
Check( TRUE, propvarWrite.vt == propvarRead.vt );
Check( FALSE, strcmp( propvarWrite.pszVal, propvarRead.pszVal ));
PropVariantClear( &propvarRead );
PropVariantInit( &propvarRead );
pPStgUserDefined->Release();
pPStgUserDefined = NULL;
// And we're done! (Everything releases automatically)
return;
}
inline BOOL operator == ( FILETIME &ft1, FILETIME &ft2 )
{
return( ft1.dwHighDateTime == ft2.dwHighDateTime
&&
ft1.dwLowDateTime == ft2.dwLowDateTime );
}
inline BOOL operator != ( FILETIME &ft1, FILETIME &ft2 )
{
return( ft1.dwHighDateTime != ft2.dwHighDateTime
||
ft1.dwLowDateTime != ft2.dwLowDateTime );
}
inline BOOL operator > ( FILETIME &ft1, FILETIME &ft2 )
{
return( ft1.dwHighDateTime > ft2.dwHighDateTime
||
ft1.dwHighDateTime == ft2.dwHighDateTime
&&
ft1.dwLowDateTime > ft2.dwLowDateTime );
}
inline BOOL operator < ( FILETIME &ft1, FILETIME &ft2 )
{
return( ft1.dwHighDateTime < ft2.dwHighDateTime
||
ft1.dwHighDateTime == ft2.dwHighDateTime
&&
ft1.dwLowDateTime < ft2.dwLowDateTime );
}
inline BOOL operator >= ( FILETIME &ft1, FILETIME &ft2 )
{
return( ft1 > ft2
||
ft1 == ft2 );
}
inline BOOL operator <= ( FILETIME &ft1, FILETIME &ft2 )
{
return( ft1 < ft2
||
ft1 == ft2 );
}
FILETIME operator - ( FILETIME &ft1, FILETIME &ft2 )
{
FILETIME ftDiff;
if( ft1 < ft2 )
{
ftDiff.dwLowDateTime = 0;
ftDiff.dwHighDateTime = 0;
}
else if( ft1.dwLowDateTime >= ft2.dwLowDateTime )
{
ftDiff.dwLowDateTime = ft1.dwLowDateTime - ft2.dwLowDateTime;
ftDiff.dwHighDateTime = ft1.dwHighDateTime - ft2.dwHighDateTime;
}
else
{
ftDiff.dwLowDateTime = ft1.dwLowDateTime - ft2.dwLowDateTime;
ftDiff.dwLowDateTime = (DWORD) -1 - ftDiff.dwLowDateTime;
ftDiff.dwHighDateTime = ft1.dwHighDateTime - ft2.dwHighDateTime - 1;
}
return( ftDiff );
}
void test_PropVariantCopy( )
{
printf( " PropVariantCopy\n" );
PROPVARIANT propvarCopy;
PropVariantInit( &propvarCopy );
for( int i = 0; i < CPROPERTIES_ALL; i++ )
{
Check(S_OK, PropVariantCopy( &propvarCopy, &g_rgcpropvarAll[i] ));
Check(S_OK, CPropVariant::Compare( &propvarCopy, &g_rgcpropvarAll[i]
));
PropVariantClear( &propvarCopy );
}
}
char* oszft(FILETIME *pft)
{
static char szBuf[32];
szBuf[0] = '\0';
sprintf(szBuf, "(H)%x (L)%x", pft->dwLowDateTime,
pft->dwHighDateTime);
return szBuf;
}
void PrintOC(char *ocsStr)
{
// simple subsitute to print both WIDE and BYTE chars
for ( ;*ocsStr; ocsStr++)
printf("%c", (char) *(ocsStr));
}
void PrintOC(WCHAR *ocsStr)
{
// simple subsitute to print both WIDE and BYTE chars
for ( ;*ocsStr; ocsStr++)
if ( (int) *ocsStr < (int) 0xff) // in range
printf("%c", (char) *(ocsStr));
else
printf("[%d]", *ocsStr);
}
void DumpTime(WCHAR *pszName, FILETIME *pft)
{
PrintOC(pszName);
printf("(H)%x (L)%x\n",
pft->dwHighDateTime,
pft->dwLowDateTime);
}
void DumpTime(char *pszName, FILETIME *pft)
{
printf("%s (H)%x (L)%x\n",
pszName,
pft->dwHighDateTime,
pft->dwLowDateTime);
}
VOID
PrintGuid(GUID *pguid)
{
printf(
"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
pguid->Data1,
pguid->Data2,
pguid->Data3,
pguid->Data4[0],
pguid->Data4[1],
pguid->Data4[2],
pguid->Data4[3],
pguid->Data4[4],
pguid->Data4[5],
pguid->Data4[6],
pguid->Data4[7]);
}
VOID
ListPropSetHeader(
STATPROPSETSTG *pspss,
OLECHAR *poszName)
{
BOOLEAN fDocumentSummarySection2;
OLECHAR oszStream[80]; // should be enough
fDocumentSummarySection2 = (BOOLEAN)
memcmp(&pspss->fmtid, &FMTID_UserDefinedProperties, sizeof(GUID)) == 0;
printf(" Property set ");
PrintGuid(&pspss->fmtid);
RtlGuidToPropertySetName(&pspss->fmtid, oszStream);
printf("\n %s Name ",
(pspss->grfFlags & PROPSETFLAG_NONSIMPLE)?
"Embedding" : "Stream");
PrintOC(oszStream);
if (poszName != NULL || fDocumentSummarySection2)
{
printf(" (");
if (poszName != NULL)
PrintOC(poszName);
else
printf("User defined properties");
printf(")");
}
printf("\n");
if (pspss->grfFlags & PROPSETFLAG_NONSIMPLE)
{
DumpTime(" Create Time ", &pspss->ctime);
}
DumpTime(" Modify Time ", &pspss->mtime);
if (pspss->grfFlags & PROPSETFLAG_NONSIMPLE)
{
DumpTime(" Access Time ", &pspss->atime);
}
}
typedef enum _PUBLICPROPSET
{
PUBPS_UNKNOWN = 0,
PUBPS_SUMMARYINFO = 3,
PUBPS_DOCSUMMARYINFO = 4,
PUBPS_USERDEFINED = 5,
} PUBLICPROPSET;
#define BSTRLEN(bstrVal) *((ULONG *) bstrVal - 1)
ULONG
SizeProp(PROPVARIANT *pv)
{
ULONG j;
ULONG cbprop = 0;
switch (pv->vt)
{
default:
case VT_EMPTY:
case VT_NULL:
break;
case VT_UI1:
cbprop = sizeof(pv->bVal);
break;
case VT_I2:
case VT_UI2:
case VT_BOOL:
cbprop = sizeof(pv->iVal);
break;
case VT_I4:
case VT_UI4:
case VT_R4:
case VT_ERROR:
cbprop = sizeof(pv->lVal);
break;
case VT_I8:
case VT_UI8:
case VT_R8:
case VT_CY:
case VT_DATE:
case VT_FILETIME:
cbprop = sizeof(pv->hVal);
break;
case VT_CLSID:
cbprop = sizeof(*pv->puuid);
break;
case VT_BLOB_OBJECT:
case VT_BLOB:
cbprop = pv->blob.cbSize + sizeof(pv->blob.cbSize);
break;
case VT_CF:
cbprop = sizeof(pv->pclipdata->cbSize) +
pv->pclipdata->cbSize;
break;
case VT_BSTR:
// count + string
cbprop = sizeof(ULONG);
if (pv->bstrVal != NULL)
{
cbprop += BSTRLEN(pv->bstrVal);
}
break;
case VT_LPSTR:
// count + string + null char
cbprop = sizeof(ULONG);
if (pv->pszVal != NULL)
{
cbprop += strlen(pv->pszVal) + 1;
}
break;
case VT_STREAM:
case VT_STREAMED_OBJECT:
case VT_STORAGE:
case VT_STORED_OBJECT:
case VT_LPWSTR:
// count + string + null char
cbprop = sizeof(ULONG);
if (pv->pwszVal != NULL)
{
cbprop += sizeof(pv->pwszVal[0]) * (wcslen(pv->pwszVal) + 1);
}
break;
// vectors
case VT_VECTOR | VT_UI1:
cbprop = sizeof(pv->caub.cElems) +
pv->caub.cElems * sizeof(pv->caub.pElems[0]);
break;
case VT_VECTOR | VT_I2:
case VT_VECTOR | VT_UI2:
case VT_VECTOR | VT_BOOL:
cbprop = sizeof(pv->cai.cElems) +
pv->cai.cElems * sizeof(pv->cai.pElems[0]);
break;
case VT_VECTOR | VT_I4:
case VT_VECTOR | VT_UI4:
case VT_VECTOR | VT_R4:
case VT_VECTOR | VT_ERROR:
cbprop = sizeof(pv->cal.cElems) +
pv->cal.cElems * sizeof(pv->cal.pElems[0]);
break;
case VT_VECTOR | VT_I8:
case VT_VECTOR | VT_UI8:
case VT_VECTOR | VT_R8:
case VT_VECTOR | VT_CY:
case VT_VECTOR | VT_DATE:
case VT_VECTOR | VT_FILETIME:
cbprop = sizeof(pv->cah.cElems) +
pv->cah.cElems * sizeof(pv->cah.pElems[0]);
break;
case VT_VECTOR | VT_CLSID:
cbprop = sizeof(pv->cauuid.cElems) +
pv->cauuid.cElems * sizeof(pv->cauuid.pElems[0]);
break;
case VT_VECTOR | VT_CF:
cbprop = sizeof(pv->caclipdata.cElems);
for (j = 0; j < pv->caclipdata.cElems; j++)
{
cbprop += sizeof(pv->caclipdata.pElems[j].cbSize) +
DwordAlign(pv->caclipdata.pElems[j].cbSize);
}
break;
case VT_VECTOR | VT_BSTR:
cbprop = sizeof(pv->cabstr.cElems);
for (j = 0; j < pv->cabstr.cElems; j++)
{
// count + string + null char
cbprop += sizeof(ULONG);
if (pv->cabstr.pElems[j] != NULL)
{
cbprop += DwordAlign(BSTRLEN(pv->cabstr.pElems[j]));
}
}
break;
case VT_VECTOR | VT_LPSTR:
cbprop = sizeof(pv->calpstr.cElems);
for (j = 0; j < pv->calpstr.cElems; j++)
{
// count + string + null char
cbprop += sizeof(ULONG);
if (pv->calpstr.pElems[j] != NULL)
{
cbprop += DwordAlign(strlen(pv->calpstr.pElems[j]) + 1);
}
}
break;
case VT_VECTOR | VT_LPWSTR:
cbprop = sizeof(pv->calpwstr.cElems);
for (j = 0; j < pv->calpwstr.cElems; j++)
{
// count + string + null char
cbprop += sizeof(ULONG);
if (pv->calpwstr.pElems[j] != NULL)
{
cbprop += DwordAlign(
sizeof(pv->calpwstr.pElems[j][0]) *
(wcslen(pv->calpwstr.pElems[j]) + 1));
}
}
break;
case VT_VECTOR | VT_VARIANT:
cbprop = sizeof(pv->calpwstr.cElems);
for (j = 0; j < pv->calpwstr.cElems; j++)
{
cbprop += SizeProp(&pv->capropvar.pElems[j]);
}
break;
}
return(DwordAlign(cbprop) + DwordAlign(sizeof(pv->vt)));
}
PUBLICPROPSET
GuidToPropSet(GUID *pguid)
{
PUBLICPROPSET pubps = PUBPS_UNKNOWN;
if (pguid != NULL)
{
if (memcmp(pguid, &FMTID_SummaryInformation, sizeof(GUID)) == 0)
{
pubps = PUBPS_SUMMARYINFO;
}
else if (memcmp(pguid, &FMTID_DocSummaryInformation, sizeof(GUID)) == 0)
{
pubps = PUBPS_DOCSUMMARYINFO;
}
else if (memcmp(pguid, &FMTID_UserDefinedProperties, sizeof(GUID)) == 0)
{
pubps = PUBPS_USERDEFINED;
}
}
return(pubps);
}
char
PrintableChar(char ch)
{
if (ch < ' ' || ch > '~')
{
ch = '.';
}
return(ch);
}
VOID
DumpHex(BYTE *pb, ULONG cb, ULONG base)
{
char *pszsep;
ULONG r, i, cbremain;
int fZero = FALSE;
int fSame = FALSE;
for (r = 0; r < cb; r += 16)
{
cbremain = cb - r;
if (r != 0 && cbremain >= 16)
{
if (pb[r] == 0)
{
ULONG j;
for (j = r + 1; j < cb; j++)
{
if (pb[j] != 0)
{
break;
}
}
if (j == cb)
{
fZero = TRUE;
break;
}
}
if (memcmp(&pb[r], &pb[r - 16], 16) == 0)
{
fSame = TRUE;
continue;
}
}
if (fSame)
{
printf("\n\t *");
fSame = FALSE;
}
unsigned int iLimit = (cbremain > 16) ? 16 : cbremain;
for (i = 0; i < iLimit; i++)
{
pszsep = " ";
if ((i % 8) == 0) // 0 or 8
{
pszsep = " ";
if (i == 0) // 0
{
// start a new line
printf("%s %04x:", r == 0? "" : "\n", r + base);
pszsep = " ";
}
}
printf("%s%02x", pszsep, pb[r + i]);
}
if (i != 0)
{
printf("%*s", 3 + (16 - i)*3 + ((i <= 8)? 1 : 0), "");
for (i = 0; i < iLimit; i++)
{
printf("%c", PrintableChar(pb[r + i]));
}
}
}
if (r != 0)
{
printf("\n");
}
if (fZero)
{
printf(" Remaining %lx bytes are zero\n", cbremain);
}
}
// Property Id's for Summary Info
#define PID_TITLE 0x00000002L // VT_LPSTR
#define PID_SUBJECT 0x00000003L // VT_LPSTR
#define PID_AUTHOR 0x00000004L // VT_LPSTR
#define PID_KEYWORDS 0x00000005L // VT_LPSTR
#define PID_COMMENTS 0x00000006L // VT_LPSTR
#define PID_TEMPLATE 0x00000007L // VT_LPSTR
#define PID_LASTAUTHOR 0x00000008L // VT_LPSTR
#define PID_REVNUMBER 0x00000009L // VT_LPSTR
#define PID_EDITTIME 0x0000000aL // VT_FILETIME
#define PID_LASTPRINTED 0x0000000bL // VT_FILETIME
#define PID_CREATE_DTM 0x0000000cL // VT_FILETIME
#define PID_LASTSAVE_DTM 0x0000000dL // VT_FILETIME
#define PID_PAGECOUNT 0x0000000eL // VT_I4
#define PID_WORDCOUNT 0x0000000fL // VT_I4
#define PID_CHARCOUNT 0x00000010L // VT_I4
#define PID_THUMBNAIL 0x00000011L // VT_CF
#define PID_APPNAME 0x00000012L // VT_LPSTR
#define PID_SECURITY_DSI 0x00000013L // VT_I4
// Property Id's for Document Summary Info
#define PID_CATEGORY 0x00000002L // VT_LPSTR
#define PID_PRESFORMAT 0x00000003L // VT_LPSTR
#define PID_BYTECOUNT 0x00000004L // VT_I4
#define PID_LINECOUNT 0x00000005L // VT_I4
#define PID_PARACOUNT 0x00000006L // VT_I4
#define PID_SLIDECOUNT 0x00000007L // VT_I4
#define PID_NOTECOUNT 0x00000008L // VT_I4
#define PID_HIDDENCOUNT 0x00000009L // VT_I4
#define PID_MMCLIPCOUNT 0x0000000aL // VT_I4
#define PID_SCALE 0x0000000bL // VT_BOOL
#define PID_HEADINGPAIR 0x0000000cL // VT_VECTOR | VT_VARIANT
#define PID_DOCPARTS 0x0000000dL // VT_VECTOR | VT_LPSTR
#define PID_MANAGER 0x0000000eL // VT_LPSTR
#define PID_COMPANY 0x0000000fL // VT_LPSTR
#define PID_LINKSDIRTY 0x00000010L // VT_BOOL
#define PID_CCHWITHSPACES 0x00000011L // VT_I4
#define PID_GUID 0x00000012L // VT_LPSTR
#define PID_SHAREDDOC 0x00000013L // VT_BOOL
#define PID_LINKBASE 0x00000014L // VT_LPSTR
#define PID_HLINKS 0x00000015L // VT_VECTOR | VT_VARIANT
#define PID_HYPERLINKSCHANGED 0x00000016L // VT_BOOL
VOID
DisplayProps(
GUID *pguid,
ULONG cprop,
PROPID apid[],
STATPROPSTG asps[],
FULLPROPSPEC afps[],
PROPVARIANT *av,
BOOLEAN fsumcat,
ULONG *pcbprop)
{
PROPVARIANT *pv;
PROPVARIANT *pvend;
STATPROPSTG *psps;
FULLPROPSPEC *pfps, *pfpsLast = NULL;
BOOLEAN fVariantVector;
PUBLICPROPSET pubps;
DECLARE_OLESTR(ocsNull,"");
ASSERT(asps == NULL || afps == NULL);
fVariantVector = (asps == NULL && afps == NULL);
pubps = GuidToPropSet(pguid);
pvend = &av[cprop];
for (pv = av, psps = asps, pfps = afps; pv < pvend; pv++, psps++, pfps++)
{
ULONG j;
ULONG cbprop;
PROPID propid;
OLECHAR *postrName;
char *psz;
BOOLEAN fNewLine = TRUE;
int ccol;
static char szNoFormat[] = " (no display format)";
char achvt[19 + 8 + 1];
cbprop = SizeProp(pv);
*pcbprop += cbprop;
postrName = NULL;
if (asps != NULL)
{
propid = psps->propid;
postrName = psps->lpwstrName;
}
else if (afps != NULL) // If multiple propsets are possible
{
if (pfpsLast == NULL || // print unique GUIDs only
memcmp(
&pfps->guidPropSet,
&pfpsLast->guidPropSet,
sizeof(pfps->guidPropSet)) != 0)
{
OLECHAR oszStream[80];
printf("%s Guid: ", pfpsLast == NULL? "" : "\n");
PrintGuid(&pfps->guidPropSet);
pubps = GuidToPropSet(&pfps->guidPropSet);
RtlGuidToPropertySetName(&pfps->guidPropSet, oszStream);
printf( " Name: " );
PrintOC(oszStream);
printf( "%s", pubps == PUBPS_USERDEFINED?
g_szEmpty : " (User defined properties)");
pfpsLast = pfps;
}
if (pfps->psProperty.ulKind == PRSPEC_PROPID)
{
propid = pfps->psProperty.propid;
}
else
{
propid = PID_ILLEGAL;
postrName = pfps->psProperty.lpwstr;
}
}
else
{
ASSERT(apid != NULL);
propid = apid[0];
}
printf(" ");
ccol = 0;
if (propid != PID_ILLEGAL)
{
printf(" %04x", propid);
ccol += 5;
if (propid & (0xf << 28))
{
ccol += 4;
}
else if (propid & (0xf << 24))
{
ccol += 3;
}
else if (propid & (0xf << 20))
{
ccol += 2;
}
else if (propid & (0xf << 16))
{
ccol++;
}
}
if (postrName != NULL)
{
printf(" '");
PrintOC(postrName);
printf("' ");
ccol += ocslen(postrName) + 3;
}
else if (fVariantVector)
{
ULONG i = pv - av;
printf("[%x]", i);
do
{
ccol++;
i >>= 4;
} while (i != 0);
ccol += 2;
}
else
{
psz = NULL;
switch (propid)
{
case PID_LOCALE: psz = "Locale"; break;
case PID_SECURITY: psz = "SecurityId"; break;
case PID_MODIFY_TIME: psz = "ModifyTime"; break;
case PID_CODEPAGE: psz = "CodePage"; break;
case PID_DICTIONARY: psz = "Dictionary"; break;
}
if (psz == NULL)
switch (pubps)
{
case PUBPS_SUMMARYINFO:
switch (propid)
{
case PID_TITLE: psz = "Title"; break;
case PID_SUBJECT: psz = "Subject"; break;
case PID_AUTHOR: psz = "Author"; break;
case PID_KEYWORDS: psz = "Keywords"; break;
case PID_COMMENTS: psz = "Comments"; break;
case PID_TEMPLATE: psz = "Template"; break;
case PID_LASTAUTHOR: psz = "LastAuthor"; break;
case PID_REVNUMBER: psz = "RevNumber"; break;
case PID_EDITTIME: psz = "EditTime"; break;
case PID_LASTPRINTED: psz = "LastPrinted"; break;
case PID_CREATE_DTM: psz = "CreateDateTime"; break;
case PID_LASTSAVE_DTM: psz = "LastSaveDateTime";break;
case PID_PAGECOUNT: psz = "PageCount"; break;
case PID_WORDCOUNT: psz = "WordCount"; break;
case PID_CHARCOUNT: psz = "CharCount"; break;
case PID_THUMBNAIL: psz = "ThumbNail"; break;
case PID_APPNAME: psz = "AppName"; break;
case PID_DOC_SECURITY: psz = "Security"; break;
}
break;
case PUBPS_DOCSUMMARYINFO:
switch (propid)
{
case PID_CATEGORY: psz = "Category"; break;
case PID_PRESFORMAT: psz = "PresFormat"; break;
case PID_BYTECOUNT: psz = "ByteCount"; break;
case PID_LINECOUNT: psz = "LineCount"; break;
case PID_PARACOUNT: psz = "ParaCount"; break;
case PID_SLIDECOUNT: psz = "SlideCount"; break;
case PID_NOTECOUNT: psz = "NoteCount"; break;
case PID_HIDDENCOUNT: psz = "HiddenCount"; break;
case PID_MMCLIPCOUNT: psz = "MmClipCount"; break;
case PID_SCALE: psz = "Scale"; break;
case PID_HEADINGPAIR: psz = "HeadingPair"; break;
case PID_DOCPARTS: psz = "DocParts"; break;
case PID_MANAGER: psz = "Manager"; break;
case PID_COMPANY: psz = "Company"; break;
case PID_LINKSDIRTY: psz = "LinksDirty"; break;
case PID_CCHWITHSPACES: psz = "CchWithSpaces"; break;
case PID_GUID: psz = "Guid"; break;
case PID_SHAREDDOC: psz = "SharedDoc"; break;
case PID_LINKBASE: psz = "LinkBase"; break;
case PID_HLINKS: psz = "HLinks"; break;
case PID_HYPERLINKSCHANGED: psz = "HyperLinksChanged";break;
}
break;
}
if (psz != NULL)
{
printf(" %s", psz);
ccol += strlen(psz) + 1;
}
}
#define CCOLPROPID 20
if (ccol != CCOLPROPID)
{
if (ccol > CCOLPROPID)
{
ccol = -1;
}
printf("%s%*s", ccol == -1? "\n" : "", CCOLPROPID - ccol, "");
}
printf(" %08x %04x %04x ", propid, cbprop, pv->vt);
psz = "";
switch (pv->vt)
{
default:
psz = achvt;
sprintf(psz, "Unknown (vt = %hx)", pv->vt);
break;
case VT_EMPTY:
printf("EMPTY");
break;
case VT_NULL:
printf("NULL");
break;
case VT_UI1:
printf("UI1 = %02lx", pv->bVal);
psz = "";
break;
case VT_I2:
psz = "I2";
goto doshort;
case VT_UI2:
psz = "UI2";
goto doshort;
case VT_BOOL:
psz = "BOOL";
doshort:
printf("%s = %04hx", psz, pv->iVal);
psz = g_szEmpty;
break;
case VT_I4:
psz = "I4";
goto dolong;
case VT_UI4:
psz = "UI4";
goto dolong;
case VT_R4:
psz = "R4";
goto dolong;
case VT_ERROR:
psz = "ERROR";
dolong:
printf("%s = %08lx", psz, pv->lVal);
psz = g_szEmpty;
break;
case VT_I8:
psz = "I8";
goto dotwodword;
case VT_UI8:
psz = "UI8";
dotwodword:
printf( "%s = %08lx:%08lx",
psz,
pv->hVal.HighPart,
pv->hVal.LowPart );
psz = g_szEmpty;
break;
case VT_R8:
psz = "R8";
goto dolonglong;
case VT_CY:
psz = "R8";
goto dolonglong;
case VT_DATE:
psz = "R8";
dolonglong:
printf(
"%s = %08lx:%08lx",
psz,
(pv->cyVal).split.Hi,
(pv->cyVal).split.Lo);
psz = g_szEmpty;
break;
case VT_FILETIME:
DumpTime("FILETIME =\n\t ", &pv->filetime);
fNewLine = FALSE; // skip newline printf
break;
case VT_CLSID:
printf("CLSID =\n\t ");
PrintGuid(pv->puuid);
break;
case VT_BLOB:
psz = "BLOB";
goto doblob;
case VT_BLOB_OBJECT:
psz = "BLOB_OBJECT";
doblob:
printf("%s (cbSize %x)", psz, pv->blob.cbSize);
if (pv->blob.cbSize != 0)
{
printf(" =\n");
DumpHex(pv->blob.pBlobData, pv->blob.cbSize, 0);
}
psz = g_szEmpty;
break;
case VT_CF:
printf(
"CF (cbSize %x, ulClipFmt %x)\n",
pv->pclipdata->cbSize,
pv->pclipdata->ulClipFmt);
DumpHex(pv->pclipdata->pClipData,
pv->pclipdata->cbSize - sizeof(pv->pclipdata->ulClipFmt),
0);
break;
case VT_STREAM:
psz = "STREAM";
goto dostring;
case VT_STREAMED_OBJECT:
psz = "STREAMED_OBJECT";
goto dostring;
case VT_STORAGE:
psz = "STORAGE";
goto dostring;
case VT_STORED_OBJECT:
psz = "STORED_OBJECT";
goto dostring;
case VT_BSTR:
printf(
"BSTR (cb = %04lx)%s\n",
pv->bstrVal == NULL? 0 : BSTRLEN(pv->bstrVal),
pv->bstrVal == NULL? " NULL" : g_szEmpty);
if (pv->bstrVal != NULL)
{
DumpHex(
(BYTE *) pv->bstrVal,
BSTRLEN(pv->bstrVal) + sizeof(WCHAR),
0);
}
break;
case VT_LPSTR:
psz = "LPSTR";
printf(
"%s = %s%s%s",
psz,
pv->pszVal == NULL? g_szEmpty : "'",
pv->pszVal == NULL? "NULL" : pv->pszVal,
pv->pszVal == NULL? g_szEmpty : "'");
psz = g_szEmpty;
break;
case VT_LPWSTR:
psz = "LPWSTR";
dostring:
printf(
"%s = %s",
psz,
pv->pwszVal == NULL? g_szEmpty : "'");
if ( pv->pwszVal == NULL)
printf("NULL");
else
PrintOC(pv->pwszVal);
printf("%s", pv->pwszVal == NULL? g_szEmpty : "'");
psz = g_szEmpty;
break;
// vectors
case VT_VECTOR | VT_UI1:
printf("UI1[%x] =", pv->caub.cElems);
for (j = 0; j < pv->caub.cElems; j++)
{
if ((j % 16) == 0)
{
printf("\n %02hx:", j);
}
printf(" %02hx", pv->caub.pElems[j]);
}
break;
case VT_VECTOR | VT_I2:
psz = "I2";
goto doshortvector;
case VT_VECTOR | VT_UI2:
psz = "UI2";
goto doshortvector;
case VT_VECTOR | VT_BOOL:
psz = "BOOL";
doshortvector:
printf("%s[%x] =", psz, pv->cai.cElems);
for (j = 0; j < pv->cai.cElems; j++)
{
if ((j % 8) == 0)
{
printf("\n %04hx:", j);
}
printf(" %04hx", pv->cai.pElems[j]);
}
psz = g_szEmpty;
break;
case VT_VECTOR | VT_I4:
psz = "I4";
goto dolongvector;
case VT_VECTOR | VT_UI4:
psz = "UI4";
goto dolongvector;
case VT_VECTOR | VT_R4:
psz = "R4";
goto dolongvector;
case VT_VECTOR | VT_ERROR:
psz = "ERROR";
dolongvector:
printf("%s[%x] =", psz, pv->cal.cElems);
for (j = 0; j < pv->cal.cElems; j++)
{
if ((j % 4) == 0)
{
printf("\n %04x:", j);
}
printf(" %08lx", pv->cal.pElems[j]);
}
psz = g_szEmpty;
break;
case VT_VECTOR | VT_I8:
psz = "I8";
goto dolonglongvector;
case VT_VECTOR | VT_UI8:
psz = "UI8";
goto dolonglongvector;
case VT_VECTOR | VT_R8:
psz = "R8";
goto dolonglongvector;
case VT_VECTOR | VT_CY:
psz = "CY";
goto dolonglongvector;
case VT_VECTOR | VT_DATE:
psz = "DATE";
dolonglongvector:
printf("%s[%x] =", psz, pv->cah.cElems);
for (j = 0; j < pv->cah.cElems; j++)
{
if ((j % 2) == 0)
{
printf("\n %04x:", j);
}
printf(
" %08lx:%08lx",
pv->cah.pElems[j].HighPart,
pv->cah.pElems[j].LowPart);
}
psz = g_szEmpty;
break;
case VT_VECTOR | VT_FILETIME:
printf("FILETIME[%x] =\n", pv->cafiletime.cElems);
for (j = 0; j < pv->cafiletime.cElems; j++)
{
printf(" %04x: ", j);
DumpTime(ocsNull, &pv->cafiletime.pElems[j]);
}
fNewLine = FALSE; // skip newline printf
break;
case VT_VECTOR | VT_CLSID:
printf("CLSID[%x] =", pv->cauuid.cElems);
for (j = 0; j < pv->cauuid.cElems; j++)
{
printf("\n %04x: ", j);
PrintGuid(&pv->cauuid.pElems[j]);
}
break;
case VT_VECTOR | VT_CF:
printf("CF[%x] =", pv->caclipdata.cElems);
for (j = 0; j < pv->caclipdata.cElems; j++)
{
printf("\n %04x: (cbSize %x, ulClipFmt %x) =\n",
j,
pv->caclipdata.pElems[j].cbSize,
pv->caclipdata.pElems[j].ulClipFmt);
DumpHex(
pv->caclipdata.pElems[j].pClipData,
pv->caclipdata.pElems[j].cbSize - sizeof(pv->caclipdata.pElems[j].ulClipFmt),
0);
}
break;
case VT_VECTOR | VT_BSTR:
printf("BSTR[%x] =", pv->cabstr.cElems);
for (j = 0; j < pv->cabstr.cElems; j++)
{
BSTR bstr = pv->cabstr.pElems[j];
printf(
"\n %04x: cb = %04lx%s\n",
j,
bstr == NULL? 0 : BSTRLEN(pv->cabstr.pElems[j]),
bstr == NULL? " NULL" : g_szEmpty);
if (bstr != NULL)
{
DumpHex((BYTE *) bstr, BSTRLEN(bstr) + sizeof(WCHAR), 0);
}
}
break;
case VT_VECTOR | VT_LPSTR:
printf("LPSTR[%x] =", pv->calpstr.cElems);
for (j = 0; j < pv->calpstr.cElems; j++)
{
CHAR *psz = pv->calpstr.pElems[j];
printf(
"\n %04x: %s%s%s",
j,
psz == NULL? g_szEmpty : "'",
psz == NULL? "NULL" : psz,
psz == NULL? g_szEmpty : "'");
}
break;
case VT_VECTOR | VT_LPWSTR:
printf("LPWSTR[%x] =", pv->calpwstr.cElems);
for (j = 0; j < pv->calpwstr.cElems; j++)
{
WCHAR *pwsz = pv->calpwstr.pElems[j];
printf( "\n %04x: %s",
j, pv->pwszVal == NULL? g_szEmpty : "'");
if ( pwsz == NULL)
printf("NULL");
else
PrintOC(pwsz);
printf("%s", pwsz == NULL? g_szEmpty : "'");
}
break;
case VT_VECTOR | VT_VARIANT:
printf("VARIANT[%x] =\n", pv->capropvar.cElems);
DisplayProps(
pguid,
pv->capropvar.cElems,
&propid,
NULL,
NULL,
pv->capropvar.pElems,
fsumcat,
pcbprop);
fNewLine = FALSE; // skip newline printf
break;
}
if (*psz != '\0')
{
printf("%s", psz);
if (pv->vt & VT_VECTOR)
{
printf("[%x]", pv->cal.cElems);
}
printf("%s", szNoFormat);
}
if (!fVariantVector && apid != NULL && apid[pv - av] != propid)
{
printf(" (bad PROPID: %04x)", apid[pv - av]);
fNewLine = TRUE;
}
if (asps != NULL && pv->vt != psps->vt)
{
printf(" (bad STATPROPSTG VARTYPE: %04x)", psps->vt);
fNewLine = TRUE;
}
if (fNewLine)
{
printf("\n");
}
}
}
STATPROPSTG aspsStatic[] = {
{ NULL, PID_CODEPAGE, VT_I2 },
{ NULL, PID_MODIFY_TIME, VT_FILETIME },
{ NULL, PID_SECURITY, VT_UI4 },
};
#define CPROPSTATIC (sizeof(aspsStatic)/sizeof(aspsStatic[0]))
#define CB_STREAM_OVERHEAD 28
#define CB_PROPSET_OVERHEAD (CB_STREAM_OVERHEAD + 8)
#define CB_PROP_OVERHEAD 8
HRESULT
DumpOlePropertySet(
IPropertySetStorage *ppsstg,
STATPROPSETSTG *pspss,
ULONG *pcprop,
ULONG *pcbprop)
{
HRESULT hr;
IEnumSTATPROPSTG *penumsps = NULL;
IPropertyStorage *pps;
ULONG cprop, cbpropset;
PROPID propid;
OLECHAR *poszName;
ULONG ispsStatic;
*pcprop = *pcbprop = 0;
hr = ppsstg->Open(
pspss->fmtid,
STGM_READWRITE | STGM_DIRECT | STGM_SHARE_EXCLUSIVE,
&pps);
if (FAILED(hr))
return (hr);
propid = PID_DICTIONARY;
hr = pps->ReadPropertyNames(1, &propid, &poszName);
if( S_FALSE == hr )
hr = S_OK;
Check( S_OK, hr );
ListPropSetHeader(pspss, poszName);
if (poszName != NULL)
{
CoTaskMemFree(poszName);
}
cprop = cbpropset = 0;
Check(S_OK, pps->Enum(&penumsps) );
ispsStatic = 0;
hr = S_OK;
while (hr == S_OK)
{
STATPROPSTG sps;
PROPSPEC propspec;
PROPVARIANT propvar;
ULONG count;
hr = S_FALSE;
if (ispsStatic == 0)
{
hr = penumsps->Next(1, &sps, &count);
}
if (hr != S_OK)
{
if (hr == S_FALSE)
{
hr = S_OK;
if (ispsStatic >= CPROPSTATIC)
{
break;
}
sps = aspsStatic[ispsStatic];
ispsStatic++;
count = 1;
}
Check( S_OK, hr );
}
PropVariantInit(&propvar);
if (sps.lpwstrName != NULL)
{
propspec.ulKind = PRSPEC_LPWSTR;
propspec.lpwstr = sps.lpwstrName;
}
else
{
propspec.ulKind = PRSPEC_PROPID;
propspec.propid = sps.propid;
}
hr = pps->ReadMultiple(1, &propspec, &propvar);
if (hr == S_FALSE)
{
if (g_fVerbose)
{
printf(
"%s(%u, %x) vt=%x returned hr=%x\n",
"IPropertyStorage::ReadMultiple",
ispsStatic,
propspec.propid,
propvar.vt,
hr);
}
ASSERT(propvar.vt == VT_EMPTY);
hr = S_OK;
}
Check( S_OK, hr );
if (ispsStatic == 0 || propvar.vt != VT_EMPTY)
{
ASSERT(count == 1);
cprop += count;
if (cprop == 1)
{
printf(g_szPropHeader);
}
DisplayProps(
&pspss->fmtid,
1,
NULL,
&sps,
NULL,
&propvar,
FALSE,
&cbpropset);
PropVariantClear(&propvar);
}
if (sps.lpwstrName != NULL)
{
CoTaskMemFree(sps.lpwstrName);
}
}
if (penumsps != NULL)
{
penumsps->Release();
}
pps->Release();
if (cprop != 0)
{
cbpropset += CB_PROPSET_OVERHEAD + cprop * CB_PROP_OVERHEAD;
printf(" %04x bytes in %u properties\n\n", cbpropset, cprop);
}
*pcprop = cprop;
*pcbprop = cbpropset;
return(hr);
}
HRESULT
DumpOlePropertySets(
IStorage *pstg,
OLECHAR *aocpath)
{
HRESULT hr = S_OK;
IPropertySetStorage *ppsstg;
ULONG cbproptotal = 0;
ULONG cproptotal = 0;
ULONG cpropset = 0;
IID IIDpsstg = IID_IPropertySetStorage;
Check(S_OK, pstg->QueryInterface(IID_IPropertySetStorage, (void **) &ppsstg) );
{
IEnumSTATPROPSETSTG *penumspss = NULL;
Check(S_OK, ppsstg->Enum(&penumspss) );
while (hr == S_OK)
{
STATPROPSETSTG spss;
ULONG count;
BOOLEAN fDocumentSummarySection2;
hr = penumspss->Next(1, &spss, &count);
if (hr != S_OK)
{
if (hr == S_FALSE)
{
hr = S_OK;
}
Check( S_OK, hr );
break;
}
ASSERT(count == 1);
fDocumentSummarySection2 = FALSE;
while (TRUE)
{
ULONG cprop, cbprop;
HRESULT hr;
DumpOlePropertySet(
ppsstg,
&spss,
&cprop,
&cbprop);
if ( STG_E_FILENOTFOUND == hr
&& fDocumentSummarySection2 )
{
hr = S_OK;
}
cpropset++;
cproptotal += cprop;
cbproptotal += cbprop;
if (memcmp(&spss.fmtid, &guidDocumentSummary, sizeof(GUID)))
{
break;
}
spss.fmtid = FMTID_UserDefinedProperties;
fDocumentSummarySection2 = TRUE;
}
}
if (penumspss != NULL)
{
penumspss->Release();
}
ppsstg->Release();
}
if ((cbproptotal | cproptotal | cpropset) != 0)
{
printf(
" %04x bytes in %u properties in %u property sets\n",
cbproptotal,
cproptotal,
cpropset);
}
return(hr);
}
inline ULONG min(ULONG ul1, ULONG ul2)
{
if (ul1 > ul2) return ul2;
else return ul1;
}
NTSTATUS
DumpOleStream(
LPSTREAM pstm,
ULONG cb)
{
ULONG cbTotal = 0;
while (TRUE)
{
ULONG cbOut;
BYTE ab[4096];
Check(S_OK, pstm->Read(ab, min(cb, sizeof(ab)), &cbOut) );
if (cbOut == 0)
{
break;
}
if (g_fVerbose)
{
DumpHex(ab, cbOut, cbTotal);
}
cb -= cbOut;
cbTotal += cbOut;
}
return(STATUS_SUCCESS);
}
VOID
DumpOleStorage(
IStorage *pstg,
LPOLESTR aocpath )
{
LPENUMSTATSTG penum;
STATSTG ss;
char *szType;
OLECHAR *pocChild;
HRESULT hr;
NTSTATUS Status = STATUS_INVALID_PARAMETER;
Check( S_OK, DumpOlePropertySets(pstg, aocpath) );
Check( S_OK, pstg->EnumElements(0, NULL, 0, &penum) );
pocChild = &aocpath[ocslen(aocpath)];
// Continue enumeration until IEnumStatStg::Next returns non-S_OK
while (TRUE)
{
ULONG ulCount;
// Enumerate one element at a time
hr = penum->Next(1, &ss, &ulCount);
if( S_FALSE == hr )
break;
else
Check( S_OK, hr );
// Select the human-readable type of object to display
switch (ss.type)
{
case STGTY_STREAM: szType = "Stream"; break;
case STGTY_STORAGE: szType = "Storage"; break;
case STGTY_LOCKBYTES: szType = "LockBytes"; break;
case STGTY_PROPERTY: szType = "Property"; break;
default: szType = "<Unknown>"; break;
}
if (g_fVerbose)
{
printf(
"Type=%hs Size=%lx Mode=%lx LocksSupported=%lx StateBits=%lx",
szType,
ss.cbSize.LowPart,
ss.grfMode,
ss.grfLocksSupported,
ss.grfStateBits);
PrintOC(aocpath);
PrintOC(ss.pwcsName);
printf("\n");
printf("ss.clsid = ");
PrintGuid(&ss.clsid);
printf("\n");
}
// If a stream, output the data in hex format.
CoTaskMemFree(ss.pwcsName);
}
penum->Release();
return;
}
//+---------------------------------------------------------
//
// Function: MungePropertyStorage
//
// Synopsis: This routine munges the properties in a
// Property Storage. The values of the properties
// remain the same, but the underlying serialization
// is new (the properties are read, the property
// storage is deleted, and the properties are
// re-written).
//
// Inputs: [IPropertySetStorage*] ppropsetgstg (in)
// The Property Storage container.
// [FMTID] fmtid
// The Property Storage to munge.
//
// Returns: None.
//
// Note: Property names in the dictionary for which
// there is no property are not munged.
//
//+---------------------------------------------------------
#define MUNGE_PROPVARIANT_STEP 10
void
MungePropertyStorage( IPropertySetStorage *ppropsetstg,
FMTID fmtid )
{
// ------
// Locals
// ------
HRESULT hr;
ULONG celt, ulIndex;
TSafeStorage< IPropertyStorage > ppropstg;
IEnumSTATPROPSTG *penumstatpropstg=NULL;
PROPVARIANT *rgpropvar = NULL;
STATPROPSTG *rgstatpropstg = NULL;
ULONG cProperties = 0;
// Allocate an array of PropVariants. We may grow this later.
rgpropvar = (PROPVARIANT*) CoTaskMemAlloc( MUNGE_PROPVARIANT_STEP * sizeof(*rgpropvar) );
Check( FALSE, NULL == rgpropvar );
// Allocate an array of STATPROPSTGs. We may grow this also.
rgstatpropstg = (STATPROPSTG*) CoTaskMemAlloc( MUNGE_PROPVARIANT_STEP * sizeof(*rgstatpropstg) );
Check( FALSE, NULL == rgstatpropstg );
// -----------------
// Get an Enumerator
// -----------------
// Open the Property Storage. We may get an error if we're attempting
// the UserDefined propset. If it's file-not-found, then simply return,
// it's not an error, and there's nothing to do.
hr = ppropsetstg->Open( fmtid,
STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
&ppropstg );
if( FMTID_UserDefinedProperties == fmtid
&&
STG_E_FILENOTFOUND == hr )
{
goto Exit;
}
Check( S_OK, hr );
// Get an Enumerator
Check(S_OK, ppropstg->Enum( &penumstatpropstg ));
// --------------------------------------------
// Read & delete in all of the properties/names
// --------------------------------------------
// Get the first property from the enumerator
hr = penumstatpropstg->Next( 1, &rgstatpropstg[cProperties], &celt );
Check( TRUE, S_OK == hr || S_FALSE == hr );
// Iterate through the properties.
while( celt > 0 )
{
PROPSPEC propspec;
propspec.ulKind = PRSPEC_PROPID;
propspec.propid = rgstatpropstg[cProperties].propid;
// Read and delete the property
Check(S_OK, ppropstg->ReadMultiple( 1, &propspec, &rgpropvar[cProperties] ));
Check(S_OK, ppropstg->DeleteMultiple( 1, &propspec ));
// If there is a property name, delete it also.
if( NULL != rgstatpropstg[cProperties].lpwstrName )
{
// We have a name.
Check(S_OK, ppropstg->DeletePropertyNames( 1, &rgstatpropstg[cProperties].propid ));
}
// Increment the property count.
cProperties++;
// Do we need to grow the arrays?
if( 0 != cProperties
&&
(cProperties % MUNGE_PROPVARIANT_STEP) == 0 )
{
// Yes - they must be reallocated.
rgpropvar = (PROPVARIANT*)
CoTaskMemRealloc( rgpropvar,
( (cProperties + MUNGE_PROPVARIANT_STEP)
*
sizeof(*rgpropvar)
));
Check( FALSE, NULL == rgpropvar );
rgstatpropstg = (STATPROPSTG*)
CoTaskMemRealloc( rgstatpropstg,
( (cProperties + MUNGE_PROPVARIANT_STEP)
*
sizeof(*rgstatpropstg)
));
Check( FALSE, NULL == rgstatpropstg );
}
// Move on to the next property.
hr = penumstatpropstg->Next( 1, &rgstatpropstg[cProperties], &celt );
Check( TRUE, S_OK == hr || S_FALSE == hr );
} // while( celt > 0 )
// -------------------------------------
// Write the properties & names back out
// -------------------------------------
for( ulIndex = 0; ulIndex < cProperties; ulIndex++ )
{
// Write the property.
PROPSPEC propspec;
propspec.ulKind = PRSPEC_PROPID;
propspec.propid = rgstatpropstg[ ulIndex ].propid;
Check(S_OK, ppropstg->WriteMultiple(1, &propspec, &rgpropvar[ulIndex], PID_FIRST_USABLE ));
// If this property has a name, write it too.
if( rgstatpropstg[ ulIndex ].lpwstrName != NULL )
{
Check(S_OK, ppropstg->WritePropertyNames(
1,
&rgstatpropstg[ulIndex].propid,
&rgstatpropstg[ulIndex].lpwstrName ));
}
} // for( ulIndex = 0; ulIndex < cProperties; ulIndex++ )
// ----
// Exit
// ----
Exit:
if( penumstatpropstg )
{
penumstatpropstg->Release();
penumstatpropstg = NULL;
}
// Free the PropVariants
if( rgpropvar )
{
FreePropVariantArray( cProperties, rgpropvar );
CoTaskMemFree( rgpropvar );
}
// Free the property names
if( rgstatpropstg )
{
for( ulIndex = 0; ulIndex < cProperties; ulIndex++ )
{
if( NULL != rgstatpropstg[ ulIndex ].lpwstrName )
{
CoTaskMemFree( rgstatpropstg[ ulIndex ].lpwstrName );
}
} // for( ulIndex = 0; ulIndex < cProperties; ulIndex++ )
CoTaskMemFree( rgstatpropstg );
}
} // MungePropertyStorage
//+---------------------------------------------------------
//
// Function: MungeStorage
//
// Synopsis: This routine munges the property sets in a
// Storage. The properties themselves are not
// modified, but the serialized bytes are.
// For each property set, all the properties are
// read, the property set is deleted, and
// the properties are re-written.
//
// Inputs: [IStorage*] pstg (in)
// The Storage to munge.
//
// Returns: None.
//
// Note: This routine only munges simple property
// sets.
//
//+---------------------------------------------------------
void
MungeStorage( IStorage *pstg )
{
// ------
// Locals
// ------
HRESULT hr;
ULONG celt;
STATPROPSETSTG statpropsetstg;
STATSTG statstg;
TSafeStorage< IPropertySetStorage > ppropsetstg;
TSafeStorage< IPropertyStorage > ppropstg;
IEnumSTATPROPSETSTG *penumstatpropsetstg;
IEnumSTATSTG *penumstatstg;
// -----------------------------------------------
// Munge each of the property sets in this Storage
// -----------------------------------------------
// Get the IPropertySetStorage interface
Check(S_OK, pstg->QueryInterface( IID_IPropertySetStorage, (VOID**) &ppropsetstg ));
// Get a property storage enumerator
Check(S_OK, ppropsetstg->Enum( &penumstatpropsetstg ));
// Get the first STATPROPSETSTG
hr = penumstatpropsetstg->Next( 1, &statpropsetstg, &celt );
Check( TRUE, S_OK == hr || S_FALSE == hr );
// Loop through the STATPROPSETSTGs.
while( celt > 0 )
{
// Is this a simple property storage (we don't
// handle non-simple sets)?
if( !(statpropsetstg.grfFlags & PROPSETFLAG_NONSIMPLE) )
{
// Munge the Property Storage.
MungePropertyStorage( ppropsetstg, statpropsetstg.fmtid );
}
// Get the next STATPROPSETSTG
// If we just did the first section of the DocSumInfo
// property set, then attempt the second section
if( FMTID_DocSummaryInformation == statpropsetstg.fmtid )
{
statpropsetstg.fmtid = FMTID_UserDefinedProperties;
}
else
{
hr = penumstatpropsetstg->Next( 1, &statpropsetstg, &celt );
Check( TRUE, S_OK == hr || S_FALSE == hr );
}
}
// We're done with the Property Storage enumerator.
penumstatpropsetstg->Release();
penumstatpropsetstg = NULL;
// ------------------------------------------
// Recursively munge each of the sub-storages
// ------------------------------------------
// Get the IEnumSTATSTG enumerator
Check(S_OK, pstg->EnumElements( 0L, NULL, 0L, &penumstatstg ));
// Get the first STATSTG structure.
hr = penumstatstg->Next( 1, &statstg, &celt );
Check( TRUE, S_OK == hr || S_FALSE == hr );
// Loop through the elements of this Storage.
while( celt > 0 )
{
// Is this a sub-Storage which must be
// munged?
if( STGTY_STORAGE & statstg.type // This is a Storage
&&
0x20 <= *statstg.pwcsName ) // But not a system Storage.
{
// We'll munge it.
IStorage *psubstg;
// Open the sub-storage.
Check(S_OK, pstg->OpenStorage( statstg.pwcsName,
NULL,
STGM_DIRECT | STGM_SHARE_EXCLUSIVE | STGM_READWRITE,
NULL,
0L,
&psubstg ));
// Munge the sub-storage.
MungeStorage( psubstg );
psubstg->Release();
psubstg = NULL;
}
CoTaskMemFree(statstg.pwcsName);
// Move on to the next Storage element.
hr = penumstatstg->Next( 1, &statstg, &celt );
Check( TRUE, S_OK == hr || S_FALSE == hr );
}
penumstatstg->Release();
penumstatstg = NULL;
} // MungeStorage
void
DisplayUsage( LPSTR pszCommand )
{
printf("\n");
printf(" Usage: %s [[options] <test directory>] [file-commands]\n", pszCommand );
printf("\n");
printf(" The <test directory> is required in order to run tests, but is\n" );
printf(" not required for the file-commands.\n" );
printf("\n");
printf(" Options:\n" );
printf(" /w enables the Word 6 compatibility test\n");
printf(" /iw creates a file for interop test\n");
printf(" /ir verfies the file created for interop test\n" );
printf("\n");
printf(" File-commands:\n" );
printf(" /g<file> specifies a file to be munGed\n" );
printf(" (propsets are read, deleted, & re-written)\n" );
printf(" /d<file> specifies a file to be Dumped\n" );
printf(" (propsets are dumped to stdout)\n" );
printf("\n");
printf(" Examples:\n" );
printf(" %s c:\\test\n", pszCommand );
printf(" %s -iw c:\\test\n", pszCommand );
printf(" %s -dMyFile.doc\n", pszCommand );
printf(" %s -gMyFile.doc\n", pszCommand );
printf("\n");
return;
}
//
// Interoperability test
//
// test_interop_write writes in a doc file
// test_interop_read reads it in and verifies that it is right.
typedef struct tagInteropTest {
VARENUM vt;
void *pv;
} interopStruct;
const int cInteropPROPS=18;
static interopStruct
avtInterop[cInteropPROPS] = {
VT_LPSTR, "Title of the document.",
VT_LPSTR, "Subject of the document.",
VT_LPSTR, "Author of the document.",
VT_LPSTR, "Keywords of the document.",
VT_LPSTR, "Comments of the document.",
VT_LPSTR, "Normal.dot",
VT_LPSTR, "Mr. Unknown",
VT_LPSTR, "3",
VT_EMPTY, 0,
VT_EMPTY, 0,
VT_EMPTY, 0,
VT_EMPTY, 0,
VT_I4, (void*) 1,
VT_I4, (void*) 7,
VT_I4, (void*) 65,
VT_EMPTY, 0,
VT_LPSTR, "Reference",
VT_I4, (void*) 1121
};
void test_interop_write()
{
printf( " Interoperability - write \n" );
DECLARE_OLESTR(szFile, "t_interop");
IStorage *pStg;
Check(S_OK,
StgCreateDocfile(szFile,
STGM_CREATE| STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
(DWORD)NULL, &pStg));
TSafeStorage< IPropertySetStorage > pPropSetStg(pStg);
IPropertyStorage *pPropStg;
Check(S_OK, pPropSetStg->Create(FMTID_SummaryInformation, NULL, 0,
STGM_SHARE_EXCLUSIVE | STGM_DIRECT | STGM_READWRITE,
&pPropStg));
PROPSPEC propspec[cInteropPROPS+2];
int i;
for (i=2; i<cInteropPROPS+2; i++)
{
propspec[i].ulKind = PRSPEC_PROPID;
propspec[i].propid = (PROPID)i;
}
PROPVARIANT propvar[cInteropPROPS+2];
for (i=2; i<cInteropPROPS+2; i++)
{
propvar[i].vt = avtInterop[i-2].vt;
switch (avtInterop[i-2].vt)
{
case VT_LPSTR:
propvar[i].pszVal = (char*)avtInterop[i-2].pv;
break;
case VT_I4:
propvar[i].lVal = (int)avtInterop[i-2].pv;
break;
default:
break;
}
}
Check(S_OK,
pPropStg->WriteMultiple(cInteropPROPS, propspec+2,
propvar+2, 2) );
pPropStg->Release();
pStg->Release();
}
void test_interop_read()
{
printf( " Interoperability - read \n" );
DECLARE_OLESTR(szFile, "t_interop");
IStorage *pStg;
Check(S_OK, StgOpenStorage(szFile, NULL,
STGM_READWRITE | STGM_SHARE_EXCLUSIVE, NULL, 0, &pStg));
TSafeStorage< IPropertySetStorage > pPropSetStg(pStg);
IPropertyStorage *pPropStg;
Check(S_OK, pPropSetStg->Open(FMTID_SummaryInformation,
STGM_SHARE_EXCLUSIVE | STGM_DIRECT | STGM_READ,
&pPropStg));
PROPSPEC propspec[cInteropPROPS+2];
int i;
for (i=2; i<cInteropPROPS+2; i++)
{
propspec[i].ulKind = PRSPEC_PROPID;
propspec[i].propid = (PROPID)i;
}
PROPVARIANT propvar[cInteropPROPS+2];
Check(S_OK, pPropStg->ReadMultiple(cInteropPROPS, propspec+2, propvar+2));
for (i=2; i<cInteropPROPS+2; i++)
{
if ( propvar[i].vt != avtInterop[i-2].vt )
{
printf( " PROPTEST: 0x%x retrieved type 0x%x, expected type 0x%x\n",
i, propvar[i].vt, avtInterop[i-2].vt );
ASSERT(propvar[i].vt == avtInterop[i-2].vt);
}
switch (propvar[i].vt)
{
case VT_LPSTR:
ASSERT(strcmp(propvar[i].pszVal, (char*)avtInterop[i-2].pv)==0);
break;
case VT_I4:
ASSERT(propvar[i].lVal == (int)avtInterop[i-2].pv);
break;
}
PropVariantClear(propvar+i);
}
pPropStg->Release();
pStg->Release();
}
void Cleanup()
{
ULONG ul;
// Clean up and exit.
if (_pstgTemp)
{
ul = _pstgTemp->Release();
assert(ul==0 && "_pstgTemp ref counting is wrong!");
}
if (_pstgTempCopyTo)
{
ul = _pstgTempCopyTo->Release();
assert( 0 == ul
&& "_pstgTempCopyTo ref counting is wrong!");
}
}
int main(int argc, char *argv[])
{
int nArgIndex;
#ifndef _UNIX
INIT_OLESTR(oszSummary, "SummaryInformation");
INIT_OLESTR(aocMap, "abcdefghijklmnopqrstuvwxyz012345");
INIT_OLESTR( oszDocumentSummary, "DocumentSummaryInformation");
#endif
ULONG ulTestOptions = 0L;
BOOL fOffice97TestDoc = FALSE;
CHAR* pszFileToMunge = NULL;
CHAR* pszFileToDump = NULL;
printf("Property Set Tests\n");
// Check for command-line switches
if( 2 > argc )
{
printf("Too few arguments\n");
DisplayUsage( argv[0] );
exit(0);
}
for( nArgIndex = 1; nArgIndex < argc; nArgIndex++ )
{
if( argv[nArgIndex][0] == '/'
||
argv[nArgIndex][0] == '-'
)
{
BOOL fNextArgument = FALSE;
for( int nOptionSubIndex = 1;
argv[nArgIndex][nOptionSubIndex] != '\0' && !fNextArgument;
nOptionSubIndex++
)
{
switch( argv[nArgIndex][nOptionSubIndex] )
{
case 'w':
case 'W':
ulTestOptions |= TEST_WORD6;
break;
case '?':
DisplayUsage(argv[0]);
exit(1);
case 'i':
if (argv[nArgIndex][nOptionSubIndex+1]=='w')
ulTestOptions |= TEST_INTEROP_W;
else if (argv[nArgIndex][nOptionSubIndex+1]=='r')
ulTestOptions |= TEST_INTEROP_R;
else
{
DisplayUsage(argv[0]);
printf("You must specify 'r' or 'w' for interop!\n");
exit(-1);
}
nOptionSubIndex++;
break;
case 'd':
case 'D':
if( NULL != pszFileToDump )
{
printf( "Error: Only one file may be dumped\n" );
DisplayUsage( argv[0] );
}
else
{
pszFileToDump = &argv[nArgIndex][nOptionSubIndex+1];
fNextArgument = TRUE;
}
if( '\0' == *pszFileToDump )
{
printf( "Error: Missing filename for dump option\n" );
DisplayUsage( argv[0] );
exit(1);
}
break;
case 'g':
case 'G':
if( NULL != pszFileToMunge )
{
printf( "Error: Only one file may be munged\n" );
DisplayUsage( argv[0] );
exit(1);
}
else
{
pszFileToMunge = &argv[nArgIndex][nOptionSubIndex+1];
fNextArgument = TRUE;
}
if( '\0' == *pszFileToMunge )
{
printf( "Error: Missing filename for munge option\n" );
DisplayUsage( argv[0] );
exit(1);
}
break;
default:
printf( "Option '%c' ignored\n",
argv[nArgIndex][nOptionSubIndex] );
break;
} // switch( argv[nArgIndex][1] )
} // for( int nOptionSubIndex = 1; ...
} // if( argv[nArgIndex][0] == '/'
else
{
break;
}
} // for( ULONG nArgIndex = 2; nArgIndex < argc; nArgIndex++ )
// If any other command-line parameters were given, ignore them.
for( int nExtraArg = nArgIndex+1; nExtraArg < argc; nExtraArg++ )
{
printf( "Illegal argument ignored: %s\n", argv[nExtraArg] );
}
OLECHAR ocsFile[256], ocsTest[256], ocsTest2[256], ocsTestOffice[256];
CHAR szDir[256];
HRESULT hr;
IStorage *pstg;
int i=0;
if ( NULL != pszFileToDump )
{
printf("DUMPING: %s\n", pszFileToDump);
printf("========================\n");
STOT( pszFileToDump, ocsFile, strlen(pszFileToDump)+1 );
Check(S_OK, StgOpenStorage( ocsFile,
(IStorage*)NULL,
(DWORD)STGM_DIRECT | STGM_READWRITE |
STGM_SHARE_EXCLUSIVE,
NULL,
0L,
&pstg ));
DumpOleStorage( pstg, ocsFile );
Check(0, pstg->Release()); // ensure we released the last reference
exit(0);
}
// Is there a file to munge?
if ( NULL != pszFileToMunge )
{
STOT(pszFileToMunge, ocsFile, strlen(pszFileToMunge)+1 );
Check(S_OK, StgOpenStorage( ocsFile,
NULL,
(DWORD) STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
NULL,
0L,
&pstg ));
MungeStorage( pstg );
PrintOC(ocsFile);
printf( " successfully munged\n");
Check(0, pstg->Release());
exit(0);
}
// Verify that the user provided a directory path.
if (nArgIndex >= argc)
{
printf( "Test directory not provided on command-line\n" );
DisplayUsage( argv[0] );
exit(1);
}
// Verify that the user-provided directory path
// exists
struct _stat filestat;
if (_stat(argv[nArgIndex], &filestat)!=0 ||
!(filestat.st_mode && S_IFDIR))
{
printf("Error in opening %s as a directory", argv[nArgIndex] );
exit(1);
}
#ifdef _WIN32 // don't bother to create a directory for test files on UNIX
// since there are some problems with creating files with
// path names
if (!(ulTestOptions & TEST_INTEROP_R) && !(ulTestOptions & TEST_INTEROP_W))
{
// Find an new directory name to use for temporary
// files ("testdirX", where "X" is a number).
do
{
strcpy(szDir, argv[nArgIndex]);
sprintf(strchr(szDir,0), "\\testdir%d", i++);
} while ( (_mkdir(szDir, 0x744) == -1) && (i<20) );
if (i>=20)
{
printf("Too many testdirX subdirectories, delete some and re-run\n");
exit(-1);
}
}
else
{
// use current directory for interop testing
szDir[0] = '.';
szDir[1] = 0;
}
#endif
#ifdef _WIN32
printf( "Generated files will be put in \"%s\"\n", szDir );
// Create "tesdoc"
STOT(szDir, ocsFile, strlen(szDir)+1);
ocscpy(ocsTest, ocsFile);
DECLARE_OLESTR(ocsTestDoc, "\\testdoc");
ocscat(ocsTest, ocsTestDoc);
#else
DECLARE_OLESTR(ocsTestDoc, "testdoc");
ocscpy(ocsTest, ocsTestDoc);
#endif
hr = StgCreateDocfile(ocsTest, STGM_DIRECT | STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
0, &_pstgTemp);
if (hr != S_OK)
{
printf("Can't create %s\n", ocsTest);
exit(1);
}
// Create "testdoc2"
#ifdef _WIN32
DECLARE_OLESTR(ocsTestDoc2,"\\testdoc2");
ocscpy(ocsTest2, ocsFile);
ocscat(ocsTest2, ocsTestDoc2);
#else
DECLARE_OLESTR(ocsTestDoc2,"testdoc2");
ocscpy(ocsTest2, ocsTestDoc2);
#endif
hr = StgCreateDocfile(ocsTest2, STGM_CREATE | STGM_READWRITE |
STGM_SHARE_EXCLUSIVE,
0, &_pstgTempCopyTo);
if (hr != S_OK)
{
printf("Can't create %ls\n", ocsTest);
exit(1);
}
Check(S_OK, PopulateRGPropVar( g_rgcpropvarAll, g_rgcpropspecAll ));
printf( "\nStandard Tests\n" );
printf( "--------------\n" );
test_WriteReadAllProperties(ulTestOptions);
test_PropertyInterfaces(_pstgTemp);
// test with Unicode, then ansi files
test_CodePages(ocsFile, ulTestOptions, TRUE);
test_CodePages(ocsFile, ulTestOptions, FALSE);
// Test the IStorage::CopyTo operation, using all combinations of
// direct and transacted mode for the base and PropSet storages.
for( int iteration = 0; iteration < 4; iteration++ )
{
DECLARE_OLESTR(aocStorageName, "#0 Test CopyTo");
aocStorageName[1] = (OLECHAR) iteration + (OLECHAR)'0';
test_CopyTo( _pstgTemp, _pstgTempCopyTo,
STGM_DIRECT,
STGM_DIRECT,
aocStorageName );
}
// Generate the stock ticker property set example
// from the OLE programmer's reference spec.
test_OLESpecTickerExample( _pstgTemp );
#ifdef _WIN32
ocscpy(ocsTestOffice, ocsFile);
DECLARE_OLESTR(ocsOffice, "\\Office");
ocscat(ocsTestOffice, ocsOffice);
#else
DECLARE_OLESTR(ocsOffice, "Office");
ocscpy(ocsTestOffice, ocsOffice);
#endif
test_Office( ocsTestOffice );
test_PropVariantValidation( _pstgTemp );
test_PropVariantCopy();
if( ulTestOptions )
{
printf( "\nOptional Tests\n" );
printf( "--------------\n" );
// If requested, test for compatibility with Word 6.0 files.
if ( ulTestOptions & TEST_WORD6 )
test_Word6(_pstgTemp);
if ( ulTestOptions & TEST_INTEROP_W)
{
test_interop_write();
}
if ( ulTestOptions & TEST_INTEROP_R)
{
test_interop_read();
}
} // if( ulTestOptions )
Cleanup();
printf("\nPASSED\n");
return 0;
}