//+============================================================================
//
//  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 <ddeml.h>      // 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<void**>(&pbag) ));
    Check( S_OK, pbag->QueryInterface( DetermineStgIID( g_enumImplementation ), reinterpret_cast<void**>(&pstg2) ));
    Check( S_OK, pstg2->QueryInterface( IID_IPropertyBagEx, reinterpret_cast<void**>(&pbag2) ));
    Check( TRUE, pbag == pbag2 && pstg == pstg2 );

    RELEASE_INTERFACE(pbag2);
    RELEASE_INTERFACE(pstg2);

    Check( S_OK, pstg->QueryInterface( IID_IUnknown, reinterpret_cast<void**>(&punk1) ));
    Check( S_OK, pbag->QueryInterface( IID_IUnknown, reinterpret_cast<void**>(&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<void**>(&pbag) ));

    // Read and verify the auto-generated properties.

    rgcpropspecDefault[0] = static_cast<PROPID>(PID_CODEPAGE);
    rgcpropspecDefault[1] = static_cast<PROPID>(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<void**>(&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<LONG>(j);
        Check( S_OK, SafeArrayPutElement( rgpsa[0], rgIndices, &lVal ));

        BSTR bstrVal = SysAllocString( OLESTR("0 BSTR Val") );
        *bstrVal = OLESTR('0') + static_cast<OLECHAR>(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<VARIANT*>(&rgcpropvar[i])->parray = rgpsa[0];
    i++;

    rgcpropvar[i].vt = VT_ARRAY | VT_BSTR;
    reinterpret_cast<VARIANT*>(&rgcpropvar[i])->parray = rgpsa[1];
    i++;

    rgcpropvar[i].vt = VT_ARRAY | VT_VARIANT;
    reinterpret_cast<VARIANT*>(&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<SERIALIZEDPROPERTYVALUE*>(rgb),
                        &cbWritten, PID_FIRST_USABLE,
                        FALSE,
                        &cIndirect,
                        &status );
        Check( TRUE, NT_SUCCESS(status) );
        Check( TRUE, NULL != pprop );

        cbAsVariant = StgPropertyLengthAsVariantWrapper(
                                reinterpret_cast<SERIALIZEDPROPERTYVALUE*>(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<SERIALIZEDPROPERTYVALUE*>(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<void**>(&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<void**>(&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<OLECHAR>(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<void**>(&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<void**>(&pBag1) ));

    Check( S_OK, g_pfnStgOpenStorageEx( ocsFile,
                                   STGM_READ | STGM_SHARE_DENY_WRITE,
                                   STGFMT_ANY,
                                   0L, NULL, NULL,
                                   IID_IPropertyBagEx,
                                   reinterpret_cast<void**>(&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<void**>(&pStorage) ));


    Check( S_OK, pStorage->QueryInterface( IID_IPropertySetStorage,
                                           reinterpret_cast<void**>(&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<long>(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<void**>(&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<void**>(&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<void**>(&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<void**>(&pbagex) ));
    DeleteBagExProperties( pbagex, OLESTR("") );
    RELEASE_INTERFACE(pbagex);

    Check( S_OK, pstg->QueryInterface( IID_IPropertyBag, reinterpret_cast<void**>(&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<void**>(&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<IUnknown*>(pStgObjectWritten);

    rgvarWritten[1].vt = VT_BYREF | VT_UNKNOWN;
    IUnknown *punkByRefVal = static_cast<IUnknown*>(pStmObjectWritten);
    rgvarWritten[1].ppunkVal = &punkByRefVal;

    rgvarRead[0].vt = VT_UNKNOWN;
    rgvarRead[0].punkVal = static_cast<IUnknown*>(pStgObjectRead);
    rgvarRead[1].vt = VT_UNKNOWN;
    rgvarRead[1].punkVal = static_cast<IUnknown*>(pStmObjectRead);

    OLECHAR *rgoszName[2] = { OLESTR("VtUnknown (persisted as Storage)"),
                              OLESTR("ByRef VtUnknown (persisted as Stream)") };

    Check( S_OK, pbag->WriteMultiple( 2, rgoszName, reinterpret_cast<PROPVARIANT*>(rgvarWritten) ));
    Check( S_OK, pbag->ReadMultiple( 2, rgoszName, reinterpret_cast<PROPVARIANT*>(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<void**>(&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<void**>(&pbag) ));
    Check( S_OK, pstg->QueryInterface( IID_IPropertyBagEx,
                                reinterpret_cast<void**>(&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<VARIANT*>(&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<void**>(&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<PROPVARIANT*>(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<PROPVARIANT*>(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<void**>(&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<void**>(&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<PROPVARIANT*>(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<void**>(&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<void**>(&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<void**>(&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<void**>(&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<void**>(&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<void**>(&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<LONG>(i);
        Check( S_OK, SafeArrayPutElement( rgpsa[0], rgIndices, &lVal ));

        // Add a BSTR
        BSTR bstrVal = SysAllocString( OLESTR("0 BSTR Val") );
        *bstrVal = OLESTR('0') + static_cast<OLECHAR>(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<PROPVARIANT*>(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<PROPVARIANT*>(&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<PROPVARIANT*>(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<PROPVARIANT*>(rgpropvarCopy) ));
    Check( S_OK, g_pfnFreePropVariantArray( sizeof(rgvarRead)/sizeof(rgvarRead[0]),
                                            reinterpret_cast<PROPVARIANT*>(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<void**>(&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<void**>(&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<void**>(&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<void**>(&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<CLSID>(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<void**>(&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<void**>(&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<void**>(&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<long>(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<IPropertySetStorage*>(&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<void**>(&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;i<celt;i++)
    {
        if (ocscmp(OLESTR("CONTENTS"), stat[i].pwcsName) != 0)
                        pStgPropSet->DestroyElement(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; l<Actual; l++)
                {
                    PROPVARIANT VarRead[3];
                    Check(S_FALSE, pPropSet->ReadMultiple(l, ps, VarRead));
                    Check(S_OK, pPropSet->WritePropertyNames(l, rgpropid, rglpostrName));
                    Check(S_FALSE, pPropSet->ReadMultiple(l, ps, VarRead));

                    Check(S_OK, pPropSet->WriteMultiple(l, ps, pVar, 1000));
                    Check(S_OK, pPropSet->ReadMultiple(l, ps, VarRead));
                    Check(S_OK, 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; c<l; c++)
                    {
                        Check( 0, ocscmp(rglpostrNameCheck[c], rglpostrName[c]) );
                        delete [] rglpostrNameCheck[c];
                    }
                    Check(S_OK, pPropSet->DeletePropertyNames(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<void**>(&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; i<WORDPROPS+2; i++)
    {
        propspec[i].ulKind = PRSPEC_PROPID;
        propspec[i].propid = (PROPID)i;
    }

    PROPVARIANT propvar[WORDPROPS+2];

    Check(S_OK, pPropStg->ReadMultiple(WORDPROPS, propspec+2, propvar+2));

    for (i=2; i<WORDPROPS+2; i++)
    {
        if ( propvar[i].vt != avt[i-2].vt )
        {
            PRINTF( " PROPTEST: 0x%x retrieved type 0x%x, expected type 0x%x\n",
                    i, propvar[i].vt, avt[i-2].vt );
            Check(TRUE, propvar[i].vt == avt[i-2].vt);
        }

        switch (propvar[i].vt)
        {
        case VT_LPSTR:
            Check(TRUE, strcmp(propvar[i].pszVal, (char*)avt[i-2].pv)==0);
            break;
        case VT_I4:
            Check(TRUE, (ULONG_PTR) propvar[i].lVal == (ULONG_PTR)avt[i-2].pv);
            break;
        }
    }

    g_pfnFreePropVariantArray( WORDPROPS, propvar+2 );

    RELEASE_INTERFACE( pPropStg );
    RELEASE_INTERFACE( pPropSetStg );
    Check( 0, RELEASE_INTERFACE(pStg) );
}


void
test_IEnumSTATPROPSTG(IStorage *pstgTemp)
{
    Status( "IEnumSTATPROPSTG\n" );

    PROPID apropid[8];
    LPOLESTR alpostrName[8];
    OLECHAR aosz[8][32];
    PROPID PropId=2;
    PROPSPEC ps[8];

    FMTID fmtid;
    IPropertyStorage *pPropStg;

    IPropertySetStorage *pPropSetStg = NULL; // TSafeStorage< IPropertySetStorage > 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; i<CPROPERTIES; i++)
        {
            apropid[i] = PropId++;
            if (outer & 1)
            {
                ps[i].ulKind = PRSPEC_LPWSTR;
                PROPGENPROPERTYNAME( aosz[i], apropid[i] );
                ps[i].lpwstr = aosz[i];
            }
            else
            {
                ps[i].ulKind = PRSPEC_PROPID;
                ps[i].propid = apropid[i];
            }
        }

        if (outer & 1)
        {
            Check(S_OK, pPropStg->WritePropertyNames(CPROPERTIES, apropid, alpostrName));
        }

        PROPVARIANT *pVar = gp.GetNext(CPROPERTIES, NULL, TRUE, (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; c<CPROPERTIES - CPROPERTIES/2; c++)
        {
            STATPROPSTG CloneStat;
            Check(S_OK, penum2->Next(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; d<CPROPERTIES; d++)
        {
            // d is for delete

            BOOL afFound[CPROPERTIES];
            ULONG cTotal = 0;

            Check(S_OK, penum->Next(CPROPERTIES-d, StatBuffer, &celt));
            Check(TRUE, celt == CPROPERTIES-d);
            penum->Reset();

            memset(afFound, 0, sizeof(afFound));

            for (ULONG iProperty=0; iProperty<CPROPERTIES; iProperty++)
            {

                // Search the StatBuffer for this property.

                for (ULONG iSearch=0; iSearch<CPROPERTIES-d; iSearch++)
                {

                    // Compare this entry in the StatBuffer to the property for which we're searching.
                    // Use the lpstrName or propid, whichever is appropriate for this pass (indicated
                    // by 'outer').

                    if ( ( (outer & 1) == 1 && 0 == ocscmp(StatBuffer[iSearch].lpwstrName, ps[iProperty].lpwstr) )
                         ||
                         ( (outer & 1) == 0 && StatBuffer[iSearch].propid == apropid[iProperty] )
                       )
                    {
                        ASSERT (!afFound[iSearch]);
                        afFound[iSearch] = TRUE;
                        cTotal++;
                        break;
                    }
                }
            }

            CleanStat(celt, StatBuffer);

            Check(TRUE, cTotal == CPROPERTIES-d);

            Check(S_OK, pPropStg->DeleteMultiple(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<void**>(&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<IPropertySetStorage> 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<void**>(&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<void**>(&pstg2) ));
        Check( S_OK, pstg2->QueryInterface( IID_IPropertySetStorage, reinterpret_cast<void**>(&ppropsetstg2) ));
        Check( S_OK, ppropsetstg2->QueryInterface( IID_IStorage, reinterpret_cast<void**>(&pstg3) ));
        Check( TRUE, pstg2 == pstg3 );

        Check( S_OK, pstg3->QueryInterface( IID_IPropertySetStorage, reinterpret_cast<void**>(&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<void**>(&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<void**>(&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<void**>(&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<void**>(&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<void**>(&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<void**>(&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<void**>(&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<void**>(&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 );

}