//+-------------------------------------------------------------- // // 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 #include #ifdef _WIN32 #include // _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; lcbSize = 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; lReadMultiple(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; cDeletePropertyNames(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; iReadMultiple(WORDPROPS, propspec+2, propvar+2)); for (i=2; iRelease(); 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; iWritePropertyNames(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; cNext(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; dNext(CPROPERTIES-d, StatBuffer, &celt)); ASSERT(celt == CPROPERTIES-d); penum->Reset(); memset(afFound, 0, sizeof(afFound)); for (ULONG iProperty=0; iPropertyDeleteMultiple(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 pStg; TSafeStorage 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 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 = ""; 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] ] [file-commands]\n", pszCommand ); printf("\n"); printf(" The 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 specifies a file to be munGed\n" ); printf(" (propsets are read, deleted, & re-written)\n" ); printf(" /d 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; iWriteMultiple(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; iReadMultiple(cInteropPROPS, propspec+2, propvar+2)); for (i=2; iRelease(); 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; }