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
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;
|
|
}
|
|
|
|
|