//+============================================================================ // // File: TestCase.cxx // // Description: // This file provides all of the actual test-cases for the // PropTest DRT. Each test is a function, with a "test_" // prefix. // //+============================================================================ #include "pch.cxx" #include // For CP_WINUNICODE #include "propstm.hxx" #include "propstg.hxx" #include "stgint.h" EXTERN_C const IID IID_IStorageTest = { /* 40621cf8-a17f-11d1-b28d-00c04fb9386d */ 0x40621cf8, 0xa17f, 0x11d1, {0xb2, 0x8d, 0x00, 0xc0, 0x4f, 0xb9, 0x38, 0x6d} }; //+--------------------------------------------------------------- // // 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. // // Inputs: [LPOLESTR] ocsDir (in) // The directory in which a file can be created. // // Outputs: None. // //+--------------------------------------------------------------- void test_WriteReadAllProperties( LPOLESTR ocsDir ) { OLECHAR ocsFile[ MAX_PATH ]; FMTID fmtidAnsi, fmtidUnicode; UINT ExpectedCodePage; IStorage *pstg = NULL, *psubstg = NULL; IStream *pstm = NULL; IPropertySetStorage *ppropsetstg = NULL; IPropertyStorage *ppropstgAnsi = NULL, *ppropstgUnicode = NULL; CPropVariant rgcpropvar[ CPROPERTIES_ALL ]; CPropVariant rgcpropvarAnsi[ CPROPERTIES_ALL ]; CPropVariant rgcpropvarUnicode[ CPROPERTIES_ALL ]; CPropVariant rgcpropvarBag[ CPROPERTIES_ALL ]; CPropVariant rgcpropvarDefault[ 2 ]; CPropSpec rgcpropspecDefault[ 2 ]; IPropertySetStorage *pPropSetStg = NULL; IPropertyStorage *pPropStg = NULL; IPropertyBagEx *pbag = NULL; ULONG ulIndex; ULONG cPropertiesAll = CPROPERTIES_ALL; Status( "Simple Write/Read Test\n" ); // ---------- // Initialize // ---------- // Generate FMTIDs. UuidCreate( &fmtidAnsi ); UuidCreate( &fmtidUnicode ); // Generate a filename from the directory name. ocscpy( ocsFile, ocsDir ); ocscat( ocsFile, OLESTR( "AllProps.stg" )); // ---------------- // Create a docfile // ---------------- Check( S_OK, g_pfnStgCreateStorageEx( ocsFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, DetermineStgFmt( g_enumImplementation ), 0L, NULL, NULL, DetermineStgIID( g_enumImplementation ), (void**) &pstg )); //(void**) &ppropsetstg )); Check( S_OK, pstg->QueryInterface( IID_IPropertySetStorage, (void**) &ppropsetstg )); // Create the Property Storages Check( S_OK, ppropsetstg->Create( fmtidAnsi, &CLSID_NULL, ( (g_Restrictions & RESTRICT_UNICODE_ONLY) ? PROPSETFLAG_DEFAULT : PROPSETFLAG_ANSI ) | ( (g_Restrictions & RESTRICT_SIMPLE_ONLY) ? PROPSETFLAG_DEFAULT: PROPSETFLAG_NONSIMPLE ), STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &ppropstgAnsi )); Check( S_OK, ppropsetstg->Create( fmtidUnicode, &CLSID_NULL, (g_Restrictions & RESTRICT_SIMPLE_ONLY) ? PROPSETFLAG_DEFAULT: PROPSETFLAG_NONSIMPLE, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &ppropstgUnicode )); // Get a property bag. This is also a convenient place to test that we can QI between // the storage and bag. IStorage *pstg2 = NULL; IPropertyBagEx *pbag2 = NULL; IUnknown *punk1 = NULL; IUnknown *punk2 = NULL; Check( S_OK, pstg->QueryInterface( IID_IPropertyBagEx, reinterpret_cast(&pbag) )); Check( S_OK, pbag->QueryInterface( DetermineStgIID( g_enumImplementation ), reinterpret_cast(&pstg2) )); Check( S_OK, pstg2->QueryInterface( IID_IPropertyBagEx, reinterpret_cast(&pbag2) )); Check( TRUE, pbag == pbag2 && pstg == pstg2 ); RELEASE_INTERFACE(pbag2); RELEASE_INTERFACE(pstg2); Check( S_OK, pstg->QueryInterface( IID_IUnknown, reinterpret_cast(&punk1) )); Check( S_OK, pbag->QueryInterface( IID_IUnknown, reinterpret_cast(&punk2) )); Check( TRUE, punk1 == punk2 ); RELEASE_INTERFACE(punk1); RELEASE_INTERFACE(punk2); // Write some simple properties Check( S_OK, ppropstgAnsi->WriteMultiple( CPROPERTIES_ALL_SIMPLE, g_rgcpropspecAll, g_rgcpropvarAll, PID_FIRST_USABLE )); // Verify the format version is 0 CheckFormatVersion(ppropstgAnsi, 0); // Write to all property sets. Check( S_OK, ppropstgAnsi->WriteMultiple( cPropertiesAll, g_rgcpropspecAll, g_rgcpropvarAll, PID_FIRST_USABLE )); Check( S_OK, ResetRGPropVar( g_rgcpropvarAll )); // Verify that the format is now version 1, since we wrote a VersionedStream property CheckFormatVersion(ppropstgAnsi, 1); Check( S_OK, ppropstgUnicode->WriteMultiple( cPropertiesAll, g_rgcpropspecAll, g_rgcpropvarAll, PID_FIRST_USABLE )); Check( S_OK, ResetRGPropVar( g_rgcpropvarAll )); Check( S_OK, pbag->WriteMultiple( cPropertiesAll, g_rgoszpropnameAll, g_rgcpropvarAll )); Check( S_OK, ResetRGPropVar( g_rgcpropvarAll )); // Close and re-open everything RELEASE_INTERFACE(pstg); RELEASE_INTERFACE(ppropsetstg); RELEASE_INTERFACE(ppropstgAnsi); RELEASE_INTERFACE(ppropstgUnicode); Check( 0, pbag->Release() ); pbag = NULL; Check( S_OK, g_pfnStgOpenStorageEx( ocsFile, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, DetermineStgFmt( g_enumImplementation ), 0L, NULL, NULL, DetermineStgIID( g_enumImplementation ), (void**) &pstg )); //(void**) &ppropsetstg )); Check( S_OK, pstg->QueryInterface( IID_IPropertySetStorage, (void**) &ppropsetstg )); // Create the Property Storages Check( S_OK, ppropsetstg->Open( fmtidAnsi, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &ppropstgAnsi )); Check( S_OK, ppropsetstg->Open( fmtidUnicode, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &ppropstgUnicode )); Check( S_OK, pstg->QueryInterface( IID_IPropertyBagEx, reinterpret_cast(&pbag) )); // Read and verify the auto-generated properties. rgcpropspecDefault[0] = static_cast(PID_CODEPAGE); rgcpropspecDefault[1] = static_cast(PID_LOCALE); Check( S_OK, ppropstgAnsi->ReadMultiple( 2, rgcpropspecDefault, rgcpropvarDefault )); ExpectedCodePage = (g_Restrictions & RESTRICT_UNICODE_ONLY) ? CP_WINUNICODE : GetACP(); Check( TRUE, VT_I2 == rgcpropvarDefault[0].vt ); Check( TRUE, ExpectedCodePage == (UINT) rgcpropvarDefault[0].iVal ); Check( TRUE, VT_UI4 == rgcpropvarDefault[1].vt ); Check( TRUE, GetUserDefaultLCID() == rgcpropvarDefault[1].ulVal ); Check( S_OK, ppropstgUnicode->ReadMultiple( 2, rgcpropspecDefault, rgcpropvarDefault )); ExpectedCodePage = CP_WINUNICODE; Check( TRUE, VT_I2 == rgcpropvarDefault[0].vt ); Check( TRUE, ExpectedCodePage == (UINT) rgcpropvarDefault[0].iVal ); Check( TRUE, VT_UI4 == rgcpropvarDefault[1].vt ); Check( TRUE, GetUserDefaultLCID() == rgcpropvarDefault[1].ulVal ); // Read from all property sets Check( S_OK, ppropstgAnsi->ReadMultiple( cPropertiesAll, g_rgcpropspecAll, rgcpropvarAnsi )); Check( S_OK, ppropstgUnicode->ReadMultiple( cPropertiesAll, g_rgcpropspecAll, rgcpropvarUnicode )); Check( S_OK, pbag->ReadMultiple( cPropertiesAll, g_rgoszpropnameAll, rgcpropvarBag, NULL )); // Compare the properties for( int i = 0; i < (int)cPropertiesAll; i++ ) { Check( TRUE, rgcpropvarAnsi[i] == g_rgcpropvarAll[i] ); Check( TRUE, rgcpropvarUnicode[i] == g_rgcpropvarAll[i] ); Check( TRUE, rgcpropvarBag[i] == g_rgcpropvarAll[i] ); } // Show that we can delete everything Check( S_OK, ppropstgAnsi->DeleteMultiple( cPropertiesAll, g_rgcpropspecAll )); Check( S_OK, ppropstgUnicode->DeleteMultiple( cPropertiesAll, g_rgcpropspecAll )); Check( S_OK, pbag->DeleteMultiple( cPropertiesAll, g_rgoszpropnameAll, 0 )); // Re-write the properties, because it's convenient for debug sometimes // to have a file around with lots of properties in it. Check( S_OK, ppropstgAnsi->WriteMultiple( cPropertiesAll, g_rgcpropspecAll, g_rgcpropvarAll, PID_FIRST_USABLE )); Check( S_OK, ResetRGPropVar( g_rgcpropvarAll )); Check( S_OK, ppropstgUnicode->WriteMultiple( cPropertiesAll, g_rgcpropspecAll, g_rgcpropvarAll, PID_FIRST_USABLE )); Check( S_OK, ResetRGPropVar( g_rgcpropvarAll )); RELEASE_INTERFACE(pstg); RELEASE_INTERFACE(ppropsetstg); RELEASE_INTERFACE(ppropstgAnsi); RELEASE_INTERFACE(ppropstgUnicode); Check( 0, pbag->Release() ); pbag = NULL; } // test_WriteReadProperties BOOL StgConvertPropertyToVariantWrapper( IN SERIALIZEDPROPERTYVALUE const *pprop, IN USHORT CodePage, OUT PROPVARIANT *pvar, IN PMemoryAllocator *pma, OUT NTSTATUS *pstatus ) { BOOL boolRet = FALSE; *pstatus = STATUS_SUCCESS; __try { boolRet = g_pfnStgConvertPropertyToVariant( pprop, CodePage, pvar, pma ); // boolRet = RtlConvertPropertyToVariant( pprop, CodePage, pvar, pma ); } __except( EXCEPTION_EXECUTE_HANDLER ) { *pstatus = GetExceptionCode(); } return( boolRet ); } ULONG StgPropertyLengthAsVariantWrapper( SERIALIZEDPROPERTYVALUE *pprop, ULONG cbprop, USHORT CodePage, BYTE flags, NTSTATUS *pstatus ) { ULONG cbRet = 0; *pstatus = STATUS_SUCCESS; __try { cbRet = g_pfnStgPropertyLengthAsVariant( pprop, cbprop, CodePage, 0 ); // cbRet = PropertyLengthAsVariant( pprop, cbprop, CodePage, 0 ); } __except( EXCEPTION_EXECUTE_HANDLER ) { *pstatus = GetExceptionCode(); } return( cbRet ); } SERIALIZEDPROPERTYVALUE * StgConvertVariantToPropertyWrapper( IN PROPVARIANT const *pvar, IN USHORT CodePage, OPTIONAL OUT SERIALIZEDPROPERTYVALUE *pprop, IN OUT ULONG *pcb, IN PROPID pid, IN BOOLEAN fVector, OPTIONAL OUT ULONG *pcIndirect, OUT NTSTATUS *pstatus ) { SERIALIZEDPROPERTYVALUE * ppropRet = NULL; *pstatus = STATUS_SUCCESS; __try { ppropRet = g_pfnStgConvertVariantToProperty( pvar, CodePage, pprop, pcb, pid, fVector, pcIndirect ); // ppropRet = RtlConvertVariantToProperty( pvar, CodePage, pprop, pcb, pid, fVariantVectorOrArray, pcIndirect ); } __except( EXCEPTION_EXECUTE_HANDLER ) { *pstatus = GetExceptionCode(); } return( ppropRet ); } void test_PidIllegal( IStorage *pstg ) { IPropertySetStorage *ppropsetstg = NULL; IPropertyStorage *ppropstg = NULL; ULONG cRefsOriginal = GetRefCount(pstg); CPropVariant rgcpropvarWrite[3], rgcpropvarRead[3]; CPropSpec rgcpropspec[3]; PROPID rgpropid[3]; LPOLESTR rgoszNames[3] = { NULL, NULL, NULL }; // Get an IPropertyStorage Check( S_OK, pstg->QueryInterface( IID_IPropertySetStorage, reinterpret_cast(&ppropsetstg) )); Check( S_OK, ppropsetstg->Create( FMTID_NULL, NULL, PROPSETFLAG_DEFAULT, STGM_CREATE|STGM_SHARE_EXCLUSIVE|STGM_READWRITE, &ppropstg )); // Write a PID_ILLEGAL property. Since it's ignored, nothing should be written. rgcpropvarWrite[0] = (long) 1234; rgcpropspec[0] = PID_ILLEGAL; Check( S_OK, ppropstg->WriteMultiple( 1, rgcpropspec, rgcpropvarWrite, PID_FIRST_USABLE )); Check( S_FALSE, ppropstg->ReadMultiple( 1, rgcpropspec, rgcpropvarRead )); // Write several normal properties SHORT sOriginal = 1234; LPOLESTR oszOriginalName = OLESTR("Second"); rgcpropvarWrite[0] = (long) 5678; rgcpropvarWrite[1] = sOriginal; rgcpropvarWrite[2] = (float) 23.5; rgcpropspec[0] = OLESTR("First"); rgcpropspec[1] = oszOriginalName; rgcpropspec[2] = OLESTR("Third"); Check( S_OK, ppropstg->WriteMultiple( 3, rgcpropspec, rgcpropvarWrite, PID_FIRST_USABLE )); Check( S_OK, ppropstg->ReadMultiple( 3, rgcpropspec, rgcpropvarRead )); Check( TRUE, rgcpropvarWrite[0] == rgcpropvarRead[0] ); Check( TRUE, rgcpropvarWrite[1] == rgcpropvarRead[1] ); Check( TRUE, rgcpropvarWrite[2] == rgcpropvarRead[2] ); // Re-write the properties except for one. The value of that property shouldn't change, // nor should its name. rgcpropvarWrite[0] = (short) 1234; rgcpropvarWrite[1] = (long) 5678; rgcpropvarWrite[2] = (double) 12.4; rgcpropspec[1] = PID_ILLEGAL; Check( S_OK, ppropstg->WriteMultiple( 3, rgcpropspec, rgcpropvarWrite, PID_FIRST_USABLE )); rgcpropspec[1] = oszOriginalName; Check( S_OK, ppropstg->ReadMultiple( 3, rgcpropspec, rgcpropvarRead )); Check( TRUE, rgcpropvarWrite[0] == rgcpropvarRead[0] ); Check( TRUE, CPropVariant(sOriginal) == rgcpropvarRead[1] ); Check( TRUE, rgcpropvarWrite[2] == rgcpropvarRead[2] ); rgpropid[0] = PID_FIRST_USABLE; rgpropid[1] = PID_FIRST_USABLE + 1; rgpropid[2] = PID_FIRST_USABLE + 2; Check( S_OK, ppropstg->ReadPropertyNames( 3, rgpropid, rgoszNames )); Check( 0, wcscmp( rgcpropspec[0].lpwstr, rgoszNames[0] )); Check( 0, wcscmp( oszOriginalName, rgoszNames[1] )); Check( 0, wcscmp( rgcpropspec[2].lpwstr, rgoszNames[2] )); for( int i = 0; i < 3; i++ ) { CoTaskMemFree( rgoszNames[i] ); rgoszNames[i] = NULL; } // Re-write the names, again skipping one of them. rgoszNames[0] = OLESTR("Updated first"); rgoszNames[1] = OLESTR("Updated second"); rgoszNames[2] = OLESTR("Updated third"); rgpropid[1] = PID_ILLEGAL; Check( S_OK, ppropstg->WritePropertyNames( 3, rgpropid, rgoszNames )); rgoszNames[0] = rgoszNames[1] = rgoszNames[2] = NULL; rgpropid[1] = PID_FIRST_USABLE + 1; Check( S_OK, ppropstg->ReadPropertyNames( 3, rgpropid, rgoszNames )); Check( 0, wcscmp( rgoszNames[0], OLESTR("Updated first") )); Check( 0, wcscmp( rgoszNames[1], oszOriginalName )); Check( 0, wcscmp( rgoszNames[2], OLESTR("Updated third") )); // Re-write just the one name, but skipping it. rgpropid[1] = PID_ILLEGAL; CoTaskMemFree( rgoszNames[1] ); rgoszNames[1] = OLESTR("Write just the second"); Check( S_OK, ppropstg->WritePropertyNames( 1, &rgpropid[1], &rgoszNames[1] )); rgoszNames[1] = NULL; rgpropid[1] = PID_FIRST_USABLE + 1; Check( S_OK, ppropstg->ReadPropertyNames( 1, &rgpropid[1], &rgoszNames[1] )); Check( 0, wcscmp( rgoszNames[1], oszOriginalName )); // Exit for( i = 0; i < 3; i++ ) CoTaskMemFree( rgoszNames[i] ); Check( 0, RELEASE_INTERFACE(ppropstg) ); Check( cRefsOriginal, RELEASE_INTERFACE(ppropsetstg) ); } void test_PropertyLengthAsVariant( ) { Status( "StgPropertyLengthAsVariant, StgConvert*\n" ); ULONG i = 0; BYTE *rgb = new BYTE[ 8192 ]; Check( TRUE, NULL != rgb ); CPropVariant rgcpropvar[ 7 ]; ULONG rgcbExpected[ 7 ]; CPropVariant *rgcpropvarSafeArray = NULL; SAFEARRAY *rgpsa[3]; SAFEARRAYBOUND rgsaBounds[] = { {2,0}, {3,10}, {4,20} }; // [0..1], [10..12], [20..23] ULONG cDims = sizeof(rgsaBounds)/sizeof(rgsaBounds[0]); ULONG cElems = 0; rgcpropvar[i] = (long) 1234; // VT_I4 rgcbExpected[i] = 0; i++; rgcpropvar[i].SetBSTR( OLESTR("Hello, world") ); // Creates a copy rgcbExpected[i] = sizeof(OLESTR("Hello, world")) + sizeof(ULONG); i++; rgcpropvar[i][2] = (short) 2; // VT_VECTOR | VT_I2 rgcpropvar[i][1] = (short) 1; rgcpropvar[i][0] = (short) 0; rgcbExpected[i] = 3 * sizeof(short); i++; rgcpropvar[i][1] = (PROPVARIANT) CPropVariant( (unsigned long) 4 ); // VT_VECTOR | VT_VARIANT rgcpropvar[i][0] = (PROPVARIANT) CPropVariant( (BSTR) OLESTR("Hi there") ); rgcbExpected[i] = 2 * sizeof(PROPVARIANT) + sizeof(OLESTR("Hi there")) + sizeof(ULONG); i++; rgpsa[0] = SafeArrayCreateEx( VT_I4, 3, rgsaBounds, NULL ); Check( TRUE, NULL != rgpsa[0] ); cElems = CalcSafeArrayElementCount( rgpsa[0] ); rgcbExpected[i+0] = sizeof(SAFEARRAY) - sizeof(SAFEARRAYBOUND) + 3 * sizeof(SAFEARRAYBOUND) + cElems * sizeof(LONG); rgpsa[1] = SafeArrayCreateEx( VT_BSTR, 3, rgsaBounds, NULL ); Check( TRUE, NULL != rgpsa[1] ); rgcbExpected[i+1] = sizeof(SAFEARRAY) - sizeof(SAFEARRAYBOUND) + 3 * sizeof(SAFEARRAYBOUND) + cElems * sizeof(BSTR); rgpsa[2] = SafeArrayCreateEx( VT_VARIANT, 3, rgsaBounds, NULL ); Check( TRUE, NULL != rgpsa[2] ); rgcbExpected[i+2] = sizeof(SAFEARRAY) - sizeof(SAFEARRAYBOUND) + 3 * sizeof(SAFEARRAYBOUND) + cElems * sizeof(PROPVARIANT); rgcpropvarSafeArray = new CPropVariant[ cElems ]; Check( FALSE, NULL == rgcpropvar ); for( ULONG j = 0; j < cElems; j++ ) { LONG rgIndices[3]; CalcSafeArrayIndices( j, rgIndices, rgsaBounds, cDims ); LONG lVal = static_cast(j); Check( S_OK, SafeArrayPutElement( rgpsa[0], rgIndices, &lVal )); BSTR bstrVal = SysAllocString( OLESTR("0 BSTR Val") ); *bstrVal = OLESTR('0') + static_cast(j); Check( S_OK, SafeArrayPutElement( rgpsa[1], rgIndices, bstrVal )); rgcbExpected[i+1] += ocslen(bstrVal) + sizeof(OLECHAR) + sizeof(ULONG); if( j & 1 ) rgcpropvarSafeArray[j] = (long) j; else { rgcpropvarSafeArray[j].SetBSTR( bstrVal ); rgcbExpected[i+2] += ocslen(bstrVal)*sizeof(OLECHAR) + sizeof(OLECHAR) + sizeof(ULONG); } Check( S_OK, SafeArrayPutElement( rgpsa[2], rgIndices, &rgcpropvarSafeArray[j] )); SysFreeString( bstrVal ); } rgcpropvar[i].vt = VT_ARRAY | VT_I4; reinterpret_cast(&rgcpropvar[i])->parray = rgpsa[0]; i++; rgcpropvar[i].vt = VT_ARRAY | VT_BSTR; reinterpret_cast(&rgcpropvar[i])->parray = rgpsa[1]; i++; rgcpropvar[i].vt = VT_ARRAY | VT_VARIANT; reinterpret_cast(&rgcpropvar[i])->parray = rgpsa[2]; i++; Check( sizeof(rgcpropvar)/sizeof(rgcpropvar[0]), i ); for( i = 0; i < sizeof(rgcpropvar)/sizeof(rgcpropvar[0]); i++ ) { PropTestMemoryAllocator ma; SERIALIZEDPROPERTYVALUE *pprop = NULL; CPropVariant cpropvarOut; ULONG cbWritten = 8192, cbAsVariant = 0; NTSTATUS status; ULONG cIndirect; pprop = StgConvertVariantToPropertyWrapper( &rgcpropvar[i], CP_WINUNICODE, reinterpret_cast(rgb), &cbWritten, PID_FIRST_USABLE, FALSE, &cIndirect, &status ); Check( TRUE, NT_SUCCESS(status) ); Check( TRUE, NULL != pprop ); cbAsVariant = StgPropertyLengthAsVariantWrapper( reinterpret_cast(rgb), cbWritten, CP_WINUNICODE, 0, &status ); Check( TRUE, NT_SUCCESS(status) ); // Check that the cbAsVariant is at least big enough. Also sanity check that // it's not huge. We use a fudge multiple of 3 for this because the // StgPropertyLengthAsVariant can way overestimate (primarily because it // doesn't know if BSTRs will need conversion). Check( TRUE, cbAsVariant >= rgcbExpected[i] ); Check( TRUE, cbAsVariant <= rgcbExpected[i]*3 ); // Check that we can convert back to a PropVariant // (False because it's not an indirect property we're converting) Check( FALSE, StgConvertPropertyToVariantWrapper( reinterpret_cast(rgb), CP_WINUNICODE, &cpropvarOut, &ma, &status )); Check( TRUE, NT_SUCCESS(status) ); Check( TRUE, cpropvarOut == rgcpropvar[i] ); } g_pfnFreePropVariantArray( cElems, rgcpropvarSafeArray ); delete[] rgcpropvarSafeArray; g_pfnFreePropVariantArray( sizeof(rgcpropvar)/sizeof(rgcpropvar[0]), rgcpropvar ); delete[] rgb; } void test_LargePropertySet( IStorage *pstg ) { FMTID fmtid; IPropertySetStorage *pPropSetStg = NULL; IPropertyStorage *pPropStg = NULL; STATPROPSETSTG statpropsetstg; CPropSpec cpropspec; PROPVARIANT propvar; Status( "Large property sets\n" ); Check( S_OK, pstg->QueryInterface( IID_IPropertySetStorage, reinterpret_cast(&pPropSetStg) )); // Create a new property set. UuidCreate( &fmtid ); Check( S_OK, pPropSetStg->Create( fmtid, NULL, PROPSETFLAG_DEFAULT, STGM_CREATE|STGM_SHARE_EXCLUSIVE|STGM_READWRITE, &pPropStg )); // Create a big property to write. Make it just about the max, 1M // (it's hard to make it exactly the right size, because it depends on the // size of the propset header, overallocs, etc.). propvar.vt = VT_BLOB; propvar.blob.cbSize = 1023 * 1024; propvar.blob.pBlobData = new BYTE[ propvar.blob.cbSize ]; Check( FALSE, NULL == propvar.blob.pBlobData ); cpropspec = OLESTR("Name"); // Write this big property Check( S_OK, pPropStg->WriteMultiple( 1, &cpropspec, &propvar, PID_FIRST_USABLE )); // Create a slightly too large property set. PropVariantClear( &propvar ); delete propvar.blob.pBlobData; propvar.vt = VT_BLOB; propvar.blob.cbSize = 1024 * 1024; propvar.blob.pBlobData = new BYTE[ propvar.blob.cbSize ]; Check( FALSE, NULL == propvar.blob.pBlobData ); // Write this too-big property Check( STG_E_MEDIUMFULL, pPropStg->WriteMultiple( 1, &cpropspec, &propvar, PID_FIRST_USABLE )); delete propvar.blob.pBlobData; RELEASE_INTERFACE( pPropStg ); } void test_VersionOneNames( IStorage *pstg ) { FMTID fmtidInsensitive, fmtidSensitive, fmtidLongNames; IPropertySetStorage *pPropSetStg = NULL; IPropertyStorage *pPropStg = NULL; STATPROPSETSTG statpropsetstg; CPropSpec rgcpropspec[2]; CPropVariant rgcpropvarWrite[2], rgcpropvarRead[2]; LPOLESTR rgposzNames[2] = { NULL, NULL }; PROPID rgpropid[2] = { PID_FIRST_USABLE, PID_FIRST_USABLE+1 }; Status( "PROPSETFLAG_CASE_SENSITIVE flag and long names\n" ); Check( S_OK, pstg->QueryInterface( IID_IPropertySetStorage, reinterpret_cast(&pPropSetStg) )); UuidCreate( &fmtidInsensitive ); UuidCreate( &fmtidSensitive ); UuidCreate( &fmtidLongNames ); // Make two passes, Unicode first, then Ansi. for( int iPass = 0; iPass < 2; iPass++ ) { DWORD propsetflagAnsi = 0 == iPass ? 0 : PROPSETFLAG_ANSI; ULONG cbLongPropertyName = 1020 * 1024; ULONG cchLongPropertyName = 0 == iPass ? cbLongPropertyName/sizeof(OLECHAR) : cbLongPropertyName; // ------------------------ // Case insensitive propset // ------------------------ Check( S_OK, pPropSetStg->Create( fmtidInsensitive, NULL, PROPSETFLAG_DEFAULT | propsetflagAnsi, STGM_CREATE|STGM_SHARE_EXCLUSIVE|STGM_READWRITE, &pPropStg )); // This should still be a version zero (original) propery set. CheckFormatVersion( pPropStg, 0); rgcpropspec[0] = OLESTR("Name"); rgcpropspec[1] = OLESTR("name"); rgcpropvarWrite[0] = (long) 0; rgcpropvarWrite[1] = (short) 1; // Write two properties with the same name (their the same because this is a // case-insensitive property set). Check( S_OK, pPropStg->WriteMultiple( 2, rgcpropspec, rgcpropvarWrite, PID_FIRST_USABLE )); // Read the names back. Check( S_OK, pPropStg->ReadPropertyNames( 2, rgpropid, rgposzNames )); // Since we really only wrote one property, we should only get one name back // Note that we get back the first name, but it's the second value! Check( 0, ocscmp( rgcpropspec[0].lpwstr, rgposzNames[0] )); Check( TRUE, NULL == rgposzNames[1] ); delete[] rgposzNames[0]; rgposzNames[0] = NULL; // Double check that we really one wrote one property (the second). Check( S_OK, pPropStg->ReadMultiple( 2, rgcpropspec, rgcpropvarRead )); Check( TRUE, VT_I2 == rgcpropvarRead[0].VarType() && rgcpropvarRead[0] == CPropVariant((short) 1) ); Check( TRUE, VT_I2 == rgcpropvarRead[1].VarType() && rgcpropvarRead[1] == CPropVariant((short) 1) ); Check( S_OK, pPropStg->Stat( &statpropsetstg )); Check( 0, statpropsetstg.grfFlags & PROPSETFLAG_CASE_SENSITIVE ); RELEASE_INTERFACE( pPropStg ); g_pfnFreePropVariantArray( 2, rgcpropvarRead ); // ---------------------- // Case sensitive propset // ---------------------- Check( S_OK, pPropSetStg->Create( fmtidSensitive, NULL, PROPSETFLAG_CASE_SENSITIVE | propsetflagAnsi, STGM_CREATE|STGM_SHARE_EXCLUSIVE|STGM_READWRITE, &pPropStg )); // Case-sensitivity requires a version 1 property set. CheckFormatVersion( pPropStg, 1 ); // Write the two names that differ only by case. Check( S_OK, pPropStg->WriteMultiple( 2, rgcpropspec, rgcpropvarWrite, PID_FIRST_USABLE )); // Read the names back and validate. Check( S_OK, pPropStg->ReadPropertyNames( 2, rgpropid, rgposzNames )); Check( TRUE, !ocscmp( rgcpropspec[0].lpwstr, rgposzNames[0] )); Check( TRUE, !ocscmp( rgcpropspec[1].lpwstr, rgposzNames[1] )); delete[] rgposzNames[0]; rgposzNames[0] = NULL; delete[] rgposzNames[1]; rgposzNames[1] = NULL; // Read the values and validate them too. Check( S_OK, pPropStg->ReadMultiple( 2, rgcpropspec, rgcpropvarRead )); Check( TRUE, VT_I4 == rgcpropvarRead[0].VarType() && rgcpropvarRead[0] == CPropVariant((long) 0) ); Check( TRUE, VT_I2 == rgcpropvarRead[1].VarType() && rgcpropvarRead[1] == CPropVariant((short) 1) ); g_pfnFreePropVariantArray( 2, rgcpropvarRead ); Check( S_OK, pPropStg->Stat( &statpropsetstg )); Check( PROPSETFLAG_CASE_SENSITIVE, statpropsetstg.grfFlags & PROPSETFLAG_CASE_SENSITIVE ); RELEASE_INTERFACE( pPropStg ); // ----------------------- // Propset with long names // ----------------------- Check( S_OK, pPropSetStg->Create( fmtidLongNames, NULL, PROPSETFLAG_DEFAULT | propsetflagAnsi, STGM_CREATE|STGM_SHARE_EXCLUSIVE|STGM_READWRITE, &pPropStg )); // So far we haven't done anything that requires a post-original property set. CheckFormatVersion( pPropStg, 0 ); // Write a short name, validate it, and validate that the format version doesn't change. rgcpropspec[0] = OLESTR("A short name"); Check( S_OK, pPropStg->WriteMultiple( 1, rgcpropspec, rgcpropvarWrite, PID_FIRST_USABLE )); Check( S_OK, pPropStg->ReadPropertyNames( 1, rgpropid, rgposzNames )); // PROPID == 2 Check( TRUE, !ocscmp( rgcpropspec[0].lpwstr, rgposzNames[0] )); CheckFormatVersion( pPropStg, 0 ); delete[] rgposzNames[0]; rgposzNames[0] = NULL; // Now create a really, really, long name. rgcpropspec[0].Alloc( cchLongPropertyName ); for( ULONG i = 0; i < cchLongPropertyName; i++ ) rgcpropspec[0][i] = OLESTR('a') + ( static_cast(i) % 26 ); rgcpropspec[0][cchLongPropertyName-1] = OLESTR('\0'); // Write this long name. Check( S_OK, pPropStg->WriteMultiple( 1, rgcpropspec, rgcpropvarWrite, PID_FIRST_USABLE )); // The property set's format version should have been automatically bumped up. CheckFormatVersion( pPropStg, 1); // Read the property using the long name Check( S_OK, pPropStg->ReadMultiple( 1, rgcpropspec, rgcpropvarRead )); Check( TRUE, rgcpropvarWrite[0] == rgcpropvarRead[0] ); rgcpropvarRead[0].Clear(); // Read and validate the property name. Check( S_OK, pPropStg->ReadPropertyNames( 1, &rgpropid[1], rgposzNames )); // PROPID == 3 Check( TRUE, !ocscmp( rgcpropspec[0].lpwstr, rgposzNames[0] )); delete[] rgposzNames[0]; rgposzNames[0] = NULL; // Try to write a long, different name. rgcpropspec[0][0] = OLESTR('#'); Check( STG_E_MEDIUMFULL, pPropStg->WriteMultiple( 1, rgcpropspec, rgcpropvarWrite, PID_FIRST_USABLE )); RELEASE_INTERFACE( pPropStg ); } // for( int iPass = 0; iPass < 2; iPass++ ) RELEASE_INTERFACE( pPropSetStg ); } // test_VersionOneNames() void test_MultipleReader( LPOLESTR ocsDir ) { OLECHAR ocsFile[ MAX_PATH ]; IPropertyBagEx *pBag1 = NULL, *pBag2 = NULL; CPropVariant rgcpropvarRead1[ CPROPERTIES_ALL ], rgcpropvarRead2[ CPROPERTIES_ALL ]; OLECHAR oszPropertyName[] = OLESTR("Simple property"); IEnumSTATPROPBAG *penum = NULL; STATPROPBAG rgstatpropbag[ CPROPERTIES_ALL + 1]; ULONG cEnum = 0; Status( "Multiple stgm_read|stgm_deny_write\n" ); ocscpy( ocsFile, ocsDir ); ocscat( ocsFile, OLESTR("test_MultipleReader") ); Check( S_OK, g_pfnStgCreateStorageEx( ocsFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, DetermineStgFmt( g_enumImplementation ), 0L, NULL, NULL, IID_IPropertyBagEx, reinterpret_cast(&pBag1) )); Check( S_OK, pBag1->WriteMultiple( CPROPERTIES_ALL, g_rgoszpropnameAll, g_rgcpropvarAll )); Check( S_OK, ResetRGPropVar( g_rgcpropvarAll )); Check( 0, RELEASE_INTERFACE(pBag1) ); Check( S_OK, g_pfnStgOpenStorageEx( ocsFile, STGM_READ | STGM_SHARE_DENY_WRITE, STGFMT_ANY, 0L, NULL, NULL, IID_IPropertyBagEx, reinterpret_cast(&pBag1) )); Check( S_OK, g_pfnStgOpenStorageEx( ocsFile, STGM_READ | STGM_SHARE_DENY_WRITE, STGFMT_ANY, 0L, NULL, NULL, IID_IPropertyBagEx, reinterpret_cast(&pBag2) )); Check( S_OK, pBag2->Enum( NULL, 0, &penum )); //OLESTR(""), 0, &penum )); Check( S_OK, penum->Next( CPROPERTIES_ALL, rgstatpropbag, &cEnum )); Check( CPROPERTIES_ALL, cEnum ); Check( S_OK, pBag1->ReadMultiple( CPROPERTIES_ALL, g_rgoszpropnameAll, rgcpropvarRead1, NULL )); Check( S_OK, pBag2->ReadMultiple( CPROPERTIES_ALL, g_rgoszpropnameAll, rgcpropvarRead2, NULL )); for( int i = 0; i < CPROPERTIES_ALL; i++ ) { Check( TRUE, rgcpropvarRead1[i] == g_rgcpropvarAll[i] ); Check( TRUE, rgcpropvarRead2[i] == g_rgcpropvarAll[i] ); delete[] rgstatpropbag[i].lpwstrName; rgstatpropbag[i].lpwstrName = NULL; } Check( 0, RELEASE_INTERFACE(penum) ); Check( 0, RELEASE_INTERFACE(pBag1) ); Check( 0, RELEASE_INTERFACE(pBag2) ); return; } // test_MultipleReader void test_Robustness(OLECHAR *poszDir) { if( PROPIMP_NTFS != g_enumImplementation ) return; Status( "NTFS property set robustness\n" ); HRESULT hr = S_OK; IStorage *pStorage = NULL; IPropertySetStorage *pPropSetStg = NULL; IPropertyStorage *pPropStg = NULL; IStorageTest *pPropStgTest = NULL; IStream *pStm = NULL; FMTID fmtid; STATSTG statstg; OLECHAR oszFile[ MAX_PATH ]; OLECHAR oszName[ MAX_PATH ]; OLECHAR oszUpdateName[ MAX_PATH ]; CPropSpec rgcpropspec[2]; CPropVariant rgcpropvarWrite[2], rgcpropvarRead[2]; ocscpy( oszFile, poszDir ); ocscat( oszFile, OLESTR("test_Robustness") ); // Create a property set and put a property into it. Check( S_OK, g_pfnStgCreateStorageEx( oszFile, STGM_READWRITE|STGM_CREATE|STGM_SHARE_EXCLUSIVE, DetermineStgFmt(g_enumImplementation), 0, NULL, NULL, DetermineStgIID( g_enumImplementation ), reinterpret_cast(&pStorage) )); Check( S_OK, pStorage->QueryInterface( IID_IPropertySetStorage, reinterpret_cast(&pPropSetStg) )); UuidCreate( &fmtid ); Check( S_OK, pPropSetStg->Create( fmtid, NULL, PROPSETFLAG_DEFAULT, STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, &pPropStg )); rgcpropspec[0] = OLESTR("Property Name"); rgcpropspec[1] = OLESTR("Second property name"); rgcpropvarWrite[0] = static_cast(23); rgcpropvarWrite[1] = OLESTR("Second property value"); Check( S_OK, pPropStg->WriteMultiple( 1, &rgcpropspec[0], &rgcpropvarWrite[0], PID_FIRST_USABLE )); Check( 0, RELEASE_INTERFACE(pPropStg) ); // Rename the property set's stream to "Updt_*", and create any empty stream // in its place. This simulates a crash during the flush of a property set. RtlGuidToPropertySetName( &fmtid, oszName ); oszName[ 0 ] = OC_PROPSET0; wcscpy( oszUpdateName, OLESTR("Updt_") ); wcscat( oszUpdateName, oszName ); Check( S_OK, pStorage->RenameElement( oszName, oszUpdateName )); Check( S_OK, pStorage->CreateStream( oszName, STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 0, 0, &pStm )); Check( 0, RELEASE_INTERFACE(pStm) ); // Open the property set in read-only mode, and verify that we can still ready // the property. Since we're opening in read-only, the streams should remain // unchanged. Check( S_OK, pPropSetStg->Open( fmtid, STGM_READ|STGM_SHARE_EXCLUSIVE, &pPropStg )); Check( S_OK, pPropStg->ReadMultiple( 1, &rgcpropspec[0], &rgcpropvarRead[0] )); Check( TRUE, rgcpropvarWrite[0] == rgcpropvarRead[0] ); Check( 0, RELEASE_INTERFACE(pPropStg) ); // Verify that the streams do not appear to have been changed. Check( S_OK, pStorage->OpenStream( oszName, NULL, STGM_READ|STGM_SHARE_EXCLUSIVE, 0, &pStm )); Check( S_OK, pStm->Stat( &statstg, STATFLAG_NONAME )); Check( TRUE, CULargeInteger(0) == CULargeInteger(statstg.cbSize) ); Check( 0, RELEASE_INTERFACE(pStm) ); Check( S_OK, pStorage->OpenStream( oszUpdateName, NULL, STGM_READ|STGM_SHARE_EXCLUSIVE, 0, &pStm )); Check( S_OK, pStm->Stat( &statstg, STATFLAG_NONAME )); Check( FALSE, CULargeInteger(0) == CULargeInteger(statstg.cbSize) ); Check( 0, RELEASE_INTERFACE(pStm) ); // Now open the property set for write. This should cause the problem to be fixed. Check( S_OK, pPropSetStg->Open( fmtid, STGM_READWRITE|STGM_SHARE_EXCLUSIVE, &pPropStg )); // Read the property back Check( S_OK, pPropStg->ReadMultiple( 1, &rgcpropspec[0], &rgcpropvarRead[0] )); Check( TRUE, rgcpropvarWrite[0] == rgcpropvarRead[0] ); // Write another property and read both properties back Check( S_OK, pPropStg->WriteMultiple( 1, &rgcpropspec[1], &rgcpropvarWrite[1], PID_FIRST_USABLE )); Check( S_OK, pPropStg->ReadMultiple( 2, &rgcpropspec[0], &rgcpropvarRead[0] )); Check( TRUE, rgcpropvarWrite[0] == rgcpropvarRead[0] ); Check( TRUE, rgcpropvarWrite[1] == rgcpropvarRead[1] ); rgcpropvarRead[1].Clear(); Check( 0, RELEASE_INTERFACE(pPropStg) ); // Verify that the streams look corrected. Check( S_OK, pStorage->OpenStream( oszName, NULL, STGM_READ|STGM_SHARE_EXCLUSIVE, 0, &pStm )); Check( S_OK, pStm->Stat( &statstg, STATFLAG_NONAME )); Check( TRUE, CULargeInteger(0) != CULargeInteger(statstg.cbSize) ); Check( 0, RELEASE_INTERFACE(pStm) ); Check( STG_E_FILENOTFOUND, pStorage->OpenStream( oszUpdateName, NULL, STGM_READ|STGM_SHARE_EXCLUSIVE, 0, &pStm )); // Write/read after disabling the stream-rename Check( S_OK, pPropSetStg->Create( fmtid, NULL, PROPSETFLAG_DEFAULT, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE, &pPropStg )); hr = pPropStg->QueryInterface( IID_IStorageTest, reinterpret_cast(&pPropStgTest) ); if( SUCCEEDED(hr) ) Check( S_OK, pPropStgTest->UseNTFS4Streams( TRUE )); if( E_NOINTERFACE == hr ) { Status( " ... Partially skipping, IStorageTest not available (free build?)\n" ); } else { Check( STG_E_FILENOTFOUND, pStorage->OpenStream( oszUpdateName, NULL, STGM_READ|STGM_SHARE_EXCLUSIVE, 0, &pStm )); Check( S_OK, pPropStg->WriteMultiple( 2, &rgcpropspec[0], &rgcpropvarWrite[0], PID_FIRST_USABLE )); Check( STG_E_FILENOTFOUND, pStorage->OpenStream( oszUpdateName, NULL, STGM_READ|STGM_SHARE_EXCLUSIVE, 0, &pStm )); Check( S_OK, pPropStg->Commit( STGC_DEFAULT )); Check( STG_E_FILENOTFOUND, pStorage->OpenStream( oszUpdateName, NULL, STGM_READ|STGM_SHARE_EXCLUSIVE, 0, &pStm )); RELEASE_INTERFACE( pPropStgTest ); Check( STG_E_FILENOTFOUND, pStorage->OpenStream( oszUpdateName, NULL, STGM_READ|STGM_SHARE_EXCLUSIVE, 0, &pStm )); Check( 0, RELEASE_INTERFACE(pPropStg) ); Check( S_OK, pPropSetStg->Open( fmtid, STGM_READ|STGM_SHARE_DENY_WRITE, &pPropStg )); Check( STG_E_FILENOTFOUND, pStorage->OpenStream( oszUpdateName, NULL, STGM_READ|STGM_SHARE_EXCLUSIVE, 0, &pStm )); Check( S_OK, pPropStg->ReadMultiple( 2, rgcpropspec, rgcpropvarRead )); Check( STG_E_FILENOTFOUND, pStorage->OpenStream( oszUpdateName, NULL, STGM_READ|STGM_SHARE_EXCLUSIVE, 0, &pStm )); Check( TRUE, rgcpropvarWrite[0] == rgcpropvarRead[0] ); Check( TRUE, rgcpropvarWrite[1] == rgcpropvarRead[1] ); g_pfnFreePropVariantArray( 2, rgcpropvarRead ); } // Write to the property set, then cause it to be shutdown and reverted. Check( 0, RELEASE_INTERFACE(pPropStg) ); Check( S_OK, pPropSetStg->Open( fmtid, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, &pPropStg )); rgcpropvarWrite[0] = OLESTR("Hello"); rgcpropvarWrite[1] = OLESTR("World"); Check( S_OK, pPropStg->WriteMultiple( 2, rgcpropspec, rgcpropvarWrite, PID_FIRST_USABLE )); RELEASE_INTERFACE(pPropSetStg); Check( 0, RELEASE_INTERFACE(pStorage) ); // Should flush the properties Check( STG_E_REVERTED, pPropStg->ReadMultiple( 2, rgcpropspec, rgcpropvarRead )); Check( 0, RELEASE_INTERFACE(pPropStg) ); Check( S_OK, g_pfnStgOpenStorageEx( oszFile, STGM_READ|STGM_SHARE_DENY_WRITE, STGFMT_ANY, 0, NULL, NULL, IID_IPropertySetStorage, reinterpret_cast(&pPropSetStg) )); Check( S_OK, pPropSetStg->Open( fmtid, STGM_READ|STGM_SHARE_EXCLUSIVE, &pPropStg )); Check( S_OK, pPropStg->ReadMultiple( 2, rgcpropspec, rgcpropvarRead )); Check( TRUE, rgcpropvarWrite[0] == rgcpropvarRead[0] ); Check( TRUE, rgcpropvarWrite[1] == rgcpropvarRead[1] ); Check( 0, RELEASE_INTERFACE(pPropStg) ); g_pfnFreePropVariantArray( 2, rgcpropvarRead ); // // Exit // RELEASE_INTERFACE(pPropSetStg); g_pfnFreePropVariantArray( 2, rgcpropvarWrite ); g_pfnFreePropVariantArray( 2, rgcpropvarRead ); } // test_Robustness void test_EmptyBag( OLECHAR *poszDir ) { OLECHAR oszFile[ MAX_PATH ]; IStorage *pstg = NULL; IPropertyBagEx *pbag = NULL; PROPVARIANT propvar; IEnumSTATPROPBAG *penum = NULL; STATPROPBAG statpropbag; ULONG cFetched; ocscpy( oszFile, poszDir ); ocscat( oszFile, OLESTR("test_EmptyBag") ); Check( S_OK, g_pfnStgCreateStorageEx( oszFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, DetermineStgFmt( g_enumImplementation ), 0L, NULL, NULL, IID_IPropertyBagEx, (void**) &pbag )); PropVariantInit( &propvar ); OLECHAR *poszName = OLESTR("test"); Check( S_FALSE, pbag->ReadMultiple( 1, &poszName, &propvar, NULL )); Check( VT_EMPTY, propvar.vt ); Check( S_OK, pbag->Enum( OLESTR(""), 0, &penum )); Check( S_FALSE, penum->Next( 1, &statpropbag, &cFetched )); Check( 0, cFetched ); Check( 0, RELEASE_INTERFACE(penum)); Check( 0, RELEASE_INTERFACE(pbag)); } // test_EmptyBag void test_BagDelete( IStorage *pstg ) { HRESULT hr = S_OK; IPropertyBagEx *pbag = NULL; IEnumSTATPROPBAG* penum = NULL; ULONG cFetched; ULONG i, j; OLECHAR * rgoszDelete[2]; // Note that some of these names only differ by case, which is legal in a bag OLECHAR *rgoszNames[] = { OLESTR("www.microsoft.com/bag/test?prop1"), OLESTR("www.microsoft.com/bag/test?PROP1"), OLESTR("www.microsoft.com/bag/2test?prop1"), OLESTR("www.microsoft.com/bag2/test?prop1") }; CPropVariant rgcpropvarRead[ ELEMENTS(rgoszNames) + 1 ]; STATPROPBAG rgstatpropbag[ ELEMENTS(rgoszNames) + 1 ]; Status( "Property bag deletions\n" ); Check( S_OK, pstg->QueryInterface( IID_IPropertyBagEx, reinterpret_cast(&pbag) )); DeleteBagExProperties( pbag, OLESTR("") ); // ------------------------------------------ // Delete bag2/test?prop1 by name & by prefix // ------------------------------------------ for( i = 0; i < 2; i++ ) { ULONG cFetchedExpected; switch(i) { case 0: // Delete by name rgoszDelete[0] = OLESTR("www.microsoft.com/bag2/test?prop1"); cFetchedExpected = ELEMENTS(rgoszNames) - 1; break; case 1: // Delete by prefix rgoszDelete[0] = OLESTR("www.microsoft.com/bag2/test"); cFetchedExpected = ELEMENTS(rgoszNames) - 1; break; default: Check( FALSE, TRUE ); //Check( FALSE, 0 == OLESTR("Invalid switch") ); } // switch(i) Check( S_OK, pbag->WriteMultiple( ELEMENTS(rgoszNames), rgoszNames, g_rgcpropvarAll )); Check( S_OK, ResetRGPropVar( g_rgcpropvarAll )); DeleteBagExProperties( pbag, rgoszDelete[0] ); Check( S_OK, pbag->Enum( OLESTR(""), 0, &penum )); Check( ELEMENTS(rgoszNames) == cFetchedExpected ? S_OK : S_FALSE, penum->Next( ELEMENTS(rgoszNames), rgstatpropbag, &cFetched )); Check( TRUE, cFetchedExpected == cFetched ); RELEASE_INTERFACE(penum); for( j = 0; j < cFetched; j++ ) { Check( TRUE, !wcscmp(rgoszNames[j], rgstatpropbag[j].lpwstrName) ); delete [] rgstatpropbag[j].lpwstrName; } } // for( i = 0; i < 2; i++ ) // ----------------------------------------------- // Delete the two "/bag/test" properties by prefix // ----------------------------------------------- rgoszDelete[0] = OLESTR("www.microsoft.com/bag/test"); Check( S_OK, pbag->WriteMultiple( ELEMENTS(rgoszNames), rgoszNames, g_rgcpropvarAll )); Check( S_OK, ResetRGPropVar( g_rgcpropvarAll )); DeleteBagExProperties( pbag, rgoszDelete[0] ); Check( S_OK, pbag->Enum( OLESTR(""), 0, &penum )); Check( S_FALSE, penum->Next( ELEMENTS(rgoszNames), rgstatpropbag, &cFetched )); Check( TRUE, 2 == cFetched ); RELEASE_INTERFACE(penum); for( j = 0; j < cFetched; j++ ) { Check( TRUE, !wcscmp(rgoszNames[j+2], rgstatpropbag[j].lpwstrName) ); delete [] rgstatpropbag[j].lpwstrName; } /* // ------------------------- // Delete all the properties // ------------------------- rgoszDelete[0] = NULL; Check( S_OK, pbag->WriteMultiple( ELEMENTS(rgoszNames), rgoszNames, g_rgcpropvarAll )); Check( S_OK, ResetRGPropVar( g_rgcpropvarAll )); Check( S_OK, pbag->Delete( OLESTR(""), DELETEPROPERTY_MASK)); Check( S_OK, pbag->Enum( OLESTR(""), 0, &penum )); Check( S_FALSE, penum->Next( ELEMENTS(rgoszNames), rgstatpropbag, &cFetched )); Check( TRUE, 0 == cFetched ); RELEASE_INTERFACE(penum); */ pbag->Release(); } // test_BagDelete void test_IPropertyBag( IStorage *pstg ) { Status( "IPropertyBag\n" ); IPropertyBagEx *pbagex = NULL; IPropertyBag *pbag = NULL; ULONG cRefsOriginal = GetRefCount( pstg ); Check( S_OK, pstg->QueryInterface( IID_IPropertyBagEx, reinterpret_cast(&pbagex) )); DeleteBagExProperties( pbagex, OLESTR("") ); RELEASE_INTERFACE(pbagex); Check( S_OK, pstg->QueryInterface( IID_IPropertyBag, reinterpret_cast(&pbag) )); VARIANT varWrite, varRead; VariantInit( &varWrite ); VariantInit( &varRead ); varWrite.vt = VT_I4; varWrite.lVal = 1234; Check( S_OK, pbag->Write( OLESTR("Variant I4"), &varWrite )); Check( S_OK, pbag->Read( OLESTR("Variant I4"), &varRead, NULL )); Check( TRUE, varWrite.vt == varRead.vt ); Check( TRUE, varWrite.lVal == varRead.lVal ); Check( cRefsOriginal, RELEASE_INTERFACE(pbag) ); } void test_BagVtUnknown( IStorage *pstg ) { HRESULT hr = S_OK; IPropertyBagEx *pbag = NULL; ULONG cRefsOriginal = 0; Status( "VT_UNKNOWN in an IPropertyBagEx\n" ); pstg->AddRef(); cRefsOriginal = pstg->Release(); Check( S_OK, pstg->QueryInterface( IID_IPropertyBagEx, reinterpret_cast(&pbag) )); DeleteBagExProperties( pbag, OLESTR("") ); CObjectWithPersistStorage *pStgObjectWritten = NULL, *pStgObjectRead = NULL; CObjectWithPersistStream *pStmObjectWritten = NULL, *pStmObjectRead = NULL; pStgObjectWritten = new CObjectWithPersistStorage( OLESTR("VtUnknown-Storage") ); pStgObjectRead = new CObjectWithPersistStorage(); pStmObjectWritten = new CObjectWithPersistStream( OLESTR("VtUnknown-Stream") ); pStmObjectRead = new CObjectWithPersistStream(); Check( TRUE, NULL != pStgObjectWritten && NULL != pStgObjectRead ); Check( TRUE, NULL != pStmObjectWritten && NULL != pStmObjectRead ); VARIANT rgvarRead[2], rgvarWritten[2]; VariantInit( &rgvarRead[0] ); VariantInit( &rgvarRead[1] ); VariantInit( &rgvarWritten[0] ); VariantInit( &rgvarWritten[1] ); rgvarWritten[0].vt = VT_UNKNOWN; rgvarWritten[0].punkVal = static_cast(pStgObjectWritten); rgvarWritten[1].vt = VT_BYREF | VT_UNKNOWN; IUnknown *punkByRefVal = static_cast(pStmObjectWritten); rgvarWritten[1].ppunkVal = &punkByRefVal; rgvarRead[0].vt = VT_UNKNOWN; rgvarRead[0].punkVal = static_cast(pStgObjectRead); rgvarRead[1].vt = VT_UNKNOWN; rgvarRead[1].punkVal = static_cast(pStmObjectRead); OLECHAR *rgoszName[2] = { OLESTR("VtUnknown (persisted as Storage)"), OLESTR("ByRef VtUnknown (persisted as Stream)") }; Check( S_OK, pbag->WriteMultiple( 2, rgoszName, reinterpret_cast(rgvarWritten) )); Check( S_OK, pbag->ReadMultiple( 2, rgoszName, reinterpret_cast(rgvarRead), NULL )); Check( TRUE, *pStgObjectRead == *pStgObjectWritten ); Check( TRUE, *pStmObjectRead == *pStmObjectWritten ); Check( 0, RELEASE_INTERFACE( pStgObjectRead )); Check( 0, RELEASE_INTERFACE( pStmObjectRead )); PROPVARIANT rgpropvarReadRaw[2]; PropVariantInit( &rgpropvarReadRaw[0] ); PropVariantInit( &rgpropvarReadRaw[1] ); Check( S_OK, pbag->ReadMultiple( 2, rgoszName, rgpropvarReadRaw, NULL )); Check( VT_STORED_OBJECT, rgpropvarReadRaw[0].vt ); Check( VT_STREAMED_OBJECT, rgpropvarReadRaw[1].vt ); STATSTG statstg; Check( S_OK, rgpropvarReadRaw[0].pStorage->Stat( &statstg, STATFLAG_NONAME )); Check( TRUE, statstg.clsid == pStgObjectWritten->GetClassID() ); Check( 0, RELEASE_INTERFACE( pStgObjectWritten )); Check( S_OK, PropVariantClear( &rgpropvarReadRaw[0] )); CLSID clsid; ULONG cbRead; Check( S_OK, rgpropvarReadRaw[1].pStream->Read( &clsid, sizeof(clsid), &cbRead )); Check( sizeof(clsid), cbRead ); Check( TRUE, clsid == pStmObjectWritten->GetClassID() ); Check( 0, RELEASE_INTERFACE( pStmObjectWritten )); Check( S_OK, PropVariantClear( &rgpropvarReadRaw[1] )); Check( cRefsOriginal, RELEASE_INTERFACE(pbag) ); } // test_BagVtUnknown void test_BagEnum( IStorage *pstg ) { IPropertyBagEx *pbag = NULL; IEnumSTATPROPBAG *penum = NULL; ULONG cFetched; ULONG i; const OLECHAR * rgoszDelete[] = { OLESTR("") }; const OLECHAR *rgoszNames[] = { OLESTR("www.microsoft.com/bag/test?prop1"), OLESTR("www.microsoft.com/bag/test?prop2"), OLESTR("www.microsoft.com/bag/2test?prop1"), OLESTR("www.microsoft.com/bag2/test?prop1") }; STATPROPBAG rgstatpropbag[ ELEMENTS(rgoszNames) + 1 ]; Status( "Property bag enumeration\n" ); // Initialize the bag Check( S_OK, pstg->QueryInterface( IID_IPropertyBagEx, reinterpret_cast(&pbag) )); DeleteBagExProperties( pbag, OLESTR("") ); Check( S_OK, pbag->WriteMultiple( ELEMENTS(rgoszNames), rgoszNames, g_rgcpropvarAll )); Check( S_OK, ResetRGPropVar( g_rgcpropvarAll )); // Try to enum n+1 elements (to get an S_FALSE) Check( S_OK, pbag->Enum( NULL, 0, &penum )); Check( S_FALSE, penum->Next( ELEMENTS(rgstatpropbag), rgstatpropbag, &cFetched )); Check( TRUE, ELEMENTS(rgoszNames) == cFetched ); for( i = 0; i < cFetched; i++ ) delete [] rgstatpropbag[i].lpwstrName; RELEASE_INTERFACE(penum); // Try to enum n elements (should get an S_OK) Check( S_OK, pbag->Enum( OLESTR(""), 0, &penum )); Check( S_OK, penum->Next( ELEMENTS(rgoszNames), rgstatpropbag, &cFetched )); Check( TRUE, ELEMENTS(rgoszNames) == cFetched ); for( i = 0; i < cFetched; i++ ) delete [] rgstatpropbag[i].lpwstrName; RELEASE_INTERFACE(penum); // Enum a subset Check( S_OK, pbag->Enum( OLESTR("www.microsoft.com/bag/test"), 0, &penum )); Check( S_FALSE, penum->Next( ELEMENTS(rgoszNames), rgstatpropbag, &cFetched )); Check( TRUE, 2 == cFetched ); for( i = 0; i < cFetched; i++ ) delete [] rgstatpropbag[i].lpwstrName; RELEASE_INTERFACE(penum); // Enum a non-extant subset Check( S_OK, pbag->Enum( OLESTR("dummy"), 0, &penum )); Check( S_FALSE, penum->Next( ELEMENTS(rgoszNames), rgstatpropbag, &cFetched )); Check( TRUE, 0 == cFetched ); RELEASE_INTERFACE(penum); // Enum a single property Check( S_OK, pbag->Enum( OLESTR("www.microsoft.com/bag/test?prop1"), 0, &penum )); Check( S_FALSE, penum->Next( 2, rgstatpropbag, &cFetched )); Check( 1, cFetched ); delete[] rgstatpropbag[0].lpwstrName; rgstatpropbag[0].lpwstrName = NULL; RELEASE_INTERFACE(penum); RELEASE_INTERFACE(pbag); } // test_BagEnum void test_BagCoercion( IStorage *pstg ) { IPropertyBag *pbag = NULL; IPropertyBagEx *pbagX = NULL; const OLECHAR *rgosz[2]; PROPVARIANT rgpropvar[2]; Status( "Property bag coercion\n" ); // Get a bag and a bagex, and clean the bag. Check( S_OK, pstg->QueryInterface( IID_IPropertyBag, reinterpret_cast(&pbag) )); Check( S_OK, pstg->QueryInterface( IID_IPropertyBagEx, reinterpret_cast(&pbagX) )); DeleteBagExProperties( pbagX, OLESTR("") ); // Initialize the bag with some properties rgpropvar[0].vt = VT_I2; rgpropvar[0].iVal = 2; rgosz[0] = OLESTR("www.microsoft.com/test/i2"); rgpropvar[1].vt = VT_UI2; rgpropvar[1].uiVal = 3; rgosz[1] = OLESTR("www.microsoft.com/test/ui2"); Check( S_OK, pbagX->WriteMultiple( 2, rgosz, rgpropvar )); g_pfnFreePropVariantArray( 2, rgpropvar ); // Read back the properties as (U)I4s with explicit coercion rgpropvar[0].vt = VT_I4; rgpropvar[1].vt = VT_UI4; Check( S_OK, pbagX->ReadMultiple( 2, rgosz, rgpropvar, NULL )); Check( TRUE, VT_I4 == rgpropvar[0].vt && 2 == rgpropvar[0].lVal ); Check( TRUE, VT_UI4 == rgpropvar[1].vt && 3 == rgpropvar[1].ulVal ); // This is an unrelated test, but while we're here, let's verify that we // can't write a PropVariant (non-Variant) type through the Bag interface. rgpropvar[0].vt= VT_I8; rgpropvar[0].hVal.QuadPart = 1; Check( STG_E_INVALIDPARAMETER, pbag->Write( rgosz[0], reinterpret_cast(&rgpropvar[0]) )); //-------- rgpropvar[0].vt = VT_LPSTR; rgpropvar[0].pszVal = "Hello, world"; rgpropvar[1].vt = VT_I4; rgpropvar[1].iVal = 123; Check( S_OK, pbagX->WriteMultiple( 2, rgosz, rgpropvar )); rgpropvar[0].vt = VT_EMPTY; rgpropvar[0].pszVal = NULL; rgpropvar[1].vt = VT_EMPTY; rgpropvar[1].iVal = -1; Check( S_OK, pbagX->ReadMultiple( 2, rgosz, rgpropvar, NULL )); Check( TRUE, VT_LPSTR == rgpropvar[0].vt && 0==strcmp( "Hello, world", rgpropvar[0].pszVal ) ); Check( TRUE, VT_I4 == rgpropvar[1].vt && 123 == rgpropvar[1].iVal ); g_pfnFreePropVariantArray( 2, rgpropvar ); //-------- Coercing Variant to Variant ------------------ rgpropvar[0].vt = VT_I4; rgpropvar[0].lVal = 123; rgpropvar[1].vt = VT_I4; rgpropvar[1].lVal = 123; Check( S_OK, pbagX->WriteMultiple( 2, rgosz, rgpropvar )); rgpropvar[0].vt = VT_BSTR; rgpropvar[1].vt = VT_I4; Check( S_OK, pbagX->ReadMultiple( 2, rgosz, rgpropvar, NULL )); Check( TRUE, VT_BSTR == rgpropvar[0].vt && !wcscmp( L"123", rgpropvar[0].bstrVal )); Check( TRUE, VT_I4 == rgpropvar[1].vt && 123 == rgpropvar[1].iVal ); g_pfnFreePropVariantArray( 2, rgpropvar ); //-------- Coercing PropVariant To PropVariant ------------ #define TEST_I8_VAL ((LONGLONG)1024*1000*1000*1000+42); rgpropvar[0].vt = VT_LPWSTR; rgpropvar[0].pwszVal = L"-312"; rgpropvar[1].vt = VT_I8; rgpropvar[1].hVal.QuadPart = TEST_I8_VAL; Check( S_OK, pbagX->WriteMultiple( 2, rgosz, rgpropvar )); rgpropvar[0].vt = VT_I4; rgpropvar[0].pszVal = NULL; rgpropvar[1].vt = VT_LPWSTR; rgpropvar[1].hVal.QuadPart = -1; Check( S_OK, pbagX->ReadMultiple(2, rgosz, rgpropvar, NULL ) ); Check( TRUE, VT_I4 == rgpropvar[0].vt && -312 == rgpropvar[0].lVal ); Check( TRUE, VT_LPWSTR == rgpropvar[1].vt && !wcscmp( L"1024000000042", rgpropvar[1].pwszVal ) ); g_pfnFreePropVariantArray( 2, rgpropvar ); //-------- Implcit Coercion PropVariant To Variant ------------ rgpropvar[0].vt = VT_I8; rgpropvar[0].hVal.QuadPart = -666; rgpropvar[1].vt = VT_VECTOR | VT_LPSTR; rgpropvar[1].calpstr.cElems = 5; rgpropvar[1].calpstr.pElems = new LPSTR[5]; rgpropvar[1].calpstr.pElems[0] = "Thirty Days hath September,"; rgpropvar[1].calpstr.pElems[1] = "April, June and No Wonder?"; rgpropvar[1].calpstr.pElems[2] = "All the Rest Have Thirty One"; rgpropvar[1].calpstr.pElems[3] = "Except my dear Grand Mother."; rgpropvar[1].calpstr.pElems[4] = "She Has a Bright Red Tricycle."; Check( S_OK, pbagX->WriteMultiple( 2, rgosz, rgpropvar )); delete rgpropvar[1].calpstr.pElems; PropVariantInit(&rgpropvar[0]); PropVariantInit(&rgpropvar[1]); rgpropvar[0].vt = VT_EMPTY; rgpropvar[1].vt = VT_EMPTY; Check( S_OK, pbag->Read(rgosz[0], (VARIANT*)&rgpropvar[0], NULL ) ); Check( S_OK, pbag->Read(rgosz[1], (VARIANT*)&rgpropvar[1], NULL ) ); Check( TRUE, VT_I4 == rgpropvar[0].vt && -666 == rgpropvar[0].lVal ); Check( TRUE, (VT_BSTR|VT_ARRAY) == rgpropvar[1].vt ); g_pfnFreePropVariantArray( 2, rgpropvar ); // // UnCoercible. // rgpropvar[0].vt = VT_UNKNOWN; rgpropvar[0].iVal = 42; // GARBAGE value; untouched in the error path Check( DISP_E_TYPEMISMATCH, pbagX->ReadMultiple( 1, rgosz, rgpropvar, NULL )); Check( TRUE, VT_UNKNOWN == rgpropvar[0].vt && 42==rgpropvar[0].iVal ); RELEASE_INTERFACE(pbagX); RELEASE_INTERFACE(pbag); } // test_BagCoercion #define LOAD_VARIANT(var,vartype,field,value) (var).field = (value); (var).vt = vartype void test_ByRef( IStorage *pstg ) { HRESULT hr = S_OK; IPropertySetStorage *pPropSetStg = NULL; IPropertyStorage *pPropStg = NULL; FMTID fmtid; Status( "ByRef Variants\n" ); UuidCreate( &fmtid ); Check( S_OK, pstg->QueryInterface( IID_IPropertySetStorage, reinterpret_cast(&pPropSetStg) )); Check( S_OK, pPropSetStg->Create( fmtid, NULL, PROPSETFLAG_DEFAULT, STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, &pPropStg )); PROPVARIANT rgvarWrite[17], rgvarRead[17]; CPropSpec rgcpropspec[17]; BYTE bVal = 1; LOAD_VARIANT(rgvarWrite[0], VT_UI1|VT_BYREF, pbVal, &bVal); rgcpropspec[0] = OLESTR("VT_UI1|VT_BYREF"); SHORT iVal = 2; LOAD_VARIANT(rgvarWrite[1], VT_I2|VT_BYREF, piVal, &iVal ); rgcpropspec[1] = OLESTR("VT_I2|VY_BYREF"); LONG lVal = 3; LOAD_VARIANT(rgvarWrite[2], VT_I4|VT_BYREF, plVal, &lVal ); rgcpropspec[2] = OLESTR("VT_I4|VY_BYREF"); FLOAT fltVal = (float)4.1; LOAD_VARIANT(rgvarWrite[3], VT_R4|VT_BYREF, pfltVal, &fltVal ); rgcpropspec[3] = OLESTR("VT_I4|VT_BYREF"); DOUBLE dblVal = 5.2; LOAD_VARIANT(rgvarWrite[4], VT_R8|VT_BYREF, pdblVal, &dblVal ); rgcpropspec[4] = OLESTR("VT_R8|VT_BYREF"); VARIANT_BOOL boolVal = VARIANT_TRUE; LOAD_VARIANT(rgvarWrite[5], VT_BOOL|VT_BYREF, pboolVal, &boolVal ); rgcpropspec[5] = OLESTR("VT_BOOL|VT_BYREF"); SCODE scode = 6; LOAD_VARIANT(rgvarWrite[6], VT_ERROR|VT_BYREF, pscode, &scode ); rgcpropspec[6] = OLESTR("VT_ERROR|VT_BYREF"); CY cyVal = { 7 }; LOAD_VARIANT(rgvarWrite[7], VT_CY|VT_BYREF, pcyVal, &cyVal ); rgcpropspec[7] = OLESTR("VT_CY|VT_BYREF"); DATE date = 8; LOAD_VARIANT(rgvarWrite[8], VT_DATE|VT_BYREF, pdate, &date ); rgcpropspec[8] = OLESTR("VT_DATE|VT_BYREF"); BSTR bstrVal = SysAllocString( OLESTR("9") ); LOAD_VARIANT(rgvarWrite[9], VT_BSTR|VT_BYREF, pbstrVal, &bstrVal ); rgcpropspec[9] = OLESTR("VT_BSTR|VT_BYREF"); DECIMAL decVal = { 10, 9, 8, 7, 6 }; LOAD_VARIANT(rgvarWrite[10], VT_DECIMAL|VT_BYREF, pdecVal, &decVal ); rgcpropspec[10] = OLESTR("VT_DECIMAL|VT_BYREF"); CHAR cVal = 11; LOAD_VARIANT(rgvarWrite[11], VT_I1 | VT_BYREF, pcVal, &cVal ); rgcpropspec[11] = OLESTR("VT_I1|VT_BYREF"); USHORT uiVal = 12; LOAD_VARIANT(rgvarWrite[12], VT_UI2 | VT_BYREF, puiVal, &uiVal ); rgcpropspec[12] = OLESTR("VT_UI2|VT_BYREF"); ULONG ulVal = 13; LOAD_VARIANT(rgvarWrite[13], VT_UI4 | VT_BYREF, pulVal, &ulVal ); rgcpropspec[13] = OLESTR("VT_UI4|VT_BYREF"); INT intVal = 14; LOAD_VARIANT(rgvarWrite[14], VT_INT | VT_BYREF, pintVal, &intVal ); rgcpropspec[14] = OLESTR("VT_INT|VT_BYREF"); UINT uintVal = 15; LOAD_VARIANT(rgvarWrite[15], VT_UINT | VT_BYREF, puintVal, &uintVal ); rgcpropspec[15] = OLESTR("VT_UINT | VT_BYREF"); CPropVariant cpropvarVal = (long) 16; Check( VT_I4, cpropvarVal.vt ); LOAD_VARIANT(rgvarWrite[16], VT_VARIANT| VT_BYREF, pvarVal, &cpropvarVal ); rgcpropspec[16] = OLESTR("VT_VARIANT | VT_BYREF"); Check( S_OK, pPropStg->WriteMultiple( sizeof(rgvarWrite)/sizeof(rgvarWrite[0]), rgcpropspec, reinterpret_cast(rgvarWrite), PID_FIRST_USABLE )); for( int i = 0; i < sizeof(rgvarRead)/sizeof(rgvarRead[0]); i++ ) PropVariantInit( &rgvarRead[i] ); Check( S_OK, pPropStg->ReadMultiple( sizeof(rgvarRead)/sizeof(rgvarRead[0]), rgcpropspec, reinterpret_cast(rgvarRead) )); Check( VT_UI1, rgvarRead[0].vt ); Check( TRUE, rgvarRead[0].bVal == *rgvarWrite[0].pbVal ); Check( VT_I2, rgvarRead[1].vt ); Check( TRUE, rgvarRead[1].iVal == *rgvarWrite[1].piVal ); Check( VT_I4, rgvarRead[2].vt ); Check( TRUE, rgvarRead[2].lVal == *rgvarWrite[2].plVal ); Check( VT_R4, rgvarRead[3].vt ); Check( TRUE, rgvarRead[3].fltVal == *rgvarWrite[3].pfltVal ); Check( VT_R8, rgvarRead[4].vt ); Check( TRUE, rgvarRead[4].dblVal == *rgvarWrite[4].pdblVal ); Check( VT_BOOL, rgvarRead[5].vt ); Check( TRUE, rgvarRead[5].boolVal == *rgvarWrite[5].pboolVal ); Check( VT_ERROR, rgvarRead[6].vt ); Check( TRUE, rgvarRead[6].scode == *rgvarWrite[6].pscode ); Check( VT_CY, rgvarRead[7].vt ); Check( 0, memcmp( &rgvarRead[7].cyVal, rgvarWrite[7].pcyVal, sizeof(CY) )); Check( VT_DATE, rgvarRead[8].vt ); Check( TRUE, rgvarRead[8].date == *rgvarWrite[8].pdate ); Check( VT_BSTR, rgvarRead[9].vt ); Check( 0, ocscmp( rgvarRead[9].bstrVal, *rgvarWrite[9].pbstrVal )); Check( VT_DECIMAL, rgvarRead[10].vt ); Check( 0, memcmp( &rgvarRead[10].decVal.scale, &rgvarWrite[10].pdecVal->scale, sizeof(decVal) - sizeof(decVal.wReserved) )); Check( VT_I1, rgvarRead[11].vt ); Check( TRUE, rgvarRead[11].cVal == *rgvarWrite[11].pcVal ); Check( VT_UI2, rgvarRead[12].vt ); Check( TRUE, rgvarRead[12].uiVal == *rgvarWrite[12].puiVal ); Check( VT_UI4, rgvarRead[13].vt ); Check( TRUE, rgvarRead[13].ulVal == *rgvarWrite[13].pulVal ); Check( VT_INT, rgvarRead[14].vt ); Check( TRUE, rgvarRead[14].intVal == *rgvarWrite[14].pintVal ); Check( VT_UINT, rgvarRead[15].vt ); Check( TRUE, rgvarRead[15].uintVal == *rgvarWrite[15].puintVal ); Check( VT_I4, rgvarRead[16].vt ); Check( TRUE, rgvarRead[16].lVal == rgvarWrite[16].pvarVal->lVal ); Check( 0, RELEASE_INTERFACE(pPropStg) ); RELEASE_INTERFACE(pPropSetStg); g_pfnFreePropVariantArray( sizeof(rgvarRead)/sizeof(rgvarRead[0]), rgvarRead ); SysFreeString( bstrVal ); } void test_SettingLocalization( IStorage *pstg ) { HRESULT hr = S_OK; IPropertySetStorage *pPropSetStg = NULL; IPropertyStorage *pPropStg = NULL; FMTID fmtid; CPropVariant rgcpropvarWrite[3], rgcpropvarRead[3]; CPropSpec rgcpropspec[3]; ULONG cRefsOriginal = GetRefCount( pstg ); Status( "Changing localization properties\n" ); UuidCreate( &fmtid ); Check( S_OK, pstg->QueryInterface( IID_IPropertySetStorage, reinterpret_cast(&pPropSetStg) )); for( int i = 0; i < 2; i++ ) { // Create a unicode or ansi property set Check( S_OK, pPropSetStg->Create( fmtid, NULL, 0 == i ? PROPSETFLAG_DEFAULT : PROPSETFLAG_ANSI, STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, &pPropStg )); // --------------------------- // Change the codepage to Ansi // --------------------------- // Set the codepage. This should work because it's currently empty // (note that it's also currently Unicode). Set it to GetACP+1 just // to be sure that we can set a non-default codepage. rgcpropspec[0] = PID_CODEPAGE; rgcpropvarWrite[0] = (short) (GetACP() + 1); Check( S_OK, pPropStg->WriteMultiple( 1, rgcpropspec, rgcpropvarWrite, PID_FIRST_USABLE )); Check( S_OK, pPropStg->ReadMultiple( 1, rgcpropspec, rgcpropvarRead )); Check( TRUE, rgcpropvarWrite[0] == rgcpropvarRead[0] ); // Now set the codepage to GetACP so that we can work on it. rgcpropvarWrite[0] = (short) GetACP(); Check( S_OK, pPropStg->WriteMultiple( 1, rgcpropspec, rgcpropvarWrite, PID_FIRST_USABLE )); Check( S_OK, pPropStg->ReadMultiple( 1, rgcpropspec, rgcpropvarRead )); Check( TRUE, rgcpropvarWrite[0] == rgcpropvarRead[0] ); // Write some named properties. The VT_LPSTR shouldn't get converted to Unicode // now that this is an Ansi property set. rgcpropvarWrite[0] = "Hello, world"; rgcpropvarWrite[1].SetBSTR( OLESTR("How are you?") ); rgcpropspec[0] = PID_FIRST_USABLE; rgcpropspec[1] = OLESTR("Second property name"); Check( S_OK, pPropStg->WriteMultiple( 2, rgcpropspec, rgcpropvarWrite, PID_FIRST_USABLE )); Check( S_OK, pPropStg->ReadMultiple( 2, rgcpropspec, rgcpropvarRead )); Check( TRUE, rgcpropvarWrite[0] == rgcpropvarRead[0] ); Check( TRUE, rgcpropvarWrite[1] == rgcpropvarRead[1] ); g_pfnFreePropVariantArray( 2, rgcpropvarRead ); // If we stat the IPropertyStorage, it should call itself Ansi. STATPROPSETSTG statpropsetstg; Check( S_OK, pPropStg->Stat( &statpropsetstg )); Check( PROPSETFLAG_ANSI, PROPSETFLAG_ANSI & statpropsetstg.grfFlags ); // Verify that we can close and re-open it and everything's still the same. Check( 0, RELEASE_INTERFACE(pPropStg) ); Check( S_OK, pPropSetStg->Open( fmtid, STGM_READWRITE|STGM_SHARE_EXCLUSIVE, &pPropStg )); Check( S_OK, pPropStg->ReadMultiple( 2, rgcpropspec, rgcpropvarRead )); Check( TRUE, rgcpropvarWrite[0] == rgcpropvarRead[0] ); Check( TRUE, rgcpropvarWrite[1] == rgcpropvarRead[1] ); g_pfnFreePropVariantArray( 2, rgcpropvarRead ); Check( S_OK, pPropStg->Stat( &statpropsetstg )); Check( PROPSETFLAG_ANSI, PROPSETFLAG_ANSI & statpropsetstg.grfFlags ); // ------------------------------ // Change the codepage to Unicode // ------------------------------ // Clear out the property set. PROPID propidDictionary = PID_DICTIONARY; Check( S_OK, pPropStg->DeleteMultiple( 2, rgcpropspec )); Check( S_OK, pPropStg->DeletePropertyNames( 1, &propidDictionary )); // Switch to Unicode rgcpropvarWrite[0] = (short) CP_WINUNICODE; rgcpropspec[0] = PID_CODEPAGE; Check( S_OK, pPropStg->WriteMultiple( 1, rgcpropspec, rgcpropvarWrite, PID_FIRST_USABLE )); Check( S_OK, pPropStg->ReadMultiple( 1, rgcpropspec, rgcpropvarRead )); Check( TRUE, rgcpropvarRead[0] == rgcpropvarWrite[0] ); // Verify with a Stat Check( S_OK, pPropStg->Stat( &statpropsetstg )); Check( 0, PROPSETFLAG_ANSI & statpropsetstg.grfFlags ); // Write & read some properties again. This time the LPSTR should be converted. rgcpropvarWrite[0] = "Hello, world"; rgcpropvarWrite[1].SetBSTR( OLESTR("How are you?") ); rgcpropspec[0] = PID_FIRST_USABLE; rgcpropspec[1] = OLESTR("Second property name"); Check( S_OK, pPropStg->WriteMultiple( 2, rgcpropspec, rgcpropvarWrite, PID_FIRST_USABLE )); Check( S_OK, pPropStg->ReadMultiple( 2, rgcpropspec, rgcpropvarRead )); Check( TRUE, rgcpropvarWrite[0] == rgcpropvarRead[0] ); Check( TRUE, rgcpropvarWrite[1] == rgcpropvarRead[1] ); g_pfnFreePropVariantArray( 2, rgcpropvarRead ); // Close, reopen, and read/stat again Check( 0, RELEASE_INTERFACE(pPropStg) ); Check( S_OK, pPropSetStg->Open( fmtid, STGM_READWRITE|STGM_SHARE_EXCLUSIVE, &pPropStg )); Check( S_OK, pPropStg->ReadMultiple( 2, rgcpropspec, rgcpropvarRead )); Check( TRUE, rgcpropvarWrite[0] == rgcpropvarRead[0] ); Check( TRUE, rgcpropvarWrite[1] == rgcpropvarRead[1] ); g_pfnFreePropVariantArray( 2, rgcpropvarRead ); Check( S_OK, pPropStg->Stat( &statpropsetstg )); Check( 0, PROPSETFLAG_ANSI & statpropsetstg.grfFlags ); Check( 0, RELEASE_INTERFACE(pPropStg) ); } // for( int i = 0; i < 2; i++ ) // ----------------------- // Validate error checking // ----------------------- // Create a new property set Check( S_OK, pPropSetStg->Create( fmtid, NULL, 0 == i ? PROPSETFLAG_DEFAULT : PROPSETFLAG_ANSI, STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, &pPropStg )); // After writing a property, we shouldn't be able to set the codepage or LCID rgcpropspec[0] = PID_FIRST_USABLE; rgcpropvarWrite[0] = (long) 1234; Check( S_OK, pPropStg->WriteMultiple( 1, rgcpropspec, rgcpropvarWrite, PID_FIRST_USABLE )); rgcpropspec[0] = PID_CODEPAGE; rgcpropvarWrite[0] = (short) 1234; Check( STG_E_INVALIDPARAMETER, pPropStg->WriteMultiple( 1, rgcpropspec, rgcpropvarWrite, PID_FIRST_USABLE )); rgcpropspec[0] = PID_LOCALE; rgcpropvarWrite[0] = (ULONG) 5678; Check( STG_E_INVALIDPARAMETER, pPropStg->WriteMultiple( 1, rgcpropspec, rgcpropvarWrite, PID_FIRST_USABLE )); // But it's settable after deleting the property rgcpropspec[0] = PID_FIRST_USABLE; Check( S_OK, pPropStg->DeleteMultiple( 1, rgcpropspec )); rgcpropspec[0] = PID_CODEPAGE; rgcpropvarWrite[0] = (short) 1234; Check( S_OK, pPropStg->WriteMultiple( 1, rgcpropspec, rgcpropvarWrite, PID_FIRST_USABLE )); rgcpropspec[1] = PID_LOCALE; rgcpropvarWrite[1] = (ULONG) 5678; Check( S_OK, pPropStg->WriteMultiple( 1, &rgcpropspec[1], &rgcpropvarWrite[1], PID_FIRST_USABLE )); Check( S_OK, pPropStg->WriteMultiple( 2, rgcpropspec, rgcpropvarWrite, PID_FIRST_USABLE )); Check( S_OK, pPropStg->ReadMultiple(2, rgcpropspec, rgcpropvarRead )); Check( TRUE, rgcpropvarWrite[0] == rgcpropvarRead[0] ); Check( TRUE, rgcpropvarWrite[1] == rgcpropvarRead[1] ); // But again it's not writable if there's a name in the dictionary rgcpropspec[0] = PID_CODEPAGE; rgcpropvarWrite[0] = (short) CP_WINUNICODE; Check( S_OK, pPropStg->WriteMultiple( 1, rgcpropspec, rgcpropvarWrite, PID_FIRST_USABLE )); PROPID rgpropid[1] = { PID_FIRST_USABLE }; LPOLESTR rglposz[1] = { OLESTR("Hello") }; Check( S_OK, pPropStg->WritePropertyNames( 1, rgpropid, rglposz )); Check( STG_E_INVALIDPARAMETER, pPropStg->WriteMultiple( 1, rgcpropspec, rgcpropvarWrite, PID_FIRST_USABLE )); Check( 0, RELEASE_INTERFACE(pPropStg) ); Check( cRefsOriginal, RELEASE_INTERFACE(pPropSetStg) ); } void test_ExtendedTypes( IStorage *pstg ) { HRESULT hr = S_OK; IPropertySetStorage *pPropSetStg = NULL; IPropertyStorage *pPropStg = NULL; FMTID fmtid; Status( "Extended Types\n" ); UuidCreate( &fmtid ); Check( S_OK, pstg->QueryInterface( IID_IPropertySetStorage, reinterpret_cast(&pPropSetStg) )); Check( S_OK, pPropSetStg->Create( fmtid, NULL, PROPSETFLAG_DEFAULT, STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, &pPropStg )); CPropVariant rgcpropvarWrite[5], rgcpropvarRead[5]; CPropSpec rgcpropspec[5]; rgcpropvarWrite[0] = (CHAR) 1; rgcpropspec[0] = OLESTR("VT_I1"); DECIMAL decVal = { 10, 9, 8, 7, 6 }; rgcpropvarWrite[1] = decVal; rgcpropspec[1] = OLESTR("VT_DECIMAL"); rgcpropvarWrite[2].SetINT( 2 ); rgcpropspec[2] = OLESTR("VT_INT"); rgcpropvarWrite[3].SetUINT( 3 ); rgcpropspec[3] = OLESTR("VT_UINT"); rgcpropvarWrite[4][1] = (CHAR) 2; rgcpropvarWrite[4][0] = (CHAR) 1; rgcpropspec[4] = OLESTR("VT_VECTOR|VT_I1"); Check( S_OK, pPropStg->WriteMultiple( sizeof(rgcpropvarWrite)/sizeof(rgcpropvarWrite[0]), rgcpropspec, rgcpropvarWrite, PID_FIRST_USABLE )); for( int i = 0; i < sizeof(rgcpropvarRead)/sizeof(rgcpropvarRead[0]); i++ ) PropVariantInit( &rgcpropvarRead[i] ); CheckFormatVersion(pPropStg, PROPSET_WFORMAT_EXPANDED_VTS); Check( S_OK, pPropStg->ReadMultiple( sizeof(rgcpropvarRead)/sizeof(rgcpropvarRead[0]), rgcpropspec, reinterpret_cast(rgcpropvarRead) )); Check( rgcpropvarRead[0].vt, rgcpropvarWrite[0].vt ); Check( TRUE, rgcpropvarWrite[0].cVal == rgcpropvarRead[0].cVal ); Check( rgcpropvarRead[1].vt, rgcpropvarWrite[1].vt ); Check( 0, memcmp( &rgcpropvarRead[1].decVal.scale, &rgcpropvarWrite[1].decVal.scale, sizeof(rgcpropvarRead[1].decVal) - sizeof(rgcpropvarRead[1].decVal.wReserved) )); Check( rgcpropvarRead[2].vt, rgcpropvarWrite[2].vt ); Check( rgcpropvarRead[2].intVal, rgcpropvarWrite[2].intVal ); Check( rgcpropvarRead[3].vt, rgcpropvarWrite[3].vt ); Check( rgcpropvarRead[3].uintVal, rgcpropvarWrite[3].uintVal ); Check( TRUE, rgcpropvarRead[4] == rgcpropvarWrite[4] ); Check( 0, RELEASE_INTERFACE(pPropStg) ); RELEASE_INTERFACE(pPropSetStg); } void test_StgOnHandle( OLECHAR *poszDir ) { HRESULT hr = S_OK; OLECHAR oszFile[ MAX_PATH ], oszDir[ MAX_PATH ]; CPropVariant cpropvarWrite, cpropvarRead; HANDLE hFile = INVALID_HANDLE_VALUE; HANDLE hDir = INVALID_HANDLE_VALUE; IPropertyBagEx *pbag = NULL; Status( "StgOpenStorageOnHandle\n" ); ocscpy( oszFile, poszDir ); ocscat( oszFile, OLESTR("test_StgOnHandle") ); ocscpy( oszDir, poszDir ); ocscat( oszDir, OLESTR("test_StgOnHandle Dir") ); // Create a storage and put a property in it. Check( S_OK, g_pfnStgCreateStorageEx( oszFile, STGM_CREATE|STGM_SHARE_EXCLUSIVE|STGM_READWRITE, DetermineStgFmt( g_enumImplementation ), 0, NULL, NULL, IID_IPropertyBagEx, reinterpret_cast(&pbag) )); OLECHAR *poszPropName = OLESTR("Prop Name"); cpropvarWrite = (long) 123; // VT_I4 Check( S_OK, pbag->WriteMultiple( 1, &poszPropName, &cpropvarWrite )); Check( 0, RELEASE_INTERFACE(pbag) ); // Create a directory and put a property in it too. Check( TRUE, CreateDirectory( oszDir, NULL )); hDir = CreateFile( oszDir, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, INVALID_HANDLE_VALUE ); Check( TRUE, INVALID_HANDLE_VALUE != hDir ); Check( S_OK, g_pfnStgOpenStorageOnHandle( hDir, STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, NULL, IID_IPropertyBagEx, reinterpret_cast(&pbag) )); CloseHandle( hDir ); Check( S_OK, pbag->WriteMultiple( 1, &poszPropName, &cpropvarWrite )); Check( 0, RELEASE_INTERFACE(pbag) ); // Open the file and read the properties hFile = CreateFile( oszFile, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL ); Check( TRUE, INVALID_HANDLE_VALUE != hFile ); Check( S_OK, g_pfnStgOpenStorageOnHandle( hFile, STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, NULL, IID_IPropertyBagEx, reinterpret_cast(&pbag) )); CloseHandle( hFile ); PropVariantClear( &cpropvarRead ); Check( S_OK, pbag->ReadMultiple( 1, &poszPropName, &cpropvarRead, NULL )); Check( TRUE, cpropvarRead == cpropvarWrite ); Check( 0, RELEASE_INTERFACE(pbag) ); // Open the directory and read the properties hFile = CreateFile( oszDir, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL ); Check( TRUE, INVALID_HANDLE_VALUE != hFile ); Check( S_OK, g_pfnStgOpenStorageOnHandle( hFile, STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, NULL, IID_IPropertyBagEx, reinterpret_cast(&pbag) )); CloseHandle( hFile ); PropVariantClear( &cpropvarRead ); Check( S_OK, pbag->ReadMultiple( 1, &poszPropName, &cpropvarRead, NULL )); Check( TRUE, cpropvarRead == cpropvarWrite ); Check( 0, RELEASE_INTERFACE(pbag) ); } void test_PropsetOnEmptyFile( OLECHAR *poszDir ) { HRESULT hr = S_OK; OLECHAR oszFile[ MAX_PATH ]; CPropVariant cpropvarWrite, cpropvarRead; HANDLE hFile = INVALID_HANDLE_VALUE; IPropertySetStorage *pset = NULL; // We only run this test for NFF property sets; there's special code there // for the case of a read-only open of an empty file. if( PROPIMP_NTFS != g_enumImplementation ) return; Status( "Empty file\n" ); ocscpy( oszFile, poszDir ); ocscat( oszFile, OLESTR("test_PropsetOnEmptyFile") ); // Create a file hFile = CreateFile( oszFile, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE ); Check( FALSE, INVALID_HANDLE_VALUE == hFile ); CloseHandle( hFile ); // Get a read-only property interface on the file. Check( S_OK, StgOpenStorageEx( oszFile, STGM_READ|STGM_SHARE_DENY_WRITE, STGFMT_ANY, 0, NULL, NULL, IID_IPropertySetStorage, reinterpret_cast(&pset) )); Check( 0, RELEASE_INTERFACE(pset) ); } void test_PropsetOnHGlobal() { HANDLE hglobal = NULL; IPropertyStorage *pPropStg = NULL; IStream *pStm = NULL; Status( "StgCreate/OpenPropStg on CreateStreamOnHGlobal\n" ); // Build up an IPropertyStorage on a memory block hglobal = GlobalAlloc( GHND, 0 ); Check( FALSE, NULL == hglobal ); Check( S_OK, CreateStreamOnHGlobal( hglobal, FALSE, &pStm )); hglobal = NULL; Check( S_OK, StgCreatePropStg( (IUnknown*) pStm, FMTID_NULL, &CLSID_NULL, PROPSETFLAG_DEFAULT, 0L, // Reserved &pPropStg )); // Write a Unicode string property to the property set CPropVariant rgcpropvarWrite[2] = { L"First Value", L"Second Value" }; CPropSpec rgcpropspec[2] = { L"First Name", L"Second Name" }; Check( S_OK, pPropStg->WriteMultiple( 1, &rgcpropspec[0], &rgcpropvarWrite[0], PID_FIRST_USABLE )); // Close the IPropertyStorage and IStream. Check( S_OK, pPropStg->Commit( STGC_DEFAULT )); // Flush to pStm Check( 0, RELEASE_INTERFACE(pPropStg) ); Check( S_OK, GetHGlobalFromStream( pStm, &hglobal )); Check( 0, RELEASE_INTERFACE(pStm) ); // Reopen everything Check( S_OK, CreateStreamOnHGlobal( hglobal, FALSE, &pStm )); hglobal = NULL; Check( S_OK, StgOpenPropStg( (IUnknown*) pStm, FMTID_NULL, PROPSETFLAG_DEFAULT, 0L, // Reserved &pPropStg )); // Write another property Check( S_OK, pPropStg->WriteMultiple( 1, &rgcpropspec[1], &rgcpropvarWrite[1], PID_FIRST_USABLE )); // Read and verify the properties CPropVariant rgcpropvarRead[2]; Check( S_OK, pPropStg->ReadMultiple( 2, rgcpropspec, rgcpropvarRead )); Check( TRUE, rgcpropvarRead[0] == rgcpropvarWrite[0] ); Check( TRUE, rgcpropvarRead[1] == rgcpropvarWrite[1] ); Check( 0, RELEASE_INTERFACE(pPropStg) ); Check( S_OK, GetHGlobalFromStream( pStm, &hglobal )); Check( 0, RELEASE_INTERFACE(pStm) ); // Reopen everything using a read-only stream. Check( S_OK, CreateStreamOnHGlobal( hglobal, TRUE, &pStm )); hglobal = NULL; CReadOnlyStream ReadOnlyStream( pStm ); Check( S_OK, StgOpenPropStg( (IUnknown*) &ReadOnlyStream, FMTID_NULL, PROPSETFLAG_DEFAULT, 0L, // Reserved &pPropStg )); Check( STG_E_ACCESSDENIED, pPropStg->WriteMultiple( 1, &rgcpropspec[1], &rgcpropvarWrite[1], PID_FIRST_USABLE )); Check( 0, RELEASE_INTERFACE(pPropStg) ); Check( 0, RELEASE_INTERFACE(pStm) ); } void test_SafeArray( IStorage *pstg ) { HRESULT hr = S_OK; IPropertySetStorage *pPropSetStg = NULL; IPropertyStorage *pPropStg = NULL; FMTID fmtid; ULONG crefpstg = 0; Status( "SafeArrays\n" ); UuidCreate( &fmtid ); pstg->AddRef(); crefpstg = pstg->Release(); // Get an IPropertyStorage from the input IStorage Check( S_OK, pstg->QueryInterface( IID_IPropertySetStorage, reinterpret_cast(&pPropSetStg) )); Check( S_OK, pPropSetStg->Create( fmtid, NULL, PROPSETFLAG_DEFAULT, STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, &pPropStg )); SAFEARRAY *rgpsa[] = { NULL, NULL, NULL }; //, NULL, NULL }; CPropVariant *rgcpropvar = NULL; SAFEARRAYBOUND rgsaBounds[] = { {2,0}, {3,10}, {4,20} }; // [0..1], [10..12], [20..23] ULONG cDims = sizeof(rgsaBounds)/sizeof(rgsaBounds[0]); ULONG cElems = 0; // Create three SafeArrays to test a fixed sized type, a variable sized type // (which is also ByRef), and a Variant. rgpsa[0] = SafeArrayCreate( VT_I4, 3, rgsaBounds ); // Try both Create and CreateEx Check( TRUE, NULL != rgpsa[0] ); rgpsa[1] = SafeArrayCreateEx( VT_BSTR, 3, rgsaBounds, NULL ); Check( TRUE, NULL != rgpsa[1] ); rgpsa[2] = SafeArrayCreateEx( VT_VARIANT, 3, rgsaBounds, NULL ); Check( TRUE, NULL != rgpsa[2] ); /* rgpsa[3] = SafeArrayCreateEx( VT_I8, 3, rgsaBounds, NULL ); Check( TRUE, NULL != rgpsa[3] ); rgpsa[4] = SafeArrayCreateEx( VT_UI8, 3, rgsaBounds, NULL ); Check( TRUE, NULL != rgpsa[4] ); */ // Determine how many elements are in the SafeArrays, and alloc that // many PropVariants. We'll need this for the SafeArray of Variants. cElems = CalcSafeArrayElementCount( rgpsa[0] ); rgcpropvar = new CPropVariant[ cElems ]; Check( FALSE, NULL == rgcpropvar ); // Fill in each of the SafeArrays. for( ULONG i = 0; i < cElems; i++ ) { LONG rgIndices[3]; // Map this this element from linear space to bounds space CalcSafeArrayIndices( i, rgIndices, rgsaBounds, cDims ); // Add an I4 LONG lVal = static_cast(i); Check( S_OK, SafeArrayPutElement( rgpsa[0], rgIndices, &lVal )); // Add a BSTR BSTR bstrVal = SysAllocString( OLESTR("0 BSTR Val") ); *bstrVal = OLESTR('0') + static_cast(i); Check( S_OK, SafeArrayPutElement( rgpsa[1], rgIndices, bstrVal )); // Add a PropVariant that could be an I4 or a BSTR if( i & 1 ) rgcpropvar[i] = (long) i; else rgcpropvar[i].SetBSTR( bstrVal ); // Copies string Check( S_OK, SafeArrayPutElement( rgpsa[2], rgIndices, &rgcpropvar[i] )); // The SafeArrays have copied the BSTR, so we can free our local copy SysFreeString( bstrVal ); // Add I8/UI8 /* LONGLONG llVal = i; Check( S_OK, SafeArrayPutElement( rgpsa[3], rgIndices, &llVal )); llVal += 1000; Check( S_OK, SafeArrayPutElement( rgpsa[4], rgIndices, &llVal )); */ } VARIANT rgvarWrite[3], rgvarRead[3]; PROPVARIANT rgpropvarCopy[3]; CPropSpec rgcpropspec[3]; // Load the SafeArrays into PropVariants LOAD_VARIANT(rgvarWrite[0], VT_ARRAY|VT_I4, parray, rgpsa[0] ); rgcpropspec[0] = OLESTR("VT_ARRAY|VT_I4"); LOAD_VARIANT(rgvarWrite[1], VT_BYREF|VT_ARRAY|VT_BSTR, pparray, &rgpsa[1] ); rgcpropspec[1] = OLESTR("VT_BYREF|VT_ARRAY|VT_BSTR"); LOAD_VARIANT(rgvarWrite[2], VT_ARRAY|VT_VARIANT, parray, rgpsa[2] ); rgcpropspec[2] = OLESTR("VT_ARRAY|VT_VARIANT"); /* LOAD_VARIANT(rgvarWrite[3], VT_ARRAY|VT_I8, parray, rgpsa[3] ); rgcpropspec[3] = OLESTR("VT_ARRAY|VT_I8"); LOAD_VARIANT(rgvarWrite[4], VT_ARRAY|VT_UI8, parray, rgpsa[4] ); rgcpropspec[4] = OLESTR("VT_ARRAY|VT_UI8"); */ // Write the PropVariant SafeArrays and verify that the propset version in the // header gets incremented. Check( S_OK, pPropStg->WriteMultiple( sizeof(rgvarWrite)/sizeof(rgvarWrite[0]), rgcpropspec, reinterpret_cast(rgvarWrite), PID_FIRST_USABLE )); CheckFormatVersion(pPropStg, PROPSET_WFORMAT_EXPANDED_VTS); // Test PropVariantCopy by copying each of the PropVariants and comparing the result. for( i = 0; i < sizeof(rgvarRead)/sizeof(rgvarRead[0]); i++ ) { PropVariantInit( &rgpropvarCopy[i] ); Check( S_OK, g_pfnPropVariantCopy( &rgpropvarCopy[i], reinterpret_cast(&rgvarWrite[i]) )); Check( rgpropvarCopy[i].vt, rgvarWrite[i].vt ); if( VT_BYREF & rgpropvarCopy[i].vt ) CompareSafeArrays( *rgpropvarCopy[i].pparray, *rgvarWrite[i].pparray ); else CompareSafeArrays( rgpropvarCopy[i].parray, rgvarWrite[i].parray ); // As long as we're looping, let's start init-ing the Read array too. VariantInit( &rgvarRead[i] ); } // Read back the values that we wrote. Check( S_OK, pPropStg->ReadMultiple( sizeof(rgvarRead)/sizeof(rgvarRead[0]), rgcpropspec, reinterpret_cast(rgvarRead) )); // Validate the Read values. For the second one, the byref should no longer // be set. Check( rgvarWrite[0].vt, rgvarRead[0].vt ); CompareSafeArrays( rgvarWrite[0].parray, rgvarRead[0].parray ); Check( 0, rgvarRead[1].vt & VT_BYREF ); Check( rgvarWrite[1].vt, rgvarRead[1].vt|VT_BYREF ); CompareSafeArrays( *rgvarWrite[1].pparray, rgvarRead[1].parray ); Check( rgvarWrite[2].vt, rgvarRead[2].vt ); CompareSafeArrays( rgvarWrite[2].parray, rgvarRead[2].parray ); /* Check( rgvarWrite[3].vt, rgvarRead[3].vt ); CompareSafeArrays( rgvarWrite[3].parray, rgvarRead[3].parray ); Check( rgvarWrite[4].vt, rgvarRead[4].vt ); CompareSafeArrays( rgvarWrite[4].parray, rgvarRead[4].parray ); */ // Free the safearrays (they're in rgvarWrite, but we don't clear that). Check( S_OK, SafeArrayDestroy( rgpsa[0] )); Check( S_OK, SafeArrayDestroy( rgpsa[1] )); Check( S_OK, SafeArrayDestroy( rgpsa[2] )); /* Check( S_OK, SafeArrayDestroy( rgpsa[3] )); Check( S_OK, SafeArrayDestroy( rgpsa[4] )); */ Check( S_OK, g_pfnFreePropVariantArray( sizeof(rgpropvarCopy)/sizeof(rgpropvarCopy[0]), reinterpret_cast(rgpropvarCopy) )); Check( S_OK, g_pfnFreePropVariantArray( sizeof(rgvarRead)/sizeof(rgvarRead[0]), reinterpret_cast(rgvarRead) )); Check( S_OK, g_pfnFreePropVariantArray( cElems, rgcpropvar )); // ------------------------------------------------------ // Verify that we can't write a safearray with a bad type // ------------------------------------------------------ LONG rgIndices[] = { 0 }; VARIANT *pvar; rgpsa[0] = SafeArrayCreateVector( VT_VARIANT, 0, 1 ); Check( TRUE, NULL != rgpsa[0] ); SafeArrayPtrOfIndex( rgpsa[0], rgIndices, reinterpret_cast(&pvar) ); pvar->vt = VT_STREAM; rgcpropvar[0].vt = VT_ARRAY | VT_VARIANT; rgcpropvar[0].parray = rgpsa[0]; rgpsa[0] = NULL; // In NT5, this returned HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), which was // the error that StgConvertVariantToPropertyNoEH got from SafeArrayGetVartype. // In Whistler, SafeArrayGetVartype is returning success, so the error doesn't // get caught until the recursive call to StgConvertVariantToPropertyNoEH, // which returns STATUS_INVALID_PARAMETER, which gets translated into // STG_E_INVALIDPARAMETER. Check( STG_E_INVALIDPARAMETER, pPropStg->WriteMultiple( 1, rgcpropspec, rgcpropvar, PID_FIRST_USABLE )); // Clear the propvar we just used (which also destroys the safearray) Check( S_OK, g_pfnPropVariantClear( &rgcpropvar[0] ) ); Check( S_OK, pPropStg->WriteMultiple( 2, rgcpropspec, rgcpropvar, PID_FIRST_USABLE )); Check( S_OK, g_pfnFreePropVariantArray( 2, rgcpropvar )); Check( 0, RELEASE_INTERFACE(pPropStg) ); Check( crefpstg, RELEASE_INTERFACE(pPropSetStg) ); delete[] rgcpropvar; } void test_ReadOnlyReservedProperties( IStorage *pStg ) { IPropertySetStorage *pPropSetStg = NULL; IPropertyStorage *pPropStg = NULL; CPropVariant cpropvar = L"Property Value"; CPropSpec cpropspec; FMTID fmtid; ULONG cRefsOriginal = GetRefCount(pStg); Status( "Read-only reserved PROPIDs\n" ); UuidCreate( &fmtid ); Check( S_OK, pStg->QueryInterface( IID_IPropertySetStorage, reinterpret_cast(&pPropSetStg) )); Check( S_OK, pPropSetStg->Create( fmtid, NULL, PROPSETFLAG_DEFAULT, STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, &pPropStg )); cpropspec = PID_BEHAVIOR + 1; Check( STG_E_INVALIDPARAMETER, pPropStg->WriteMultiple( 1, &cpropspec, &cpropvar, PID_FIRST_USABLE )); cpropspec = PID_MAX_READONLY; Check( STG_E_INVALIDPARAMETER, pPropStg->WriteMultiple( 1, &cpropspec, &cpropvar, PID_FIRST_USABLE )); cpropspec = PID_MAX_READONLY + 1; Check( S_OK, pPropStg->WriteMultiple( 1, &cpropspec, &cpropvar, PID_FIRST_USABLE )); Check( 0, RELEASE_INTERFACE(pPropStg) ); Check( cRefsOriginal, RELEASE_INTERFACE(pPropSetStg) ); } void test_LowMemory( IStorage *pstg ) { HRESULT hr = S_OK; IPropertySetStorage *psetstg = NULL; IPropertyStorage *ppropstg = NULL; IStorageTest *ptest = NULL; CPropSpec rgcpropspec[2]; CPropVariant rgcpropvarWrite[2], rgcpropvarRead[2]; int i; Status( "Low-memory mapped stream code\n" ); Check( S_OK, pstg->QueryInterface( IID_IPropertySetStorage, reinterpret_cast(&psetstg) )); for( i = 0; i < 2; i++ ) { DWORD propsetflag = i == 0 ? PROPSETFLAG_DEFAULT : PROPSETFLAG_NONSIMPLE; FMTID fmtid; UuidCreate( &fmtid ); Check( S_OK, psetstg->Create( fmtid, NULL, propsetflag, STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, &ppropstg )); // Go into low-memory mode hr = ppropstg->QueryInterface( IID_IStorageTest, reinterpret_cast(&ptest) ); if( SUCCEEDED(hr) ) hr = ptest->SimulateLowMemory( TRUE ); // IStorageTest isn't available in a free build. As of this writing // it's not available in docfile. if( E_NOINTERFACE == hr ) { Status( " ... Partially skipping, IStorageTest not available\n" ); continue; } else Check( S_OK, hr ); // Write and read properties rgcpropspec[0] = OLESTR("First property"); rgcpropvarWrite[0] = "Hello, world"; rgcpropspec[1] = OLESTR("Second property"); rgcpropvarWrite[1] = "How are you?"; Check( S_OK, ppropstg->WriteMultiple( 2, rgcpropspec, rgcpropvarWrite, PID_FIRST_USABLE )); Check( S_OK, ppropstg->ReadMultiple( 2, rgcpropspec, rgcpropvarRead )); Check( TRUE, rgcpropvarWrite[0] == rgcpropvarRead[0] ); Check( TRUE, rgcpropvarWrite[1] == rgcpropvarRead[1] ); g_pfnFreePropVariantArray( 2, rgcpropvarRead ); // Write, commit, and read g_pfnFreePropVariantArray( 2, rgcpropvarWrite ); rgcpropvarWrite[0] = CBlob( L"go blue" ); rgcpropvarWrite[1] = static_cast(fmtid); Check( S_OK, ppropstg->WriteMultiple( 2, rgcpropspec, rgcpropvarWrite, PID_FIRST_USABLE )); Check( S_OK, ppropstg->ReadMultiple( 2, rgcpropspec, rgcpropvarRead )); Check( TRUE, rgcpropvarWrite[0] == rgcpropvarRead[0] ); Check( TRUE, rgcpropvarWrite[1] == rgcpropvarRead[1] ); g_pfnFreePropVariantArray( 2, rgcpropvarRead ); Check( S_OK, ppropstg->Commit( STGC_DEFAULT )); Check( S_OK, ppropstg->ReadMultiple( 2, rgcpropspec, rgcpropvarRead )); Check( TRUE, rgcpropvarWrite[0] == rgcpropvarRead[0] ); Check( TRUE, rgcpropvarWrite[1] == rgcpropvarRead[1] ); g_pfnFreePropVariantArray( 2, rgcpropvarRead ); // Write, close, reopen, and read g_pfnFreePropVariantArray( 2, rgcpropvarWrite ); rgcpropvarWrite[0] = 0.1234; rgcpropvarWrite[1] = CClipData("Hi"); Check( S_OK, ppropstg->WriteMultiple( 2, rgcpropspec, rgcpropvarWrite, PID_FIRST_USABLE )); RELEASE_INTERFACE(ptest); RELEASE_INTERFACE(ppropstg); Check( S_OK, psetstg->Open( fmtid, STGM_READ|STGM_SHARE_EXCLUSIVE, &ppropstg )); Check( S_OK, ppropstg->ReadMultiple( 2, rgcpropspec, rgcpropvarRead )); Check( TRUE, rgcpropvarWrite[0] == rgcpropvarRead[0] ); Check( TRUE, rgcpropvarWrite[1] == rgcpropvarRead[1] ); g_pfnFreePropVariantArray( 2, rgcpropvarRead ); RELEASE_INTERFACE(ppropstg); } // for( i = 0; i < 2; i++ ) //Exit: RELEASE_INTERFACE(ptest); RELEASE_INTERFACE(ppropstg); RELEASE_INTERFACE(psetstg); } void test_BagOpenMethod( IStorage *pstg ) { HRESULT hr = S_OK; IPropertyBagEx *pbag = NULL; OLECHAR * rgoszDelete[2]; CPropVariant cpropvar; PROPVARIANT propvar; VERSIONEDSTREAM VersionedStream; GUID guidVersion2; OLECHAR *pwszName = { OLESTR("Versioned Stream") }; IUnknown *punk = NULL; IStream *pstm = NULL; CHAR rgbStreamDataWrite[50] = "Stream data"; CHAR rgbStreamDataRead[100]; ULONG cbRead; STATSTG statstg; Status( "IPropertyBagEx::Open\n" ); Check( S_OK, pstg->QueryInterface( IID_IPropertyBagEx, reinterpret_cast(&pbag) )); // Create a VersionedStream UuidCreate( &VersionedStream.guidVersion ); VersionedStream.pStream = NULL; cpropvar = VersionedStream; // Write the versioned stream (causing a stream to be created) and read it back. Check( S_OK, pbag->WriteMultiple( 1, &pwszName, &cpropvar )); cpropvar.Clear(); Check( S_OK, pbag->ReadMultiple( 1, &pwszName, &cpropvar, NULL )); Check( TRUE, VT_VERSIONED_STREAM == cpropvar.VarType() && NULL != cpropvar.pVersionedStream->pStream ); // Put some data in the stream and release it. Check( S_OK, cpropvar.pVersionedStream->pStream->Write( rgbStreamDataWrite, sizeof(rgbStreamDataWrite), NULL )); cpropvar.Clear(); // Now read that VersionedStream, with the proper GUID Check( S_OK, pbag->Open( NULL, pwszName, VersionedStream.guidVersion, 0, IID_IStream, &punk )); Check( S_OK, punk->QueryInterface( IID_IStream, reinterpret_cast(&pstm) )); // Verify the data. Check( S_OK, pstm->Read( rgbStreamDataRead, sizeof(rgbStreamDataRead), &cbRead )); Check( TRUE, cbRead == sizeof(rgbStreamDataWrite) ); Check( TRUE, 0 == strcmp( rgbStreamDataWrite, rgbStreamDataRead )); RELEASE_INTERFACE(pstm); RELEASE_INTERFACE(punk); // Attempt to read the same VersionedStream with a bad GUID UuidCreate( &guidVersion2 ); Check( STG_E_FILEALREADYEXISTS, pbag->Open( NULL, pwszName, guidVersion2, 0, IID_IStream, &punk )); // Attempt with a bad guid again, but this time cause a new property to be created. Check( S_OK, pbag->Open( NULL, pwszName, guidVersion2, OPENPROPERTY_OVERWRITE, IID_IStream, &punk )); Check( S_OK, punk->QueryInterface( IID_IStream, reinterpret_cast(&pstm) )); Check( S_OK, pstm->Stat( &statstg, STATFLAG_NONAME )); Check( TRUE, CULargeInteger(0) == statstg.cbSize ); RELEASE_INTERFACE(pstm); RELEASE_INTERFACE(punk); // Show that we can overwrite an existing property of a different type, but only // by setting the overwrite flag. cpropvar = static_cast(45); Check( S_OK, pbag->WriteMultiple( 1, &pwszName, &cpropvar )); Check( STG_E_FILEALREADYEXISTS, pbag->Open( NULL, pwszName, guidVersion2, 0, IID_IStream, &punk )); Check( S_OK, pbag->Open( NULL, pwszName, guidVersion2, OPENPROPERTY_OVERWRITE, IID_IStream, &punk )); RELEASE_INTERFACE(punk); // Show that if a property doesn't exist, Open creates it. Check( S_OK, pbag->DeleteMultiple( 1, &pwszName, 0 )); PropVariantClear( &cpropvar ); Check( S_FALSE, pbag->ReadMultiple( 1, &pwszName, &cpropvar, NULL )); Check( S_OK, pbag->Open( NULL, pwszName, guidVersion2, 0, IID_IStream, &punk )); RELEASE_INTERFACE(punk); RELEASE_INTERFACE(pbag); } // test_BagOpenMethod void test_StandaloneAPIs( LPOLESTR ocsDir ) { OLECHAR ocsFile[ MAX_PATH + 1 ]; FMTID fmtidStgPropStg, fmtidStgPropSetStg; IStorage *pstg = NULL; //TSafeStorage< IStorage > pstg; IStream *pstmInMemory = NULL; IStorage *pstgInMemory = NULL; IPropertySetStorage *ppropsetstg = NULL; //TSafeStorage< IPropertySetStorage > ppropsetstg; CPropVariant rgcpropvar[ CPROPERTIES_ALL ]; IPropertySetStorage *pPropSetStg; IPropertyStorage *pPropStg; DWORD propsetflag; ULONG cPropertiesAll; ULONG ulIndex; Status( "Standalone API test\n" ); // Generate FMTIDs. UuidCreate( &fmtidStgPropStg ); UuidCreate( &fmtidStgPropSetStg ); // Generate a filename from the directory name. ocscpy( ocsFile, ocsDir ); ocscat( ocsFile, OLESTR( "IPropAPIs.stg" )); // Create a storage. Check( S_OK, g_pfnStgCreateStorageEx( ocsFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, DetermineStgFmt( g_enumImplementation ), 0L, NULL, NULL, DetermineStgIID( g_enumImplementation ), (void**) &pstg )); // Run the following part of the test twice; once for a simple // property set and once for a non-simple. for( int i = 0; i < 2; i++ ) { ILockBytes *pLockBytes = NULL; IStorage *pstgInMemory = NULL; #ifdef _MAC Handle hglobal; hglobal = NewHandle( 0 ); #else HANDLE hglobal; hglobal = GlobalAlloc( GPTR, 0 ); #endif Check( TRUE, NULL != hglobal ); if( 0 == i ) { // Create simple IPropertyStorage Check(S_OK, CreateStreamOnHGlobal( hglobal, TRUE, &pstmInMemory )); Check( S_OK, g_pfnStgCreatePropStg( (IUnknown*) pstmInMemory, fmtidStgPropStg, &CLSID_NULL, PROPSETFLAG_ANSI, 0L, // Reserved &pPropStg )); } else { // If we're not allowed to do non-simple, skip out now. if( (RESTRICT_SIMPLE_ONLY & g_Restrictions) || (RESTRICT_NON_HIERARCHICAL & g_Restrictions) ) { break; } // Create a non-simple IPropertyStorage Check( S_OK, CreateILockBytesOnHGlobal( hglobal, TRUE, &pLockBytes )); Check( S_OK, StgCreateDocfileOnILockBytes( pLockBytes, STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 0, &pstgInMemory )); Check( S_OK, g_pfnStgCreatePropStg( (IUnknown*) pstgInMemory, fmtidStgPropStg, &CLSID_NULL, PROPSETFLAG_ANSI | PROPSETFLAG_NONSIMPLE, 0, &pPropStg )); } // Write to the property set. Check( S_OK, pPropStg->WriteMultiple( 0 == i ? CPROPERTIES_ALL_SIMPLE : CPROPERTIES_ALL, g_rgcpropspecAll, g_rgcpropvarAll, PID_FIRST_USABLE )); Check( S_OK, ResetRGPropVar( g_rgcpropvarAll )); // Read from the property set Check( S_OK, pPropStg->ReadMultiple( 0 == i ? CPROPERTIES_ALL_SIMPLE : CPROPERTIES_ALL, g_rgcpropspecAll, rgcpropvar )); // Compare the properties for( ulIndex = 0; 0 == i ? (ulIndex < CPROPERTIES_ALL_SIMPLE) : (ulIndex < CPROPERTIES_ALL); ulIndex++ ) { Check( TRUE, rgcpropvar[ulIndex] == g_rgcpropvarAll[ulIndex] ); rgcpropvar[ulIndex].Clear(); } pPropStg->Release(); pPropStg = NULL; // ------------------- // Test StgOpenPropStg // ------------------- // Open the IPropertyStorage Check( S_OK, g_pfnStgOpenPropStg( 0 == i ? (IUnknown*) pstmInMemory : (IUnknown*) pstgInMemory, fmtidStgPropStg, PROPSETFLAG_DEFAULT | (0 == i ? 0 : PROPSETFLAG_NONSIMPLE), 0L, // Reserved &pPropStg )); // Read from the property set Check( S_OK, pPropStg->ReadMultiple( 0 == i ? CPROPERTIES_ALL_SIMPLE : CPROPERTIES_ALL, g_rgcpropspecAll, rgcpropvar )); // Compare the properties for( ulIndex = 0; 0 == i ? (ulIndex < CPROPERTIES_ALL_SIMPLE) : (ulIndex < CPROPERTIES_ALL); ulIndex++ ) { Check( TRUE, rgcpropvar[ulIndex] == g_rgcpropvarAll[ulIndex] ); rgcpropvar[ulIndex].Clear(); } pPropStg->Release(); pPropStg = NULL; RELEASE_INTERFACE( pstmInMemory ); RELEASE_INTERFACE( pstgInMemory ); RELEASE_INTERFACE( pLockBytes ); } // -------------------------------- // Test StgCreatePropSetStg::Create // -------------------------------- // This is equivalent to the previous tests, but // uses StgCreatePropSetStg to create an IPropertySetStorage, // and uses that to create a property set. // Create the IPropertySetStorage Check( S_OK, g_pfnStgCreatePropSetStg( pstg, 0L, // Reserved &pPropSetStg )); // Create an IPropertyStorage. Create it non-simple, unless the underlying // IStorage (i.e. NTFS) doesn't support it. if( (RESTRICT_SIMPLE_ONLY & g_Restrictions) || (RESTRICT_NON_HIERARCHICAL & g_Restrictions) ) { propsetflag = PROPSETFLAG_DEFAULT; cPropertiesAll = CPROPERTIES_ALL_SIMPLE; } else { propsetflag = PROPSETFLAG_NONSIMPLE; cPropertiesAll = CPROPERTIES_ALL; } Check( S_OK, pPropSetStg->Create( fmtidStgPropSetStg, &CLSID_NULL, propsetflag, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &pPropStg )); // Write to the property set. Check( S_OK, pPropStg->WriteMultiple( cPropertiesAll, g_rgcpropspecAll, g_rgcpropvarAll, PID_FIRST_USABLE )); Check( S_OK, ResetRGPropVar( g_rgcpropvarAll )); //----------------------------------------------------------------------- // Close it all up and then open it again. // This will exercise the g_pfnStgOpenStorageEx API // pPropStg->Commit(STGC_DEFAULT); pPropStg->Release(); pPropStg = NULL; pPropSetStg->Release(); pPropSetStg = NULL; pstg->Release(); pstg = NULL; Check( S_OK, g_pfnStgOpenStorageEx( ocsFile, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, STGFMT_ANY, //DetermineStgFmt( g_enumImplementation ), // BUGBUG: Use STGFMT_ANY when StgEx can handle it 0L, NULL, NULL, IID_IPropertySetStorage, (void**) &pPropSetStg )); Check( S_OK, pPropSetStg->Open( fmtidStgPropSetStg, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &pPropStg)); //pPropSetStg->Release(); // //----------------------------------------------------------------------- // // Read from the property set // Check( S_OK, pPropStg->ReadMultiple( cPropertiesAll, g_rgcpropspecAll, rgcpropvar )); // Compare the properties for( ulIndex = 0; ulIndex < cPropertiesAll; ulIndex++ ) { Check( TRUE, rgcpropvar[ulIndex] == g_rgcpropvarAll[ulIndex] ); rgcpropvar[ulIndex].Clear(); } // Clean up RELEASE_INTERFACE( pPropStg ); RELEASE_INTERFACE( pPropSetStg ); } // // IPropertySetStorage tests // void test_IPropertySetStorage_IUnknown(IStorage *pStorage) { // Only use this an IStorage-based property set, since this test // assumes that IStorage & IPropertySetStorage are on the same // object. if( PROPIMP_DOCFILE_IPROP == g_enumImplementation ) { return; } Status( "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; HRESULT hr=S_OK; 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(DetermineStgIID( g_enumImplementation ), (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(); // void *pvVirtFuncTable = *(void**)ppss3; ppss3->Release(); // Check(STG_E_INVALIDHANDLE, ((IPropertySetStorage*)&pvVirtFuncTable)->QueryInterface(IID_IUnknown, (void**)&punk3)); } #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 ) { Status( "PropVariant Validation\n" ); IPropertySetStorage *pPSStg = NULL; // TSafeStorage< IPropertySetStorage > pPSStg( pStg ); IPropertyStorage *pPStg = NULL; // TSafeStorage< IPropertyStorage > pPStg; CPropVariant cpropvar; CLIPDATA clipdata; PROPSPEC propspec; const LPWSTR wszText = L"Unicode Text String"; FMTID fmtid; UuidCreate( &fmtid ); Check( S_OK, StgToPropSetStg( pStg, &pPSStg )); 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( 0, RELEASE_INTERFACE(pPStg) ); RELEASE_INTERFACE(pPSStg); } void test_ParameterValidation(IStorage *pStg) { // We only run this test on WIN32 builds, because we need // the VirtualAlloc routine. #ifdef WIN32 Status( "Parameter Validation\n" ); IPropertySetStorage *pPSStg = NULL; IPropertyStorage *pPStg = NULL; FMTID fmtid; UuidCreate( &fmtid ); LPFMTID pfmtidNULL = NULL; LPFMTID pfmtidInvalid = (LPFMTID) INVALID_POINTER; PAPP_COMPAT_INFO pAppCompatInfo = NULL; DWORD dwOldProtect; Check( S_OK, StgToPropSetStg( pStg, &pPSStg )); // By default, pointer validation is turned off in ole32 (as of Whistler). // Enable it for our process so that we can test the checks. PAPP_COMPAT_INFO pAppCompatInfoSave = (PAPP_COMPAT_INFO) NtCurrentPeb()->AppCompatInfo; APP_COMPAT_INFO AppCompatInfoNew; memset( &AppCompatInfoNew, 0, sizeof(AppCompatInfoNew) ); AppCompatInfoNew.CompatibilityFlags.QuadPart |= KACF_OLE32VALIDATEPTRS; NtCurrentPeb()->AppCompatInfo = &AppCompatInfoNew; NtCurrentPeb()->AppCompatFlags.QuadPart = AppCompatInfoNew.CompatibilityFlags.QuadPart; // Define two invalid property names OLECHAR oszTooLongName[ CCH_MAXPROPNAMESZ + 1 ]; LPOLESTR poszTooLongName = oszTooLongName; OLECHAR oszTooShortName[] = { L"" }; LPOLESTR poszTooShortName = oszTooShortName; PROPSPEC propspecTooLongName = { PRSPEC_LPWSTR }; PROPSPEC propspecTooShortName = { PRSPEC_LPWSTR }; propspecTooLongName.lpwstr = oszTooLongName; propspecTooShortName.lpwstr = oszTooShortName; for( int i = 0; i < sizeof(oszTooLongName)/sizeof(oszTooLongName[0]); i++ ) oszTooLongName[i] = OLESTR('a'); oszTooLongName[ sizeof(oszTooLongName)/sizeof(oszTooLongName[0]) ] = OLESTR('\0'); // Define several arrays which will be created with special // protections. For all of this vectors, the first element // will be in a page to which we have all access rights. The // second element will be in a page for which we have no access, // read access, or all access. The variables are named // according to the access rights in the second element. // The '...Base' variables are pointers to the base of // the allocated memory (and must therefore be freed). // The corresponding variables without the "Base" postfix // are the vector pointers. PROPSPEC *rgpropspecNoAccessBase, *rgpropspecNoAccess; CPropVariant *rgcpropvarReadAccessBase, *rgcpropvarReadAccess; CPropVariant *rgcpropvarNoAccessBase, *rgcpropvarNoAccess; PROPID *rgpropidNoAccessBase, *rgpropidNoAccess; PROPID *rgpropidReadAccessBase, *rgpropidReadAccess; LPWSTR *rglpwstrNoAccessBase, *rglpwstrNoAccess; LPWSTR *rglpwstrReadAccessBase, *rglpwstrReadAccess; STATPROPSETSTG *rgStatPSStgReadAccessBase, *rgStatPSStgReadAccess; STATPROPSTG *rgStatPStgReadAccessBase, *rgStatPStgReadAccess; PROPSPEC rgpropspecAllAccess[1]; CPropVariant rgcpropvarAllAccess[1]; PROPID rgpropidAllAccess[1]; LPWSTR rglpwstrAllAccess[1]; LPWSTR rglpwstrInvalid[1]; STATPROPSETSTG rgStatPSStgAllAccess[1]; STATPROPSTG rgStatPStgAllAccess[1]; // Allocate memory for the vectors and set the vector // pointers. PROPID propidDefault = PID_FIRST_USABLE; LPWSTR lpwstrNameDefault = L"Property Name"; Check(TRUE, Alloc2PageVector( &rgpropspecNoAccessBase, &rgpropspecNoAccess, (ULONG) PAGE_NOACCESS, (PROPSPEC*) NULL )); Check(TRUE, Alloc2PageVector( &rgcpropvarReadAccessBase, &rgcpropvarReadAccess, (ULONG) PAGE_READONLY, (CPropVariant*) NULL )); Check(TRUE, Alloc2PageVector( &rgcpropvarNoAccessBase, &rgcpropvarNoAccess, (ULONG) PAGE_NOACCESS, (CPropVariant*) NULL )); Check(TRUE, Alloc2PageVector( &rgpropidNoAccessBase, &rgpropidNoAccess, (ULONG) PAGE_NOACCESS, &propidDefault )); Check(TRUE, Alloc2PageVector( &rgpropidReadAccessBase, &rgpropidReadAccess, (ULONG) PAGE_READONLY, &propidDefault )); Check(TRUE, Alloc2PageVector( &rglpwstrNoAccessBase, &rglpwstrNoAccess, (ULONG) PAGE_NOACCESS, &lpwstrNameDefault )); Check(TRUE, Alloc2PageVector( &rglpwstrReadAccessBase, &rglpwstrReadAccess, (ULONG) PAGE_READONLY, &lpwstrNameDefault )); Check(TRUE, Alloc2PageVector( &rgStatPSStgReadAccessBase, &rgStatPSStgReadAccess, (ULONG) PAGE_READONLY, (STATPROPSETSTG*) NULL )); Check(TRUE, Alloc2PageVector( &rgStatPStgReadAccessBase, &rgStatPStgReadAccess, (ULONG) PAGE_READONLY, (STATPROPSTG*) NULL )); rglpwstrAllAccess[0] = rglpwstrNoAccess[0] = rglpwstrReadAccess[0] = L"Property Name"; // Create restricted buffers for misc tests BYTE *pbReadOnly = (BYTE*) VirtualAlloc( NULL, 1, MEM_COMMIT, PAGE_READONLY ); Check( TRUE, pbReadOnly != NULL ); BYTE *pbNoAccess = (BYTE*) VirtualAlloc( NULL, 1, MEM_COMMIT, PAGE_NOACCESS ); // ---------------------------------------- // Test IPropertySetStorage::QueryInterface // ---------------------------------------- IUnknown *pUnk = NULL; #if 0 // This test cannot run because CPropertySetStorage::QueryInterface is a virtual // function, and since CExposedDocFile is derived from CPropertySetStorage, // it is inaccessibl. // Invalid REFIID Check(E_INVALIDARG, ((CExposedDocFile*)&pPSStg)->CPropertySetStorage::QueryInterface( (REFIID) *pfmtidNULL, (void**)&pUnk )); Check(E_INVALIDARG, pPSStg->QueryInterface( (REFIID) *pfmtidInvalid, (void**)&pUnk )); // Invalid IUnknown* Check(E_INVALIDARG, pPSStg->QueryInterface( IID_IUnknown, NULL )); Check(E_INVALIDARG, pPSStg->QueryInterface( IID_IUnknown, (void**) INVALID_POINTER )); #endif // -------------------------------- // Test IPropertySetStorage::Create // -------------------------------- // Invalid REFFMTID Check(E_INVALIDARG, pPSStg->Create( *pfmtidNULL, NULL, PROPSETFLAG_DEFAULT, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &pPStg )); Check(E_INVALIDARG, pPSStg->Create( *pfmtidInvalid, NULL, PROPSETFLAG_DEFAULT, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &pPStg )); // Invalid Class ID pointer Check(E_INVALIDARG, pPSStg->Create( FMTID_NULL, (GUID*) INVALID_POINTER, PROPSETFLAG_DEFAULT, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &pPStg )); // Invalid PropSetFlag Check(STG_E_INVALIDFLAG, pPSStg->Create( FMTID_NULL, &CLSID_NULL, PROPSETFLAG_UNBUFFERED, // Only supported in APIs STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &pPStg )); Check(STG_E_INVALIDFLAG, pPSStg->Create( FMTID_NULL, &CLSID_NULL, 0xffffffff, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &pPStg )); // Invalid mode Check(STG_E_INVALIDFLAG, pPSStg->Create( FMTID_NULL, &CLSID_NULL, PROPSETFLAG_DEFAULT, STGM_DIRECT | STGM_SHARE_DENY_NONE, &pPStg )); // Invalid IPropertyStorage** Check(E_INVALIDARG, pPSStg->Create( FMTID_NULL, &CLSID_NULL, PROPSETFLAG_DEFAULT, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, NULL )); Check(E_INVALIDARG, pPSStg->Create( FMTID_NULL, &CLSID_NULL, PROPSETFLAG_DEFAULT, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, (IPropertyStorage **) INVALID_POINTER )); // ------------------------------ // Test IPropertySetStorage::Open // ------------------------------ // Invalid REFFMTID Check(E_INVALIDARG, pPSStg->Open( *pfmtidNULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &pPStg )); Check(E_INVALIDARG, pPSStg->Open( *pfmtidInvalid, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &pPStg )); Check(STG_E_INVALIDFLAG, pPSStg->Open( FMTID_NULL, STGM_DIRECT | STGM_SHARE_DENY_NONE, &pPStg )); // Invalid IPropertyStorage** Check(E_INVALIDARG, pPSStg->Open( FMTID_NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, NULL )); Check(E_INVALIDARG, pPSStg->Open( FMTID_NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, (IPropertyStorage**) INVALID_POINTER )); // -------------------------------- // Test IPropertySetStorage::Delete // -------------------------------- // Invalid REFFMTID. Check(E_INVALIDARG, pPSStg->Delete( *pfmtidNULL )); Check(E_INVALIDARG, pPSStg->Delete( (REFFMTID) *pfmtidInvalid )); // ------------------------------ // Test IPropertySetStorage::Enum // ------------------------------ // Invalid IEnumSTATPROPSETSTG Check(E_INVALIDARG, pPSStg->Enum( (IEnumSTATPROPSETSTG **) NULL )); Check(E_INVALIDARG, pPSStg->Enum( (IEnumSTATPROPSETSTG **) INVALID_POINTER )); // ------------- // Test PROPSPEC // ------------- // Create a PropertyStorage Check(S_OK, pPSStg->Create( fmtid, NULL, PROPSETFLAG_DEFAULT, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &pPStg )); // Invalid ulKind rgpropspecAllAccess[0].ulKind = (ULONG) -1; rgpropspecAllAccess[0].lpwstr = NULL; Check(E_INVALIDARG, pPStg->ReadMultiple( 1, rgpropspecAllAccess, rgcpropvarAllAccess )); Check(E_INVALIDARG, pPStg->WriteMultiple( 1, rgpropspecAllAccess, rgcpropvarAllAccess, 2 )); Check(E_INVALIDARG, pPStg->DeleteMultiple( 1, rgpropspecAllAccess )); // Too short PROPSPEC rgpropspecNoAccess[0].ulKind = PRSPEC_PROPID; rgpropspecNoAccess[0].propid = 2; Check(E_INVALIDARG, pPStg->ReadMultiple( 2, rgpropspecNoAccess, rgcpropvarAllAccess )); Check(E_INVALIDARG, pPStg->WriteMultiple( 2, rgpropspecNoAccess, rgcpropvarAllAccess, 2 )); Check(E_INVALIDARG, pPStg->DeleteMultiple( 2, rgpropspecNoAccess )); // ------------------------------------- // Test IPropertyStorage::QueryInterface // ------------------------------------- // Invalid REFIID Check(E_INVALIDARG, pPStg->QueryInterface( (REFIID) *pfmtidNULL, (void**)&pUnk )); Check(E_INVALIDARG, pPStg->QueryInterface( (REFIID) *pfmtidInvalid, (void**)&pUnk )); // Invalid IUnknown* Check(E_INVALIDARG, pPStg->QueryInterface( IID_IUnknown, NULL )); Check(E_INVALIDARG, pPStg->QueryInterface( IID_IUnknown, (void**) INVALID_POINTER )); // ----------------------------------- // Test IPropertyStorage::ReadMultiple // ----------------------------------- rgpropspecAllAccess[0].ulKind = PRSPEC_LPWSTR; rgpropspecAllAccess[0].lpwstr = OLESTR("Test Property"); // Too short count Check(S_FALSE, pPStg->ReadMultiple( 0, rgpropspecAllAccess, rgcpropvarAllAccess)); // Too long a count for the PropVariant Check(E_INVALIDARG, pPStg->ReadMultiple( 2, rgpropspecAllAccess, (PROPVARIANT*) (void*) rgcpropvarReadAccess )); // Invalid PropVariant[] Check(E_INVALIDARG, pPStg->ReadMultiple( 1, rgpropspecAllAccess, NULL )); Check(E_INVALIDARG, pPStg->ReadMultiple( 1, rgpropspecAllAccess, (LPPROPVARIANT) INVALID_POINTER )); // Bad PROPSPECs // If we ever add a version-0 property set compatibility mode, we should add this test back. // Check(STG_E_INVALIDPARAMETER, pPStg->ReadMultiple( 1, &propspecTooLongName, rgcpropvarAllAccess )); Check(STG_E_INVALIDPARAMETER, pPStg->ReadMultiple( 1, &propspecTooShortName, rgcpropvarAllAccess )); // ------------------------------------ // Test IPropertyStorage::WriteMultiple // ------------------------------------ rgpropspecAllAccess[0].ulKind = PRSPEC_LPWSTR; rgpropspecAllAccess[0].lpwstr = L"Test Property"; rgcpropvarAllAccess[0] = (long) 1; // Too short count Check(S_OK, pPStg->WriteMultiple( 0, rgpropspecAllAccess, (PROPVARIANT*)(void*)rgcpropvarAllAccess, 2)); // Too short PropVariant Check(E_INVALIDARG, pPStg->WriteMultiple( 2, rgpropspecAllAccess, (PROPVARIANT*)(void*)rgcpropvarNoAccess, PID_FIRST_USABLE )); // Invalid PropVariant[] Check(E_INVALIDARG, pPStg->WriteMultiple( 1, rgpropspecAllAccess, NULL, 2)); Check(E_INVALIDARG, pPStg->WriteMultiple( 1, rgpropspecAllAccess, (LPPROPVARIANT) INVALID_POINTER, PID_FIRST_USABLE)); // Bad PROPSPECs // If we ever add a version-0 property set compatibility mode, we should add this test back. // Check(STG_E_INVALIDPARAMETER, pPStg->WriteMultiple( 1, &propspecTooLongName, rgcpropvarAllAccess, // PID_FIRST_USABLE )); Check(STG_E_INVALIDPARAMETER, pPStg->WriteMultiple( 1, &propspecTooShortName, rgcpropvarAllAccess, PID_FIRST_USABLE )); // ------------------------------------- // Test IPropertyStorage::DeleteMultiple // ------------------------------------- // Invalid count Check(S_OK, pPStg->DeleteMultiple( 0, rgpropspecAllAccess )); // Bad PROPSPECs // If we ever add a version-0 property set compatibility mode, we should add this test back. // Check(STG_E_INVALIDPARAMETER, pPStg->DeleteMultiple( 1, &propspecTooLongName )); Check(STG_E_INVALIDPARAMETER, pPStg->DeleteMultiple( 1, &propspecTooShortName )); // ---------------------------------------- // Test IPropertyStorage::ReadPropertyNames // ---------------------------------------- // Create a property with the name we're going to use. rgpropspecAllAccess[0].ulKind = PRSPEC_LPWSTR; rgpropspecAllAccess[0].lpwstr = rglpwstrAllAccess[0]; Check(S_OK, pPStg->WriteMultiple( 1, rgpropspecAllAccess, &rgcpropvarAllAccess[0], PID_FIRST_USABLE )); // Invalid count Check(S_FALSE, pPStg->ReadPropertyNames( 0, rgpropidAllAccess, rglpwstrAllAccess )); // Too short PROPID[] or LPWSTR[] Check(E_INVALIDARG, pPStg->ReadPropertyNames( 2, rgpropidNoAccess, rglpwstrAllAccess )); Check(E_INVALIDARG, pPStg->ReadPropertyNames( 2, rgpropidAllAccess, rglpwstrReadAccess )); // Invalid rgpropid[] Check(E_INVALIDARG, pPStg->ReadPropertyNames( 1, NULL, rglpwstrAllAccess )); Check(E_INVALIDARG, pPStg->ReadPropertyNames( 1, (PROPID*) INVALID_POINTER, rglpwstrAllAccess )); // Invalid rglpwstr[] Check(E_INVALIDARG, pPStg->ReadPropertyNames( 1, rgpropidAllAccess, NULL )); Check(E_INVALIDARG, pPStg->ReadPropertyNames( 1, rgpropidAllAccess, (LPWSTR*) INVALID_POINTER )); // ----------------------------------------- // Test IPropertyStorage::WritePropertyNames // ----------------------------------------- // Invalid count Check(S_OK, pPStg->WritePropertyNames( 0, NULL, rglpwstrAllAccess )); // Too short PROPID[] or LPWSTR[] Check(E_INVALIDARG, pPStg->WritePropertyNames( 2, rgpropidNoAccess, rglpwstrAllAccess )); Check(E_INVALIDARG, pPStg->WritePropertyNames( 2, rgpropidAllAccess, rglpwstrNoAccess )); Check(S_OK, pPStg->WritePropertyNames( 2, rgpropidAllAccess, rglpwstrReadAccess )); // Invalid rgpropid[] Check(E_INVALIDARG, pPStg->WritePropertyNames( 1, NULL, rglpwstrAllAccess )); Check(E_INVALIDARG, pPStg->WritePropertyNames( 1, (PROPID*) INVALID_POINTER, rglpwstrAllAccess )); // Invalid rglpwstr[] Check(E_INVALIDARG, pPStg->WritePropertyNames( 1, rgpropidAllAccess, NULL )); Check(E_INVALIDARG, pPStg->WritePropertyNames( 1, rgpropidAllAccess, (LPWSTR*) INVALID_POINTER )); // Invalid name. rglpwstrInvalid[0] = NULL; Check(E_INVALIDARG, pPStg->WritePropertyNames( 1, rgpropidAllAccess, rglpwstrInvalid )); rglpwstrInvalid[0] = (LPWSTR) INVALID_POINTER; Check(E_INVALIDARG, pPStg->WritePropertyNames( 1, rgpropidAllAccess, rglpwstrInvalid )); // Invalid length names // If we ever add a version-0 property set compatibility mode, we should add this test back. // Check(STG_E_INVALIDPARAMETER, pPStg->WritePropertyNames( 1, rgpropidAllAccess, &poszTooLongName )); Check(STG_E_INVALIDPARAMETER, pPStg->WritePropertyNames( 1, rgpropidAllAccess, &poszTooShortName )); // ------------------------------------------ // Test IPropertyStorage::DeletePropertyNames // ------------------------------------------ // Invalid count Check(S_OK, pPStg->DeletePropertyNames( 0, rgpropidAllAccess )); // Too short PROPID[] Check(E_INVALIDARG, pPStg->DeletePropertyNames( 2, rgpropidNoAccess )); Check(S_OK, pPStg->DeletePropertyNames( 2, rgpropidReadAccess )); // Invalid rgpropid[] Check(E_INVALIDARG, pPStg->DeletePropertyNames( 1, NULL )); Check(E_INVALIDARG, pPStg->DeletePropertyNames( 1, (PROPID*) INVALID_POINTER )); // --------------------------- // Test IPropertyStorage::Enum // --------------------------- // Invalid IEnumSTATPROPSTG Check(E_INVALIDARG, pPStg->Enum( NULL )); Check(E_INVALIDARG, pPStg->Enum( (IEnumSTATPROPSTG**) INVALID_POINTER )); // -------------------------------------- // Test IPropertyStorage::SetElementTimes // -------------------------------------- Check(E_INVALIDARG, pPStg->SetTimes( (FILETIME*) INVALID_POINTER, NULL, NULL )); Check(E_INVALIDARG, pPStg->SetTimes( NULL, (FILETIME*) INVALID_POINTER, NULL )); Check(E_INVALIDARG, pPStg->SetTimes( NULL, NULL, (FILETIME*) INVALID_POINTER )); // ------------------------------- // Test IPropertyStorage::SetClass // ------------------------------- Check(E_INVALIDARG, pPStg->SetClass( (REFCLSID) *pfmtidNULL )); Check(E_INVALIDARG, pPStg->SetClass( (REFCLSID) *pfmtidInvalid )); // --------------------------- // Test IPropertyStorage::Stat // --------------------------- Check(E_INVALIDARG, pPStg->Stat( NULL )); Check(E_INVALIDARG, pPStg->Stat( (STATPROPSETSTG*) INVALID_POINTER )); // ------------------------------ // Test IEnumSTATPROPSETSTG::Next // ------------------------------ ULONG cEltFound; IEnumSTATPROPSETSTG *pESPSStg = NULL; // TSafeStorage< IEnumSTATPROPSETSTG > pESPSStg; Check(S_OK, pPSStg->Enum( &pESPSStg )); // Invalid STATPROPSETSTG* Check(E_INVALIDARG, pESPSStg->Next( 1, NULL, &cEltFound )); Check(E_INVALIDARG, pESPSStg->Next( 1, (STATPROPSETSTG*) INVALID_POINTER, &cEltFound )); // Invalid pceltFound Check(S_OK, pESPSStg->Next( 1, rgStatPSStgAllAccess, NULL )); Check(STG_E_INVALIDPARAMETER, pESPSStg->Next( 2, rgStatPSStgAllAccess, NULL )); Check(E_INVALIDARG, pESPSStg->Next( 2, rgStatPSStgAllAccess, (ULONG*) INVALID_POINTER )); // Too short STATPROPSETSTG[] Check(E_INVALIDARG, pESPSStg->Next( 2, rgStatPSStgReadAccess, &cEltFound )); // ------------------------------- // Test IEnumSTATPROPSETSTG::Clone // ------------------------------- // Invalid IEnumSTATPROPSETSTG** Check(E_INVALIDARG, pESPSStg->Clone( NULL )); Check(E_INVALIDARG, pESPSStg->Clone( (IEnumSTATPROPSETSTG**) INVALID_POINTER )); // --------------------------- // Test IEnumSTATPROPSTG::Next // --------------------------- IEnumSTATPROPSTG *pESPStg = NULL; // TSafeStorage< IEnumSTATPROPSTG > pESPStg; Check(S_OK, pPStg->Enum( &pESPStg )); // Invalid STATPROPSETSTG* Check(E_INVALIDARG, pESPStg->Next( 1, NULL, &cEltFound )); Check(E_INVALIDARG, pESPStg->Next( 1, (STATPROPSTG*) INVALID_POINTER, &cEltFound )); // Invalid pceltFound Check(S_OK, pESPStg->Next( 1, rgStatPStgAllAccess, NULL )); Check(STG_E_INVALIDPARAMETER, pESPStg->Next( 2, rgStatPStgAllAccess, NULL )); Check(E_INVALIDARG, pESPStg->Next( 2, rgStatPStgAllAccess, (ULONG*) INVALID_POINTER )); // Too short STATPROPSTG[] Check(E_INVALIDARG, pESPStg->Next( 2, rgStatPStgReadAccess, &cEltFound )); // ---------------------------- // Test IEnumSTATPROPSTG::Clone // ---------------------------- // Invalid IEnumSTATPROPSETSTG** Check(E_INVALIDARG, pESPStg->Clone( NULL )); Check(E_INVALIDARG, pESPStg->Clone( (IEnumSTATPROPSTG**) INVALID_POINTER )); // -------------------------------------------- // Test PropStgNameToFmtId & FmtIdToPropStgName // -------------------------------------------- // We're done with the IPropertyStorage and IPropertySetStorage // now, but we need the pointers for some calls below, so let's // free them now. RELEASE_INTERFACE(pPStg); RELEASE_INTERFACE(pPSStg); RELEASE_INTERFACE(pESPStg); // In some cases we can't test these APIs, so only test them // if we have the function pointers. if( g_pfnPropStgNameToFmtId && g_pfnFmtIdToPropStgName ) { OLECHAR oszPropStgName[ CCH_MAX_PROPSTG_NAME+1 ]; FMTID fmtidPropStgName = FMTID_NULL; // Validate the FMTID parm Check( E_INVALIDARG, g_pfnPropStgNameToFmtId( oszPropStgName, pfmtidNULL )); Check( E_INVALIDARG, g_pfnPropStgNameToFmtId( oszPropStgName, pfmtidInvalid )); Check( E_INVALIDARG, g_pfnPropStgNameToFmtId( oszPropStgName, (FMTID*) pbReadOnly )); Check( E_INVALIDARG, g_pfnFmtIdToPropStgName( pfmtidNULL, oszPropStgName )); Check( E_INVALIDARG, g_pfnFmtIdToPropStgName( pfmtidInvalid, oszPropStgName )); Check( S_OK, g_pfnFmtIdToPropStgName( (FMTID*) pbReadOnly, oszPropStgName )); // Validate the name parameter /* Check( STG_E_INVALIDNAME, g_pfnPropStgNameToFmtId( NULL, &fmtidPropStgName )); Check( STG_E_INVALIDNAME, g_pfnPropStgNameToFmtId( (LPOLESTR) INVALID_POINTER, &fmtidPropStgName )); Check( STG_E_INVALIDNAME, g_pfnPropStgNameToFmtId( (LPOLESTR) pbNoAccess, &fmtidPropStgName)); Check( S_OK, g_pfnPropStgNameToFmtId( (LPOLESTR) pbReadOnly, &fmtidPropStgName )); Check( E_INVALIDARG, g_pfnFmtIdToPropStgName( &fmtidPropStgName, NULL )); Check( E_INVALIDARG, g_pfnFmtIdToPropStgName( &fmtidPropStgName, (LPOLESTR) INVALID_POINTER )); Check( E_INVALIDARG, g_pfnFmtIdToPropStgName( &fmtidPropStgName, (LPOLESTR) pbReadOnly )); */ } // if( g_pfnPropStgNameToFmtId && g_pfnFmtIdToPropStgName ) // ------------------------------------------ // Test StgCreatePropStg, StgOpenPropStg APIs // ------------------------------------------ // In some cases we can't test these APIs, so only test them // if we have the function pointers. if( g_pfnStgCreatePropSetStg && g_pfnStgCreatePropStg && g_pfnStgOpenPropStg && !(RESTRICT_SIMPLE_ONLY & g_Restrictions) ) { FMTID fmtidPropStgName = FMTID_NULL; IStream *pStm = NULL; // TSafeStorage< IStream > pStm; // We need a Stream for one of the tests. Check( S_OK, pStg->CreateStream( OLESTR( "Parameter Validation" ), STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0L, 0L, &pStm )); // Test the IUnknown Check( E_INVALIDARG, g_pfnStgCreatePropStg( NULL, fmtidPropStgName, NULL, PROPSETFLAG_DEFAULT, 0, &pPStg )); Check( E_INVALIDARG, g_pfnStgOpenPropStg( NULL, fmtidPropStgName, PROPSETFLAG_DEFAULT, 0L, &pPStg )); // Test the FMTID Check( E_INVALIDARG, g_pfnStgCreatePropStg( (IUnknown*) pStm, *pfmtidNULL, NULL, PROPSETFLAG_DEFAULT, 0, &pPStg )); Check( E_INVALIDARG, g_pfnStgOpenPropStg( (IUnknown*) pStm, *pfmtidNULL, PROPSETFLAG_DEFAULT, 0, &pPStg )); Check( E_INVALIDARG, g_pfnStgCreatePropStg( (IUnknown*) pStm, *pfmtidInvalid, NULL, PROPSETFLAG_DEFAULT, 0, &pPStg )); Check( E_INVALIDARG, g_pfnStgOpenPropStg( (IUnknown*) pStm, *pfmtidInvalid, PROPSETFLAG_DEFAULT, 0, &pPStg )); // Test the CLSID Check( E_INVALIDARG, g_pfnStgCreatePropStg( (IUnknown*) pStm, fmtidPropStgName, (CLSID*) pfmtidInvalid, PROPSETFLAG_DEFAULT, 0, &pPStg )); // Test grfFlags Check( STG_E_INVALIDFLAG, g_pfnStgCreatePropStg( (IUnknown*) pStm, fmtidPropStgName, NULL, 0x8000, 0L, &pPStg )); Check( STG_E_INVALIDFLAG, g_pfnStgOpenPropStg( (IUnknown*) pStm, fmtidPropStgName, 0x8000, 0L, &pPStg )); Check( E_NOINTERFACE, g_pfnStgCreatePropStg( (IUnknown*) pStm, fmtidPropStgName, NULL, PROPSETFLAG_NONSIMPLE, 0L, &pPStg )); Check( E_NOINTERFACE, g_pfnStgCreatePropStg( (IUnknown*) pStg, fmtidPropStgName, NULL, PROPSETFLAG_DEFAULT, 0L, &pPStg )); Check( E_NOINTERFACE, g_pfnStgOpenPropStg( (IUnknown*) pStm, fmtidPropStgName, PROPSETFLAG_NONSIMPLE, 0L, &pPStg )); Check( E_NOINTERFACE, g_pfnStgOpenPropStg( (IUnknown*) pStg, fmtidPropStgName, PROPSETFLAG_DEFAULT , 0L, &pPStg )); // Test IPropertyStorage** Check( E_INVALIDARG, g_pfnStgCreatePropStg( (IUnknown*) pStm, fmtidPropStgName, NULL, PROPSETFLAG_DEFAULT, 0L, NULL )); Check( E_INVALIDARG, g_pfnStgOpenPropStg( (IUnknown*) pStm, fmtidPropStgName, PROPSETFLAG_DEFAULT, 0L, NULL )); Check( E_INVALIDARG, g_pfnStgCreatePropStg( (IUnknown*) pStm, fmtidPropStgName, NULL, PROPSETFLAG_DEFAULT, 0L, (IPropertyStorage**) INVALID_POINTER )); Check( E_INVALIDARG, g_pfnStgOpenPropStg( (IUnknown*) pStm, fmtidPropStgName, PROPSETFLAG_DEFAULT, 0L, (IPropertyStorage**) INVALID_POINTER )); Check( E_INVALIDARG, g_pfnStgCreatePropStg( (IUnknown*) pStm, fmtidPropStgName, NULL, PROPSETFLAG_DEFAULT, 0L, (IPropertyStorage**) pbReadOnly )); Check( E_INVALIDARG, g_pfnStgOpenPropStg( (IUnknown*) pStm, fmtidPropStgName, PROPSETFLAG_DEFAULT, 0L, (IPropertyStorage**) pbReadOnly )); RELEASE_INTERFACE(pStm); } // if( g_pfnStgCreatePropSetStg && g_pfnStgCreatePropStg && g_pfnStgOpenPropStg ) // If we're not using IStorage::QueryInterface to get an IPropertySetStorage, // we must be using the new APIs, so let's test them. // ---------------------------- // Test StgCreatePropSetStg API // ---------------------------- // In some cases we can't test these APIs, so only test them // if we have the function pointers. if( g_pfnStgCreatePropSetStg && g_pfnStgCreatePropStg && g_pfnStgOpenPropStg ) { // Test the IStorage* Check( E_INVALIDARG, g_pfnStgCreatePropSetStg( NULL, 0L, &pPSStg )); Check( E_INVALIDARG, g_pfnStgCreatePropSetStg( (IStorage*) INVALID_POINTER, 0L, &pPSStg )); // Test the IPropertySetStorage** Check( E_INVALIDARG, g_pfnStgCreatePropSetStg( pStg, 0L, NULL )); Check( E_INVALIDARG, g_pfnStgCreatePropSetStg( pStg, 0L, (IPropertySetStorage**) INVALID_POINTER )); // ------------------------------------------------------------- // Test g_pfnPropVariantCopy, PropVariantClear & FreePropVariantArray // ------------------------------------------------------------- // PropVariantCopy Check( E_INVALIDARG, g_pfnPropVariantCopy( rgcpropvarAllAccess, NULL )); Check( E_INVALIDARG, g_pfnPropVariantCopy( rgcpropvarAllAccess, (PROPVARIANT*) INVALID_POINTER )); Check( E_INVALIDARG, g_pfnPropVariantCopy( NULL, rgcpropvarAllAccess )); Check( E_INVALIDARG, g_pfnPropVariantCopy( (PROPVARIANT*) INVALID_POINTER, rgcpropvarAllAccess )); Check( E_INVALIDARG, g_pfnPropVariantCopy( (PROPVARIANT*) pbReadOnly, rgcpropvarAllAccess )); // PropVariantClear Check( S_OK, g_pfnPropVariantClear( NULL )); Check( E_INVALIDARG, g_pfnPropVariantClear( (PROPVARIANT*) INVALID_POINTER )); Check( E_INVALIDARG, g_pfnPropVariantClear( (PROPVARIANT*) pbReadOnly )); // FreePropVariantArray Check( E_INVALIDARG, g_pfnFreePropVariantArray( 1, NULL )); Check( E_INVALIDARG, g_pfnFreePropVariantArray( 1, (PROPVARIANT*) INVALID_POINTER )); Check( S_OK, g_pfnFreePropVariantArray( 1, (PROPVARIANT*) (void*)rgcpropvarReadAccess )); Check( E_INVALIDARG, g_pfnFreePropVariantArray( 2, (PROPVARIANT*) (void*)rgcpropvarReadAccess )); } // if( g_pfnStgCreatePropSetStg && g_pfnStgCreatePropStg && g_pfnStgOpenPropStg ) // ---- // Exit // ---- VirtualFree( rgpropspecNoAccessBase, 0, MEM_RELEASE ); VirtualFree( rgcpropvarReadAccessBase, 0, MEM_RELEASE ); VirtualFree( rgcpropvarNoAccessBase, 0, MEM_RELEASE ); VirtualFree( rgpropidNoAccessBase, 0, MEM_RELEASE ); VirtualFree( rgpropidReadAccessBase, 0, MEM_RELEASE ); VirtualFree( rglpwstrNoAccessBase, 0, MEM_RELEASE ); VirtualFree( rglpwstrReadAccessBase, 0, MEM_RELEASE ); VirtualFree( rgStatPSStgReadAccessBase, 0, MEM_RELEASE ); VirtualFree( rgStatPStgReadAccessBase, 0, MEM_RELEASE ); RELEASE_INTERFACE(pPSStg); RELEASE_INTERFACE(pPStg); RELEASE_INTERFACE(pESPSStg); NtCurrentPeb()->AppCompatInfo = pAppCompatInfoSave; #endif // #ifdef WIN32 } // test_ParameterValidation(IStorage *pStg) // 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) { Status( "IPropertySetStorage::Create/Open/Delete\n" ); FMTID fmtid; PROPSPEC propspec; UuidCreate(&fmtid); for (int i=0; i<4; i++) { DWORD propsetflag; if( !(g_Restrictions & RESTRICT_SIMPLE_ONLY) ) propsetflag = (i & 2) == 0 ? PROPSETFLAG_DEFAULT : PROPSETFLAG_NONSIMPLE; else propsetflag = PROPSETFLAG_DEFAULT; { IPropertySetStorage *pPropSetStg = NULL; // TSafeStorage< IPropertySetStorage > pPropSetStg(pStorage); IPropertyStorage *PropStg, *PropStg2; Check( S_OK, StgToPropSetStg( pStorage, &pPropSetStg )); Check( S_OK, pPropSetStg->Create(fmtid, NULL, propsetflag, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_DIRECT | STGM_READWRITE, &PropStg)); Check( S_OK, pPropSetStg->Create(fmtid, NULL, propsetflag, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_DIRECT | STGM_READWRITE, &PropStg2 )); Check( STG_E_REVERTED, PropStg->Commit(0) ); RELEASE_INTERFACE( PropStg ); RELEASE_INTERFACE( PropStg2 ); RELEASE_INTERFACE( pPropSetStg ); } { IPropertySetStorage *pPropSetStg = NULL; // TSafeStorage< IPropertySetStorage > pPropSetStg(pStorage); IPropertyStorage *PropStg, *PropStg2; Check( S_OK, StgToPropSetStg( pStorage, &pPropSetStg )); // use STGM_FAILIFTHERE Check(STG_E_FILEALREADYEXISTS, pPropSetStg->Create(fmtid, NULL, propsetflag, 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->Open(fmtid, STGM_SHARE_EXCLUSIVE | STGM_DIRECT | STGM_READWRITE, &PropStg2)); Check( STG_E_ACCESSDENIED, pPropSetStg->Create( fmtid, NULL, propsetflag, STGM_SHARE_EXCLUSIVE | STGM_DIRECT | STGM_READWRITE, &PropStg2)); // Docfile allows an open element to be deleted (putting open objects // in the reverted state). NTFS doesn't allow the delete though. Check( (g_Restrictions & RESTRICT_NON_HIERARCHICAL) ? STG_E_SHAREVIOLATION : S_OK, pPropSetStg->Delete(fmtid) ); propspec.ulKind = PRSPEC_PROPID; propspec.propid = 1000; PROPVARIANT propvar; propvar.vt = VT_I4; propvar.lVal = 12345; Check((g_Restrictions & RESTRICT_NON_HIERARCHICAL) ? S_OK : STG_E_REVERTED, PropStg->WriteMultiple(1, &propspec, &propvar, 2)); // force dirty RELEASE_INTERFACE(PropStg); RELEASE_INTERFACE(pPropSetStg); //Check(S_OK, pPropSetStg->Delete(fmtid)); } } } void test_IPropertySetStorage_SummaryInformation(IStorage *pStorage) { if( g_Restrictions & RESTRICT_NON_HIERARCHICAL ) return; Status( "SummaryInformation\n" ); IPropertySetStorage *pPropSetStg = NULL; // TSafeStorage< IPropertySetStorage > pPropSetStg(pStorage); IPropertyStorage *PropStg; IStream *pstm; Check( S_OK, StgToPropSetStg( pStorage, &pPropSetStg )); Check(S_OK, pPropSetStg->Create(FMTID_SummaryInformation, NULL, PROPSETFLAG_DEFAULT, STGM_SHARE_EXCLUSIVE | STGM_DIRECT | STGM_READWRITE, &PropStg)); RELEASE_INTERFACE(PropStg); Check(S_OK, pStorage->OpenStream(OLESTR("\005SummaryInformation"), NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pstm)); RELEASE_INTERFACE(pstm); RELEASE_INTERFACE(pPropSetStg); } // // Check STGM_FAILIFTHERE and ~STGM_FAILIFTHERE in following cases // Check overwriting simple with extant non-simple // Check overwriting simple with simple // Check overwriting non-simple with simple // Check overwriting non-simple with non-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). Status( "IPropertySetStorage, FaleIfThere\n" ); IPropertySetStorage *pPropSetStg = NULL; // TSafeStorage< IPropertySetStorage > pPropSetStg(pStorage); ULONG cStorageRefs = GetRefCount( pStorage ); Check( S_OK, StgToPropSetStg( pStorage, &pPropSetStg )); // 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; DWORD propsetflagNonSimple = (g_Restrictions & RESTRICT_SIMPLE_ONLY) ? PROPSETFLAG_DEFAULT : PROPSETFLAG_NONSIMPLE; UuidCreate(&fmtid); Check(S_OK, pPropSetStg->Create(fmtid, NULL, (i & 1) == 1 ? propsetflagNonSimple : 0, STGM_SHARE_EXCLUSIVE | STGM_DIRECT | STGM_READWRITE, &PropStg)); PropStg->Release(); Check((i&4) == 4 ? S_OK : STG_E_FILEALREADYEXISTS, pPropSetStg->Create(fmtid, NULL, (i & 2) == 2 ? propsetflagNonSimple : 0, ( (i & 4) == 4 ? STGM_CREATE : STGM_FAILIFTHERE ) | STGM_SHARE_EXCLUSIVE | STGM_DIRECT | STGM_READWRITE, &PropStg)); if (PropStg) { PropStg->Release(); } } RELEASE_INTERFACE( pPropSetStg ); Check( cStorageRefs, GetRefCount( pStorage )); } // // // // Bad this pointer. // Call all methods with a bad this pointer, check we get STG_E_INVALIDHANDLE // void test_IPropertySetStorage_BadThis(IStorage *pIgnored) { Status( "Bad IPropertySetStorage 'this' pointer\n" ); IPropertySetStorage *pBad; IID iid; FMTID fmtid; void *pv; IPropertyStorage *pps; IEnumSTATPROPSETSTG *penm; pBad = reinterpret_cast(&iid); Check(STG_E_INVALIDHANDLE,pBad->QueryInterface(iid, &pv)); Check(0, pBad->AddRef()); Check(0, pBad->Release()); Check(STG_E_INVALIDHANDLE,pBad->Create( fmtid, NULL, 0, 0, &pps)); Check(STG_E_INVALIDHANDLE,pBad->Open(fmtid, 0, &pps)); Check(STG_E_INVALIDHANDLE,pBad->Delete( fmtid )); Check(STG_E_INVALIDHANDLE,pBad->Enum( &penm )); } // Transacted mode // Create a non-simple property set with one VT_STREAM child, close it // Open it in transacted mode // Write another VT_STORAGE child // Close and revert // Check that the second child does not exist. // Repeat and close and commit and check the child exists. void test_IPropertySetStorage_TransactedMode(IStorage *pStorage) { FMTID fmtid; UuidCreate(&fmtid); if( g_Restrictions & ( RESTRICT_DIRECT_ONLY | RESTRICT_SIMPLE_ONLY )) return; Status( "Transacted Mode\n" ); { // // create a substorage "teststg" with a propset // create a stream "src" which is then written via VT_STREAM as propid 7fffffff CTempStorage pSubStorage(coCreate, pStorage, OLESTR("teststg")); IPropertySetStorage *pPropSetStg = NULL; // TSafeStorage< IPropertySetStorage > pPropSetStg(pSubStorage); Check( S_OK, StgToPropSetStg( pSubStorage, &pPropSetStg )); IPropertyStorage *pPropSet; IStream *pstm; Check(S_OK, pPropSetStg->Create(fmtid, NULL, PROPSETFLAG_NONSIMPLE, STGM_READWRITE | STGM_DIRECT | STGM_SHARE_EXCLUSIVE, &pPropSet)); PROPSPEC ps; ps.ulKind = PRSPEC_PROPID; ps.propid = 0x7ffffffd; Check(S_OK, pStorage->CreateStream(OLESTR("src"), STGM_DIRECT|STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0,0, &pstm)); Check(S_OK, pstm->Write(L"billmo", 14, NULL)); Check(S_OK, pstm->Seek(g_li0, STREAM_SEEK_SET, NULL)); PROPVARIANT pv; pv.vt = VT_STREAM; pv.pStream = pstm; Check(S_OK, pPropSet->WriteMultiple(1, &ps, &pv, 2)); // copies the stream in Check( 0, RELEASE_INTERFACE(pPropSet) ); Check( 0, RELEASE_INTERFACE(pstm) ); RELEASE_INTERFACE(pPropSetStg); } { IPropertyStorage *pPropSet; // Reopen the propset in transacted and add one with id 0x7ffffffe CTempStorage pSubStorage(coOpen, pStorage, OLESTR("teststg"), STGM_TRANSACTED); IPropertySetStorage *pPropSetStg = NULL; // TSafeStorage< IPropertySetStorage > pPropSetStg(pSubStorage); Check( S_OK, StgToPropSetStg( pSubStorage, &pPropSetStg )); // Create a storage object to copy CTempStorage pstgSrc; CTempStorage pTestChild(coCreate, pstgSrc, OLESTR("testchild")); Check(S_OK, pPropSetStg->Open(fmtid, STGM_READWRITE | STGM_DIRECT | STGM_SHARE_EXCLUSIVE, &pPropSet)); // copy in the storage object PROPSPEC ps[2]; ps[0].ulKind = PRSPEC_PROPID; ps[0].propid = 0x7ffffffe; ps[1].ulKind = PRSPEC_PROPID; ps[1].propid = 0x7ffffff0; PROPVARIANT pv[2]; pv[0].vt = VT_STORAGE; pv[0].pStorage = pTestChild; pv[1].vt = VT_I4; pv[1].lVal = 123; Check(S_OK, pPropSet->WriteMultiple(2, ps, pv, 2)); // copies the storage in pSubStorage->Revert(); // throws away the storage // check that property set operations return stg_e_reverted Check(STG_E_REVERTED, pPropSet->WriteMultiple(2, ps, pv, 2)); Check(STG_E_REVERTED, pPropSet->ReadMultiple(1, ps+1, pv+1)); Check(STG_E_REVERTED, pPropSet->DeleteMultiple(1, ps+1)); LPOLESTR pstr = L"pstr"; Check(STG_E_REVERTED, pPropSet->ReadPropertyNames(1, &ps[1].propid, &pstr)); Check(STG_E_REVERTED, pPropSet->WritePropertyNames(1, &ps[1].propid, &pstr)); Check(STG_E_REVERTED, pPropSet->DeletePropertyNames(1, &ps[1].propid)); Check(STG_E_REVERTED, pPropSet->Commit(STGC_DEFAULT)); Check(STG_E_REVERTED, pPropSet->Revert()); IEnumSTATPROPSTG *penum; Check(STG_E_REVERTED, pPropSet->Enum(&penum)); FILETIME ft; Check(STG_E_REVERTED, pPropSet->SetTimes(&ft, &ft, &ft)); CLSID clsid; Check(STG_E_REVERTED, pPropSet->SetClass(clsid)); STATPROPSETSTG statpropsetstg; Check(STG_E_REVERTED, pPropSet->Stat(&statpropsetstg)); Check( 0, RELEASE_INTERFACE(pPropSet) ); RELEASE_INTERFACE(pPropSetStg); } { IPropertyStorage *pPropSet; // Reopen the propset in direct mode and check that the // second child is not there. CTempStorage pSubStorage(coOpen, pStorage, OLESTR("teststg")); IPropertySetStorage *pPropSetStg = NULL; // TSafeStorage< IPropertySetStorage > pPropSetStg(pSubStorage); Check( S_OK, StgToPropSetStg( pSubStorage, &pPropSetStg )); Check(S_OK, pPropSetStg->Open(fmtid, STGM_READWRITE | STGM_DIRECT | STGM_SHARE_EXCLUSIVE, &pPropSet)); // read out the storage object PROPSPEC aps[2]; aps[0].ulKind = PRSPEC_PROPID; aps[0].propid = 0x7ffffffe; // storage not expected aps[1].ulKind = PRSPEC_PROPID; aps[1].propid = 0x7ffffffd; // stream is expected PROPVARIANT apv[2]; Check(S_FALSE, pPropSet->ReadMultiple(1, aps, apv)); Check(S_OK, pPropSet->ReadMultiple(2, aps, apv)); // opens the stream Check(TRUE, apv[0].vt == VT_EMPTY); Check(TRUE, apv[1].vt == VT_STREAM); Check(TRUE, apv[1].pStream != NULL); WCHAR wcsBillMo[7]; Check(S_OK, apv[1].pStream->Read(wcsBillMo, 14, NULL)); Check(TRUE, wcscmp(L"billmo", wcsBillMo) == 0); Check( 0, RELEASE_INTERFACE(apv[1].pStream) ); Check( 0, RELEASE_INTERFACE(pPropSet) ); RELEASE_INTERFACE(pPropSetStg); } } // // test that the buffer is correctly reverted // void test_IPropertySetStorage_TransactedMode2(IStorage *pStorage) { if( g_Restrictions & (RESTRICT_DIRECT_ONLY | RESTRICT_SIMPLE_ONLY )) return; Status( "Transacted Mode 2\n" ); // // write and commit a property A // write and revert a property B // write and commit a property C // check that property B does not exist FMTID fmtid; PROPSPEC ps; PROPVARIANT pv; IPropertyStorage *pPropStg; IPropertySetStorage *pPropSetStg = NULL; // TSafeStorage< IPropertySetStorage > pPropSetStg(pStorage); Check( S_OK, StgToPropSetStg( pStorage, &pPropSetStg )); UuidCreate(&fmtid); // We'll run this test twice, once with a Create and the other // with an Open (this way, we test both of the CPropertyStorage // constructors). for( int i = 0; i < 2; i++ ) { if( i == 0 ) { Check(S_OK, pPropSetStg->Create(fmtid, NULL, PROPSETFLAG_NONSIMPLE, STGM_TRANSACTED | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, &pPropStg)); } else { Check(S_OK, pPropSetStg->Open(fmtid, STGM_TRANSACTED | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, &pPropStg)); } ps.ulKind = PRSPEC_PROPID; ps.propid = 6; pv.vt = VT_I4; pv.lVal = 1; Check(S_OK, pPropStg->WriteMultiple(1, &ps, &pv, 0x2000)); Check(S_OK, pPropStg->Commit(STGC_DEFAULT)); ps.propid = 7; pv.lVal = 2; Check(S_OK, pPropStg->WriteMultiple(1, &ps, &pv, 0x2000)); Check(S_OK, pPropStg->Revert()); ps.propid = 8; pv.lVal = 3; Check(S_OK, pPropStg->WriteMultiple(1, &ps, &pv, 0x2000)); Check(S_OK, pPropStg->Commit(STGC_DEFAULT)); ps.propid = 6; Check(S_OK, pPropStg->ReadMultiple(1, &ps, &pv)); Check(TRUE, pv.lVal == 1); Check(TRUE, pv.vt == VT_I4); ps.propid = 7; Check(S_FALSE, pPropStg->ReadMultiple(1, &ps, &pv)); ps.propid = 8; Check(S_OK, pPropStg->ReadMultiple(1, &ps, &pv)); Check(TRUE, pv.lVal == 3); Check(TRUE, pv.vt == VT_I4); RELEASE_INTERFACE(pPropStg); } // for( int i = 0; i < 2; i++ ) RELEASE_INTERFACE(pPropSetStg); } void test_IPropertySetStorage_SubPropertySet(IStorage *pStorage) { FMTID fmtid; PROPSPEC ps; PROPVARIANT pv; IPropertyStorage *pPropStg; IPropertySetStorage *pSubSetStg; IPropertyStorage *pPropStg2; if( g_Restrictions & RESTRICT_SIMPLE_ONLY ) return; Status( "Sub Property Set\n" ); for (int i=0; i<2; i++) { IPropertySetStorage *pPropSetStg = NULL; // TSafeStorage< IPropertySetStorage > pPropSetStg(pStorage); ULONG cStorageRefs = GetRefCount( pStorage ); Check( S_OK, StgToPropSetStg( pStorage, &pPropSetStg )); UuidCreate(&fmtid); Check(S_OK, pPropSetStg->Create(fmtid, NULL, PROPSETFLAG_NONSIMPLE, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, &pPropStg)); ps.ulKind = PRSPEC_PROPID; ps.propid = 6; pv.vt = VT_STORAGE; pv.pStorage = NULL; Check(S_OK, pPropStg->WriteMultiple(1, &ps, &pv, 0x2000)); Check(S_OK, pPropStg->ReadMultiple(1, &ps, &pv)); Check(S_OK, StgToPropSetStg( pv.pStorage, &pSubSetStg )); Check(S_OK, pSubSetStg->Create(fmtid, NULL, i==0 ? PROPSETFLAG_NONSIMPLE : PROPSETFLAG_DEFAULT, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, &pPropStg2)); IStorage *pstgTmp = pv.pStorage; pv.pStorage = NULL; if (i==1) { pv.vt = VT_I4; } Check(S_OK, pPropStg2->WriteMultiple(1, &ps, &pv, 0x2000)); pPropStg->Release(); pstgTmp->Release(); pSubSetStg->Release(); pPropStg2->Release(); RELEASE_INTERFACE(pPropSetStg); Check( cStorageRefs, GetRefCount(pStorage) ); } } /* The following sequence of operations: - open transacted docfile - open property set inside docfile - write properties - commit docfile - release property set results in a STG_E_REVERTED error being detected */ void test_IPropertySetStorage_CommitAtRoot(IStorage *pStorage) { if( g_Restrictions & RESTRICT_DIRECT_ONLY ) return; Status( "Commit at root\n" ); for (int i=0; i<6; i++) { FMTID fmtid; IStorage *pstgT = NULL; Check(S_OK, g_pfnStgCreateStorageEx(NULL, STGM_CREATE | STGM_DELETEONRELEASE | STGM_TRANSACTED | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, STGFMT_STORAGE, 0, NULL, NULL, IID_IStorage, reinterpret_cast(&pstgT) )); IPropertySetStorage *pPropSetStg = NULL; // TSafeStorage< IPropertySetStorage > pPropSetStg(pStorage); Check( S_OK, StgToPropSetStg( pstgT, &pPropSetStg )); UuidCreate(&fmtid); IPropertyStorage *pPropStg = NULL; Check(S_OK, pPropSetStg->Create(fmtid, NULL, PROPSETFLAG_DEFAULT, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, &pPropStg)); PROPSPEC propspec; propspec.ulKind = PRSPEC_PROPID; propspec.propid = 1000; PROPVARIANT propvar; propvar.vt = VT_I4; propvar.lVal = 12345; Check(S_OK, pPropStg->WriteMultiple(1, &propspec, &propvar, 2)); // force dirty switch (i) { case 0: Check(S_OK, pstgT->Commit(STGC_DEFAULT)); pstgT->Release(); pPropStg->Release(); break; case 1: Check(S_OK, pstgT->Commit(STGC_DEFAULT)); pPropStg->Release(); pstgT->Release(); break; case 2: pstgT->Release(); pPropStg->Release(); break; case 3: pPropStg->Commit(STGC_DEFAULT); pPropStg->Release(); pstgT->Release(); break; case 4: pPropStg->Commit(STGC_DEFAULT); pstgT->Release(); pPropStg->Release(); break; case 5: pPropStg->Release(); pstgT->Release(); break; } Check( 0, RELEASE_INTERFACE(pstgT) ); } } 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); test_IPropertySetStorage_TransactedMode(pStorage); test_IPropertySetStorage_TransactedMode2(pStorage); test_IPropertySetStorage_SubPropertySet(pStorage); test_IPropertySetStorage_CommitAtRoot(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 test_IEnumSTATPROPSETSTG(IStorage *pStorage) { Status( "IEnumSTATPROPSETSTG\n" ); FMTID afmtid[8]; CLSID aclsid[8]; IPropertyStorage *pPropSet; memset( afmtid, 0, sizeof(afmtid) ); memset( aclsid, 0, sizeof(aclsid) ); IPropertySetStorage *pPropSetStg = NULL; // TSafeStorage< IPropertySetStorage > pPropSetStg(pStorage); FILETIME ftStart; Check( S_OK, StgToPropSetStg( pStorage, &pPropSetStg )); CoFileTimeNow(&ftStart); IEnumSTATPROPSETSTG *penum, *penum2; STATPROPSETSTG StatBuffer[6]; Check(S_OK, pPropSetStg->Enum(&penum)); while( S_OK == penum->Next( 1, &StatBuffer[0], NULL )) pPropSetStg->Delete( StatBuffer[0].fmtid ); RELEASE_INTERFACE( penum ); Check(S_OK, pPropSetStg->Enum(&penum)); Check( S_FALSE, penum->Next( 1, &StatBuffer[0], NULL )); RELEASE_INTERFACE( penum ); for (int i=0; i<5; i++) { ULONG cFetched; if (i & 4) afmtid[i] = FMTID_SummaryInformation; else UuidCreate(&afmtid[i]); UuidCreate(&aclsid[i]); Check(S_OK, pPropSetStg->Create( afmtid[i], aclsid+i, ( (i & 1) && !(g_Restrictions & RESTRICT_SIMPLE_ONLY) ? PROPSETFLAG_NONSIMPLE : 0) | ( (i & 2) && !(g_Restrictions & RESTRICT_UNICODE_ONLY) ? PROPSETFLAG_ANSI : 0), STGM_READWRITE | STGM_DIRECT | STGM_SHARE_EXCLUSIVE, &pPropSet)); pPropSet->Release(); Check(S_OK, pPropSetStg->Enum(&penum)); Check( S_FALSE, penum->Next( i+2, &StatBuffer[0], &cFetched )); Check( S_OK, penum->Reset() ); Check( S_OK, penum->Next( i+1, &StatBuffer[0], &cFetched )); RELEASE_INTERFACE( penum ); } ULONG celt; 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)); Check(TRUE, punk == punk2); punk->Release(); penum3->Release(); punk2->Release(); // test S_FALSE Check(S_FALSE, penum->Next(6, StatBuffer, &celt)); Check(TRUE, 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)); Check(TRUE, celt == 3); celt = 0; Check(S_OK, penum->Clone(&penum2)); Check(S_OK, penum->Next(2, StatBuffer, &celt)); Check(TRUE, 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)); Check(TRUE, celt == 0); Check(S_FALSE, penum2->Next(1, StatBuffer, &celt)); Check(TRUE, 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_FALSE, penum->Next(5+1-d, StatBuffer, &celt)); Check(TRUE, celt == 5-d ); penum->Reset(); memset(afFound, 0, sizeof(afFound)); for (ULONG iPropSet=0; iPropSet<5; iPropSet++) { for (ULONG iSearch=0; iSearch<5-d; iSearch++) { if (0 == memcmp(&StatBuffer[iSearch].fmtid, &afmtid[iPropSet], sizeof(StatBuffer[0].fmtid))) { Check(FALSE, afFound[iPropSet]); afFound[iPropSet] = TRUE; break; } } if (iPropSet < d) Check(FALSE, afFound[iPropSet]); if (iSearch == 5-d) { Check(TRUE, iPropSet < d); continue; } Check(TRUE, ( (StatBuffer[iSearch].grfFlags & PROPSETFLAG_NONSIMPLE) ? 1u : 0u ) == ( (!(g_Restrictions & RESTRICT_SIMPLE_ONLY) && (iPropSet & 1)) ? 1u : 0u) ); Check(TRUE, (StatBuffer[iSearch].grfFlags & PROPSETFLAG_ANSI) == 0); // We should have a clsid if this is a non-simple property set and we're not disallowing // hierarchical storages (i.e. it's not NTFS). if( (PROPSETFLAG_NONSIMPLE & StatBuffer[iSearch].grfFlags) && !(RESTRICT_NON_HIERARCHICAL & g_Restrictions) ) Check(TRUE, StatBuffer[iSearch].clsid == aclsid[iPropSet]); else Check(TRUE, 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])); penum->Release(); Check(S_OK, pPropSetStg->Enum(&penum)); // Check(S_OK, penum->Reset()); } penum->Release(); penum2->Release(); pPropSetStg->Release(); } // Creation tests // // Access flags/Valid parameters/Permissions // Check readonly cannot be written - // WriteProperties, WritePropertyNames void test_IPropertyStorage_Access(IStorage *pStorage) { Status( "IPropertyStorage creation (access) tests\n" ); IPropertySetStorage *pPropSetStg = NULL; // TSafeStorage< IPropertySetStorage > pPropSetStg(pStorage); FMTID fmtid; ULONG cStorageRefs = GetRefCount(pStorage); Check( S_OK, StgToPropSetStg( pStorage, &pPropSetStg )); 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; ps.lpwstr = OLESTR("testprop"); PROPVARIANT pv; pv.vt = VT_LPSTR; pv.pszVal = (LPSTR) "testval"; 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, (LPOLESTR*) &pv.pszVal)); 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)); RELEASE_INTERFACE(pPropStg); RELEASE_INTERFACE(pPropSetStg); } // Creation tests // Check VT_STREAM etc not usable with simple. // void test_IPropertyStorage_Create(IStorage *pStorage) { Status( "IPropertyStorage creation (simple/non-simple) tests\n" ); IPropertySetStorage *pPropSetStg = NULL; FMTID fmtid; ULONG cStorageRefs = GetRefCount( pStorage ); Check( S_OK, StgToPropSetStg( pStorage, &pPropSetStg )); UuidCreate(&fmtid); // check by name IPropertyStorage *pPropStg; Check(S_OK, pPropSetStg->Create(fmtid, NULL, PROPSETFLAG_DEFAULT, 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; Check(STG_E_PROPSETMISMATCHED, pPropStg->WriteMultiple(1, &ps, &pv, 2000)); pPropStg->Release(); Check( cStorageRefs, RELEASE_INTERFACE(pStorage) ); } // // // Stat (Create four combinations) // Check non-simple/simple flag // Check ansi/wide fflag // Also test clsid on propset void test_IPropertyStorage_Stat(IStorage *pStorage) { Status( "IPropertyStorage::Stat\n" ); DWORD dwOSVersion = 0; IPropertySetStorage *pPropSetStg = NULL; // TSafeStorage< IPropertySetStorage > pPropSetStg(pStorage); FMTID fmtid; UuidCreate(&fmtid); IPropertyStorage *pPropSet; STATPROPSETSTG StatPropSetStg; ULONG cStorageRefs = GetRefCount( pStorage ); Check( S_OK, StgToPropSetStg( pStorage, &pPropSetStg )); // Calculate the OS Version #ifdef _MAC { // Get the Mac System Version (e.g., 7.53). OSErr oserr; SysEnvRec theWorld; oserr = SysEnvirons( curSysEnvVers, &theWorld ); Check( TRUE, noErr == oserr ); dwOSVersion = MAKEPSVER( OSKIND_MACINTOSH, HIBYTE(theWorld.systemVersion), LOBYTE(theWorld.systemVersion) ); } #else dwOSVersion = MAKELONG( LOWORD(GetVersion()), OSKIND_WIN32 ); #endif for (ULONG i=0; i<4; i++) { FILETIME ftStart; CoFileTimeNow(&ftStart); memset(&StatPropSetStg, 0, sizeof(StatPropSetStg)); CLSID clsid; UuidCreate(&clsid); Check(S_OK, pPropSetStg->Create(fmtid, &clsid, ((i & 1) && 0 == (g_Restrictions & RESTRICT_SIMPLE_ONLY) ? PROPSETFLAG_NONSIMPLE : 0) | ((i & 2) && 0 == (g_Restrictions & RESTRICT_UNICODE_ONLY) ? PROPSETFLAG_ANSI : 0), STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, &pPropSet)); CheckStat(pPropSet, fmtid, clsid, ( ((i & 1) && !(g_Restrictions & RESTRICT_SIMPLE_ONLY) ? PROPSETFLAG_NONSIMPLE : 0) | ((i & 2) && !(g_Restrictions & RESTRICT_UNICODE_ONLY) ? PROPSETFLAG_ANSI : 0) ), ftStart, dwOSVersion ); pPropSet->Release(); Check(S_OK, pPropSetStg->Open(fmtid, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, &pPropSet)); CheckStat(pPropSet, fmtid, clsid, ((i & 1) && !(g_Restrictions & RESTRICT_SIMPLE_ONLY) ? PROPSETFLAG_NONSIMPLE : 0) | ((i & 2) && !(g_Restrictions & RESTRICT_UNICODE_ONLY)? PROPSETFLAG_ANSI : 0), ftStart, dwOSVersion ); UuidCreate(&clsid); Check(S_OK, pPropSet->SetClass(clsid)); pPropSet->Release(); Check(S_OK, pPropSetStg->Open(fmtid, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, &pPropSet)); CheckStat(pPropSet, fmtid, clsid, ((i & 1) && !(g_Restrictions & RESTRICT_SIMPLE_ONLY) ? PROPSETFLAG_NONSIMPLE : 0) | ((i & 2) && !(g_Restrictions & RESTRICT_UNICODE_ONLY) ? PROPSETFLAG_ANSI : 0), ftStart, dwOSVersion ); pPropSet->Release(); } RELEASE_INTERFACE(pPropSetStg); } // ReadMultiple // Check none found S_FALSE // // Success case non-simple readmultiple // Create a non-simple property set // Create two sub non-simples // Close all // Open the non-simple // Query for the two sub-nonsimples // Try writing to them // Close all // Open the non-simple // Query for the two sub-nonsimples // Check read back // Close all void test_IPropertyStorage_ReadMultiple_Normal(IStorage *pStorage) { if( g_Restrictions & RESTRICT_SIMPLE_ONLY ) return; Status( "IPropertyStorage::ReadMultiple (normal)\n" ); IPropertySetStorage *pPropSetStg = NULL; FMTID fmtid; UuidCreate(&fmtid); IPropertyStorage *pPropSet; ULONG cStorageRefs = GetRefCount( pStorage ); Check( S_OK, StgToPropSetStg( pStorage, &pPropSetStg )); Check(S_OK, pPropSetStg->Create(fmtid, NULL, PROPSETFLAG_NONSIMPLE, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, &pPropSet)); // none found PROPSPEC ps[2]; ps[0].ulKind = PRSPEC_LPWSTR; ps[0].lpwstr = L"testname"; ps[1].ulKind = PRSPEC_PROPID; ps[1].propid = 1000; PROPVARIANT pv[2]; PROPVARIANT pvSave[2]; PROPVARIANT pvExtra[2]; Check(S_FALSE, pPropSet->ReadMultiple(2, ps, pv)); PropVariantInit( &pv[0] ); pv[0].vt = VT_STREAM; pv[0].pStream = NULL; PropVariantInit( &pv[1] ); pv[1].vt = VT_STORAGE; pv[1].pStorage = NULL; memcpy(pvSave, pv, sizeof(pvSave)); memcpy(pvExtra, pv, sizeof(pvExtra)); // write the two sub non-simples Check(S_OK, pPropSet->WriteMultiple(2, ps, pv, 1000)); // re-open them Check(S_OK, pPropSet->ReadMultiple(2, ps, pv)); Check(TRUE, pv[0].pStream != NULL); Check(TRUE, pv[1].pStorage != NULL); // check status of write when already open Check(S_OK, pPropSet->WriteMultiple(2, ps, pvSave, 1000)); Check(STG_E_REVERTED, pv[0].pStream->Commit(0)); Check(STG_E_REVERTED, pv[1].pStorage->Commit(0)); Check(S_OK, pPropSet->ReadMultiple(2, ps, pvExtra)); Check(TRUE, pvExtra[0].pStream != NULL); Check(TRUE, pvExtra[1].pStorage != NULL); Check(S_OK, pvExtra[0].pStream->Commit(0)); Check(S_OK, pvExtra[1].pStorage->Commit(0)); pvExtra[0].pStream->Release(); pvExtra[1].pStorage->Release(); pv[0].pStream->Release(); pv[1].pStorage->Release(); Check(S_OK, pPropSet->ReadMultiple(2, ps, pv)); Check(TRUE, pv[0].pStream != NULL); Check(TRUE, pv[1].pStorage != NULL); Check(S_OK, pv[0].pStream->Write("billmotest", sizeof("billmotest"), NULL)); IStream *pStm; Check(S_OK, pv[1].pStorage->CreateStream(OLESTR("teststream"), STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStm)); pStm->Release(); pv[0].pStream->Release(); pv[1].pStorage->Release(); pPropSet->Release(); // re-re-open them Check(S_OK, pPropSetStg->Open(fmtid, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, &pPropSet)); Check(S_OK, pPropSet->ReadMultiple(2, ps, pv)); Check(TRUE, pv[0].pStream != NULL); Check(TRUE, pv[0].pStorage != NULL); // read the stream and storage and check the contents. char szBillMo[32]; Check(S_OK, pv[0].pStream->Read(szBillMo, 11, NULL)); Check(TRUE, 0 == strcmp(szBillMo, "billmotest")); Check(S_OK, pv[1].pStorage->OpenStream(OLESTR("teststream"), NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pStm)); pStm->Release(); pv[1].pStorage->Release(); pv[0].pStream->Release(); pPropSet->Release(); RELEASE_INTERFACE(pPropSetStg); } // // CleanupOpenedObjects for ReadMultiple (two iterations one for "VT_STORAGE then VT_STREAM", one for // "VT_STREAM then VT_STORAGE") // Create property set // Create a "VT_STREAM then VT_STORAGE" // Open the second one exclusive // Formulate a query so that both are read - > will fail but ... // Check that the first one is still openable // void test_IPropertyStorage_ReadMultiple_Cleanup(IStorage *pStorage) { if( g_Restrictions & RESTRICT_SIMPLE_ONLY ) return; Status( "IPropertyStorage::ReadMultiple (cleanup)\n" ); IPropertySetStorage *pPropSetStg = NULL; // TSafeStorage< IPropertySetStorage > pPropSetStg(pStorage); FMTID fmtid; ULONG cStorageRefs = GetRefCount( pStorage ); Check( S_OK, StgToPropSetStg( pStorage, &pPropSetStg )); UuidCreate(&fmtid); for (LONG i=0;i<2;i++) { IPropertyStorage * pPropSet; Check(S_OK, pPropSetStg->Create(fmtid, NULL, PROPSETFLAG_NONSIMPLE, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, &pPropSet)); // none found PROPSPEC ps[2]; ps[0].ulKind = PRSPEC_PROPID; ps[0].propid = 1000; ps[1].ulKind = PRSPEC_PROPID; ps[1].propid = 2000; PROPVARIANT pv[2]; pv[0].vt = (i == 0) ? VT_STREAM : VT_STORAGE; pv[0].pStream = NULL; pv[1].vt = (i == 1) ? VT_STORAGE : VT_STREAM; pv[1].pStorage = NULL; // write the two sub non-simples // OFS gives driver internal error when overwriting a stream with a storage. Check(S_OK, pPropSet->WriteMultiple(2, ps, pv, 1000)); // open both Check(S_OK, pPropSet->ReadMultiple(2, ps, pv)); // ** // close the first ONLY and reopen both PROPVARIANT pv2[2]; if (i==0) pv[0].pStream->Release(); else pv[0].pStorage->Release(); // reading both should fail because second is still open Check(STG_E_ACCESSDENIED, pPropSet->ReadMultiple(2, ps, pv2)); // failure should not prevent this from succeeding Check(S_OK, pPropSet->ReadMultiple(1, ps, pv2)); // *** // cleanup from ** and *** if (i==0) { pv2[0].pStream->Release(); // *** pv[1].pStorage->Release(); // ** } else { pv2[0].pStorage->Release(); // *** pv[1].pStream->Release(); // ** } pPropSet->Release(); } RELEASE_INTERFACE(pPropSetStg); } // Reading an inconsistent non-simple // Create a non-simple // Create a sub-stream/storage // Close all // Delete the actual stream // Read the indirect property -> should not exist. // void test_IPropertyStorage_ReadMultiple_Inconsistent(IStorage *pStorage) { if( g_Restrictions & RESTRICT_SIMPLE_ONLY ) return; if( PROPIMP_NTFS == g_enumImplementation ) return; Status( "IPropertyStorage::ReadMultiple (inconsistent test)\n" ); IPropertySetStorage *pPropSetStg = NULL; FMTID fmtid; ULONG cStorageRefs = GetRefCount( pStorage ); Check( S_OK, StgToPropSetStg( pStorage, &pPropSetStg )); UuidCreate(&fmtid); IPropertyStorage * pPropSet; Check(S_OK, pPropSetStg->Create(fmtid, NULL, PROPSETFLAG_NONSIMPLE, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, &pPropSet)); // none found PROPSPEC ps[3]; ps[0].ulKind = PRSPEC_PROPID; ps[0].propid = 1000; ps[1].ulKind = PRSPEC_PROPID; ps[1].propid = 2000; ps[2].ulKind = PRSPEC_PROPID; ps[2].propid = 3000; PROPVARIANT pv[3]; pv[0].vt = VT_STREAM; pv[0].pStream = NULL; pv[1].vt = VT_STORAGE; pv[1].pStorage = NULL; pv[2].vt = VT_UI4; pv[2].ulVal = 12345678; // write the two sub non-simples Check(S_OK, pPropSet->WriteMultiple(3, ps, pv, 1000)); pPropSet->Release(); Check(S_OK, pStorage->Commit(STGC_DEFAULT)); // delete the propsets OLECHAR ocsPropsetName[48]; // get name of the propset storage RtlGuidToPropertySetName(&fmtid, ocsPropsetName); // open it CTempStorage pStgPropSet(coOpen, pStorage, ocsPropsetName); // enumerate the non-simple properties. IEnumSTATSTG *penum; STATSTG stat[4]; ULONG celt; Check(S_OK, pStgPropSet->EnumElements(0, NULL, 0, &penum)); Check(S_OK, penum->Next(3, stat, &celt)); penum->Release(); for (ULONG i=0;iDestroyElement(stat[i].pwcsName); delete [] stat[i].pwcsName; } pStgPropSet.Release(); Check(S_OK, pPropSetStg->Open(fmtid, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, &pPropSet)); Check(S_OK, pPropSet->ReadMultiple(3, ps, pv)); Check(TRUE, pv[0].vt == VT_EMPTY); Check(TRUE, pv[1].vt == VT_EMPTY); Check(TRUE, pv[2].vt == VT_UI4); Check(TRUE, pv[2].ulVal == 12345678); pPropSet->Release(); RELEASE_INTERFACE(pPropSetStg); } void test_IPropertyStorage_ReadMultiple(IStorage *pStorage) { test_IPropertyStorage_ReadMultiple_Normal(pStorage); test_IPropertyStorage_ReadMultiple_Cleanup(pStorage); test_IPropertyStorage_ReadMultiple_Inconsistent(pStorage); } // Overwrite a non-simple property with a simple in a simple propset void test_IPropertyStorage_WriteMultiple_Overwrite1(IStorage *pStgBase) { if( g_Restrictions & RESTRICT_SIMPLE_ONLY ) return; if( PROPIMP_NTFS == g_enumImplementation ) return; Status( "IPropertyStorage::WriteMultiple (overwrite 1)\n" ); CTempStorage pStgSimple(coCreate, pStgBase, OLESTR("ov1_simp")); CTempStorage pStorage(coCreate, pStgBase, OLESTR("ov1_stg")); IPropertySetStorage *pPropSetStg = NULL; IPropertySetStorage *pPropSetSimpleStg = NULL; FMTID fmtid, fmtidSimple; UuidCreate(&fmtid); UuidCreate(&fmtidSimple); ULONG cStorageRefs = GetRefCount( pStorage ); Check( S_OK, StgToPropSetStg( pStorage, &pPropSetStg )); Check( S_OK, StgToPropSetStg( pStgSimple, &pPropSetSimpleStg )); // create a simple set with a non-simple child by copying the contents // stream a non-simple to a property set stream (simple) // create a nonsimple propset (will contain the contents stream) IPropertyStorage * pPropSet; Check(S_OK, pPropSetStg->Create(fmtid, NULL, PROPSETFLAG_NONSIMPLE, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, &pPropSet)); // none found PROPSPEC ps[2]; ps[0].ulKind = PRSPEC_PROPID; ps[0].propid = 1000; ps[1].ulKind = PRSPEC_LPWSTR; ps[1].lpwstr = OLESTR("foobar"); PROPVARIANT pv[2]; pv[0].vt = VT_STREAM; pv[0].pStream = NULL; pv[1].vt = VT_UI1; pv[1].bVal = 66; Check(S_OK, pPropSet->WriteMultiple(2, ps, pv, 100)); // invalid parameter PROPVARIANT pvInvalid[2]; PROPSPEC psInvalid[2]; psInvalid[0].ulKind = PRSPEC_PROPID; psInvalid[0].propid = 1000; psInvalid[1].ulKind = PRSPEC_PROPID; psInvalid[1].propid = 1001; pvInvalid[0].vt = (VARTYPE)-99; pvInvalid[1].vt = (VARTYPE)-100; Check(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), pPropSet->WriteMultiple(1, psInvalid, pvInvalid, 100)); Check(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), pPropSet->WriteMultiple(2, psInvalid, pvInvalid, 100)); pPropSet->Release(); // create a simple propset (will be overwritten) IPropertyStorage * pPropSetSimple; Check(S_OK, pPropSetSimpleStg->Create(fmtidSimple, NULL, 0, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, &pPropSetSimple)); pPropSetSimple->Release(); OLECHAR ocsNonSimple[48]; OLECHAR ocsSimple[48]; // get the name of the simple propset RtlGuidToPropertySetName(&fmtidSimple, ocsSimple); // get the name of the non-simple propset RtlGuidToPropertySetName(&fmtid, ocsNonSimple); // open non-simple as a storage (will copy the simple to this) IStorage *pstgPropSet; Check(S_OK, pStorage->OpenStorage(ocsNonSimple, NULL, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, NULL, 0, &pstgPropSet)); // copy the contents of the non-simple to the propset of the simple IStream *pstmNonSimple; Check(S_OK, pstgPropSet->OpenStream(OLESTR("CONTENTS"), NULL, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, &pstmNonSimple)); IStream *pstmSimple; Check(S_OK, pStgSimple->OpenStream(ocsSimple, NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pstmSimple)); ULARGE_INTEGER uli; memset(&uli, 0xff, sizeof(uli)); Check(S_OK, pstmNonSimple->CopyTo(pstmSimple, uli, NULL, NULL)); pstmSimple->Release(); pstmNonSimple->Release(); pstgPropSet->Release(); // But now the FMTID *in* the simple property set doesn't // match the string-ized FMTID which is the Stream's name. So, // rename the Stream to match the property set's FMTID. Check(S_OK, pStgSimple->RenameElement( ocsSimple, ocsNonSimple )); // now we have a simple propset with a non-simple VT type Check(S_OK, pPropSetSimpleStg->Open(fmtid, // Use the non-simple FMTID now STGM_SHARE_EXCLUSIVE | STGM_READWRITE, &pPropSetSimple)); Check(S_FALSE, pPropSetSimple->ReadMultiple(1, ps, pv)); Check(S_OK, pPropSetSimple->ReadMultiple(2, ps, pv)); Check(TRUE, pv[0].vt == VT_EMPTY); Check(TRUE, pv[1].vt == VT_UI1); Check(TRUE, pv[1].bVal == 66); RELEASE_INTERFACE( pPropSetSimpleStg ); pPropSetSimple->Release(); RELEASE_INTERFACE(pPropSetStg); } // Overwrite a non-simple with a simple in a non-simple // check that the non-simple is actually deleted // Delete a non-simple // check that the non-simple is actually deleted void test_IPropertyStorage_WriteMultiple_Overwrite2(IStorage *pStorage) { if( g_Restrictions & RESTRICT_SIMPLE_ONLY ) return; if( PROPIMP_NTFS == g_enumImplementation ) return; Status( "IPropertyStorage::WriteMultiple (overwrite 2)\n" ); IPropertySetStorage *pPropSetStg = NULL; // TSafeStorage< IPropertySetStorage > pPropSetStg(pStorage); FMTID fmtid; ULONG cStorageRefs = GetRefCount( pStorage ); Check( S_OK, StgToPropSetStg( pStorage, &pPropSetStg )); UuidCreate(&fmtid); IPropertyStorage *pPropSet; Check(S_OK, pPropSetStg->Create(fmtid, NULL, PROPSETFLAG_NONSIMPLE, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, &pPropSet)); // create the non-simple PROPSPEC ps[5]; ps[0].ulKind = PRSPEC_PROPID; ps[0].propid = 1000; ps[1].ulKind = PRSPEC_PROPID; ps[1].propid = 1001; ps[2].ulKind = PRSPEC_PROPID; ps[2].propid = 1002; ps[3].ulKind = PRSPEC_PROPID; ps[3].propid = 1003; ps[4].ulKind = PRSPEC_PROPID; ps[4].propid = 1004; PROPVARIANT pv[5]; pv[0].vt = VT_STORAGE; pv[0].pStorage = NULL; pv[1].vt = VT_STREAM; pv[1].pStream = NULL; pv[2].vt = VT_STORAGE; pv[2].pStorage = NULL; pv[3].vt = VT_STREAM; pv[3].pStream = NULL; pv[4].vt = VT_STREAM; pv[4].pStream = NULL; Check(S_OK, pPropSet->WriteMultiple(5, ps, pv, 2000)); pPropSet->Release(); // get the name of the propset OLECHAR ocsPropsetName[48]; RtlGuidToPropertySetName(&fmtid, ocsPropsetName); IStorage *pstgPropSet; Check(S_OK, pStorage->OpenStorage(ocsPropsetName, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, 0, &pstgPropSet)); // get the names of the non-simple property IEnumSTATSTG *penum; STATSTG statProp[6]; ULONG celt; Check(S_OK, pstgPropSet->EnumElements(0, NULL, 0, &penum)); Check(S_OK, penum->Next(5, statProp, &celt)); Check(TRUE, celt == 5); delete [] statProp[0].pwcsName; delete [] statProp[1].pwcsName; delete [] statProp[2].pwcsName; delete [] statProp[3].pwcsName; delete [] statProp[4].pwcsName; penum->Release(); // reopen the property set and delete the non-simple pstgPropSet->Release(); Check(S_OK, pPropSetStg->Open(fmtid, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, &pPropSet)); pv[0].vt = VT_LPWSTR; pv[0].pwszVal = L"Overwrite1"; pv[1].vt = VT_LPWSTR; pv[1].pwszVal = L"Overwrite2"; pv[2].vt = VT_LPWSTR; pv[2].pwszVal = L"Overwrite3"; pv[3].vt = VT_LPWSTR; pv[3].pwszVal = L"Overwrite4"; pv[4].vt = VT_LPWSTR; pv[4].pwszVal = L"Overwrite5"; Check(S_OK, pPropSet->WriteMultiple(2, ps, pv, 2000)); Check(S_OK, pPropSet->DeleteMultiple(1, ps+2)); Check(S_OK, pPropSet->DeleteMultiple(2, ps+3)); pPropSet->Release(); // open the propset as storage again and check that the VT_STORAGE is gone. Check(S_OK, pStorage->OpenStorage(ocsPropsetName, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, 0, &pstgPropSet)); // check they were removed STATSTG statProp2[5]; Check(S_OK, pstgPropSet->EnumElements(0, NULL, 0, &penum)); Check(S_FALSE, penum->Next(5, statProp2, &celt)); Check(TRUE, celt == 1); // contents delete [] statProp2[0].pwcsName; penum->Release(); pstgPropSet->Release(); RELEASE_INTERFACE(pPropSetStg); } // Write a VT_STORAGE over a VT_STREAM // check for cases: when not already open, when already open(access denied) // Write a VT_STREAM over a VT_STORAGE // check for cases: when not already open, when already open(access denied) void test_IPropertyStorage_WriteMultiple_Overwrite3(IStorage *pStorage) { if( g_Restrictions & RESTRICT_SIMPLE_ONLY ) return; Status( "IPropertyStorage::WriteMultiple (overwrite 3)\n" ); IPropertySetStorage *pPropSetStg = NULL; // TSafeStorage< IPropertySetStorage > pPropSetStg(pStorage); FMTID fmtid; ULONG cStorageRefs = GetRefCount( pStorage ); Check( S_OK, StgToPropSetStg( pStorage, &pPropSetStg )); UuidCreate(&fmtid); IPropertyStorage *pPropSet; Check(S_OK, pPropSetStg->Create(fmtid, NULL, PROPSETFLAG_NONSIMPLE, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, &pPropSet)); PROPSPEC ps[2]; ps[0].ulKind = PRSPEC_LPWSTR; ps[0].lpwstr = OLESTR("stream_storage"); ps[1].ulKind = PRSPEC_LPWSTR; ps[1].lpwstr = OLESTR("storage_stream"); PROPVARIANT pv[2]; pv[0].vt = VT_STREAMED_OBJECT; pv[0].pStream = NULL; pv[1].vt = VT_STORED_OBJECT; pv[1].pStorage = NULL; PROPVARIANT pvSave[2]; pvSave[0] = pv[0]; pvSave[1] = pv[1]; Check(S_OK, pPropSet->WriteMultiple(2, ps, pv, 1000)); // swap them around PROPVARIANT pvTemp; pvTemp = pv[0]; pv[0] = pv[1]; pv[1] = pvTemp; Check(S_OK, pPropSet->WriteMultiple(2, ps, pv, 1000)); memset(pv, 0, sizeof(pv)); Check(S_OK, pPropSet->ReadMultiple(2, ps, pv)); Check(TRUE, pv[0].vt == VT_STORED_OBJECT); Check(TRUE, pv[1].vt == VT_STREAMED_OBJECT); Check(TRUE, pv[0].pStorage != NULL); Check(TRUE, pv[1].pStream != NULL); STATSTG stat; stat.type = 0; Check(S_OK, pv[0].pStorage->Stat(&stat, STATFLAG_NONAME)); Check(TRUE, stat.type == STGTY_STORAGE); Check(S_OK, pv[1].pStream->Stat(&stat, STATFLAG_NONAME)); Check(TRUE, stat.type == STGTY_STREAM); STATSTG stat2; stat2.type = 0; // swap them around again, but this time with access denied Check(S_OK, pPropSet->WriteMultiple(2, ps, pvSave, 1000)); Check(STG_E_REVERTED, pv[0].pStorage->Stat(&stat, STATFLAG_NONAME)); pv[0].pStorage->Release(); Check(S_OK, pPropSet->WriteMultiple(2, ps, pvSave, 1000)); Check(STG_E_REVERTED, pv[1].pStream->Stat(&stat, STATFLAG_NONAME)); pv[1].pStream->Release(); pPropSet->Release(); RELEASE_INTERFACE(pPropSetStg); } // // test using IStorage::Commit to commit the changes in a nested // property set // void test_IPropertyStorage_Commit(IStorage *pStorage) { if( g_Restrictions & ( RESTRICT_SIMPLE_ONLY | RESTRICT_DIRECT_ONLY) ) return; Status( "IPropertyStorage::Commit\n" ); // 8 scenarios: (simple+non-simple) * (direct+transacted) * (release only + commit storage + commit propset) for (int i=0; i<32; i++) { CTempStorage pDeeper(coCreate, pStorage, GetNextTest(), (i & 1) ? STGM_TRANSACTED : STGM_DIRECT); IPropertySetStorage *pPropSetStg = NULL; // TSafeStorage< IPropertySetStorage > pPropSetStg(pDeeper); FMTID fmtid; ULONG cDeeperRefs = GetRefCount( pDeeper ); Check( S_OK, StgToPropSetStg( pDeeper, &pPropSetStg )); UuidCreate(&fmtid); IPropertyStorage *pPropSet; Check(S_OK, pPropSetStg->Create(fmtid, NULL, (i&8) ? PROPSETFLAG_NONSIMPLE : PROPSETFLAG_DEFAULT, STGM_SHARE_EXCLUSIVE | STGM_READWRITE | ((i&16) && (i&8) ? STGM_TRANSACTED : STGM_DIRECT), &pPropSet)); 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)); memset(&pv, 0, sizeof(pv)); Check(S_OK, pPropSet->ReadMultiple(1, &ps, &pv)); Check(TRUE, 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)) { memset(&pv, 0, sizeof(pv)); Check( !((i&16) && (i&8)) || (i&0x1c)==0x1c ? S_OK : S_FALSE, pPropSet->ReadMultiple(1, &ps, &pv)); if (!((i&16) && (i&8)) || (i&0x1c)==0x1c) Check(TRUE, pv.lVal == 2345); pPropSet->Release(); } RELEASE_INTERFACE(pPropSetStg); Check( cDeeperRefs, GetRefCount( pDeeper )); } } void test_IPropertyStorage_WriteMultiple(IStorage *pStorage) { test_IPropertyStorage_WriteMultiple_Overwrite1(pStorage); test_IPropertyStorage_WriteMultiple_Overwrite2(pStorage); test_IPropertyStorage_WriteMultiple_Overwrite3(pStorage); test_IPropertyStorage_Commit(pStorage); } // this serves as a test for WritePropertyNames, ReadPropertyNames, DeletePropertyNames // DeleteMultiple, PropVariantCopy, FreePropVariantArray. void test_IPropertyStorage_DeleteMultiple(IStorage *pStorage) { Status( "IPropertyStorage::DeleteMultiple\n" ); IPropertySetStorage *pPropSetStg = NULL; // TSafeStorage< IPropertySetStorage > pPropSetStg(pStorage); FMTID fmtid; ULONG cStorageRefs = GetRefCount( pStorage ); Check( S_OK, StgToPropSetStg( pStorage, &pPropSetStg )); UuidCreate(&fmtid); IPropertyStorage *pPropSet; int PropId = 3; for (int type=0; type<2; type++) { BOOL fSimple = ( type == 0 || (g_Restrictions & RESTRICT_SIMPLE_ONLY) ); UuidCreate(&fmtid); Check(S_OK, pPropSetStg->Create(fmtid, NULL, fSimple ? PROPSETFLAG_DEFAULT : PROPSETFLAG_NONSIMPLE, 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, fSimple )) { PROPSPEC ps[3]; PROPID rgpropid[3]; LPOLESTR rglpostrName[3]; OLECHAR aosz[3][16]; for (int s=0; s<3; s++) { PROPGENPROPERTYNAME( &aosz[s][0], PropId ); 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, g_pfnFreePropVariantArray(l, VarRead)); Check(S_OK, pPropSet->DeleteMultiple(l, ps)); Check(S_FALSE, pPropSet->ReadMultiple(l, ps, VarRead)); Check(S_OK, g_pfnFreePropVariantArray(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)); } g_pfnFreePropVariantArray(Actual, pVar); delete pVar; } } pPropSet->Release(); } RELEASE_INTERFACE(pPropSetStg); } void test_IPropertyStorage(IStorage *pStorage) { test_IPropertyStorage_Access(pStorage); test_IPropertyStorage_Create(pStorage); test_IPropertyStorage_Stat(pStorage); test_IPropertyStorage_ReadMultiple(pStorage); test_IPropertyStorage_WriteMultiple(pStorage); test_IPropertyStorage_DeleteMultiple(pStorage); } // // Word6.0 summary information // Open // Read fields // Stat // void test_Word6(IStorage *pStorage, CHAR *pszTemporaryDirectory) { Status( "Word 6.0 compatibility test\n" ); extern unsigned char g_achTestDoc[]; extern unsigned g_cbTestDoc; OLECHAR oszTempFile[ MAX_PATH + 1 ]; CHAR szTempFile[ MAX_PATH + 1 ]; strcpy( szTempFile, pszTemporaryDirectory ); strcat( szTempFile, "word6.doc" ); PropTest_mbstoocs( oszTempFile, sizeof(oszTempFile), szTempFile ); PROPTEST_FILE_HANDLE hFile = PropTest_CreateFile( szTempFile ); #ifdef _MAC Check(TRUE, (PROPTEST_FILE_HANDLE) -1 != hFile); #else Check(TRUE, INVALID_HANDLE_VALUE != hFile); #endif DWORD cbWritten; PropTest_WriteFile(hFile, g_achTestDoc, g_cbTestDoc, &cbWritten); Check(TRUE, cbWritten == g_cbTestDoc); PropTest_CloseHandle(hFile); IStorage *pStg; Check(S_OK, g_pfnStgOpenStorageEx(oszTempFile, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, STGFMT_ANY, 0, NULL, NULL, IID_IStorage, reinterpret_cast(&pStg) )); IPropertySetStorage *pPropSetStg = NULL; // TSafeStorage< IPropertySetStorage > pPropSetStg(pStg); IPropertyStorage *pPropStg; ULONG cStorageRefs = GetRefCount( pStg ); Check( S_OK, StgToPropSetStg( pStg, &pPropSetStg )); 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]; for (int i=2; iReadMultiple(WORDPROPS, propspec+2, propvar+2)); for (i=2; i pPropSetStg(pstgTemp); ULONG cStorageRefs = GetRefCount( pstgTemp ); Check( S_OK, StgToPropSetStg( pstgTemp, &pPropSetStg )); UuidCreate(&fmtid); for (int setup=0; setup<8; setup++) { alpostrName[setup] = &aosz[setup][0]; } CGenProps gp; // simple/non-simple, ansi/wide, named/not named for (int outer=0; outer<8; outer++) { UuidCreate(&fmtid); Check(S_OK, pPropSetStg->Create(fmtid, NULL, ((outer&4) && !(g_Restrictions & RESTRICT_SIMPLE_ONLY) ? PROPSETFLAG_NONSIMPLE : 0) | ((outer&2) && !(g_Restrictions & RESTRICT_UNICODE_ONLY) ? 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, (outer&4)==0); // no non-simple Check(TRUE, pVar != NULL); Check(S_OK, pPropStg->WriteMultiple(CPROPERTIES, ps, pVar, 1000)); g_pfnFreePropVariantArray(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)); Check(TRUE, punk == punk2); punk->Release(); penum3->Release(); punk2->Release(); // test S_FALSE Check(S_FALSE, penum->Next( CPROPERTIES+1, StatBuffer, &celt)); Check(TRUE, 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)); Check(TRUE, celt == CPROPERTIES/2); CleanStat(celt, StatBuffer); celt = 0; Check(S_OK, penum->Clone(&penum2)); Check(S_OK, penum->Next(CPROPERTIES - CPROPERTIES/2, StatBuffer, &celt)); Check(TRUE, celt == CPROPERTIES - CPROPERTIES/2); // check the clone for (int c=0; cNext(1, &CloneStat, NULL)); Check(TRUE, IsEqualSTATPROPSTG(&CloneStat, StatBuffer+c)); CleanStat(1, &CloneStat); } CleanStat(celt, StatBuffer); // check both empty celt = 0; Check(S_FALSE, penum->Next(1, StatBuffer, &celt)); Check(TRUE, celt == 0); Check(S_FALSE, penum2->Next(1, StatBuffer, &celt)); Check(TRUE, 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)); Check(TRUE, 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(); } RELEASE_INTERFACE( pPropSetStg ); Check( cStorageRefs, GetRefCount(pstgTemp) ); } void test_MaxPropertyName(IStorage *pstgTemp) { if( PROPIMP_NTFS == g_enumImplementation ) return; Status( "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. IStorage *pstg = NULL; // TSafeStorage< IStorage > pstg; Check(S_OK, pstgTemp->CreateStorage( OLESTR("MaxPropNameTest"), 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. IPropertySetStorage *pPropSetStg = NULL; // TSafeStorage< IPropertySetStorage > pPropSetStg(pstg); IPropertyStorage *pPropStg = NULL; // TSafeStorage< IPropertyStorage > pPropStg; Check( S_OK, StgToPropSetStg( pstg, &pPropSetStg )); // ---------------------------------- // 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 greater than the old max length // (NT5 removes the name length limitation, was 255 not including the terminator). OLECHAR *poszPropertyName; poszPropertyName = new OLECHAR[ (CCH_MAXPROPNAMESZ+1) * sizeof(OLECHAR) ]; Check(TRUE, poszPropertyName != NULL ); for( ULONG ulIndex = 0; ulIndex < CCH_MAXPROPNAMESZ; ulIndex++ ) poszPropertyName[ ulIndex ] = OLESTR('a') + (OLECHAR) ( ulIndex % 26 ); poszPropertyName[ CCH_MAXPROPNAMESZ ] = OLESTR('\0'); // Write out a property with this oldmax+1 name. PROPSPEC propspec; propspec.ulKind = PRSPEC_LPWSTR; propspec.lpwstr = poszPropertyName; cpropvar = (long) 0x1234; Check(S_OK, pPropStg->WriteMultiple( 1, &propspec, &cpropvar, PID_FIRST_USABLE )); // Write out a property with a minimum-character name. propspec.lpwstr = OLESTR("X"); Check(S_OK, pPropStg->WriteMultiple( 1, &propspec, &cpropvar, PID_FIRST_USABLE )); // Write out a property with a below-minimum-character name. propspec.lpwstr = OLESTR(""); Check(STG_E_INVALIDPARAMETER, pPropStg->WriteMultiple( 1, &propspec, &cpropvar, PID_FIRST_USABLE )); delete [] poszPropertyName; Check( 0, RELEASE_INTERFACE(pPropStg )); RELEASE_INTERFACE(pPropSetStg); Check( 0, RELEASE_INTERFACE(pstg) ); } void test_CodePages( LPOLESTR poszDirectory ) { if( g_Restrictions & RESTRICT_UNICODE_ONLY ) return; Status( "Code Page compatibility\n" ); // -------------- // Initialization // -------------- OLECHAR oszBadFile[ MAX_PATH ]; OLECHAR oszGoodFile[ MAX_PATH ]; OLECHAR oszUnicodeFile[ MAX_PATH ]; OLECHAR oszMacFile[ MAX_PATH ]; HRESULT hr = S_OK; IStorage *pStgBad = NULL, *pStgGood = NULL, *pStgUnicode = NULL, *pStgMac = NULL; // TSafeStorage< IStorage > pStgBad, pStgGood, pStgUnicode, pStgMac; CPropVariant cpropvarWrite, cpropvarRead; Check( TRUE, GetACP() == CODEPAGE_DEFAULT ); // ------------------------------ // Create test ANSI property sets // ------------------------------ // Create a property set with a bad codepage. ocscpy( oszBadFile, poszDirectory ); ocscat( oszBadFile, OLESTR( "\\BadCP.stg" )); CreateCodePageTestFile( oszBadFile, &pStgBad ); ModifyPropSetCodePage( pStgBad, FMTID_NULL, CODEPAGE_BAD ); // Create a property set with a good codepage. ocscpy( oszGoodFile, poszDirectory ); ocscat( oszGoodFile, OLESTR("\\GoodCP.stg") ); CreateCodePageTestFile( oszGoodFile, &pStgGood ); ModifyPropSetCodePage( pStgGood, FMTID_NULL, CODEPAGE_GOOD ); // Create a property set that has the OS Kind (in the // header) set to "Mac". ocscpy( oszMacFile, poszDirectory ); ocscat( oszMacFile, OLESTR("\\MacKind.stg") ); CreateCodePageTestFile( oszMacFile, &pStgMac ); ModifyOSVersion( pStgMac, 0x00010904 ); // --------------------------- // Open the Ansi property sets // --------------------------- IPropertySetStorage *pPropSetStgBad = NULL; // TSafeStorage< IPropertySetStorage > pPropSetStgBad(pStgBad); Check( S_OK, StgToPropSetStg( pStgBad, &pPropSetStgBad )); IPropertySetStorage *pPropSetStgGood = NULL; // TSafeStorage< IPropertySetStorage > pPropSetStgGood(pStgGood); Check( S_OK, StgToPropSetStg( pStgGood, &pPropSetStgGood )); IPropertySetStorage *pPropSetStgMac = NULL; // TSafeStorage< IPropertySetStorage > pPropSetStgMac(pStgMac); Check( S_OK, StgToPropSetStg( pStgMac, &pPropSetStgMac )); IPropertyStorage *pPropStgBad = NULL, *pPropStgGood = NULL, *pPropStgMac = NULL; 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)); // ------------------------------------------ // Test BSTRs in the three ANSI property sets // ------------------------------------------ PROPSPEC propspec; PROPVARIANT propvar; PropVariantInit( &propvar ); // Attempt to read by name. propspec.ulKind = PRSPEC_LPWSTR; propspec.lpwstr = CODEPAGE_TEST_NAMED_PROPERTY; Check(S_OK, pPropStgMac->ReadMultiple( 1, &propspec, &propvar )); g_pfnPropVariantClear( &propvar ); #ifndef OLE2ANSI // No error is generated if BSTRs are Ansi Check( HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION), pPropStgBad->ReadMultiple( 1, &propspec, &propvar )); #endif Check(S_OK, pPropStgGood->ReadMultiple( 1, &propspec, &propvar )); // Attempt to write by name. If this test fails, it may be because // the machine doesn't support CODEPAGE_GOOD (this is the case by default // on Win95). To remedy this situation, go to control panel, add/remove // programs, windows setup (tab), check MultiLanguage support, then // click OK. You'll have to restart the computer after this. Check(S_OK, pPropStgMac->WriteMultiple( 1, &propspec, &propvar, PID_FIRST_USABLE )); #ifndef OLE2ANSI // No error is generated if BSTRs are Ansi Check(HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION), pPropStgBad->WriteMultiple( 1, &propspec, &propvar, PID_FIRST_USABLE )); #endif Check(S_OK, pPropStgGood->WriteMultiple( 1, &propspec, &propvar, PID_FIRST_USABLE )); g_pfnPropVariantClear( &propvar ); // Attempt to read the BSTR property propspec.ulKind = PRSPEC_PROPID; propspec.propid = CODEPAGE_TEST_UNNAMED_BSTR_PROPID; Check(S_OK, pPropStgMac->ReadMultiple( 1, &propspec, &propvar )); g_pfnPropVariantClear( &propvar ); #ifndef OLE2ANSI // No error is generated if BSTRs are Ansi Check(HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION), pPropStgBad->ReadMultiple( 1, &propspec, &propvar )); #endif Check(S_OK, pPropStgGood->ReadMultiple( 1, &propspec, &propvar )); // Attempt to write the BSTR property Check(S_OK, pPropStgMac->WriteMultiple( 1, &propspec, &propvar, PID_FIRST_USABLE )); #ifndef OLE2ANSI // No error is generated if BSTRs are Ansi Check(HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION), pPropStgBad->WriteMultiple( 1, &propspec, &propvar, PID_FIRST_USABLE )); #endif Check(S_OK, pPropStgGood->WriteMultiple( 1, &propspec, &propvar, PID_FIRST_USABLE )); g_pfnPropVariantClear( &propvar ); // Attempt to read the BSTR Vector property propspec.ulKind = PRSPEC_PROPID; propspec.propid = CODEPAGE_TEST_VBSTR_PROPID; Check(S_OK, pPropStgMac->ReadMultiple( 1, &propspec, &propvar )); g_pfnPropVariantClear( &propvar ); #ifndef OLE2ANSI // No error is generated if BSTRs are Ansi Check(HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION), pPropStgBad->ReadMultiple( 1, &propspec, &propvar )); #endif Check(S_OK, pPropStgGood->ReadMultiple( 1, &propspec, &propvar )); // Attempt to write the BSTR Vector property Check(S_OK, pPropStgMac->WriteMultiple( 1, &propspec, &propvar, PID_FIRST_USABLE )); #ifndef OLE2ANSI // No error is generated if BSTRs are Ansi Check(HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION), pPropStgBad->WriteMultiple( 1, &propspec, &propvar, PID_FIRST_USABLE )); #endif Check(S_OK, pPropStgGood->WriteMultiple( 1, &propspec, &propvar, PID_FIRST_USABLE )); g_pfnPropVariantClear( &propvar ); // Attempt to read the Variant Vector which has a BSTR propspec.ulKind = PRSPEC_PROPID; propspec.propid = CODEPAGE_TEST_VPROPVAR_BSTR_PROPID; Check(S_OK, pPropStgMac->ReadMultiple( 1, &propspec, &propvar )); g_pfnPropVariantClear( &propvar ); #ifndef OLE2ANSI // No error is generated if BSTRs are Ansi Check(HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION), pPropStgBad->ReadMultiple( 1, &propspec, &propvar )); #endif Check(S_OK, pPropStgGood->ReadMultiple( 1, &propspec, &propvar )); // Attempt to write the Variant Vector which has a BSTR Check(S_OK, pPropStgMac->WriteMultiple( 1, &propspec, &propvar, PID_FIRST_USABLE )); #ifndef OLE2ANSI // No error is generated if BSTRs are Ansi Check(HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION), pPropStgBad->WriteMultiple( 1, &propspec, &propvar, PID_FIRST_USABLE )); #endif Check(S_OK, pPropStgGood->WriteMultiple( 1, &propspec, &propvar, PID_FIRST_USABLE )); g_pfnPropVariantClear( &propvar ); // Attempt to read the I4 property. Reading the bad property set // takes special handling, because it will return a different result // depending on whether NTDLL is checked or free (the free will work, // the checked generates an error in its validation checking). propspec.ulKind = PRSPEC_PROPID; propspec.propid = 4; Check(S_OK, pPropStgMac->ReadMultiple( 1, &propspec, &propvar )); g_pfnPropVariantClear( &propvar ); hr = pPropStgBad->ReadMultiple( 1, &propspec, &propvar ); Check(TRUE, S_OK == hr || HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION) == hr ); g_pfnPropVariantClear( &propvar ); Check(S_OK, pPropStgGood->ReadMultiple( 1, &propspec, &propvar )); // 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 || HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION) == hr ); Check(S_OK, pPropStgGood->WriteMultiple( 1, &propspec, &propvar, PID_FIRST_USABLE )); g_pfnPropVariantClear( &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). ocscpy( oszUnicodeFile, poszDirectory ); ocscat( oszUnicodeFile, OLESTR("\\UnicodCP.stg") ); Check(S_OK, g_pfnStgCreateStorageEx(oszUnicodeFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, DetermineStgFmt( g_enumImplementation ), 0, NULL, NULL, DetermineStgIID( g_enumImplementation ), reinterpret_cast(&pStgUnicode) )); IPropertySetStorage *pPropSetStgUnicode = NULL; // TSafeStorage< IPropertySetStorage > pPropSetStgUnicode(pStgUnicode); Check( S_OK, StgToPropSetStg( pStgUnicode, &pPropSetStgUnicode )); IPropertyStorage *pPropStgUnicode = NULL; // TSafeStorage< IPropertyStorage > pPropStgUnicode; Check(S_OK, pPropSetStgUnicode->Create(FMTID_NULL, &CLSID_NULL, PROPSETFLAG_NONSIMPLE, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, &pPropStgUnicode)); // Write/verify an LPSTR property. propspec.ulKind = PRSPEC_LPWSTR; propspec.lpwstr = OLESTR("LPSTR Property"); 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 propspec.lpwstr = OLESTR("Vector of LPSTR properties"); 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 propspec.lpwstr = OLESTR("Variant Vector with an LPSTR"); cpropvarWrite[1] = (PROPVARIANT) CPropVariant("LPSTR in a Variant Vector"); cpropvarWrite[0] = (PROPVARIANT) CPropVariant((long) 22); // an I4 Check(TRUE, (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(); // Write/verify a Stream. cpropvarWrite = (IStream*) NULL; propspec.lpwstr = OLESTR("An IStream"); Check(S_OK, pPropStgUnicode->WriteMultiple( 1, &propspec, &cpropvarWrite, PID_FIRST_USABLE )); Check(S_OK, pPropStgUnicode->ReadMultiple( 1, &propspec, &cpropvarRead )); cpropvarRead.Clear(); // There's nothing more we can check for the VT_STREAM property, a manual // check is required to verify that it was written correctly. RELEASE_INTERFACE(pStgBad); RELEASE_INTERFACE(pStgGood); RELEASE_INTERFACE(pStgUnicode); RELEASE_INTERFACE(pStgMac); RELEASE_INTERFACE(pPropSetStgBad); RELEASE_INTERFACE(pPropStgBad); RELEASE_INTERFACE(pPropStgGood); RELEASE_INTERFACE(pPropStgMac); RELEASE_INTERFACE(pPropSetStgGood); RELEASE_INTERFACE(pPropSetStgMac); RELEASE_INTERFACE(pPropSetStgUnicode); RELEASE_INTERFACE(pPropStgUnicode); } void test_PropertyInterfaces(IStorage *pstgTemp) { Status( "Property Interface\n" ); g_nIndent++; // this test depends on being first for enumerator test_IEnumSTATPROPSETSTG(pstgTemp); test_MaxPropertyName(pstgTemp); test_IPropertyStorage(pstgTemp); test_IPropertySetStorage(pstgTemp); test_IEnumSTATPROPSTG(pstgTemp); --g_nIndent; } //=================================================================== // // 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 ) { if( g_Restrictions & RESTRICT_NON_HIERARCHICAL ) return; char szMessage[ 128 ]; sprintf( szMessage, "IStorage::CopyTo (Base Storage is %s, PropSets are %s)\n", ulBaseStgTransaction & STGM_TRANSACTED ? "transacted" : "direct", ulPropSetTransaction & STGM_TRANSACTED ? "transacted" : "direct" ); Status( szMessage ); // --------------- // Local Variables // --------------- OLECHAR const *poszTestSubStorage = OLESTR( "TestStorage" ); OLECHAR const *poszTestSubStream = OLESTR( "TestStream" ); OLECHAR const *poszTestDataPreCommit = OLESTR( "Test Data (pre-commit)" ); OLECHAR const *poszTestDataPostCommit = OLESTR( "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. IStorage *pstgBaseSource = NULL; IStorage *pstgBaseDestination = NULL; IStorage *pstgSub = NULL; // A sub-storage of the base. IStream *pstmSub = NULL; // A Stream in the sub-storage (pstgSub) PROPSPEC propspec; PROPVARIANT propvarSourceSimple, propvarSourceNonSimple, 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. // --------------------------------------------------------- IPropertySetStorage *pPropSetStgSource = NULL; // TSafeStorage< IPropertySetStorage > pPropSetStgSource(pstgBaseSource); Check( S_OK, StgToPropSetStg( pstgBaseSource, &pPropSetStgSource )); IPropertyStorage *pPropStgSource1 = NULL, *pPropStgSource2 = NULL, *pPropStgDestination = NULL; // 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; OLECHAR *poszPropSetName = OLESTR("Property Set for CopyTo Test"); Check(TRUE, CWC_MAXPROPNAMESZ >= ocslen(poszPropSetName) + sizeof(OLECHAR) ); Check(S_OK, pPropStgSource1->WritePropertyNames( 1, &pidDictionary, &poszPropSetName )); // 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)); // --------------------------------------------------------------- // Write to a new *non-simple* property set in the Source storage. // --------------------------------------------------------------- // Create a property set. Check(S_OK, pPropSetStgSource->Create(fmtidNonSimple, NULL, PROPSETFLAG_NONSIMPLE, STGM_CREATE | STGM_SHARE_EXCLUSIVE | ulPropSetTransaction | STGM_READWRITE, &pPropStgSource2)); // Set data in the PROPVARIANT for the non-simple test. propvarSourceNonSimple.vt = VT_I4; propvarSourceNonSimple.lVal = lNonSimplePreCommit; // Write the PROPVARIANT to the property set. Check(S_OK, pPropStgSource2->WriteMultiple(1, &propspec, &propvarSourceNonSimple, 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 non-simple property set. Check(S_OK, pPropStgSource2->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)); // Write to the non-simple property set. propvarSourceNonSimple.lVal = lNonSimplePostCommit; Check(S_OK, pPropStgSource2->WriteMultiple(1, &propspec, &propvarSourceNonSimple, PID_FIRST_USABLE )); // ------------------------------------------- // 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. // ---------------------------------------------------------- IPropertySetStorage *pPropSetStgDestination = NULL; // TSafeStorage< IPropertySetStorage > pPropSetStgDestination(pstgBaseDestination); Check( S_OK, StgToPropSetStg( pstgBaseDestination, &pPropSetStgDestination )); // 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( poszPropSetName, poszPropSetNameDestination ) ) { bReadPropertyNamePassed = TRUE; } delete [] 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 *non-simple* property set in the destination Storage. // ---------------------------------------------------------------- // Open the non-simple property set. Check(S_OK, pPropSetStgDestination->Open(fmtidNonSimple, STGM_SHARE_EXCLUSIVE | STGM_DIRECT | STGM_READWRITE, &pPropStgDestination)); // Read the PROPVARIANT that we wrote earlier. Check(S_OK, pPropStgDestination->ReadMultiple(1, &propspec, &propvarDestination)); // Verify that they're the same. Check(TRUE, propvarDestination.vt == propvarSourceNonSimple.vt ); Check(TRUE, propvarDestination.lVal == ( STGM_TRANSACTED & ulPropSetTransaction ? lNonSimplePreCommit : lNonSimplePostCommit )); 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 // ---- RELEASE_INTERFACE( pPropSetStgSource ); RELEASE_INTERFACE(pPropStgSource1); RELEASE_INTERFACE(pPropStgSource2); RELEASE_INTERFACE(pPropStgDestination); RELEASE_INTERFACE(pPropSetStgDestination); RELEASE_INTERFACE(pstgBaseSource); RELEASE_INTERFACE(pstgBaseDestination); RELEASE_INTERFACE(pstgSub); RELEASE_INTERFACE(pstmSub); // 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 ) { Status( "Generate the Stock Ticker example from the OLE Programmer's Ref\n" ); // ------ // Locals // ------ FMTID fmtid; PROPSPEC propspec; LPOLESTR oszPropSetName = OLESTR( "Stock Quote" ); LPOLESTR oszTickerSymbolName = OLESTR( "Ticker Symbol" ); LPOLESTR oszOpenName = OLESTR( "Opening Price" ); LPOLESTR oszCloseName = OLESTR( "Last Closing Price" ); LPOLESTR oszHighName = OLESTR( "High Price" ); LPOLESTR oszLowName = OLESTR( "Low Price" ); LPOLESTR oszLastName = OLESTR( "Last Price" ); LPOLESTR oszVolumeName = OLESTR( "Volume" ); // --------------------------------- // Create a new simple property set. // --------------------------------- IPropertySetStorage *pPropSetStg = NULL; // TSafeStorage< IPropertySetStorage > pPropSetStg(pstg); IPropertyStorage *pPropStg; ULONG cStorageRefs = GetRefCount( pstg ); Check( S_OK, StgToPropSetStg( pstg, &pPropSetStg )); 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, &oszPropSetName )); // 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, &oszHighName )); // Write the ticker symbol. propspec.propid = PID_SYMBOL; Check(S_OK, pPropStg->WritePropertyNames(1, &propspec.propid, &oszTickerSymbolName)); // Write the rest of the dictionary. propspec.propid = PID_LOW; Check(S_OK, pPropStg->WritePropertyNames(1, &propspec.propid, &oszLowName)); propspec.propid = PID_OPEN; Check(S_OK, pPropStg->WritePropertyNames(1, &propspec.propid, &oszOpenName)); propspec.propid = PID_CLOSE; Check(S_OK, pPropStg->WritePropertyNames(1, &propspec.propid, &oszCloseName)); propspec.propid = PID_LAST; Check(S_OK, pPropStg->WritePropertyNames(1, &propspec.propid, &oszLastName)); propspec.propid = PID_VOLUME; Check(S_OK, pPropStg->WritePropertyNames(1, &propspec.propid, &oszVolumeName)); // Write out the ticker symbol. propspec.propid = PID_SYMBOL; PROPVARIANT propvar; propvar.vt = VT_LPWSTR; propvar.pwszVal = L"MSFT"; Check(S_OK, pPropStg->WriteMultiple(1, &propspec, &propvar, 2)); // ---- // Exit // ---- Check(S_OK, pPropStg->Commit( STGC_DEFAULT )); Check(0, pPropStg->Release()); Check(S_OK, pstg->Commit( STGC_DEFAULT )); RELEASE_INTERFACE( pPropSetStg ); Check( cStorageRefs, GetRefCount(pstg) ); return; } // test_OLESpecTickerExample() void test_Office( LPOLESTR wszTestFile ) { Status( "Generate Office Property Sets\n" ); IStorage *pStg = NULL; IPropertyStorage *pPStgSumInfo=NULL, *pPStgDocSumInfo=NULL, *pPStgUserDefined=NULL; IPropertySetStorage *pPSStg = NULL; // TSafeStorage pPSStg; PROPVARIANT propvarWrite, propvarRead; PROPSPEC propspec; PropVariantInit( &propvarWrite ); PropVariantInit( &propvarRead ); // Create the file Check( S_OK, g_pfnStgCreateStorageEx( wszTestFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, DetermineStgFmt( g_enumImplementation ), 0L, NULL, NULL, IID_IPropertySetStorage, (void**) &pPSStg )); // Create the SummaryInformation property set. Check(S_OK, pPSStg->Create( FMTID_SummaryInformation, NULL, (g_Restrictions & RESTRICT_UNICODE_ONLY) ? PROPSETFLAG_DEFAULT : 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 )); g_pfnPropVariantClear( &propvarRead ); PropVariantInit( &propvarRead ); pPStgSumInfo->Release(); pPStgSumInfo = NULL; // Create the DocumentSummaryInformation property set. Check(S_OK, pPSStg->Create( FMTID_DocSummaryInformation, NULL, (g_Restrictions & RESTRICT_UNICODE_ONLY ) ? PROPSETFLAG_DEFAULT: 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 ); g_pfnPropVariantClear( &propvarRead ); PropVariantInit( &propvarRead ); pPStgDocSumInfo->Release(); pPStgDocSumInfo = NULL; // Create the UserDefined property set. Check(S_OK, pPSStg->Create( FMTID_UserDefinedProperties, NULL, (g_Restrictions & RESTRICT_UNICODE_ONLY) ? PROPSETFLAG_DEFAULT : 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; propspec.lpwstr = OLESTR("PropTest String"); 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 )); g_pfnPropVariantClear( &propvarRead ); PropVariantInit( &propvarRead ); pPStgUserDefined->Release(); pPStgUserDefined = NULL; RELEASE_INTERFACE(pPSStg); // And we're done! (Everything releases automatically) return; } void test_Office2(IStorage *pStorage) { if( g_Restrictions & RESTRICT_NON_HIERARCHICAL ) return; Status( "Testing Office Property Sets\n" ); IStorage *pSubStorage = NULL; // TSafeStorage< IStorage > pSubStorage; IPropertySetStorage *pPropSetStg = NULL; // TSafeStorage< IPropertySetStorage > pPropSetStg; IPropertyStorage *pPropStg = NULL; // TSafeStorage< IPropertyStorage > pPropStg; CPropSpec cpropspec; // ---------------------------------- // Create a sub-storage for this test // ---------------------------------- Check(S_OK, pStorage->CreateStorage( OLESTR("test_Office2"), STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pSubStorage )); Check(S_OK, StgToPropSetStg( pSubStorage, &pPropSetStg )); // -------------------------------------------------------- // Test the Create/Delete of the DocumentSummaryInformation // property set (this requires special code because it // has two sections). // -------------------------------------------------------- // 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, &pPropStg)); pPropStg->Release(); pPropStg = 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, &pPropStg )); pPropStg->Release(); pPropStg = NULL; Check(S_OK, pPropSetStg->Delete( FMTID_UserDefinedProperties )); Check(S_OK, pPropSetStg->Delete( FMTID_DocSummaryInformation )); // -------------------------------------------- // Test the Create/Open of the DSI property set // -------------------------------------------- // 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( int i = 0; i < 2; i++ ) { // Create the first section. Check(S_OK, pPropSetStg->Create(FMTID_DocSummaryInformation, NULL, PROPSETFLAG_DEFAULT, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_DIRECT | STGM_READWRITE, &pPropStg)); pPropStg->Release(); pPropStg = NULL; // Create the second section. Check(S_OK, pPropSetStg->Create(FMTID_UserDefinedProperties, NULL, PROPSETFLAG_DEFAULT, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_DIRECT | STGM_READWRITE, &pPropStg)); pPropStg->Release(); pPropStg = NULL; if( i == 0 ) { // Delete the second section, then the first. Check(S_OK, pPropSetStg->Delete( FMTID_UserDefinedProperties )); Check(S_OK, pPropSetStg->Delete( FMTID_DocSummaryInformation )); } else { // Delete the first section, then *attempt* to delete the second. Check(S_OK, pPropSetStg->Delete( FMTID_DocSummaryInformation )); Check(STG_E_FILENOTFOUND, pPropSetStg->Delete( FMTID_UserDefinedProperties )); } } // for( i = 0; i < 2; i++ ) // ------------------------------------------------------------------ // Verify that we can create the UD propset (the 2nd section) without // harming the first section. // ------------------------------------------------------------------ { CPropSpec rgcpropspec[2]; CPropVariant rgcpropvarWrite[2]; CPropVariant cpropvarRead; // Create the first section. Check(S_OK, pPropSetStg->Create(FMTID_DocSummaryInformation, NULL, PROPSETFLAG_DEFAULT, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_DIRECT | STGM_READWRITE, &pPropStg)); // Write a property to the first section. rgcpropspec[0] = OLESTR("Test DSI Property"); rgcpropvarWrite[0] = (DWORD) 1; Check(S_OK, pPropStg->WriteMultiple( 1, rgcpropspec[0], &rgcpropvarWrite[0], PID_FIRST_USABLE )); pPropStg->Release(); pPropStg = NULL; // *Create* the second section Check(S_OK, pPropSetStg->Create(FMTID_UserDefinedProperties, NULL, PROPSETFLAG_DEFAULT, STGM_SHARE_EXCLUSIVE | STGM_READWRITE | STGM_CREATE, &pPropStg )); // Write a property to the second section rgcpropspec[1] = OLESTR("Test UD Property"); rgcpropvarWrite[1] = (DWORD) 2; Check(S_OK, pPropStg->WriteMultiple( 1, rgcpropspec[1], &rgcpropvarWrite[1], PID_FIRST_USABLE )); pPropStg->Release(); pPropStg = NULL; // Verify the properties from each of the sections. Check(S_OK, pPropSetStg->Open(FMTID_DocSummaryInformation, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, &pPropStg )); Check(S_OK, pPropStg->ReadMultiple( 1, rgcpropspec[0], &cpropvarRead )); Check(TRUE, rgcpropvarWrite[0] == cpropvarRead ); cpropvarRead.Clear(); pPropStg->Release(); pPropStg = NULL; Check(S_OK, pPropSetStg->Open(FMTID_UserDefinedProperties, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, &pPropStg )); Check(S_OK, pPropStg->ReadMultiple( 1, rgcpropspec[1], &cpropvarRead )); Check(TRUE, rgcpropvarWrite[1] == cpropvarRead ); cpropvarRead.Clear(); pPropStg->Release(); pPropStg = NULL; } // ------------------------------------- // 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, &pPropStg)); // 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"; Check(TRUE, cpropvarWrite.Count() == 4 ); // Write the property cpropspec = OLESTR("A Vector of LPSTRs"); Check(S_OK, pPropStg->WriteMultiple( 1, cpropspec, &cpropvarWrite, 2 )); // Read the property back. Check(S_OK, pPropStg->ReadMultiple( 1, cpropspec, &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] )); } // ---- // Exit // ---- RELEASE_INTERFACE(pSubStorage); RELEASE_INTERFACE(pPropSetStg); RELEASE_INTERFACE(pPropStg); return; } void test_PropVariantCopy( ) { Status( "PropVariantCopy\n" ); PROPVARIANT propvarCopy; PropVariantInit( &propvarCopy ); VERSIONEDSTREAM VersionedStream; UuidCreate( &VersionedStream.guidVersion ); VersionedStream.pStream = NULL; for( int i = 0; i < CPROPERTIES_ALL; i++ ) { Check(S_OK, g_pfnPropVariantCopy( &propvarCopy, &g_rgcpropvarAll[i] )); // g_pfnPropVariantCopy( &propvarCopy, &g_rgcpropvarAll[i] )); Check(S_OK, CPropVariant::Compare( &propvarCopy, &g_rgcpropvarAll[i] )); g_pfnPropVariantClear( &propvarCopy ); // If this is a stream, take the opportunity to do a test of vt_versioned_stream. if( VT_STREAM == g_rgcpropvarAll[i].vt ) { VersionedStream.pStream = g_rgcpropvarAll[i].pStream; CPropVariant cpropvar = VersionedStream; Check( S_OK, g_pfnPropVariantCopy( &propvarCopy, &cpropvar )); Check( S_OK, CPropVariant::Compare( &propvarCopy, &cpropvar )); g_pfnPropVariantClear( &propvarCopy ); } } Check( S_OK, ResetRGPropVar( g_rgcpropvarAll )); } #define PERFORMANCE_ITERATIONS 300 #define STABILIZATION_ITERATIONS 10 void test_Performance( IStorage *pStg ) { //#ifndef _MAC if( g_Restrictions & RESTRICT_NON_HIERARCHICAL ) return; Status( "Performance\n" ); CPropVariant rgcpropvar[2]; CPropSpec rgpropspec[2]; IPropertySetStorage *pPSStg = NULL; // TSafeStorage< IPropertySetStorage > pPSStg( pStg ); Check( S_OK, StgToPropSetStg( pStg, &pPSStg )); IPropertyStorage *pPStg = NULL; // TSafeStorage< IPropertyStorage > pPStg; IStream *pStm = NULL; // TSafeStorage< IStream > pStm; FMTID fmtid; ULONG ulCount; DWORD dwSumTimes; FILETIME filetimeStart, filetimeEnd; BYTE *pPropertyBuffer; ULONG cbPropertyBuffer; UuidCreate( &fmtid ); rgcpropvar[0][0] = L"I wish I were an Oscar Meyer wiener,"; rgcpropvar[0][1] = L"That is what I'd truly like to be."; rgcpropvar[1][0] = "For if I were an Oscar Meyer wiener,"; rgcpropvar[1][1] = "Everyone would be in love with me."; Check(TRUE, (VT_LPWSTR | VT_VECTOR) == rgcpropvar[0].VarType() ); Check(TRUE, (VT_LPSTR | VT_VECTOR) == rgcpropvar[1].VarType() ); // ---------------- // Test an IStorage // ---------------- // Create a buffer to write which is the same size as // the properties in rgcpropvar. cbPropertyBuffer = sizeof(WCHAR) * (2 + wcslen(rgcpropvar[0][0]) + wcslen(rgcpropvar[0][1])); cbPropertyBuffer += (2 + strlen(rgcpropvar[1][0]) + strlen(rgcpropvar[1][1])); pPropertyBuffer = new BYTE[ cbPropertyBuffer ]; PRINTF( " Docfile CreateStream/Write/Release = " ); dwSumTimes = 0; // Perform the test iterations for( ulCount = 0; ulCount < PERFORMANCE_ITERATIONS + STABILIZATION_ITERATIONS; ulCount++ ) { if( ulCount == STABILIZATION_ITERATIONS ) CoFileTimeNow( &filetimeStart ); Check(S_OK, pStg->CreateStream( OLESTR("StoragePerformance"), STGM_CREATE | STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0L, 0L, &pStm )); Check(S_OK, pStm->Write( pPropertyBuffer, cbPropertyBuffer, NULL )); pStm->Release(); pStm = NULL; } CoFileTimeNow( &filetimeEnd ); filetimeEnd -= filetimeStart; PRINTF( "%4.2f ms\n", (float)filetimeEnd.dwLowDateTime / 10000 // # of 100 nanosec units in 1 ms / PERFORMANCE_ITERATIONS ); // ------------------------------------------------------ // Try Creating a Property Set and writing two properties // ------------------------------------------------------ rgpropspec[0] = OLESTR("First Property"); rgpropspec[1] = OLESTR("Second Property"); PRINTF( " PropSet Create(Overwrite)/WriteMultiple/Release = " ); dwSumTimes = 0; for( ulCount = 0; ulCount < PERFORMANCE_ITERATIONS + STABILIZATION_ITERATIONS; ulCount++ ) { if( ulCount == STABILIZATION_ITERATIONS ) CoFileTimeNow( &filetimeStart) ; Check(S_OK, pPSStg->Create( fmtid, NULL, PROPSETFLAG_DEFAULT | PROPSETFLAG_NONSIMPLE, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &pPStg )); Check(S_OK, pPStg->WriteMultiple( 2, rgpropspec, rgcpropvar, PID_FIRST_USABLE )); pPStg->Release(); pPStg = NULL; } CoFileTimeNow( &filetimeEnd ); filetimeEnd -= filetimeStart; PRINTF( "%4.2f ms\n", (float)filetimeEnd.dwLowDateTime / 10000 // 100 ns units to 1 ms units / PERFORMANCE_ITERATIONS ); // ------------------------------------------------------ // WriteMultiple (with named properties) Performance Test // ------------------------------------------------------ PRINTF( " WriteMultiple (named properties) = " ); Check(S_OK, pPSStg->Create( fmtid, NULL, PROPSETFLAG_DEFAULT | PROPSETFLAG_NONSIMPLE, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &pPStg )); for( ulCount = 0; ulCount < PERFORMANCE_ITERATIONS + STABILIZATION_ITERATIONS; ulCount++ ) { if( ulCount == STABILIZATION_ITERATIONS ) CoFileTimeNow( &filetimeStart ); for( int i = 0; i < CPROPERTIES_ALL; i++ ) { Check(S_OK, pPStg->WriteMultiple( 1, &g_rgcpropspecAll[i], &g_rgcpropvarAll[i], PID_FIRST_USABLE )); } Check( S_OK, ResetRGPropVar( g_rgcpropvarAll )); } CoFileTimeNow( &filetimeEnd ); filetimeEnd -= filetimeStart; PRINTF( "%4.2f ms\n", (float) filetimeEnd.dwLowDateTime / 10000 // 100 ns units to 1 ms units / PERFORMANCE_ITERATIONS ); pPStg->Release(); pPStg = NULL; // -------------------------------------------------------- // WriteMultiple (with unnamed properties) Performance Test // -------------------------------------------------------- { CPropSpec rgcpropspecPIDs[ CPROPERTIES_ALL ]; PRINTF( " WriteMultiple (unnamed properties) = " ); Check(S_OK, pPSStg->Create( fmtid, NULL, PROPSETFLAG_DEFAULT | PROPSETFLAG_NONSIMPLE, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &pPStg )); for( ulCount = 0; ulCount < CPROPERTIES_ALL; ulCount++ ) { rgcpropspecPIDs[ ulCount ] = ulCount + PID_FIRST_USABLE; } for( ulCount = 0; ulCount < PERFORMANCE_ITERATIONS + STABILIZATION_ITERATIONS; ulCount++ ) { if( ulCount == STABILIZATION_ITERATIONS ) CoFileTimeNow( &filetimeStart ); for( int i = 0; i < CPROPERTIES_ALL; i++ ) { Check(S_OK, pPStg->WriteMultiple( 1, &rgcpropspecPIDs[i], &g_rgcpropvarAll[i], PID_FIRST_USABLE )); } Check( S_OK, ResetRGPropVar( g_rgcpropvarAll )); } CoFileTimeNow( &filetimeEnd ); filetimeEnd -= filetimeStart; PRINTF( "%4.2f ms\n", (float) filetimeEnd.dwLowDateTime / 10000 // 100 ns units to 1 ms units / PERFORMANCE_ITERATIONS ); pPStg->Release(); pPStg = NULL; } //#endif // #ifndef _MAC } // test_Performance() // // Function: test_CoFileTimeNow // // This function has nothing to do with the property set code, // but a property test happenned to expose a bug in it, so this // was just as good a place as any to test the fix. // void test_CoFileTimeNow() { #ifndef _MAC // No need to test this on the Mac, and we can't // because it doesn't support SYSTEMTIME. Status( "CoFileTimeNow " ); FILETIME ftCoFileTimeNow; FILETIME ftCalculated; SYSTEMTIME stCalculated; // Test the input validation Check(E_INVALIDARG, CoFileTimeNow( NULL )); Check(E_INVALIDARG, CoFileTimeNow( (FILETIME*) 0x01234567 )); // The bug in CoFileTimeNow caused it to report a time that was // 900 ms short, 50% of the time. So let's just bounds check // it several times as a verification. for( int i = 0; i < 20; i++ ) { Check(S_OK, CoFileTimeNow( &ftCoFileTimeNow )); GetSystemTime(&stCalculated); Check(TRUE, SystemTimeToFileTime(&stCalculated, &ftCalculated)); Check(TRUE, ftCoFileTimeNow <= ftCalculated ); Check(S_OK, CoFileTimeNow( &ftCoFileTimeNow )); Check(TRUE, ftCoFileTimeNow >= ftCalculated ); // The CoFileTimeNow bug caused it to report the correct // time for a second, then the 900 ms short time for a second. // So let's sleep in this loop and ensure that we cover both // seconds. if( g_fVerbose ) PRINTF( "." ); Sleep(200); } PRINTF( "\n" ); #endif // #ifndef _MAC } void test_PROPSETFLAG_UNBUFFERED( IStorage *pStg ) { // ---------- // Initialize // ---------- if( PROPIMP_DOCFILE_OLE32 != g_enumImplementation && PROPIMP_DOCFILE_IPROP != g_enumImplementation ) return; Status( "PROPSETFLAG_UNBUFFERED\n" ); IStorage *pStgBase = NULL; IPropertyStorage *pPropStgUnbuffered = NULL, *pPropStgBuffered = NULL; IStream *pstmUnbuffered = NULL, *pstmBuffered = NULL; CPropSpec cpropspec; CPropVariant cpropvar; FMTID fmtidUnbuffered, fmtidBuffered; OLECHAR oszPropStgNameUnbuffered[ CCH_MAX_PROPSTG_NAME+1 ], oszPropStgNameBuffered[ CCH_MAX_PROPSTG_NAME+1 ]; // Generate two FMTIDs UuidCreate( &fmtidUnbuffered ); UuidCreate( &fmtidBuffered ); // ---------------------------- // Create the Property Storages // ---------------------------- // Create a transacted Storage Check( S_OK, pStg->CreateStorage( OLESTR("test_PROPSETFLAG_UNBUFFERED"), STGM_CREATE | STGM_TRANSACTED | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0L, 0L, &pStgBase )); // Verify that we have the necessary APIs Check( TRUE, g_pfnFmtIdToPropStgName && g_pfnPropStgNameToFmtId && g_pfnStgCreatePropSetStg && g_pfnStgCreatePropStg && g_pfnStgOpenPropStg ); // What are the property storages' stream names? g_pfnFmtIdToPropStgName( &fmtidUnbuffered, oszPropStgNameUnbuffered ); g_pfnFmtIdToPropStgName( &fmtidBuffered, oszPropStgNameBuffered ); // Create Streams for the property storages Check( S_OK, pStgBase->CreateStream( oszPropStgNameUnbuffered, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0L, 0L, &pstmUnbuffered )); Check( S_OK, pStgBase->CreateStream( oszPropStgNameBuffered, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0L, 0L, &pstmBuffered )); // Create two direct-mode IPropertyStorages (one buffered, one not) Check( S_OK, g_pfnStgCreatePropStg( (IUnknown*) pstmUnbuffered, fmtidUnbuffered, &CLSID_NULL, PROPSETFLAG_UNBUFFERED, 0L, // Reserved &pPropStgUnbuffered )); pPropStgUnbuffered->Commit( STGC_DEFAULT ); pstmUnbuffered->Release(); pstmUnbuffered = NULL; Check( S_OK, g_pfnStgCreatePropStg( (IUnknown*) pstmBuffered, fmtidBuffered, &CLSID_NULL, PROPSETFLAG_DEFAULT, 0L, // Reserved &pPropStgBuffered )); pPropStgBuffered->Commit( STGC_DEFAULT ); pstmBuffered->Release(); pstmBuffered = NULL; // ------------------------- // Write, Commit, and Revert // ------------------------- // Write to both property storages cpropvar = "A Test String"; cpropspec = OLESTR("Property Name"); Check( S_OK, pPropStgUnbuffered->WriteMultiple( 1, cpropspec, &cpropvar, PID_FIRST_USABLE )); Check( S_OK, pPropStgBuffered->WriteMultiple( 1, cpropspec, &cpropvar, PID_FIRST_USABLE )); // Commit the base Storage. This should only cause // the Unbuffered property to be commited. pStgBase->Commit( STGC_DEFAULT ); // Revert the base Storage, and release the property storages. // This should cause the property in the buffered property storage // to be lost. pStgBase->Revert(); pPropStgUnbuffered->Release(); pPropStgUnbuffered = NULL; pPropStgBuffered->Release(); pPropStgBuffered = NULL; // ----------------------------- // Re-Open the property storages // ----------------------------- // Open the property storage Streams Check( S_OK, pStgBase->OpenStream( oszPropStgNameUnbuffered, 0L, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0L, &pstmUnbuffered )); Check( S_OK, pStgBase->OpenStream( oszPropStgNameBuffered, 0L, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0L, &pstmBuffered )); // Get IPropertyStorage interfaces Check( S_OK, g_pfnStgOpenPropStg( (IUnknown*) pstmUnbuffered, fmtidUnbuffered, PROPSETFLAG_UNBUFFERED, 0L, // Reserved &pPropStgUnbuffered )); pstmUnbuffered->Release(); pstmUnbuffered = NULL; Check( S_OK, g_pfnStgOpenPropStg( (IUnknown*) pstmBuffered, fmtidBuffered, PROPSETFLAG_DEFAULT, 0L, // Reserved &pPropStgBuffered )); pstmBuffered->Release(); pstmBuffered = NULL; // -------------------- // Validate the results // -------------------- // We should only find the property in the un-buffered property set. cpropvar.Clear(); Check( S_OK, pPropStgUnbuffered->ReadMultiple( 1, cpropspec, &cpropvar )); cpropvar.Clear(); Check( S_FALSE, pPropStgBuffered->ReadMultiple( 1, cpropspec, &cpropvar )); cpropvar.Clear(); } // test_PROPSETFLAG_UNBUFFERED() void test_PropStgNameConversion2() { Status( "FmtIdToPropStgName & PropStgNameToFmtId\n" ); // ------ // Locals // ------ FMTID fmtidOriginal, fmtidNew; OLECHAR oszPropStgName[ CCH_MAX_PROPSTG_NAME+1 ]; // ---------------------------------- // Do a simple conversion and inverse // ---------------------------------- UuidCreate( &fmtidOriginal ); fmtidNew = FMTID_NULL; Check( S_OK, g_pfnFmtIdToPropStgName( &fmtidOriginal, oszPropStgName )); Check( S_OK, g_pfnPropStgNameToFmtId( oszPropStgName, &fmtidNew )); Check( TRUE, fmtidOriginal == fmtidNew ); // ----------------------- // Check the special-cases // ----------------------- // Summary Information Check( S_OK, g_pfnFmtIdToPropStgName( &FMTID_SummaryInformation, oszPropStgName )); Check( 0, ocscmp( oszPropStgName, oszSummaryInformation )); Check( S_OK, g_pfnPropStgNameToFmtId( oszPropStgName, &fmtidNew )); Check( TRUE, FMTID_SummaryInformation == fmtidNew ); // DocSumInfo (first section) Check( S_OK, g_pfnFmtIdToPropStgName( &FMTID_DocSummaryInformation, oszPropStgName )); Check( 0, ocscmp( oszPropStgName, oszDocSummaryInformation )); Check( S_OK, g_pfnPropStgNameToFmtId( oszPropStgName, &fmtidNew )); Check( TRUE, FMTID_DocSummaryInformation == fmtidNew ); // DocSumInfo (second section) Check( S_OK, g_pfnFmtIdToPropStgName( &FMTID_UserDefinedProperties, oszPropStgName )); Check( 0, ocscmp( oszPropStgName, oszDocSummaryInformation )); Check( S_OK, g_pfnPropStgNameToFmtId( oszPropStgName, &fmtidNew )); Check( TRUE, FMTID_DocSummaryInformation == fmtidNew ); // GlobalInfo (for PictureIt!) Check( S_OK, g_pfnFmtIdToPropStgName( &fmtidGlobalInfo, oszPropStgName )); Check( 0, ocscmp( oszPropStgName, oszGlobalInfo )); Check( S_OK, g_pfnPropStgNameToFmtId( oszPropStgName, &fmtidNew )); Check( TRUE, fmtidGlobalInfo == fmtidNew ); // ImageContents (for PictureIt!) Check( S_OK, g_pfnFmtIdToPropStgName( &fmtidImageContents, oszPropStgName )); Check( 0, ocscmp( oszPropStgName, oszImageContents )); Check( S_OK, g_pfnPropStgNameToFmtId( oszPropStgName, &fmtidNew )); Check( TRUE, fmtidImageContents == fmtidNew ); // ImageInfo (for PictureIt!) Check( S_OK, g_pfnFmtIdToPropStgName( &fmtidImageInfo, oszPropStgName )); Check( 0, ocscmp( oszPropStgName, oszImageInfo )); Check( S_OK, g_pfnPropStgNameToFmtId( oszPropStgName, &fmtidNew )); Check( TRUE, fmtidImageInfo == fmtidNew ); } // test_PropStgNameConversion() void test_PropStgNameConversion( IStorage *pStg ) { if( g_Restrictions & RESTRICT_NON_HIERARCHICAL ) return; Status( "Special-case property set names\n" ); // ------ // Locals // ------ IStorage *pStgSub = NULL; IPropertyStorage *pPropStg = NULL; IPropertySetStorage *pPropSetStg = NULL; IEnumSTATSTG *pEnumStg = NULL; IEnumSTATPROPSETSTG *pEnumPropSet = NULL; STATSTG rgstatstg[ NUM_WELL_KNOWN_PROPSETS ]; STATPROPSETSTG rgstatpropsetstg[ NUM_WELL_KNOWN_PROPSETS ]; UINT i; DWORD cEnum; BOOL bSumInfo= FALSE, bDocSumInfo= FALSE, bGlobalInfo= FALSE, bImageContents= FALSE, bImageInfo= FALSE; // ------------------------------ // Create a Storage for this test // ------------------------------ Check( S_OK, pStg->CreateStorage( OLESTR("Special Cases"), STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStgSub )); // And get an IPropertySetStorage Check( S_OK, StgToPropSetStg( pStgSub, &pPropSetStg )); // -------------------------------------------------- // Create one of each of the well-known property sets // -------------------------------------------------- Check( S_OK, pPropSetStg->Create( FMTID_SummaryInformation, &CLSID_NULL, PROPSETFLAG_ANSI, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &pPropStg )); RELEASE_INTERFACE( pPropStg ); Check( S_OK, pPropSetStg->Create( FMTID_DocSummaryInformation, &CLSID_NULL, PROPSETFLAG_ANSI, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &pPropStg )); RELEASE_INTERFACE( pPropStg ); Check( S_OK, pPropSetStg->Create( FMTID_UserDefinedProperties, &CLSID_NULL, PROPSETFLAG_ANSI, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &pPropStg )); RELEASE_INTERFACE( pPropStg ); Check( S_OK, pPropSetStg->Create( fmtidGlobalInfo, &CLSID_NULL, PROPSETFLAG_ANSI, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &pPropStg )); RELEASE_INTERFACE( pPropStg ); Check( S_OK, pPropSetStg->Create( fmtidImageContents, &CLSID_NULL, PROPSETFLAG_ANSI, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &pPropStg )); RELEASE_INTERFACE( pPropStg ); Check( S_OK, pPropSetStg->Create( fmtidImageInfo, &CLSID_NULL, PROPSETFLAG_ANSI, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &pPropStg )); RELEASE_INTERFACE( pPropStg ); // --------------------------------- // Verify the FMTID->Name conversion // --------------------------------- // We verify this by enumerating the Storage's streams, // and checking for the expected names (e.g., we should see // "SummaryInformation", "DocumentSummaryInformation", etc.) Check( S_OK, pStgSub->EnumElements( 0, NULL, 0, &pEnumStg )); // Get all of the names. Check( S_FALSE, pEnumStg->Next( NUM_WELL_KNOWN_PROPSETS, rgstatstg, &cEnum )); // There should only be WellKnown-1 stream names, since // the UserDefined property set is part of the // DocumentSummaryInformation stream. Check( TRUE, cEnum == NUM_WELL_KNOWN_PROPSETS - 1 ); for( i = 0; i < cEnum; i++ ) { if( !ocscmp( rgstatstg[i].pwcsName, oszSummaryInformation )) bSumInfo= TRUE; else if( !ocscmp( rgstatstg[i].pwcsName, oszDocSummaryInformation )) bDocSumInfo= TRUE; else if( !ocscmp( rgstatstg[i].pwcsName, oszGlobalInfo )) bGlobalInfo= TRUE; else if( !ocscmp( rgstatstg[i].pwcsName, oszImageContents )) bImageContents= TRUE; else if( !ocscmp( rgstatstg[i].pwcsName, oszImageInfo )) bImageInfo= TRUE; delete [] rgstatstg[i].pwcsName; } // Verify that we found all the names we expected to find. Check( TRUE, bSumInfo && bDocSumInfo && bGlobalInfo && bImageContents && bImageInfo ); RELEASE_INTERFACE( pEnumStg ); // --------------------------------- // Verify the Name->FMTID Conversion // --------------------------------- // We do this by enumerating the property sets with IPropertySetStorage, // and verify that it correctly converts the Stream names to the // expected FMTIDs. bSumInfo = bDocSumInfo = bGlobalInfo = bImageContents = bImageInfo = FALSE; // Get the enumerator. Check( S_OK, pPropSetStg->Enum( &pEnumPropSet )); // Get all the property sets. Check( S_FALSE, pEnumPropSet->Next( NUM_WELL_KNOWN_PROPSETS, rgstatpropsetstg, &cEnum )); Check( TRUE, cEnum == NUM_WELL_KNOWN_PROPSETS - 1 ); // Look for each of the expected FMTIDs. We only look at WellKnown-1, // because the UserDefined property set doesn't get enumerated. for( i = 0; i < NUM_WELL_KNOWN_PROPSETS - 1; i++ ) { if( rgstatpropsetstg[i].fmtid == FMTID_SummaryInformation ) bSumInfo = TRUE; else if( rgstatpropsetstg[i].fmtid == FMTID_DocSummaryInformation ) bDocSumInfo = TRUE; else if( rgstatpropsetstg[i].fmtid == fmtidGlobalInfo ) bGlobalInfo = TRUE; else if( rgstatpropsetstg[i].fmtid == fmtidImageContents ) bImageContents = TRUE; else if( rgstatpropsetstg[i].fmtid == fmtidImageInfo ) bImageInfo = TRUE; } // NOTE: There is no way(?) to test the name-to-FMTID // conversion for the UserDefined property set without // calling the conversion function directly, but that // function isn't exported on Win95. // Verify that we found all of the expected FMTIDs Check( TRUE, bSumInfo && bDocSumInfo && bGlobalInfo && bImageContents && bImageInfo ); RELEASE_INTERFACE( pEnumPropSet ); RELEASE_INTERFACE( pPropSetStg ); RELEASE_INTERFACE( pStgSub ); } // test_PropStgNameConversion() //----------------------------------------------------------------------------- // // Function: test_SimpleLeaks // // This is a simple leak test. It doesn't test all functionality for // leaks; it just checks the common path: create, open, read, write, // and delete. // //----------------------------------------------------------------------------- void test_SimpleLeaks( LPOLESTR poszDir ) { IStorage *pStg = NULL; IPropertySetStorage *pPropSetStg = NULL; SYSTEM_PROCESS_INFORMATION spiStart, spiEnd; OLECHAR oszTempFile[ MAX_PATH + 1 ]; ocscpy( oszTempFile, poszDir ); ocscat( oszTempFile, OLESTR("SimpleLeakTest") ); Status( "Simple Leak Test " ); Check( STATUS_SUCCESS, GetProcessInfo(&spiStart) ); for( long i = 0; i < 1*1000*1000; i++ ) { if( i % (50*1000) == 0 ) PRINTF( "x"); CPropSpec rgpropspec[2]; CPropVariant rgpropvarWrite[2], rgpropvarRead[2]; IPropertyStorage *pPropStg = NULL; Check( S_OK, g_pfnStgCreateStorageEx( oszTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE | ( ((i&1) && !(g_Restrictions & RESTRICT_DIRECT_ONLY)) ? STGM_TRANSACTED : STGM_DIRECT ), DetermineStgFmt( g_enumImplementation ), 0L, NULL, NULL, IID_IPropertySetStorage, (void**) &pPropSetStg)); Check( S_OK, pPropSetStg->Create( FMTID_NULL, NULL, ( (i&2) && !(g_Restrictions & RESTRICT_UNICODE_ONLY) ? PROPSETFLAG_ANSI : PROPSETFLAG_DEFAULT ) | ( (i&4) && !(g_Restrictions & RESTRICT_SIMPLE_ONLY) ? PROPSETFLAG_NONSIMPLE : PROPSETFLAG_DEFAULT ), STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE | ( (i&8) && !(g_Restrictions & RESTRICT_DIRECT_ONLY) ? STGM_TRANSACTED : STGM_DIRECT ), &pPropStg )); rgpropspec[0] = OLESTR("Property Name"); rgpropspec[1] = 1000; rgpropvarWrite[0] = "Hello, world"; rgpropvarWrite[1] = (ULONG) 23; Check( S_OK, pPropStg->WriteMultiple( 2, rgpropspec, rgpropvarWrite, PID_FIRST_USABLE )); Check( S_OK, pPropStg->Commit( STGC_DEFAULT )); Check( 0, pPropStg->Release() ); Check( S_OK, pPropSetStg->Open( FMTID_NULL, ( (i&16) && !(g_Restrictions & RESTRICT_DIRECT_ONLY) ? STGM_TRANSACTED : STGM_DIRECT ) | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &pPropStg )); Check( S_OK, pPropStg->ReadMultiple( 2, rgpropspec, rgpropvarRead )); Check( TRUE, rgpropvarRead[0] == rgpropvarWrite[0] && rgpropvarRead[1] == rgpropvarWrite[1] ); Check( S_OK, pPropStg->DeleteMultiple( 2, rgpropspec )); Check( S_OK, pPropStg->Commit( STGC_DEFAULT )); Check( 0, pPropStg->Release() ); Check( S_OK, pPropSetStg->Delete( FMTID_NULL )); Check( 0, pPropSetStg->Release() ); } Check( STATUS_SUCCESS, GetProcessInfo(&spiEnd) ); if( g_fVerbose ) { PRINTF( "\n" ); PRINTF( " process id %I64u\n", (ULONG_PTR) spiEnd.UniqueProcessId ); PRINTF( " threads %lu, %lu\n", spiStart.NumberOfThreads, spiEnd.NumberOfThreads ); PRINTF( " handles %lu, %lu\n", spiStart.HandleCount, spiEnd.HandleCount ); PRINTF( " virtual size %lu, %lu\n", spiStart.VirtualSize, spiEnd.VirtualSize ); PRINTF( " peak virtual size %lu, %lu\n", spiStart.PeakVirtualSize, spiEnd.PeakVirtualSize ); PRINTF( " working set %lu, %lu\n", spiStart.WorkingSetSize, spiEnd.WorkingSetSize ); PRINTF( " peak working set %lu, %lu\n", spiStart.PeakWorkingSetSize, spiEnd.PeakWorkingSetSize ); PRINTF( " pagefile usage %lu, %lu\n", spiStart.PagefileUsage, spiEnd.PagefileUsage ); PRINTF( " peak pagefile usage %lu, %lu\n", spiStart.PeakPagefileUsage, spiEnd.PeakPagefileUsage ); PRINTF( " private memory %lu, %lu\n", spiStart.PrivatePageCount, spiEnd.PrivatePageCount ); PRINTF( " quota paged pool %lu, %lu\n", spiStart.QuotaPagedPoolUsage, spiEnd.QuotaPagedPoolUsage ); PRINTF( " peak quota paged pool %lu, %lu\n", spiStart.QuotaPeakPagedPoolUsage, spiEnd.QuotaPeakPagedPoolUsage ); PRINTF( " quota non-paged pool %lu, %lu\n", spiStart.QuotaNonPagedPoolUsage, spiEnd.QuotaNonPagedPoolUsage ); PRINTF( " peak quota non-paged pool %lu, %lu\n", spiStart.QuotaPeakNonPagedPoolUsage, spiEnd.QuotaPeakNonPagedPoolUsage ); } // Ensure that the working set and pagefile usage didn't change by // more than 5% ULONG ulWorkingSetDifference = spiEnd.WorkingSetSize > spiStart.WorkingSetSize ? spiEnd.WorkingSetSize - spiStart.WorkingSetSize : spiStart.WorkingSetSize - spiEnd.WorkingSetSize; ULONG ulPagefileUsageDifference = spiEnd.PagefileUsage > spiStart.PagefileUsage ? spiEnd.PagefileUsage - spiStart.PagefileUsage : spiStart.PagefileUsage - spiEnd.PagefileUsage; Check( TRUE, ( ulWorkingSetDifference == 0 || spiStart.WorkingSetSize/ulWorkingSetDifference >= 20 ) && ( ulPagefileUsageDifference == 0 || spiStart.PagefileUsage/ulPagefileUsageDifference >= 20 ) ); } // test_SimpleLeaks //----------------------------------------------------------------------------- // // Function: test_SimpleDocFile // // This function tests PropSet functionality on Simple DocFile. // This test comes in multiple phases: // 1) A simple docfile is created and a minimal amount of data is stored // in it. // 2) The docfile is closed and opened again. The test attempts to write // a small string to the property storage in it. This should succeed. // Then it attempts to write a 4K string, which should fail. // 3) The docfile is closed and opened again. The test writes 3 small // strings to the prop storage. This should be successful. // // 4) The docfile is deleted. A new docfile with a property set storage is // created, and more than 4K data is written to it. // 5) The docfile is opened and writing additional data to it should fail. // //----------------------------------------------------------------------------- #define FOUR_K_SIZE 0x1000 // Make it at least 4K. #define THREE_H_SIZE 300 // 300 bytes #define ONE_H_SIZE 100 // 100 bytes void test_SimpleDocFile(LPOLESTR oszDir) { IStorage *pDfStg = NULL; IPropertySetStorage *pPropSetStg = NULL; IPropertyStorage *pPropStg = NULL; OLECHAR oszFile[MAX_PATH]; CPropSpec rgPropSpec[3]; CPropVariant rgPropVariant[3]; LPSTR pFourKString; int i; if( RESTRICT_NON_HIERARCHICAL & g_Restrictions ) return; // NFF doesn't support simp mode Status( "Simple-mode docfile\n" ); // // Generate a filename from the directory name. // ocscpy( oszFile, oszDir ); ocscat( oszFile, OLESTR( "SimpDoc.stg" )); // // allocate a buffer with 1 less than 4K // and fill it with characters. // pFourKString = new CHAR[ FOUR_K_SIZE ]; Check(TRUE, pFourKString != NULL); pFourKString[0] = '\0'; for (i=0; i < ((FOUR_K_SIZE/8)-1); i++) { strcat(pFourKString,"abcd1234"); } strcat(pFourKString,"abcd123"); rgPropSpec[0] = 0x10; rgPropSpec[1] = 0x11; rgPropSpec[2] = 0x12; //------------------- // 1st Test - setup //------------------- // Create a Docfile. // Check( S_OK, g_pfnStgCreateStorageEx( oszFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_SIMPLE, DetermineStgFmt( g_enumImplementation ), 0L, NULL, NULL, DetermineStgIID( g_enumImplementation ), reinterpret_cast(&pDfStg) )); Check(S_OK, StgToPropSetStg( pDfStg, &pPropSetStg )); // Test that we can QI between IStorage and IPropertySetStorage if( UsingQIImplementation() ) { IStorage *pstg2 = NULL, *pstg3 = NULL; IPropertySetStorage *ppropsetstg2 = NULL, *ppropsetstg3 = NULL; ULONG cRefs = GetRefCount( pDfStg ); Check( S_OK, pDfStg->QueryInterface( IID_IStorage, reinterpret_cast(&pstg2) )); Check( S_OK, pstg2->QueryInterface( IID_IPropertySetStorage, reinterpret_cast(&ppropsetstg2) )); Check( S_OK, ppropsetstg2->QueryInterface( IID_IStorage, reinterpret_cast(&pstg3) )); Check( TRUE, pstg2 == pstg3 ); Check( S_OK, pstg3->QueryInterface( IID_IPropertySetStorage, reinterpret_cast(&ppropsetstg3) )); Check( TRUE, ppropsetstg2 == ppropsetstg3 ); RELEASE_INTERFACE(ppropsetstg3); RELEASE_INTERFACE(ppropsetstg2); RELEASE_INTERFACE(pstg3); Check( cRefs, RELEASE_INTERFACE(pstg2) ); } Check( S_OK, pPropSetStg->Create( FMTID_UserDefinedProperties, &CLSID_NULL, PROPSETFLAG_ANSI, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &pPropStg )); // // Write several strings to the property storage // rgPropVariant[0] = "Hello, world"; Check(S_OK, pPropStg->WriteMultiple(1, rgPropSpec, rgPropVariant, PID_FIRST_USABLE)); rgPropVariant[0] = "New string for offset 0"; Check(S_OK, pPropStg->WriteMultiple(1, rgPropSpec, rgPropVariant, PID_FIRST_USABLE)); rgPropVariant[1] = "First string for offset 1"; Check(S_OK, pPropStg->WriteMultiple(3, rgPropSpec, rgPropVariant, PID_FIRST_USABLE)); // // Release the storages and docfile. // RELEASE_INTERFACE(pPropStg); RELEASE_INTERFACE(pPropSetStg); RELEASE_INTERFACE(pDfStg); //-------------- // 2nd Test //-------------- // // Now Open the DocFile and storages // and write a small stream followed by a 4K stream. // // Check(S_OK, g_pfnStgOpenStorageEx(oszFile, STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_SIMPLE, STGFMT_ANY, 0L, NULL, NULL, IID_IStorage, reinterpret_cast(&pDfStg) )); Check(S_OK, StgToPropSetStg( pDfStg, &pPropSetStg )); Check(S_OK, pPropSetStg->Open(FMTID_UserDefinedProperties, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, &pPropStg)); // // Write a small string followed by a string that is at least 4K. // The large string write should fail because the simple stream allocates // a minimum size stream of 4K, and on an Open will not allow the stream to // grow. // rgPropVariant[0] = "After Open, Hello, world"; rgPropVariant[1] = pFourKString; rgPropVariant[2] = "Another string after the long one"; Check(S_OK, pPropStg->WriteMultiple(1, rgPropSpec, rgPropVariant, PID_FIRST_USABLE)); Check(STG_E_INVALIDFUNCTION, pPropStg->WriteMultiple(2, rgPropSpec, rgPropVariant, PID_FIRST_USABLE)); Check(STG_E_INVALIDFUNCTION, pPropStg->WriteMultiple(3, rgPropSpec, rgPropVariant, PID_FIRST_USABLE)); Check(S_OK, pPropStg->WriteMultiple(1, rgPropSpec, rgPropVariant, PID_FIRST_USABLE)); RELEASE_INTERFACE(pPropStg); RELEASE_INTERFACE(pPropSetStg); RELEASE_INTERFACE(pDfStg); //-------------- // 3rd Test //-------------- // // Open the DocFile again, and write smaller strings to the same // location. // Check(S_OK, g_pfnStgOpenStorageEx(oszFile, STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_SIMPLE, STGFMT_ANY, 0, NULL, NULL, IID_IStorage, reinterpret_cast(&pDfStg) )); Check(S_OK, StgToPropSetStg( pDfStg, &pPropSetStg )); Check(S_OK, pPropSetStg->Open(FMTID_UserDefinedProperties, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, &pPropStg)); // // The smaller strings can be written because they fit in under the 4K // size of the simple stream buffer. // rgPropVariant[0] = "2nd open, small string"; rgPropVariant[1] = "small string2"; rgPropVariant[2] = "small string3"; Check(S_OK, pPropStg->WriteMultiple(1, rgPropSpec, rgPropVariant, PID_FIRST_USABLE)); Check(S_OK, pPropStg->WriteMultiple(2, rgPropSpec, rgPropVariant, PID_FIRST_USABLE)); Check(S_OK, pPropStg->WriteMultiple(3, rgPropSpec, rgPropVariant, PID_FIRST_USABLE)); RELEASE_INTERFACE(pPropStg); RELEASE_INTERFACE(pPropSetStg); RELEASE_INTERFACE(pDfStg); //--------------------------------- // 4th Test - Create Large PropSet //--------------------------------- // // Create a Docfile and fill with more than 4K. // Check( S_OK, g_pfnStgCreateStorageEx( oszFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_SIMPLE, DetermineStgFmt( g_enumImplementation ), 0L, NULL, NULL, DetermineStgIID( g_enumImplementation ), reinterpret_cast(&pDfStg) )); Check(S_OK, StgToPropSetStg( pDfStg, &pPropSetStg )); Check( S_OK, pPropSetStg->Create( FMTID_NULL, &CLSID_NULL, PROPSETFLAG_ANSI, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &pPropStg )); rgPropSpec[0] = 0x10; rgPropSpec[1] = 0x11; rgPropSpec[2] = 0x12; // // Write several strings to the property storage // The first one is a 4K string. // rgPropVariant[0] = pFourKString; rgPropVariant[1] = "First string for offset 1"; rgPropVariant[2] = "small string3"; Check(S_OK, pPropStg->WriteMultiple(3, rgPropSpec, rgPropVariant, PID_FIRST_USABLE)); // // Release the storages and docfile. // RELEASE_INTERFACE(pPropStg); RELEASE_INTERFACE(pPropSetStg); RELEASE_INTERFACE(pDfStg); //-------------- // 5th Test //-------------- // // Open the DocFile again, and write the same strings in a different // order. // Check(S_OK, g_pfnStgOpenStorageEx(oszFile, STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_SIMPLE, STGFMT_ANY, 0, NULL, NULL, IID_IStorage, reinterpret_cast(&pDfStg) )); Check(S_OK, StgToPropSetStg( pDfStg, &pPropSetStg )); Check(S_OK, pPropSetStg->Open(CLSID_NULL, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, &pPropStg)); // // The smaller strings can be written because they fit in under the 4K // size of the simple stream buffer. // rgPropVariant[0] = "small string0"; rgPropVariant[1] = "First string for offset 1"; rgPropVariant[2] = pFourKString; Check(S_OK, pPropStg->WriteMultiple(3, rgPropSpec, rgPropVariant, PID_FIRST_USABLE)); RELEASE_INTERFACE(pPropStg); RELEASE_INTERFACE(pPropSetStg); RELEASE_INTERFACE(pDfStg); //-------------- // 6th Test //-------------- // // Open the DocFile again, and write larger strings to the same // location. This should fail. // Check(S_OK, g_pfnStgOpenStorageEx(oszFile, STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_SIMPLE, STGFMT_ANY, 0, NULL, NULL, IID_IStorage, reinterpret_cast(&pDfStg) )); Check(S_OK, StgToPropSetStg( pDfStg, &pPropSetStg )); Check(S_OK, pPropSetStg->Open(CLSID_NULL, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, &pPropStg)); // // Now write the same thing again, only with one extra character. // This should fail. // rgPropVariant[0] = "First string for offset 0"; rgPropVariant[1] = pFourKString; rgPropVariant[2] = "small string00000"; Check(STG_E_INVALIDFUNCTION, pPropStg->WriteMultiple(3, rgPropSpec, rgPropVariant, PID_FIRST_USABLE)); RELEASE_INTERFACE(pPropStg); RELEASE_INTERFACE(pPropSetStg); RELEASE_INTERFACE(pDfStg); delete [] pFourKString; //-------------- // 7th Test - - A NON-SIMPLE MODE TEST //-------------- // // Create and write to a property set with an element of 400 bytes. // Then delete 100 bytes. Commit the changes. The property set should // have shrunk by at least 100 bytes. // // allocate a buffer with 300 bytes and fill it. // and fill it with characters. // LPSTR pThreeHString = NULL; LPSTR pOneHString = NULL; // // Fill the 3 Hundred Byte String // pThreeHString = new CHAR[ THREE_H_SIZE ]; Check(TRUE, pThreeHString != NULL); pThreeHString[0] = '\0'; for (i=0; i < ((THREE_H_SIZE/8)-1); i++) { strcat(pThreeHString,"abcd1234"); } strcat(pThreeHString,"abc"); // // Fill the 1 Hundred Byte String // pOneHString = new CHAR[ ONE_H_SIZE ]; Check(TRUE, pOneHString != NULL); pOneHString[0] = '\0'; for (i=0; i < ((ONE_H_SIZE/8)-1); i++) { strcat(pOneHString,"xyxy8787"); } strcat(pOneHString,"xyx"); // // Create a Docfile and fill with the string // Check( S_OK, g_pfnStgCreateStorageEx( oszFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, DetermineStgFmt( g_enumImplementation ), 0, NULL, NULL, DetermineStgIID( g_enumImplementation ), reinterpret_cast(&pDfStg) )); Check(S_OK, StgToPropSetStg( pDfStg, &pPropSetStg )); Check( S_OK, pPropSetStg->Create( FMTID_NULL, &CLSID_NULL, PROPSETFLAG_ANSI, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &pPropStg )); rgPropSpec[0] = 0x10; rgPropSpec[1] = 0x11; // // Write the string to the property storage // rgPropVariant[0] = pThreeHString; rgPropVariant[1] = pOneHString; Check(S_OK, pPropStg->WriteMultiple(2, rgPropSpec, rgPropVariant, PID_FIRST_USABLE)); // // Commit the changes and close. // Check(S_OK, pPropStg->Commit( STGC_DEFAULT )); RELEASE_INTERFACE(pPropStg); RELEASE_INTERFACE(pPropSetStg); RELEASE_INTERFACE(pDfStg); // // Check the size of the property set. // Check(S_OK, g_pfnStgOpenStorageEx(oszFile, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, STGFMT_ANY, 0, NULL, NULL, IID_IStorage, reinterpret_cast(&pDfStg) )); IStream *pStm; STATSTG StatBuf; OLECHAR ocsPropSetName[30]; DWORD cbStream; RtlGuidToPropertySetName(&FMTID_NULL, ocsPropSetName); Check(S_OK, pDfStg->OpenStream( ocsPropSetName, NULL, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, &pStm)); Check(S_OK, pStm->Stat( &StatBuf,STATFLAG_NONAME)); if (StatBuf.cbSize.HighPart != 0) { printf("FAILURE: test_SimpleDocFile: Test 7: Size High part is not zero\n"); } cbStream = StatBuf.cbSize.LowPart; RELEASE_INTERFACE(pStm); // // Delete // Check(S_OK, StgToPropSetStg( pDfStg, &pPropSetStg )); Check(S_OK, pPropSetStg->Open(CLSID_NULL, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, &pPropStg)); Check(S_OK, pPropStg->DeleteMultiple(1, &rgPropSpec[1])); // // Commit the changes and close. // Check(S_OK, pPropStg->Commit( STGC_DEFAULT )); RELEASE_INTERFACE(pPropStg); RELEASE_INTERFACE(pPropSetStg); RELEASE_INTERFACE(pDfStg); // // Check the size of the property set. // Check(S_OK, g_pfnStgOpenStorageEx(oszFile, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, STGFMT_ANY, 0, NULL, NULL, IID_IStorage, reinterpret_cast(&pDfStg) )); RtlGuidToPropertySetName(&FMTID_NULL, ocsPropSetName); Check(S_OK, pDfStg->OpenStream( ocsPropSetName, NULL, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, &pStm)); Check(S_OK, pStm->Stat( &StatBuf,STATFLAG_NONAME)); Check(TRUE, (StatBuf.cbSize.HighPart == 0)); Check(TRUE, (cbStream - StatBuf.cbSize.LowPart > 100)); // // Release the storages and docfile. // delete [] pThreeHString; delete [] pOneHString; RELEASE_INTERFACE(pStm); RELEASE_INTERFACE(pDfStg); } // test_SimpleDocFile //----------------------------------------------------------------------------- // // Function: test_ex_api // // This function tests the StgOpenStorageEx API to make sure it correctly // opens an NTFS flat file property set when called with STGFMT_ANY for a // property set that was created on an NTFS flat file. // //----------------------------------------------------------------------------- void test_ex_api(LPOLESTR oszDir) { IStorage *pDfStg = NULL; IPropertySetStorage *pPropSetStg = NULL; IPropertyStorage *pPropStg = NULL; OLECHAR oszFile[MAX_PATH]; CPropSpec rgPropSpec[3]; CPropVariant rgPropVariant[3]; LPSTR pFourKString; int i; HRESULT hr; FMTID fmtidAnsi; Status( "Ex API Tests\n" ); // // Generate a filename from the directory name. // ocscpy( oszFile, oszDir ); ocscat( oszFile, OLESTR( "StgApi.dat" )); // // Create a property set storage and a prop storage // Check( S_OK, g_pfnStgCreateStorageEx( oszFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, DetermineStgFmt( g_enumImplementation ), 0L, NULL, NULL, IID_IPropertySetStorage, (void**) &pPropSetStg)); Check(S_OK,pPropSetStg->Create( FMTID_NULL, NULL, PROPSETFLAG_DEFAULT, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &pPropStg )); // // Write a string to it. // rgPropSpec[0] = 0x10; rgPropVariant[0] = "Hello, world"; Check(S_OK, pPropStg->WriteMultiple(1, rgPropSpec, rgPropVariant, PID_FIRST_USABLE)); // // Close it // pPropStg->Release(); pPropStg = NULL; pPropSetStg->Release(); pPropSetStg = NULL; // // Open it. // Check(S_OK,g_pfnStgOpenStorageEx( oszFile, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, STGFMT_ANY, 0L, NULL, NULL, IID_IPropertySetStorage, (void**) &pPropSetStg )); UuidCreate( &fmtidAnsi ); // // Attempt to create an ANSI prop storage // Check(S_OK, pPropSetStg->Create( fmtidAnsi, &CLSID_NULL, PROPSETFLAG_ANSI, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &pPropStg )); // // Clean up before exiting. // if (pPropStg) { pPropStg->Release(); pPropStg = NULL; } if (pPropSetStg) { pPropSetStg->Release(); pPropSetStg = NULL; } } void test_UnsupportedProperties( IStorage *pStg ) { IPropertySetStorage *pPropSetStg = NULL; IPropertyStorage *pPropStg = NULL; CPropVariant rgcpropvarWrite[2], cpropvarRead; CPropSpec rgcpropspec[2]; Status( "Unsupported VarTypes\n" ); FMTID fmtid; UuidCreate(&fmtid); // Start by creating a property set with a couple of properties in it. Check( S_OK, StgToPropSetStg( pStg, &pPropSetStg )); Check( S_OK, pPropSetStg->Create( fmtid, NULL, PROPSETFLAG_DEFAULT | PROPSETFLAG_CASE_SENSITIVE, STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, &pPropStg )); rgcpropvarWrite[0] = (long) 1234; // VT_I4 rgcpropvarWrite[1] = (short) 56; // VT_I2 rgcpropspec[0] = PID_FIRST_USABLE; rgcpropspec[1] = PID_FIRST_USABLE + 1; Check( S_OK, pPropStg->WriteMultiple( 2, rgcpropspec, rgcpropvarWrite, PID_FIRST_USABLE )); // Modify the first property so that it has an invalid VT RELEASE_INTERFACE( pPropStg ); ModifyPropertyType( pStg, fmtid, rgcpropspec[0].propid, 0x500 ); // Try to read that property back (the one with the invalid VT) Check( S_OK, pPropSetStg->Open( fmtid, STGM_READWRITE|STGM_SHARE_EXCLUSIVE, &pPropStg )); Check( HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), pPropStg->ReadMultiple( 1, &rgcpropspec[0], &cpropvarRead )); // Verify that we can read back the other property Check( S_OK, pPropStg->ReadMultiple( 1, &rgcpropspec[1], &cpropvarRead )); Check( TRUE, cpropvarRead == rgcpropvarWrite[1] ); // And verify that we can't write a property with an invalid VT rgcpropvarWrite[0].vt = 0x500; Check( HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), pPropStg->WriteMultiple( 1, &rgcpropspec[0], &rgcpropvarWrite[0], PID_FIRST_USABLE )); RELEASE_INTERFACE( pPropStg ); RELEASE_INTERFACE( pPropSetStg ); }