2575 lines
68 KiB
C++
2575 lines
68 KiB
C++
|
|
|
|
|
|
//+============================================================================
|
|
//
|
|
// File: bag.cxx
|
|
//
|
|
// This file implements the IPropertyBagEx implementation used
|
|
// in docfile, NSS (Native Structured Storage) and NFF (NTFS
|
|
// Flat-File). IPropertyBagEx is required to be QI-able
|
|
// from IStorage, so CPropertyBagEx is instantiated as a
|
|
// data member of the various IStorage implementations.
|
|
//
|
|
// History:
|
|
//
|
|
// 3/10/98 MikeHill - Don't create a property set until the bag
|
|
// is modified
|
|
// - Add a constructor & IUnknown so that this class can
|
|
// be instantiated standalone (used by
|
|
// StgCreate/OpenStorageOnHandle).
|
|
// - Added dbg tracing.
|
|
// - Removed Commit-after-writes.
|
|
// - Chagned from STATPROPS structure to STATPROPBAG.
|
|
// - Added parameter validation.
|
|
// - Added a ShutDown method.
|
|
// 5/6/98 MikeHill
|
|
// - Split IPropertyBag from IPropertyBagEx, new BagEx
|
|
// DeleteMultiple method.
|
|
// - Added support for VT_UNKNOWN/VT_DISPATCH.
|
|
// - Beefed up dbg outs.
|
|
// - Use CoTaskMem rather than new/delete.
|
|
// 5/18/98 MikeHill
|
|
// - Moved some initialization from the constructors
|
|
// to a new Init method.
|
|
// - Disallow non-Variant types in IPropertyBag::Write.
|
|
// 6/11/98 MikeHill
|
|
// - Add the new reserved parameter to DeleteMultiple.
|
|
//
|
|
//+============================================================================
|
|
|
|
|
|
#include <pch.cxx>
|
|
#include "chgtype.hxx"
|
|
|
|
#define FORCE_EXCLUSIVE(g) ((g & ~STGM_SHARE_MASK) | STGM_SHARE_EXCLUSIVE)
|
|
//
|
|
// Add this prototype to chgtype.hxx when chgtype.hxx is finalized.
|
|
//
|
|
HRESULT ImplicitPropVariantToVariantChangeType(
|
|
PROPVARIANT *pDest,
|
|
const PROPVARIANT *pSrc,
|
|
LCID lcid );
|
|
|
|
HRESULT HrPropVarVECTORToSAFEARRAY(
|
|
PROPVARIANT *pDest,
|
|
const PROPVARIANT *pSrc,
|
|
LCID lcid,
|
|
VARTYPE vtCoerce );
|
|
|
|
HRESULT LoadPropVariantFromVectorElem(
|
|
PROPVARIANT *pDest,
|
|
const PROPVARIANT *pSrc,
|
|
int idx);
|
|
|
|
HRESULT PutPropVariantDataIntoSafeArray(
|
|
SAFEARRAY *psa,
|
|
const PROPVARIANT *pSrc,
|
|
int idx);
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: CPropertyBagEx
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
// Normal constructor
|
|
CPropertyBagEx::CPropertyBagEx( DWORD grfMode )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
propITrace( "CPropertyBagEx::CPropertyBagEx(IPropertySetStorage)" );
|
|
propTraceParameters(( "0x%x", grfMode ));
|
|
|
|
// mask out the stgm_transacted bit; the bag is
|
|
// interpreted as part of the parent storage, and should therefore
|
|
// be in the storage's transaction set.
|
|
|
|
_grfMode = grfMode & ~STGM_TRANSACTED;
|
|
_lcid = LOCALE_NEUTRAL;
|
|
_fLcidInitialized = FALSE;
|
|
|
|
_ppropstg = NULL;
|
|
_ppropsetstgContainer = NULL;
|
|
_pBlockingLock = NULL;
|
|
|
|
// We won't use this ref-count, we'll rely on _ppropsetstgContainer
|
|
_cRefs = 0;
|
|
|
|
} // CPropertyBagEx::CPropertyBagEx(grfMode)
|
|
|
|
// Constructor for building an IPropertyBagEx on an IPropertyStorage
|
|
CPropertyBagEx::CPropertyBagEx( DWORD grfMode,
|
|
IPropertyStorage *ppropstg,
|
|
IBlockingLock *pBlockingLock )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
propITrace( "CPropertyBagEx::CPropertyBagEx(IPropertyStorage)" );
|
|
propTraceParameters(( "0x%x, 0x%x", ppropstg, pBlockingLock ));
|
|
|
|
new(this) CPropertyBagEx( grfMode );
|
|
|
|
Init (NULL, pBlockingLock);
|
|
|
|
// We addref and keep the IPropertyStorage
|
|
_ppropstg = ppropstg;
|
|
_ppropstg->AddRef();
|
|
|
|
// We keep and addref the BlockingLock
|
|
_pBlockingLock->AddRef();
|
|
|
|
// We also keep our own ref-count
|
|
_cRefs = 1;
|
|
|
|
} // CPropertyBagEx::CPropertyBagEx(grfMode, IPropertyStorage*, IBlockingLock*)
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: CPropertyBagEx::Init (non-interface method)
|
|
//
|
|
// This method is used by the caller to provide the IPropertySetStorage
|
|
// (and IBlockingLock) that is to contain the property bag.
|
|
// This must be called after the simple grfMode-based constructor.
|
|
//
|
|
// This method does *not* addref these interfaces.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
void
|
|
CPropertyBagEx::Init( IPropertySetStorage *ppropsetstg, IBlockingLock *pBlockingLock )
|
|
{
|
|
DfpAssert( NULL == _ppropsetstgContainer
|
|
&&
|
|
NULL == _pBlockingLock
|
|
&&
|
|
NULL == _ppropstg );
|
|
|
|
_ppropsetstgContainer = ppropsetstg;
|
|
_pBlockingLock = pBlockingLock;
|
|
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: ~CPropertyBagEx
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
CPropertyBagEx::~CPropertyBagEx()
|
|
{
|
|
#if DBG
|
|
IStorageTest *ptest = NULL;
|
|
HRESULT hr = S_OK;
|
|
|
|
if( NULL != _ppropstg )
|
|
{
|
|
hr = _ppropstg->QueryInterface( IID_IStorageTest, reinterpret_cast<void**>(&ptest) );
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
hr = ptest->IsDirty();
|
|
DfpAssert( S_FALSE == hr || E_NOINTERFACE == hr );
|
|
RELEASE_INTERFACE(ptest);
|
|
}
|
|
}
|
|
#endif // #if DBG
|
|
|
|
ShutDown();
|
|
}
|
|
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: OpenPropStg
|
|
//
|
|
// Open the bag's IPropertyStorage. If FILE_OPEN_IF is specified, we open
|
|
// it only if it already exists. if FILE_OPEN is specified, we'll create it
|
|
// if necessary.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
|
|
HRESULT
|
|
CPropertyBagEx::OpenPropStg( DWORD dwDisposition )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
IPropertyStorage *ppropstg = NULL;
|
|
PROPVARIANT propvarCodePage;
|
|
PROPSPEC propspecCodePage;
|
|
STATPROPSETSTG statpropsetstg;
|
|
|
|
propITrace( "CPropertyBagEx::OpenPropStg" );
|
|
propTraceParameters(( "%s", FILE_OPEN_IF == dwDisposition
|
|
? "OpenIf"
|
|
: (FILE_OPEN == dwDisposition ? "Open" : "Unknown disposition")
|
|
));
|
|
|
|
DfpAssert( FILE_OPEN == dwDisposition || FILE_OPEN_IF == dwDisposition );
|
|
PropVariantInit( &propvarCodePage );
|
|
|
|
// Does the IPropertyStorage need to be opened?
|
|
|
|
if( NULL == _ppropstg )
|
|
{
|
|
// Try to open the property storage
|
|
// We have to open exclusive, no matter how the parent was opened.
|
|
|
|
hr = _ppropsetstgContainer->Open( FMTID_PropertyBag,
|
|
FORCE_EXCLUSIVE(_grfMode) & ~STGM_CREATE,
|
|
&ppropstg );
|
|
|
|
if( STG_E_FILENOTFOUND == hr && FILE_OPEN == dwDisposition )
|
|
{
|
|
// It didn't exist, and we're supposed to remedy that.
|
|
hr = _ppropsetstgContainer->Create( FMTID_PropertyBag, NULL,
|
|
PROPSETFLAG_CASE_SENSITIVE | PROPSETFLAG_NONSIMPLE,
|
|
FORCE_EXCLUSIVE(_grfMode) | STGM_CREATE,
|
|
&ppropstg );
|
|
}
|
|
else if( STG_E_FILENOTFOUND == hr && FILE_OPEN_IF == dwDisposition )
|
|
{
|
|
// This is an expected error.
|
|
propSuppressExitErrors();
|
|
}
|
|
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
// Verify that this is a Unicode property set
|
|
|
|
propspecCodePage.ulKind = PRSPEC_PROPID;
|
|
propspecCodePage.propid = PID_CODEPAGE;
|
|
hr = ppropstg->ReadMultiple( 1, &propspecCodePage, &propvarCodePage );
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
if( VT_I2 != propvarCodePage.vt
|
|
||
|
|
CP_WINUNICODE != propvarCodePage.iVal )
|
|
{
|
|
propDbg(( DEB_ERROR, "Non-unicode codepage found in supposed property bag (0x%x)\n",
|
|
propvarCodePage.iVal ));
|
|
hr = STG_E_INVALIDHEADER;
|
|
goto Exit;
|
|
}
|
|
|
|
// Also verify that the property set is non-simple and case-sensitive
|
|
|
|
hr = ppropstg->Stat( &statpropsetstg );
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
if( !(PROPSETFLAG_CASE_SENSITIVE & statpropsetstg.grfFlags)
|
|
||
|
|
!(PROPSETFLAG_NONSIMPLE & statpropsetstg.grfFlags) )
|
|
{
|
|
propDbg(( DEB_ERROR, "Supposed property bag isn't case sensitive or isn't non-simple (0x%x)\n",
|
|
statpropsetstg.grfFlags ));
|
|
hr = STG_E_INVALIDHEADER;
|
|
goto Exit;
|
|
}
|
|
|
|
_ppropstg = ppropstg;
|
|
ppropstg = NULL;
|
|
}
|
|
|
|
// Even if we already have an open IPropertyStorage, we may not have yet read the
|
|
// Locale ID (this happens in the case where the CPropertyBagEx(IPropertyStorage*)
|
|
// constructor is used).
|
|
|
|
if( !_fLcidInitialized )
|
|
{
|
|
hr = GetLCID();
|
|
if( FAILED(hr) ) goto Exit;
|
|
}
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
hr = S_OK;
|
|
|
|
Exit:
|
|
|
|
DfpVerify( 0 == RELEASE_INTERFACE(ppropstg) );
|
|
return(hr);
|
|
|
|
} // CPropertyBagEx::OpenPropStg
|
|
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: GetLCID
|
|
//
|
|
// Get the LocalID from the property set represented by _ppropstg.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CPropertyBagEx::GetLCID()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
PROPSPEC propspecLCID;
|
|
PROPVARIANT propvarLCID;
|
|
|
|
propITrace( "CPropertyBagEx::GetLCID" );
|
|
|
|
propspecLCID.ulKind = PRSPEC_PROPID;
|
|
propspecLCID.propid = PID_LOCALE;
|
|
|
|
if( SUCCEEDED( hr = _ppropstg->ReadMultiple( 1, &propspecLCID, &propvarLCID ))
|
|
&&
|
|
VT_UI4 == propvarLCID.vt )
|
|
{
|
|
_lcid = propvarLCID.uiVal;
|
|
}
|
|
else if( S_FALSE == hr )
|
|
{
|
|
_lcid = GetUserDefaultLCID();
|
|
}
|
|
|
|
PropVariantClear( &propvarLCID );
|
|
return( hr );
|
|
|
|
} // CPropertyBagEx::GetLCID()
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: IUnknown methods
|
|
//
|
|
// If we have a parent (_ppropsetstgParent), we forward all calls to there.
|
|
// Otherwise, we implement all calls here. Whether or not we have a parent
|
|
// depends on which of the two constructors is called. In NFF, NSS, and docfile,
|
|
// we have a parent. When creating a standalone property bag implementation
|
|
// (in StgCreate/OpenStorageOnHandle), we have no such parent.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
HRESULT STDMETHODCALLTYPE
|
|
CPropertyBagEx::QueryInterface(
|
|
/* [in] */ REFIID riid,
|
|
/* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject)
|
|
{
|
|
// Do we have a parent?
|
|
if( NULL != _ppropsetstgContainer )
|
|
{
|
|
// Yes, refer the call
|
|
DfpAssert( 0 == _cRefs );
|
|
return( _ppropsetstgContainer->QueryInterface( riid, ppvObject ));
|
|
}
|
|
else
|
|
{
|
|
// No, we have no parent.
|
|
if( IID_IPropertyBagEx == riid
|
|
||
|
|
IID_IUnknown == riid )
|
|
{
|
|
AddRef();
|
|
*ppvObject = static_cast<IPropertyBagEx*>(this);
|
|
return( S_OK );
|
|
}
|
|
|
|
else if( IID_IPropertyBag == riid )
|
|
{
|
|
AddRef();
|
|
*ppvObject = static_cast<IPropertyBag*>(this);
|
|
return( S_OK );
|
|
}
|
|
}
|
|
|
|
return( E_NOINTERFACE );
|
|
}
|
|
|
|
|
|
ULONG STDMETHODCALLTYPE
|
|
CPropertyBagEx::AddRef( void)
|
|
{
|
|
// Do we have a parent?
|
|
if( NULL != _ppropsetstgContainer )
|
|
{
|
|
// Yes. The parent does the ref-counting
|
|
DfpAssert( 0 == _cRefs );
|
|
return( _ppropsetstgContainer->AddRef() );
|
|
}
|
|
else
|
|
{
|
|
// No. We don't have a parent, and we must do the ref-counting.
|
|
LONG lRefs = InterlockedIncrement( &_cRefs );
|
|
return( lRefs );
|
|
}
|
|
}
|
|
|
|
|
|
ULONG STDMETHODCALLTYPE
|
|
CPropertyBagEx::Release( void)
|
|
{
|
|
// Do we have a parent?
|
|
if( NULL != _ppropsetstgContainer )
|
|
{
|
|
// Yes. The container does the ref-counting
|
|
DfpAssert( 0 == _cRefs );
|
|
return( _ppropsetstgContainer->Release() );
|
|
}
|
|
else
|
|
{
|
|
// No. We don't have a parent, and we must do the ref-counting.
|
|
LONG lRefs = InterlockedDecrement( &_cRefs );
|
|
if( 0 == lRefs )
|
|
{
|
|
// Only in this case (where we have no parent) do we release
|
|
// the IBlockingLock
|
|
RELEASE_INTERFACE( _pBlockingLock );
|
|
|
|
delete this;
|
|
}
|
|
return( lRefs );
|
|
}
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: Read/Write (IPropertyBag)
|
|
//
|
|
// These methods simply thunk to ReadMultiple/WriteMultiple.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
HRESULT STDMETHODCALLTYPE
|
|
CPropertyBagEx::Read(
|
|
/* [in] */ LPCOLESTR pszPropName,
|
|
/* [out][in] */ VARIANT __RPC_FAR *pVar,
|
|
/* [in] */ IErrorLog __RPC_FAR *pErrorLog)
|
|
{
|
|
HRESULT hr=S_OK, hr2=S_OK;
|
|
PROPVARIANT *ppropvar, propvarTmp;
|
|
|
|
ppropvar = reinterpret_cast<PROPVARIANT*>(pVar);
|
|
propvarTmp = *ppropvar;
|
|
|
|
hr = ReadMultiple(1, &pszPropName, &propvarTmp, pErrorLog );
|
|
if( !FAILED( hr ) )
|
|
{
|
|
hr2 = ImplicitPropVariantToVariantChangeType( ppropvar, &propvarTmp, _lcid );
|
|
PropVariantClear( &propvarTmp );
|
|
}
|
|
if( FAILED(hr2) )
|
|
return hr2;
|
|
else
|
|
return hr;
|
|
} // CPropertyBagEx::Read
|
|
|
|
|
|
HRESULT STDMETHODCALLTYPE
|
|
CPropertyBagEx::Write(
|
|
/* [in] */ LPCOLESTR pszPropName,
|
|
/* [in] */ VARIANT __RPC_FAR *pVar)
|
|
{
|
|
if( !IsVariantType( pVar->vt ))
|
|
return( STG_E_INVALIDPARAMETER );
|
|
|
|
return( WriteMultiple(1, &pszPropName, reinterpret_cast<PROPVARIANT*>(pVar) ));
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: CPropertyBagEx::ReadMultiple (IPropertyBagEx)
|
|
//
|
|
// This method calls down to IPropertyStorage::ReadMultiple. It has the
|
|
// additional behaviour of coercing the property types into those specified
|
|
// by the caller.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
HRESULT STDMETHODCALLTYPE
|
|
CPropertyBagEx::ReadMultiple(
|
|
/* [in] */ ULONG cprops,
|
|
/* [size_is][in] */ LPCOLESTR const __RPC_FAR rgoszPropNames[ ],
|
|
/* [size_is][out][in] */ PROPVARIANT __RPC_FAR rgpropvar[ ],
|
|
/* [in] */ IErrorLog __RPC_FAR *pErrorLog)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ULONG i;
|
|
PROPSPEC *rgpropspec = NULL; // PERF: use TStackBuffer
|
|
PROPVARIANT *rgpropvarRead = NULL;
|
|
BOOL fAtLeastOnePropertyRead = FALSE;
|
|
|
|
propXTrace( "CPropertyBagEx::ReadMultiple" );
|
|
|
|
_pBlockingLock->Lock( INFINITE );
|
|
|
|
// ----------
|
|
// Validation
|
|
// ----------
|
|
|
|
if (0 == cprops)
|
|
{
|
|
hr = S_OK;
|
|
goto Exit;
|
|
}
|
|
|
|
if (S_OK != (hr = ValidateInRGLPOLESTR( cprops, rgoszPropNames )))
|
|
goto Exit;
|
|
if (S_OK != (hr = ValidateOutRGPROPVARIANT( cprops, rgpropvar )))
|
|
goto Exit;
|
|
|
|
propTraceParameters(( "%d, 0x%x, 0x%x, 0x%x", cprops, rgoszPropNames, rgpropvar, pErrorLog ));
|
|
|
|
// --------------
|
|
// Initialization
|
|
// --------------
|
|
|
|
// Open the underlying property set if it exists.
|
|
|
|
hr = OpenPropStg( FILE_OPEN_IF );
|
|
if( STG_E_FILENOTFOUND == hr )
|
|
{
|
|
// The property storage for this bag doesn't even exist. So we simulate
|
|
// the reading non-extant properties by setting the vt's to emtpy and returning
|
|
// s_false.
|
|
|
|
for( ULONG i = 0; i < cprops; i++ )
|
|
rgpropvar[i].vt = VT_EMPTY;
|
|
|
|
hr = S_FALSE;
|
|
goto Exit;
|
|
}
|
|
else if( FAILED(hr) )
|
|
goto Exit;
|
|
|
|
|
|
// The property set existed and is now open.
|
|
|
|
// Alloc propspec & propvar arrays.
|
|
// PERF: Make these stack-based for typical-sized reads.
|
|
|
|
rgpropspec = reinterpret_cast<PROPSPEC*>
|
|
( CoTaskMemAlloc( cprops * sizeof(PROPSPEC) ));
|
|
if( NULL == rgpropspec )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
rgpropvarRead = reinterpret_cast<PROPVARIANT*>
|
|
( CoTaskMemAlloc( cprops * sizeof(PROPVARIANT) ));
|
|
if( NULL == rgpropvarRead )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
// Put the names into the propspecs.
|
|
|
|
for( i = 0; i < cprops; i++ )
|
|
{
|
|
PropVariantInit( &rgpropvarRead[i] );
|
|
rgpropspec[i].ulKind = PRSPEC_LPWSTR;
|
|
rgpropspec[i].lpwstr = const_cast<LPOLESTR>(rgoszPropNames[i]);
|
|
}
|
|
|
|
// ----------------------------
|
|
// Read & coerce the properties
|
|
// ----------------------------
|
|
|
|
// Read the properties into the local PropVar array
|
|
hr = _ppropstg->ReadMultiple(cprops, rgpropspec, rgpropvarRead );
|
|
if( FAILED(hr) ) goto Exit;
|
|
if( S_FALSE != hr )
|
|
fAtLeastOnePropertyRead = TRUE;
|
|
|
|
// Coerce the properties as necessary
|
|
|
|
for( i = 0; i < cprops; i++ )
|
|
{
|
|
// If the caller requested a type (something other than VT_EMPTY), and the
|
|
// requested type isn't the same as the read type, then a coercion is necessary.
|
|
if( VT_EMPTY != rgpropvar[i].vt && rgpropvarRead[i].vt != rgpropvar[i].vt )
|
|
{
|
|
PROPVARIANT propvar;
|
|
PropVariantInit( &propvar );
|
|
|
|
// Does this property require conversion
|
|
// (i.e. to a VT_UNKNOWN/DISPATCH)?
|
|
if( PropertyRequiresConversion( rgpropvar[i].vt )
|
|
&&
|
|
( VT_STORED_OBJECT == rgpropvarRead[i].vt
|
|
||
|
|
VT_STREAMED_OBJECT == rgpropvarRead[i].vt
|
|
)
|
|
)
|
|
{
|
|
// Load the object from the stream/storage in rgpropvarRead
|
|
// into propvar.
|
|
|
|
propvar = rgpropvar[i];
|
|
hr = LoadObject( &propvar, &rgpropvarRead[i] );
|
|
if( FAILED(hr) )
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
// Coerce the property in rgpropvarRead into propvar, according
|
|
// to the caller-requested type (which is specified in rgpropvar).
|
|
|
|
hr = PropVariantChangeType( &propvar, &rgpropvarRead[i],
|
|
_lcid, 0, rgpropvar[i].vt );
|
|
if( FAILED(hr) )
|
|
{
|
|
propDbg(( DEB_ERROR, "CPropertyBagEx(0x%x), PropVariantChangeType"
|
|
"(0x%x,0x%x,%d,%d,%d) returned %08x\n",
|
|
this, &propvar, &rgpropvarRead[i], _lcid, 0, rgpropvar[i].vt, hr ));
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Get rid of the value original read, and keep the coerced value.
|
|
PropVariantClear( &rgpropvarRead[i] );
|
|
rgpropvarRead[i] = propvar;
|
|
}
|
|
} // for( i = 0; i < cprops; i++ )
|
|
|
|
// No errors from here to the end.
|
|
// Copy the PropVars from the local array to the caller's
|
|
|
|
for( i = 0; i < cprops; i++ )
|
|
rgpropvar[i] = rgpropvarRead[i];
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
CoTaskMemFree( rgpropvarRead );
|
|
rgpropvarRead = NULL;
|
|
|
|
if( SUCCEEDED(hr) )
|
|
hr = fAtLeastOnePropertyRead ? S_OK : S_FALSE;
|
|
|
|
|
|
Exit:
|
|
|
|
// We needn't free the rgpropspec[*].lpwstr members, they point
|
|
// into rgszPropName
|
|
if( NULL != rgpropspec )
|
|
CoTaskMemFree( rgpropspec );
|
|
|
|
if( NULL != rgpropvarRead )
|
|
{
|
|
for( i = 0; i < cprops; i++ )
|
|
PropVariantClear( &rgpropvarRead[i] );
|
|
CoTaskMemFree( rgpropvarRead );
|
|
}
|
|
|
|
_pBlockingLock->Unlock();
|
|
return( hr );
|
|
|
|
} // CPropertyBagEx::ReadMultiple
|
|
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: CPropertyBagEx::LoadObject
|
|
//
|
|
// This method loads an IUnknown or IDispatch object from its persisted state
|
|
// in a stream or storage (by QI-ing for IPersistStream or IPersistStorage,
|
|
// respectively).
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CPropertyBagEx::LoadObject( OUT PROPVARIANT *ppropvarOut, IN PROPVARIANT *ppropvarIn ) const
|
|
{
|
|
HRESULT hr = S_OK;
|
|
IUnknown *punk = NULL;
|
|
IPersistStorage *pPersistStg = NULL;
|
|
IPersistStream *pPersistStm = NULL;
|
|
|
|
propITrace( "CPropertyBagEx::LoadObject" );
|
|
|
|
DfpAssert( VT_STREAMED_OBJECT == ppropvarIn->vt || VT_STORED_OBJECT == ppropvarIn->vt );
|
|
DfpAssert( VT_UNKNOWN == ppropvarOut->vt || VT_DISPATCH == ppropvarOut->vt );
|
|
|
|
punk = ppropvarOut->punkVal;
|
|
DfpAssert( reinterpret_cast<void*>(&ppropvarOut->punkVal)
|
|
==
|
|
reinterpret_cast<void*>(&ppropvarOut->pdispVal) );
|
|
|
|
// --------------------------
|
|
// Read in an IPersistStorage
|
|
// --------------------------
|
|
|
|
if( VT_STORED_OBJECT == ppropvarIn->vt )
|
|
{
|
|
DfpAssert( NULL != ppropvarIn->pStorage );
|
|
|
|
// Get an IUnknown if the caller didn't provide one.
|
|
|
|
if( NULL == punk )
|
|
{
|
|
STATSTG statstg;
|
|
|
|
hr = ppropvarIn->pStorage->Stat( &statstg, STATFLAG_NONAME );
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
hr = CoCreateInstance( statstg.clsid, NULL, CLSCTX_ALL, IID_IUnknown,
|
|
reinterpret_cast<void**>(&punk) );
|
|
if( FAILED(hr) ) goto Exit;
|
|
}
|
|
|
|
// QI for IPersistStorage
|
|
|
|
hr = punk->QueryInterface( IID_IPersistStorage, reinterpret_cast<void**>(&pPersistStg) );
|
|
if( FAILED(hr) )
|
|
{
|
|
propDbg(( DEB_ERROR, "Caller didn't provide IPersistStorage in IProeprtyBagEx::ReadMultiple (%08x)", hr ));
|
|
goto Exit;
|
|
}
|
|
|
|
// IPersist::Load the storage
|
|
|
|
hr = pPersistStg->Load( ppropvarIn->pStorage );
|
|
if( FAILED(hr) )
|
|
{
|
|
propDbg(( DEB_ERROR, "Failed IPersistStorage::Load in IPropretyBagEx::ReadMultiple (%08x)", hr ));
|
|
goto Exit;
|
|
}
|
|
|
|
} // if( VT_STORED_OBJECT == ppropvarIn->vt )
|
|
|
|
|
|
// -------------------------
|
|
// Read in an IPersistStream
|
|
// -------------------------
|
|
|
|
else
|
|
{
|
|
DfpAssert( VT_STREAMED_OBJECT == ppropvarIn->vt );
|
|
DfpAssert( NULL != ppropvarIn->pStream );
|
|
|
|
CLSID clsid;
|
|
ULONG cbRead;
|
|
|
|
// Skip over the clsid
|
|
|
|
hr = ppropvarIn->pStream->Read( &clsid, sizeof(clsid), &cbRead );
|
|
if( FAILED(hr) ) goto Exit;
|
|
if( sizeof(clsid) != cbRead )
|
|
{
|
|
hr = STG_E_INVALIDHEADER;
|
|
propDbg(( DEB_ERROR, "Clsid missing in VT_STREAMED_OBJECT in IPropertyBagEx::ReadMultiple (%d bytes)",
|
|
cbRead ));
|
|
goto Exit;
|
|
}
|
|
|
|
// Get an IUnknown if the caller didn't provide one.
|
|
|
|
if( NULL == punk )
|
|
{
|
|
hr = CoCreateInstance( clsid, NULL, CLSCTX_ALL, IID_IUnknown,
|
|
reinterpret_cast<void**>(&punk) );
|
|
if( FAILED(hr) ) goto Exit;
|
|
}
|
|
|
|
// QI for the IPersistStream
|
|
|
|
hr = punk->QueryInterface( IID_IPersistStream, reinterpret_cast<void**>(&pPersistStm) );
|
|
if( FAILED(hr) )
|
|
{
|
|
propDbg(( DEB_ERROR, "Caller didn't provide IPersistStream in IPropertyBagEx::ReadMultiple (%08x)", hr ));
|
|
goto Exit;
|
|
}
|
|
|
|
// Load the remainder of the stream
|
|
|
|
IFDBG( ULONG cRefs = GetRefCount( ppropvarIn->pStream ));
|
|
|
|
hr = pPersistStm->Load( ppropvarIn->pStream );
|
|
if( FAILED(hr) )
|
|
{
|
|
propDbg(( DEB_ERROR, "Failed IPersistStream::Load in IPropertyBagEx::ReadMultiple (%08x)", hr ));
|
|
goto Exit;
|
|
}
|
|
|
|
DfpAssert( GetRefCount( ppropvarIn->pStream ) == cRefs );
|
|
|
|
} // if( VT_STORED_OBJECT == ppropvarIn->vt ) ... else
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
ppropvarOut->punkVal = punk;
|
|
punk = NULL;
|
|
hr = S_OK;
|
|
|
|
Exit:
|
|
|
|
RELEASE_INTERFACE( pPersistStg );
|
|
RELEASE_INTERFACE( pPersistStm );
|
|
RELEASE_INTERFACE( punk );
|
|
|
|
return( hr );
|
|
|
|
} // CPropertyBagEx::LoadObject
|
|
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: CPropertyBagEx::WriteMultiple (IPropertyBagEx)
|
|
//
|
|
// This method calls down to IPropertyStorage::WriteMultiple. Additionally,
|
|
// this method is atomic. So if the WriteMultiple fails, the IPropertyStorage
|
|
// is reverted and reopened.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
HRESULT STDMETHODCALLTYPE
|
|
CPropertyBagEx::WriteMultiple(
|
|
/* [in] */ ULONG cprops,
|
|
/* [size_is][in] */ LPCOLESTR const __RPC_FAR rgoszPropNames[ ],
|
|
/* [size_is][in] */ const PROPVARIANT __RPC_FAR rgpropvar[ ])
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ULONG i;
|
|
PROPSPEC *prgpropspec = NULL;
|
|
BOOL fInterfaces = FALSE;
|
|
|
|
_pBlockingLock->Lock( INFINITE );
|
|
|
|
CStackPropVarArray rgpropvarCopy;
|
|
propXTrace( "CPropertyBagEx::WriteMultiple" );
|
|
|
|
hr = rgpropvarCopy.Init( cprops );
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
|
|
// ----------
|
|
// Validation
|
|
// ----------
|
|
|
|
if (0 == cprops)
|
|
{
|
|
hr = S_OK;
|
|
goto Exit;
|
|
}
|
|
|
|
if (S_OK != (hr = ValidateInRGLPOLESTR( cprops, rgoszPropNames )))
|
|
goto Exit;
|
|
if (S_OK != (hr = ValidateInRGPROPVARIANT( cprops, rgpropvar )))
|
|
goto Exit;
|
|
|
|
propTraceParameters(("%lu, 0x%x, 0x%x", cprops, rgoszPropNames, rgpropvar));
|
|
|
|
// --------------
|
|
// Initialization
|
|
// --------------
|
|
|
|
// Open the property storage if it isn't already, creating if necessary.
|
|
|
|
hr = OpenPropStg( FILE_OPEN );
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
// PERF: Create a local array of propspecs for the WriteMultiple call
|
|
|
|
prgpropspec = reinterpret_cast<PROPSPEC*>( CoTaskMemAlloc( cprops * sizeof(PROPSPEC) ));
|
|
if( NULL == prgpropspec )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
// Set up the PROPSPEC and PROPVARIANT arrays to be written.
|
|
|
|
for( i = 0; i < cprops; i++ )
|
|
{
|
|
// Point the propspecs at the caller-provided names
|
|
|
|
prgpropspec[i].ulKind = PRSPEC_LPWSTR;
|
|
prgpropspec[i].lpwstr = const_cast<LPOLESTR>(rgoszPropNames[i]);
|
|
|
|
// Make a copy of the input PropVariant array, converting VT_UNKNOWN and
|
|
// VT_DISPATCH into VT_EMPTY. We'll handle these after the initial write.
|
|
|
|
rgpropvarCopy[i] = rgpropvar[i];
|
|
if( PropertyRequiresConversion( rgpropvarCopy[i].vt ))
|
|
{
|
|
if( NULL == rgpropvarCopy[i].punkVal )
|
|
{
|
|
hr = E_INVALIDARG;
|
|
goto Exit;
|
|
}
|
|
|
|
// We'll handle this particular property after the initial WriteMultiple.
|
|
fInterfaces = TRUE;
|
|
PropVariantInit( &rgpropvarCopy[i] );
|
|
}
|
|
}
|
|
|
|
// --------------------
|
|
// Write the properties
|
|
// --------------------
|
|
|
|
// Write all the properties, though the VT_UNKNOWN and VT_DISPATCH have been
|
|
// switched to VT_EMPTY.
|
|
|
|
hr = _ppropstg->WriteMultiple(cprops, prgpropspec, rgpropvarCopy, PID_FIRST_USABLE );
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
// Now write the VT_UNKNOWN and VT_DISPATCH properties individually, converting
|
|
// first to VT_STREAMED_OBJECT or VT_STORED_OBJECT.
|
|
|
|
if( fInterfaces )
|
|
{
|
|
hr = WriteObjects( cprops, prgpropspec, rgpropvar );
|
|
if( FAILED(hr) ) goto Exit;
|
|
}
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
hr = S_OK;
|
|
|
|
Exit:
|
|
|
|
// We don't need to delete the lpwstr fields, they just point to the caller-provided
|
|
// names.
|
|
if( NULL != prgpropspec )
|
|
CoTaskMemFree( prgpropspec );
|
|
|
|
// We don't need to clear rgpropvarCopy; it wasn't a deep copy.
|
|
|
|
_pBlockingLock->Unlock();
|
|
return( hr );
|
|
|
|
} // CPropertyBagEx::WriteMultiple
|
|
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: CPropertyBagEx::WriteObjects
|
|
//
|
|
// This method writes VT_UNKNOWN and VT_DISPATCH properties. It scans the
|
|
// input rgpropvar array for such properties. For each that it finds, it QIs
|
|
// for IPersistStorage or IPersistStream (in that order), creates a
|
|
// VT_STORED_OBJECT/VT_STREAMED_OBJECT property (respectively), and persists the object
|
|
// to that property.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CPropertyBagEx::WriteObjects( IN ULONG cprops,
|
|
IN const PROPSPEC rgpropspec[],
|
|
IN const PROPVARIANT rgpropvar[] )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ULONG i;
|
|
|
|
propITrace( "CPropertyBagEx::WriteObjects" );
|
|
|
|
for( i = 0; i < cprops; i++ )
|
|
{
|
|
// Is this a type we need to handle?
|
|
|
|
if( PropertyRequiresConversion( rgpropvar[i].vt ))
|
|
{
|
|
hr = WriteOneObject( &rgpropspec[i], &rgpropvar[i] );
|
|
if( FAILED(hr) ) goto Exit;
|
|
}
|
|
}
|
|
|
|
hr = S_OK;
|
|
|
|
Exit:
|
|
|
|
return( hr );
|
|
|
|
} // CPropertyBagEx::WriteObjects
|
|
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
//
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CPropertyBagEx::WriteOneObject( const IN PROPSPEC *ppropspec, const IN PROPVARIANT *ppropvar )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
PROPVARIANT propvarNew;
|
|
IPersistStorage *pPersistStg = NULL;
|
|
IPersistStream *pPersistStm = NULL;
|
|
IUnknown *punk = NULL;
|
|
|
|
PropVariantInit( &propvarNew );
|
|
|
|
DfpAssert( PropertyRequiresConversion( ppropvar->vt ));
|
|
|
|
DfpAssert( reinterpret_cast<const void*const*>(&ppropvar->punkVal)
|
|
==
|
|
reinterpret_cast<const void*const*>(&ppropvar->pdispVal)
|
|
&&
|
|
reinterpret_cast<const void*const*>(&ppropvar->pdispVal)
|
|
==
|
|
reinterpret_cast<const void*const*>(&ppropvar->ppdispVal)
|
|
&&
|
|
reinterpret_cast<const void*const*>(&ppropvar->ppdispVal)
|
|
==
|
|
reinterpret_cast<const void*const*>(&ppropvar->ppunkVal) );
|
|
|
|
// --------------------
|
|
// Look for an IPersist
|
|
// --------------------
|
|
|
|
// Get the IUnknown pointer (good for both VT_UNKNOWN and VT_DISPATCH).
|
|
|
|
if( VT_BYREF & ppropvar->vt )
|
|
punk = *ppropvar->ppunkVal;
|
|
else
|
|
punk = ppropvar->punkVal;
|
|
|
|
DfpAssert( NULL != punk );
|
|
|
|
// QI for IPersistStorage, then IPersistStream. If we find one or the other,
|
|
// set up propvarNew in preparation for a write.
|
|
|
|
hr = punk->QueryInterface( IID_IPersistStorage, reinterpret_cast<void**>(&pPersistStg) );
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
propvarNew.vt = VT_STORED_OBJECT;
|
|
}
|
|
else if( E_NOINTERFACE == hr )
|
|
{
|
|
hr = punk->QueryInterface( IID_IPersistStream, reinterpret_cast<void**>(&pPersistStm) );
|
|
if( SUCCEEDED(hr) )
|
|
propvarNew.vt = VT_STREAMED_OBJECT;
|
|
}
|
|
if( FAILED(hr) )
|
|
{
|
|
propDbg(( DEB_WARN, "Couldn't find an IPersist interface in IPropertyBagEx::WriteMultiple" ));
|
|
goto Exit;
|
|
}
|
|
|
|
// ----------------------------------
|
|
// Create the stream/storage property
|
|
// ----------------------------------
|
|
|
|
// Write this empty VT_STREAMED/STORED_OBJECT (has a NULL pstm/pstg value). This will cause the
|
|
// property set to create a new stream/storage.
|
|
|
|
hr = _ppropstg->WriteMultiple( 1, ppropspec, &propvarNew, PID_FIRST_USABLE );
|
|
if( FAILED(hr) )
|
|
{
|
|
propDbg(( DEB_ERROR, "Couldn't write %d for interface in IPropertyBagEx", propvarNew.vt ));
|
|
goto Exit;
|
|
}
|
|
|
|
// Get an interface pointer for that new stream/storage.
|
|
|
|
hr = _ppropstg->ReadMultiple( 1, ppropspec, &propvarNew );
|
|
if( FAILED(hr) || S_FALSE == hr )
|
|
{
|
|
propDbg(( DEB_ERROR, "Couldn't read stream/storage back for interface in IPropertyBagEx::WriteMultiple" ));
|
|
if( !FAILED(hr) )
|
|
hr = STG_E_WRITEFAULT;
|
|
goto Exit;
|
|
}
|
|
|
|
// --------------------
|
|
// Persist the property
|
|
// --------------------
|
|
|
|
if( NULL != pPersistStg )
|
|
{
|
|
CLSID clsid;
|
|
|
|
DfpAssert( VT_STORED_OBJECT == propvarNew.vt );
|
|
|
|
// Set the clsid
|
|
|
|
hr = pPersistStg->GetClassID( &clsid );
|
|
if( E_NOTIMPL == hr )
|
|
clsid = CLSID_NULL;
|
|
else if( FAILED(hr) )
|
|
goto Exit;
|
|
|
|
hr = propvarNew.pStorage->SetClass( clsid );
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
// Persist to VT_STORED_OBJECT
|
|
|
|
hr = pPersistStg->Save( propvarNew.pStorage, FALSE /* Not necessarily same as load */ );
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
hr = pPersistStg->SaveCompleted( propvarNew.pStorage );
|
|
if( FAILED(hr) ) goto Exit;
|
|
}
|
|
else
|
|
{
|
|
CLSID clsid;
|
|
ULONG cbWritten;
|
|
|
|
DfpAssert( VT_STREAMED_OBJECT == propvarNew.vt );
|
|
DfpAssert( NULL != pPersistStm );
|
|
|
|
#if 1 == DBG
|
|
// This stream should be at its start.
|
|
{
|
|
CULargeInteger culi;
|
|
hr = propvarNew.pStream->Seek( CLargeInteger(0), STREAM_SEEK_CUR, &culi );
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
DfpAssert( CULargeInteger(0) == culi );
|
|
}
|
|
#endif // #if 1 == DBG
|
|
|
|
|
|
// Write the clsid
|
|
|
|
DfpAssert( NULL != pPersistStm );
|
|
hr = pPersistStm->GetClassID( &clsid );
|
|
if( E_NOTIMPL == hr )
|
|
clsid = CLSID_NULL;
|
|
else if( FAILED(hr) )
|
|
goto Exit;
|
|
|
|
hr = propvarNew.pStream->Write( &clsid, sizeof(clsid), &cbWritten );
|
|
if( FAILED(hr) || sizeof(clsid) != cbWritten )
|
|
goto Exit;
|
|
|
|
// Persist to VT_STREAMED_OBJECT
|
|
|
|
IFDBG( ULONG cRefs = GetRefCount( propvarNew.pStream ));
|
|
hr = pPersistStm->Save( propvarNew.pStream, TRUE /* Clear dirty flag */ );
|
|
if( FAILED(hr) ) goto Exit;
|
|
DfpAssert( GetRefCount( propvarNew.pStream ) == cRefs );
|
|
}
|
|
|
|
|
|
Exit:
|
|
|
|
RELEASE_INTERFACE(pPersistStg);
|
|
RELEASE_INTERFACE(pPersistStm);
|
|
|
|
PropVariantClear( &propvarNew );
|
|
|
|
return( hr );
|
|
|
|
} // CPropertyBagEx::WriteOneObject
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: CPropertyBagEx::DeleteMultiple (IPropertyBagEx)
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
HRESULT STDMETHODCALLTYPE
|
|
CPropertyBagEx::DeleteMultiple( /*[in]*/ ULONG cprops,
|
|
/*[in]*/ LPCOLESTR const rgoszPropNames[],
|
|
/*[in]*/ DWORD dwReserved )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
IEnumSTATPROPBAG *penum = NULL;
|
|
STATPROPBAG statpropbag = { NULL };
|
|
PROPSPEC *prgpropspec = NULL;
|
|
ULONG i;
|
|
|
|
// --------------
|
|
// Initialization
|
|
// --------------
|
|
|
|
propXTrace( "CPropertyBagEx::DeleteMultiple" );
|
|
|
|
_pBlockingLock->Lock( INFINITE );
|
|
|
|
// Validate the input
|
|
|
|
if (S_OK != (hr = ValidateInRGLPOLESTR( cprops, rgoszPropNames )))
|
|
goto Exit;
|
|
|
|
if( 0 != dwReserved )
|
|
{
|
|
hr = STG_E_INVALIDPARAMETER;
|
|
goto Exit;
|
|
}
|
|
|
|
propTraceParameters(( "%d, 0x%x", cprops, rgoszPropNames ));
|
|
|
|
// If it's not already open, open the property storage now. If it doesn't exist,
|
|
// then our mission is accomplished; the properties don't exist.
|
|
|
|
hr = OpenPropStg( FILE_OPEN_IF ); // Open only if it already exists
|
|
if( STG_E_FILENOTFOUND == hr )
|
|
{
|
|
hr = S_OK;
|
|
goto Exit;
|
|
}
|
|
else if( FAILED(hr) )
|
|
goto Exit;
|
|
|
|
// Create an array of PROPSPECs, and load with the caller-provided
|
|
// names.
|
|
|
|
prgpropspec = reinterpret_cast<PROPSPEC*>
|
|
( CoTaskMemAlloc( cprops * sizeof(PROPSPEC) ));
|
|
if( NULL == prgpropspec )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
for( i = 0; i < cprops; i++ )
|
|
{
|
|
prgpropspec[i].ulKind = PRSPEC_LPWSTR;
|
|
prgpropspec[i].lpwstr = const_cast<LPOLESTR>(rgoszPropNames[i]);
|
|
}
|
|
|
|
// ---------------------
|
|
// Delete the properties
|
|
// ---------------------
|
|
|
|
hr = _ppropstg->DeleteMultiple( cprops, prgpropspec );
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
hr = S_OK;
|
|
|
|
Exit:
|
|
|
|
CoTaskMemFree( prgpropspec );
|
|
|
|
_pBlockingLock->Unlock();
|
|
|
|
return( hr );
|
|
|
|
} // CPropertyBagEx::DeleteMultiple
|
|
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: CPropertyBagEx::Open (IPropertyBagEx)
|
|
//
|
|
// This method reads a VT_VERSIONED_STREAM from the underlying property set,
|
|
// and returns the IStream pointer in *ppUnk. If the property doesn't already
|
|
// exist, and guidPropertyType is not GUID_NULL, then an empty property is
|
|
// created first. An empty property can also be created over an existing
|
|
// one by specifying OPENPROPERTY_OVERWRITE.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
HRESULT STDMETHODCALLTYPE
|
|
CPropertyBagEx::Open(
|
|
/* [in] */ IUnknown __RPC_FAR *pUnkOuter,
|
|
/* [in] */ LPCOLESTR pwszPropName,
|
|
/* [in] */ GUID guidPropertyType,
|
|
/* [in] */ DWORD dwFlags,
|
|
/* [in] */ REFIID riid,
|
|
/* [out] */ IUnknown __RPC_FAR *__RPC_FAR *ppUnk)
|
|
{
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
PROPVARIANT propvar;
|
|
IUnknown *pUnk = NULL;
|
|
DBGBUF(buf1);
|
|
DBGBUF(buf2);
|
|
|
|
// --------------
|
|
// Initialization
|
|
// --------------
|
|
|
|
propXTrace( "CPropertyBagEx::Open" );
|
|
|
|
PropVariantInit( &propvar );
|
|
|
|
_pBlockingLock->Lock( INFINITE );
|
|
|
|
// Validation
|
|
|
|
GEN_VDATEPTRIN_LABEL( pUnkOuter, IUnknown*, E_INVALIDARG, Exit, hr );
|
|
if (S_OK != (hr = ValidateInRGLPOLESTR( 1, &pwszPropName )))
|
|
goto Exit;
|
|
GEN_VDATEREADPTRIN_LABEL( &riid, IID*, E_INVALIDARG, Exit, hr );
|
|
|
|
propTraceParameters(( "0x%x, %ws, %s, 0x%x, %s",
|
|
pUnkOuter, pwszPropName, DbgFmtId(guidPropertyType,buf1),dwFlags, DbgFmtId(riid,buf2) ));
|
|
|
|
|
|
// Initialize out-parms
|
|
*ppUnk = NULL;
|
|
|
|
|
|
// We don't support aggregation
|
|
if( NULL != pUnkOuter )
|
|
{
|
|
propDbg(( DEB_ERROR, "CPropertyBagEx(0x%x)::Open doesn't support pUnkOuter\n", this ));
|
|
hr = E_NOTIMPL;
|
|
goto Exit;
|
|
}
|
|
|
|
// Check for unsupported flags
|
|
if( 0 != (~OPENPROPERTY_OVERWRITE & dwFlags) )
|
|
{
|
|
propDbg(( DEB_ERROR, "CPropertyBagEx(0x%x)::Open, invalid dwFlags (0x%x)\n", this, dwFlags ));
|
|
hr = E_NOTIMPL;
|
|
goto Exit;
|
|
}
|
|
|
|
// We don't support anything but IID_IStream
|
|
if( IID_IStream != riid )
|
|
{
|
|
hr = E_NOTIMPL;
|
|
goto Exit;
|
|
}
|
|
|
|
// -----------------
|
|
// Read the property
|
|
// -----------------
|
|
|
|
// Attempt to read the property
|
|
hr = ReadMultiple( 1, &pwszPropName, &propvar, NULL );
|
|
|
|
// If the property doesn't exist but we were given a valid guid, create a new one.
|
|
if( S_FALSE == hr && GUID_NULL != guidPropertyType )
|
|
{
|
|
VERSIONEDSTREAM VersionedStream;
|
|
PROPVARIANT propvarCreate;
|
|
|
|
// Write a new property speicifying NULL for the stream
|
|
|
|
VersionedStream.guidVersion = guidPropertyType;
|
|
VersionedStream.pStream = NULL;
|
|
propvarCreate.vt = VT_VERSIONED_STREAM;
|
|
propvarCreate.pVersionedStream = &VersionedStream;
|
|
|
|
hr = WriteMultiple( 1, &pwszPropName, &propvarCreate );
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
// Read the property back, getting an IStream*
|
|
hr = ReadMultiple( 1, &pwszPropName, &propvar, NULL );
|
|
}
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
// Is the type or guidPropertyType wrong? (If the caller specified GUID_NULL,
|
|
// then any guidVersion is OK.)
|
|
|
|
if( VT_VERSIONED_STREAM != propvar.vt
|
|
||
|
|
GUID_NULL != guidPropertyType
|
|
&&
|
|
guidPropertyType != propvar.pVersionedStream->guidVersion )
|
|
{
|
|
if( OPENPROPERTY_OVERWRITE & dwFlags )
|
|
{
|
|
// Delete the existing property
|
|
hr = DeleteMultiple( 1, &pwszPropName, 0 );
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
// Recursive call
|
|
// PERF: Optimize this so that we don't have to re-do the parameter checking.
|
|
hr = Open( pUnkOuter, pwszPropName, guidPropertyType, dwFlags & ~OPENPROPERTY_OVERWRITE, riid, ppUnk );
|
|
|
|
} // if( OPENPROPERTY_OVERWRITE & dwFlags )
|
|
else
|
|
{
|
|
propDbg(( DEB_ERROR, "CPropertyBagEx(0x%x)::Open couldn't overwrite existing property - %ws, %d\n",
|
|
this, pwszPropName, propvar.vt ));
|
|
hr = STG_E_FILEALREADYEXISTS;
|
|
} // if( OPENPROPERTY_OVERWRITE & dwFlags ) ... else
|
|
|
|
// Unconditional goto Exit; we either just called Open recursively to do the work,
|
|
// or set an error.
|
|
goto Exit;
|
|
}
|
|
|
|
// We have a good property, QI for the riid and we're done
|
|
|
|
DfpAssert( IID_IStream == riid );
|
|
|
|
hr = propvar.pVersionedStream->pStream->QueryInterface( IID_IUnknown, reinterpret_cast<void**>(&pUnk) );
|
|
if( FAILED(hr) )
|
|
{
|
|
pUnk = NULL;
|
|
goto Exit;
|
|
}
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
*ppUnk = pUnk;
|
|
pUnk = NULL;
|
|
hr = S_OK;
|
|
|
|
|
|
Exit:
|
|
|
|
if( NULL != pUnk )
|
|
pUnk->Release();
|
|
|
|
PropVariantClear( &propvar );
|
|
|
|
_pBlockingLock->Unlock();
|
|
return( hr );
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: CPropertyBagEx::Enum (IPropertyBagEx)
|
|
//
|
|
// This method returns an enumerator of properties in a bag. If
|
|
// ENUMPROPERTY_MASK is set in dwFlags, poszPropNameMask is treated as a
|
|
// prefix, and the enumerator returns all those properties which match
|
|
// that prefix.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
HRESULT STDMETHODCALLTYPE
|
|
CPropertyBagEx::Enum(
|
|
/* [in] */ LPCOLESTR poszPropNameMask,
|
|
/* [in] */ DWORD dwFlags,
|
|
/* [out] */ IEnumSTATPROPBAG __RPC_FAR *__RPC_FAR *ppenum)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CEnumSTATPROPBAG *penum = NULL;
|
|
|
|
propXTrace( "CPropertyBagEx::Enum" );
|
|
|
|
_pBlockingLock->Lock( INFINITE );
|
|
|
|
// Validate inputs
|
|
|
|
if (NULL != poszPropNameMask && S_OK != (hr = ValidateInRGLPOLESTR( 1, &poszPropNameMask )))
|
|
goto Exit;
|
|
GEN_VDATEPTROUT_LABEL( ppenum, IEnumSTATPROPBAG*, E_INVALIDARG, Exit, hr );
|
|
|
|
if( 0 != dwFlags )
|
|
{
|
|
propDbg(( DEB_ERROR, "CPropertyBagEx(0x%x)::Enum - invalid dwFlags (%08x)\n", this, dwFlags ));
|
|
hr = E_INVALIDARG;
|
|
goto Exit;
|
|
}
|
|
|
|
propTraceParameters(( "%ws, 0x%08x, 0x%x", poszPropNameMask, dwFlags, ppenum ));
|
|
|
|
// Initialize output
|
|
|
|
*ppenum = NULL;
|
|
|
|
// Open the property storage, if it exists
|
|
|
|
hr = OpenPropStg( FILE_OPEN_IF );
|
|
if( STG_E_FILENOTFOUND == hr )
|
|
hr = S_OK;
|
|
else if( FAILED(hr) )
|
|
goto Exit;
|
|
|
|
// Create an enumerator
|
|
|
|
penum = new CEnumSTATPROPBAG( _pBlockingLock );
|
|
if( NULL == penum )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
// And initialize it
|
|
|
|
hr = penum->Init( _ppropstg, poszPropNameMask, dwFlags ); // _ppropstg could be NULL
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
hr = S_OK;
|
|
*ppenum = static_cast<IEnumSTATPROPBAG*>( penum );
|
|
penum = NULL;
|
|
|
|
Exit:
|
|
|
|
if( NULL != penum )
|
|
penum->Release();
|
|
|
|
_pBlockingLock->Unlock();
|
|
|
|
return( hr );
|
|
|
|
} // CPropertyBagEx::Enum
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: CPropertyBagEx::Commit
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CPropertyBagEx::Commit( DWORD grfCommitFlags )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
propITrace( "CPropertyBagEx::Commit" );
|
|
propTraceParameters(( "%08x", grfCommitFlags ));
|
|
|
|
// As an optimization, we only take the lock if we're really going to
|
|
// do the commit.
|
|
|
|
if( NULL != _ppropstg )
|
|
{
|
|
_pBlockingLock->Lock( INFINITE );
|
|
|
|
if( NULL != _ppropstg && IsWriteable() )
|
|
{
|
|
hr = _ppropstg->Commit( grfCommitFlags );
|
|
}
|
|
|
|
_pBlockingLock->Unlock();
|
|
}
|
|
|
|
return( hr );
|
|
|
|
} // CPropertyBagEx::Commit
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: CPropertyBagEx::ShutDown
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CPropertyBagEx::ShutDown()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
propITrace( "CPropertyBagEx::ShutDown" );
|
|
|
|
if( NULL != _pBlockingLock )
|
|
_pBlockingLock->Lock( INFINITE );
|
|
|
|
// Release the property storage that holds the bag. It is because
|
|
// of this call that we need this separate ShutDown method; we can't
|
|
// release this interface in the destructor, since it may be reverted
|
|
// by that point.
|
|
|
|
DfpVerify( 0 == RELEASE_INTERFACE(_ppropstg) );
|
|
|
|
// We didn't AddRef these, so we don't need to release them.
|
|
|
|
if( NULL != _ppropsetstgContainer )
|
|
_ppropsetstgContainer = NULL;
|
|
|
|
if( NULL != _pBlockingLock )
|
|
{
|
|
_pBlockingLock->Unlock();
|
|
_pBlockingLock = NULL;
|
|
}
|
|
|
|
return( hr );
|
|
|
|
} // CPropertyBagEx::ShutDown
|
|
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: CSTATPROPBAGArray::Init
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CSTATPROPBAGArray::Init( IPropertyStorage *ppropstg, const OLECHAR *poszPrefix, DWORD dwFlags )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
propITrace( "CSTATPROPBAGArray::Init" );
|
|
propTraceParameters(( "0x%x, %ws", ppropstg, poszPrefix ));
|
|
|
|
_pBlockingLock->Lock( INFINITE );
|
|
|
|
// Keep the IPropertyBagEx::Enum flags
|
|
_dwFlags = dwFlags;
|
|
|
|
// Copy the prefix string
|
|
if( NULL == poszPrefix )
|
|
_poszPrefix = NULL;
|
|
else
|
|
{
|
|
_poszPrefix = reinterpret_cast<OLECHAR*>
|
|
( CoTaskMemAlloc( ( (ocslen(poszPrefix)+1) * sizeof(OLECHAR) )));
|
|
if( NULL == _poszPrefix )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
ocscpy( _poszPrefix, poszPrefix );
|
|
}
|
|
|
|
// If we were given an IPropertyStorage, enum it. Otherwise, we'll leave
|
|
// _penum NULL and always return 0 for *pcFetched.
|
|
|
|
if( NULL != ppropstg )
|
|
{
|
|
hr = ppropstg->Enum( &_penum );
|
|
if( FAILED(hr) ) goto Exit;
|
|
}
|
|
|
|
hr = S_OK;
|
|
|
|
Exit:
|
|
|
|
_pBlockingLock->Unlock();
|
|
return( hr );
|
|
|
|
} // CSTATPROPBAGArray::Init
|
|
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: CSTATPROPBAGArray::AddRef/Release
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
ULONG
|
|
CSTATPROPBAGArray::AddRef()
|
|
{
|
|
return( InterlockedIncrement( &_cReferences ));
|
|
}
|
|
|
|
ULONG
|
|
CSTATPROPBAGArray::Release()
|
|
{
|
|
LONG lRet = InterlockedDecrement( &_cReferences );
|
|
|
|
if( 0 == lRet )
|
|
delete this;
|
|
|
|
return( 0 > lRet ? 0 : lRet );
|
|
}
|
|
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: CSTATPROPBAGArray::NextAt
|
|
//
|
|
// This method gets the *pcFetched STATPROPBAG structures in the
|
|
// enumeration starting from index iNext. This is implemented using
|
|
// the IEnumSTATPROPSTG enumerator in _penum.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
|
|
HRESULT
|
|
CSTATPROPBAGArray::NextAt( ULONG iNext, STATPROPBAG *prgstatpropbag, ULONG *pcFetched )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
STATPROPSTG statpropstg = { NULL };
|
|
ULONG iMatch = 0;
|
|
ULONG iFetched = 0;
|
|
|
|
propITrace( "CSTATPROPBAGArray::NextAt" );
|
|
propTraceParameters(( "%d, 0x%x, 0x%x", iNext, prgstatpropbag, pcFetched ));
|
|
|
|
_pBlockingLock->Lock( INFINITE );
|
|
|
|
// If there's nothing to enumerate, then we're done
|
|
|
|
if( NULL == _penum )
|
|
{
|
|
hr = S_FALSE;
|
|
*pcFetched = 0;
|
|
goto Exit;
|
|
}
|
|
|
|
// Reset the IEnumSTATPROPBAGTG index (doesn't reload, just resets the index).
|
|
hr = _penum->Reset();
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
// Walk the IEnumSTATPROPBAGTG enumerator, looking for matches.
|
|
|
|
hr = _penum->Next( 1, &statpropstg, NULL );
|
|
while( S_OK == hr && iFetched < *pcFetched )
|
|
{
|
|
// Does this property have a name (all properties in a bag must have
|
|
// a name)?
|
|
|
|
if( NULL != statpropstg.lpwstrName )
|
|
{
|
|
// Yes, we have a name. Are we enumerating all properties (in which case
|
|
// _poszPrefix is NULL), or does this property name match the prefix?
|
|
|
|
if( NULL == _poszPrefix
|
|
||
|
|
statpropstg.lpwstrName == ocsstr( statpropstg.lpwstrName, _poszPrefix )
|
|
||
|
|
!ocscmp( statpropstg.lpwstrName, _poszPrefix ) )
|
|
{
|
|
// Yes, this property matches the prefix and is therefore part
|
|
// of this enumeration. But is this the index into the enumeration
|
|
// that we're looking for?
|
|
|
|
if( iNext == iMatch )
|
|
{
|
|
// Yes, everything matches, and we have a property that should
|
|
// be returned.
|
|
|
|
prgstatpropbag[ iFetched ].lpwstrName = statpropstg.lpwstrName;
|
|
statpropstg.lpwstrName = NULL;
|
|
|
|
prgstatpropbag[ iFetched ].vt = statpropstg.vt;
|
|
|
|
// GUID is not current supported by the enumeration
|
|
prgstatpropbag[ iFetched ].guidPropertyType = GUID_NULL;
|
|
|
|
// Show that we're now looking for the i+1 index
|
|
iNext++;
|
|
|
|
iFetched++;
|
|
}
|
|
|
|
// Increment the number of property matches we've had.
|
|
// (iMatch will increment until it equals iNext, after
|
|
// which the two will always both increment and remain equal).
|
|
|
|
iMatch++;
|
|
|
|
} // if( NULL == _poszPrefix ...
|
|
} // if( NULL != statpropstg.lpwstrName )
|
|
|
|
CoTaskMemFree( statpropstg.lpwstrName );
|
|
statpropstg.lpwstrName = NULL;
|
|
hr = _penum->Next( 1, &statpropstg, NULL );
|
|
|
|
} // while( S_OK == hr && iFetched < *pcFetched )
|
|
|
|
// Did we get an error on a _penum->Next call?
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
|
|
// If we reached this point, there was no error. Determine if
|
|
// OK or FALSE should be returned.
|
|
|
|
if( iFetched == *pcFetched )
|
|
hr = S_OK;
|
|
else
|
|
hr = S_FALSE;
|
|
|
|
*pcFetched = iFetched;
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
Exit:
|
|
|
|
CoTaskMemFree( statpropstg.lpwstrName );
|
|
|
|
_pBlockingLock->Unlock();
|
|
return( hr );
|
|
|
|
} // CSTATPROPBAGArray::NextAt
|
|
|
|
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: CEnumSTATPROPBAG::CEnumSTATPROPBAG (copy constructor)
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
CEnumSTATPROPBAG::CEnumSTATPROPBAG( const CEnumSTATPROPBAG &Other )
|
|
{
|
|
propDbg(( DEB_ITRACE, "CEnumSTATPROPBAG::CEnumSTATPROPBAG (copy constructor)" ));
|
|
|
|
Other._pBlockingLock->Lock( INFINITE );
|
|
|
|
new(this) CEnumSTATPROPBAG( Other._pBlockingLock );
|
|
|
|
_iarray = Other._iarray;
|
|
|
|
Other._parray->AddRef();
|
|
_parray = Other._parray;
|
|
|
|
Other._pBlockingLock->Unlock();
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: CEnumSTATPROPBAG::~CEnumSTATPROPBAG
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
CEnumSTATPROPBAG::~CEnumSTATPROPBAG()
|
|
{
|
|
propDbg(( DEB_ITRACE, "CEnumSTATPROPBAG::~CEnumSTATPROPBAG\n" ));
|
|
|
|
_pBlockingLock->Release();
|
|
if( NULL != _parray )
|
|
_parray->Release();
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: CEnumSTATPROPBAG::Init
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CEnumSTATPROPBAG::Init( IPropertyStorage *ppropstg, LPCOLESTR poszPrefix, DWORD dwFlags )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
propITrace( "CEnumSTATPROPBAG::Init" );
|
|
propTraceParameters(( "0x%x, %ws", ppropstg, poszPrefix ));
|
|
|
|
// Create a STATPROPBAG Array
|
|
_parray = new CSTATPROPBAGArray( _pBlockingLock );
|
|
if( NULL == _parray )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
// Load the array from the IPropertyStorage
|
|
hr = _parray->Init( ppropstg, poszPrefix, dwFlags );
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
hr = S_OK;
|
|
|
|
Exit:
|
|
|
|
return( hr );
|
|
} // CEnumSTATPROPBAG::Init
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: CEnumSTATPROPBAG::QueryInterface/AddRef/Release
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
HRESULT STDMETHODCALLTYPE
|
|
CEnumSTATPROPBAG::QueryInterface( REFIID riid, void **ppvObject )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
// Validate the inputs
|
|
VDATEREADPTRIN( &riid, IID );
|
|
VDATEPTROUT( ppvObject, void* );
|
|
|
|
*ppvObject = NULL;
|
|
|
|
if( IID_IEnumSTATPROPBAG == riid || IID_IUnknown == riid )
|
|
{
|
|
*ppvObject = static_cast<IEnumSTATPROPBAG*>(this);
|
|
AddRef();
|
|
}
|
|
else
|
|
hr = E_NOINTERFACE;
|
|
|
|
return(hr);
|
|
|
|
} // CEnumSTATPROPBAG::QueryInterface
|
|
|
|
ULONG STDMETHODCALLTYPE
|
|
CEnumSTATPROPBAG::AddRef()
|
|
{
|
|
return( InterlockedIncrement( &_cRefs ));
|
|
}
|
|
|
|
ULONG STDMETHODCALLTYPE
|
|
CEnumSTATPROPBAG::Release()
|
|
{
|
|
LONG lRet = InterlockedDecrement( &_cRefs );
|
|
|
|
if( 0 == lRet )
|
|
delete this;
|
|
|
|
return( 0 > lRet ? 0 : lRet );
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: CEnumSTATPROPBAG::Next
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
HRESULT STDMETHODCALLTYPE
|
|
CEnumSTATPROPBAG::Next( ULONG celt, STATPROPBAG *rgelt, ULONG *pceltFetched )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ULONG celtFetched = celt;
|
|
|
|
propXTrace( "CEnumSTATPROPBAG::Next" );
|
|
|
|
_pBlockingLock->Lock( INFINITE );
|
|
|
|
// Validate the inputs
|
|
|
|
if (NULL == pceltFetched)
|
|
{
|
|
if (celt != 1)
|
|
return(STG_E_INVALIDPARAMETER);
|
|
}
|
|
else
|
|
{
|
|
VDATEPTROUT( pceltFetched, ULONG );
|
|
*pceltFetched = 0;
|
|
}
|
|
propTraceParameters(( "%d, 0x%x, 0x%x", celt, rgelt, pceltFetched ));
|
|
|
|
// Get the next set of stat structures
|
|
hr = _parray->NextAt( _iarray, rgelt, &celtFetched );
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
// Advance the index
|
|
_iarray += celtFetched;
|
|
|
|
// Return the count to the caller
|
|
if( NULL != pceltFetched )
|
|
*pceltFetched = celtFetched;
|
|
|
|
hr = celtFetched == celt ? S_OK : S_FALSE;
|
|
|
|
Exit:
|
|
|
|
_pBlockingLock->Unlock();
|
|
return( hr );
|
|
|
|
} // CEnumSTATPROPBAG::Next
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: CEnumSTATPROPBAG::Reset
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
HRESULT STDMETHODCALLTYPE
|
|
CEnumSTATPROPBAG::Reset( )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
propXTrace( "CEnumSTATPROPBAG::Reset" );
|
|
|
|
_pBlockingLock->Lock( INFINITE );
|
|
_iarray = 0;
|
|
_pBlockingLock->Unlock();
|
|
|
|
return( hr );
|
|
|
|
} // CEnumSTATPROPBAG::Reset
|
|
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: CEnumSTATPROPBAG::Skip
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
HRESULT STDMETHODCALLTYPE
|
|
CEnumSTATPROPBAG::Skip( ULONG celt )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
STATPROPBAG statpropbag = { NULL };
|
|
|
|
propXTrace( "CEnumSTATPROPBAG::Skip" );
|
|
propTraceParameters( ("%d", celt ));
|
|
|
|
_pBlockingLock->Lock( INFINITE );
|
|
|
|
while( celt )
|
|
{
|
|
ULONG cFetched = 1;
|
|
|
|
hr = _parray->NextAt( _iarray, &statpropbag, &cFetched );
|
|
CoTaskMemFree( statpropbag.lpwstrName );
|
|
statpropbag.lpwstrName = NULL;
|
|
|
|
if( FAILED(hr) )
|
|
goto Exit;
|
|
else if( S_FALSE == hr )
|
|
break;
|
|
else
|
|
{
|
|
_iarray++;
|
|
hr = S_OK;
|
|
--celt;
|
|
}
|
|
}
|
|
|
|
|
|
Exit:
|
|
|
|
CoTaskMemFree( statpropbag.lpwstrName );
|
|
|
|
_pBlockingLock->Unlock();
|
|
return( hr );
|
|
|
|
} // CEnumSTATPROPBAG::Skip
|
|
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: CEnumSTATPROPBAG::Clone
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
HRESULT STDMETHODCALLTYPE
|
|
CEnumSTATPROPBAG::Clone( IEnumSTATPROPBAG **ppenum )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CEnumSTATPROPBAG *penum = NULL;
|
|
|
|
propXTrace( "CEnumSTATPROPBAG::Clone" );
|
|
|
|
// Validate the input
|
|
|
|
VDATEPTROUT( ppenum, IEnumSTATPROPSTG* );
|
|
*ppenum = NULL;
|
|
propTraceParameters(( "0x%x", ppenum ));
|
|
|
|
penum = new CEnumSTATPROPBAG( *this );
|
|
if( NULL == penum )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
*ppenum = static_cast<IEnumSTATPROPBAG*>(penum);
|
|
penum = NULL;
|
|
|
|
Exit:
|
|
|
|
if( NULL != penum )
|
|
delete penum ;
|
|
|
|
return( hr );
|
|
|
|
} // CEnumSTATPROPBAG::Clone
|
|
|
|
|
|
//
|
|
// Add this functionality to chgtype.cxx when chgtype.cxx is finalized.
|
|
//
|
|
//
|
|
// These are the Property Variant types that need to be
|
|
// dumbed down to the VARIANT types.
|
|
//
|
|
struct {
|
|
VARTYPE vtSrc, vtDest;
|
|
} const ImplicitCoercionLookup[] = {
|
|
// Src Dest
|
|
{VT_I8, VT_I4},
|
|
{VT_UI8, VT_UI4},
|
|
{VT_LPSTR, VT_BSTR},
|
|
{VT_LPWSTR, VT_BSTR},
|
|
{VT_FILETIME, VT_DATE},
|
|
{VT_BLOB, VT_ARRAY|VT_UI1},
|
|
{VT_STREAM, VT_UNKNOWN},
|
|
{VT_STREAMED_OBJECT, VT_UNKNOWN},
|
|
{VT_STORAGE, VT_UNKNOWN},
|
|
{VT_STORED_OBJECT, VT_UNKNOWN},
|
|
{VT_BLOB_OBJECT, VT_ARRAY|VT_UI1},
|
|
{VT_CF, VT_ARRAY|VT_UI1},
|
|
{VT_CLSID, VT_BSTR},
|
|
};
|
|
|
|
HRESULT
|
|
ImplicitPropVariantToVariantChangeType(
|
|
PROPVARIANT *pDest, // Omit the hungarian to make
|
|
const PROPVARIANT *pSrc, // the code look cleaner.
|
|
LCID lcid )
|
|
{
|
|
HRESULT hr=S_OK;
|
|
VARTYPE vtCoerce;
|
|
VARTYPE vtType;
|
|
int i;
|
|
|
|
//
|
|
// Safe arrays are only built from old VARIANT types.
|
|
// They are easy so get them out of the way.
|
|
//
|
|
if( VT_ARRAY & pSrc->vt )
|
|
{
|
|
return PropVariantCopy( pDest, pSrc );
|
|
}
|
|
|
|
vtType = pSrc->vt & VT_TYPEMASK;
|
|
vtCoerce = VT_EMPTY;
|
|
for(i=0; i<ELEMENTS(ImplicitCoercionLookup); i++)
|
|
{
|
|
if( ImplicitCoercionLookup[i].vtSrc == vtType )
|
|
{
|
|
vtCoerce = ImplicitCoercionLookup[i].vtDest;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(VT_VECTOR & pSrc->vt)
|
|
{
|
|
if( VT_EMPTY == vtCoerce )
|
|
vtCoerce = pSrc->vt & VT_TYPEMASK;
|
|
|
|
return HrPropVarVECTORToSAFEARRAY( pDest, pSrc, lcid, vtCoerce);
|
|
}
|
|
|
|
if( VT_EMPTY == vtCoerce )
|
|
hr = PropVariantCopy( pDest, pSrc ); // Optimization.
|
|
else
|
|
hr = PropVariantChangeType( pDest, pSrc, lcid, 0, vtCoerce );
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
HrPropVarVECTORToSAFEARRAY(
|
|
PROPVARIANT *pDest,
|
|
const PROPVARIANT *pSrc,
|
|
LCID lcid,
|
|
VARTYPE vtCoerce )
|
|
{
|
|
DWORD i;
|
|
HRESULT hr = S_OK;
|
|
SAFEARRAY *psaT=NULL;
|
|
SAFEARRAYBOUND sabound;
|
|
PROPVARIANT propvarT1, propvarT2;
|
|
|
|
PropVariantInit( &propvarT1 );
|
|
PropVariantInit( &propvarT2 );
|
|
|
|
PROPASSERT (VT_VECTOR & pSrc->vt);
|
|
|
|
VARTYPE vt;
|
|
|
|
vt = pSrc->vt & VT_TYPEMASK;
|
|
|
|
// Create the SafeArray
|
|
sabound.lLbound = 0;
|
|
sabound.cElements = pSrc->cac.cElems;
|
|
psaT = PrivSafeArrayCreate(vtCoerce, 1, &sabound );
|
|
if(psaT == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Error;
|
|
}
|
|
|
|
// Convert single typed Vectors
|
|
// Pull each element from the Vector,
|
|
// Change it's type, per requested.
|
|
// Put it into the Array.
|
|
if(VT_VARIANT != vt)
|
|
{
|
|
for(i=0; i<pSrc->cac.cElems; i++)
|
|
{
|
|
hr = LoadPropVariantFromVectorElem( &propvarT1, pSrc, i );
|
|
if( FAILED(hr) )
|
|
goto Error;
|
|
|
|
//
|
|
// PERF: Performance
|
|
// If the pSrc->vt == vtCoerce, then we can skip the ChangeType
|
|
// and the following clean. And go directly to "Put" of T1.
|
|
//
|
|
hr = PropVariantChangeType( &propvarT2, &propvarT1, lcid, 0, vtCoerce );
|
|
PropVariantClear( &propvarT1 );
|
|
|
|
if( FAILED(hr) )
|
|
goto Error;
|
|
|
|
hr = PutPropVariantDataIntoSafeArray( psaT, &propvarT2, i);
|
|
PropVariantClear( &propvarT2 );
|
|
if( FAILED(hr) )
|
|
goto Error;
|
|
}
|
|
}
|
|
// Convert Vectors of Variants
|
|
// Do Implisit Coercion of each Variant into an new Variant
|
|
// Put the new Variant into the Array.
|
|
else
|
|
{
|
|
for(i=0; i<pSrc->cac.cElems; i++)
|
|
{
|
|
hr = ImplicitPropVariantToVariantChangeType(
|
|
&propvarT2, &pSrc->capropvar.pElems[i], lcid );
|
|
if( FAILED(hr) )
|
|
goto Error;
|
|
|
|
hr = PrivSafeArrayPutElement( psaT, (long*)&i, (void*)&propvarT2 );
|
|
PropVariantClear( &propvarT2 );
|
|
if( FAILED(hr) )
|
|
goto Error;
|
|
}
|
|
}
|
|
|
|
pDest->vt = VT_ARRAY | vtCoerce;
|
|
pDest->parray = psaT;
|
|
psaT = NULL;
|
|
|
|
Error:
|
|
if(NULL != psaT)
|
|
PrivSafeArrayDestroy( psaT );
|
|
|
|
return hr;
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
// Dup routines
|
|
//-----------------------------------------------------------------
|
|
|
|
LPSTR
|
|
PropDupStr( const LPSTR lpstr )
|
|
{
|
|
int cch;
|
|
|
|
if( NULL == lpstr )
|
|
return NULL;
|
|
else
|
|
{
|
|
cch = lstrlenA( lpstr ) + 1;
|
|
return (LPSTR)AllocAndCopy( cch*sizeof(CHAR), lpstr );
|
|
}
|
|
}
|
|
|
|
LPWSTR
|
|
PropDupWStr( const LPWSTR lpwstr )
|
|
{
|
|
int cch;
|
|
|
|
if( NULL == lpwstr )
|
|
return NULL;
|
|
else
|
|
{
|
|
cch = lstrlenW( lpwstr ) + 1;
|
|
return (LPWSTR)AllocAndCopy( cch*sizeof(WCHAR), lpwstr );
|
|
}
|
|
}
|
|
|
|
LPCLSID
|
|
PropDupCLSID( const LPCLSID pclsid )
|
|
{
|
|
return (LPCLSID)AllocAndCopy( sizeof(CLSID), pclsid);
|
|
}
|
|
|
|
CLIPDATA*
|
|
PropDupClipData( const CLIPDATA* pclipdata )
|
|
{
|
|
CLIPDATA* pcdNew=NULL;
|
|
CLIPDATA* pclipdataNew=NULL;
|
|
PVOID pvMem=NULL;
|
|
|
|
pcdNew = new CLIPDATA;
|
|
pvMem = AllocAndCopy( CBPCLIPDATA( *pclipdata ), pclipdata->pClipData);
|
|
|
|
if( NULL == pvMem || NULL == pcdNew )
|
|
goto Error;
|
|
|
|
pcdNew->cbSize = pclipdata->cbSize;
|
|
pcdNew->ulClipFmt = pclipdata->ulClipFmt;
|
|
pcdNew->pClipData = (BYTE*)pvMem;
|
|
pclipdataNew = pcdNew;
|
|
pcdNew = NULL;
|
|
pvMem = NULL;
|
|
|
|
Error:
|
|
if( NULL != pcdNew )
|
|
delete pcdNew;
|
|
if( NULL != pvMem )
|
|
delete pvMem;
|
|
|
|
return pclipdataNew;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
//
|
|
// LoadPropVariantFromVectorElem()
|
|
// This routine will load a provided PropVariant from an element of a
|
|
// provided SafeArray at the provided index.
|
|
// All the PropVariant Vector types are supported.
|
|
//
|
|
//------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
LoadPropVariantFromVectorElem(
|
|
PROPVARIANT *pDest,
|
|
const PROPVARIANT *pSrc,
|
|
int idx)
|
|
{
|
|
VARTYPE vt;
|
|
HRESULT hr=S_OK;
|
|
|
|
vt = pSrc->vt & VT_TYPEMASK;
|
|
|
|
switch( vt )
|
|
{
|
|
case VT_I1:
|
|
pDest->cVal = pSrc->cac.pElems[idx];
|
|
break;
|
|
|
|
case VT_UI1:
|
|
pDest->bVal = pSrc->caub.pElems[idx];
|
|
break;
|
|
|
|
case VT_I2:
|
|
pDest->iVal = pSrc->cai.pElems[idx];
|
|
break;
|
|
|
|
case VT_UI2:
|
|
pDest->uiVal = pSrc->caui.pElems[idx];
|
|
break;
|
|
|
|
case VT_I4:
|
|
pDest->lVal = pSrc->cal.pElems[idx];
|
|
break;
|
|
|
|
case VT_UI4:
|
|
pDest->ulVal = pSrc->caul.pElems[idx];
|
|
break;
|
|
|
|
case VT_R4:
|
|
pDest->fltVal = pSrc->caflt.pElems[idx];
|
|
break;
|
|
|
|
case VT_R8:
|
|
pDest->dblVal = pSrc->cadbl.pElems[idx];
|
|
break;
|
|
|
|
case VT_CY:
|
|
pDest->cyVal = pSrc->cacy.pElems[idx];
|
|
break;
|
|
|
|
case VT_DATE:
|
|
pDest->date = pSrc->cadate.pElems[idx];
|
|
break;
|
|
|
|
case VT_BSTR:
|
|
if( NULL == pSrc->cabstr.pElems[idx] )
|
|
pDest->bstrVal = NULL;
|
|
else
|
|
{
|
|
pDest->bstrVal = PrivSysAllocString( pSrc->cabstr.pElems[idx] );
|
|
if( NULL == pDest->bstrVal)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Error;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VT_BOOL:
|
|
pDest->boolVal = pSrc->cabool.pElems[idx];
|
|
break;
|
|
|
|
case VT_ERROR:
|
|
pDest->scode = pSrc->cascode.pElems[idx];
|
|
break;
|
|
|
|
case VT_I8:
|
|
pDest->hVal = pSrc->cah.pElems[idx];
|
|
break;
|
|
|
|
case VT_UI8:
|
|
pDest->uhVal = pSrc->cauh.pElems[idx];
|
|
break;
|
|
|
|
// String Copy
|
|
case VT_LPSTR:
|
|
if( NULL == pSrc->calpstr.pElems[idx] )
|
|
pDest->pszVal = NULL;
|
|
else
|
|
{
|
|
pDest->pszVal = PropDupStr( pSrc->calpstr.pElems[idx] );
|
|
if( NULL == pDest->pszVal)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Error;
|
|
}
|
|
}
|
|
break;
|
|
|
|
// Wide String Copy
|
|
case VT_LPWSTR:
|
|
if( NULL == pSrc->calpwstr.pElems[idx] )
|
|
pDest->pwszVal = NULL;
|
|
else
|
|
{
|
|
pDest->pwszVal = PropDupWStr( pSrc->calpwstr.pElems[idx] );
|
|
if( NULL == pDest->pwszVal)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Error;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VT_FILETIME:
|
|
pDest->filetime = pSrc->cafiletime.pElems[idx];
|
|
break;
|
|
|
|
//
|
|
// The Variant takes a pointer to a CLIPDATA but the
|
|
// vector is an array of CLIPDATA structs.
|
|
//
|
|
case VT_CF:
|
|
pDest->pclipdata = PropDupClipData(&pSrc->caclipdata.pElems[idx]);
|
|
if( NULL == pDest->pclipdata)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Error;
|
|
}
|
|
break;
|
|
|
|
//
|
|
// The Variant takes a pointer to a CLSID but the
|
|
// vector is an array of CLSID structs.
|
|
//
|
|
case VT_CLSID:
|
|
pDest->puuid = PropDupCLSID(&pSrc->cauuid.pElems[idx]);
|
|
if( NULL == pDest->puuid)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Error;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return DISP_E_TYPEMISMATCH;
|
|
}
|
|
pDest->vt = vt;
|
|
|
|
Error:
|
|
return hr;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------
|
|
//
|
|
// PutPropVariantDataIntoSafeArray
|
|
// This will take the data part of a propvariant and "Put" it into the
|
|
// a SafeArray at the provided index.
|
|
// Only the insection of PROPVARIANT vector types with old VARIANT types
|
|
// are supported.
|
|
//
|
|
//------------------------------------------------------------------------
|
|
|
|
// PERF: Reimplement this without using the PropVariantCopy
|
|
|
|
HRESULT
|
|
PutPropVariantDataIntoSafeArray(
|
|
SAFEARRAY *psa,
|
|
const PROPVARIANT *pSrc,
|
|
int idx)
|
|
{
|
|
VARTYPE vt;
|
|
HRESULT hr=S_OK;
|
|
PROPVARIANT propvarT;
|
|
const void *pv=NULL;
|
|
|
|
vt = pSrc->vt & VT_TYPEMASK;
|
|
|
|
PROPASSERT(vt == pSrc->vt);
|
|
|
|
PropVariantInit( &propvarT );
|
|
hr = PropVariantCopy( &propvarT, pSrc );
|
|
if( FAILED( hr ) ) goto Exit;
|
|
|
|
switch( vt )
|
|
{
|
|
case VT_I1:
|
|
pv = &propvarT.cVal;
|
|
break;
|
|
|
|
case VT_UI1:
|
|
pv = &propvarT.bVal;
|
|
break;
|
|
|
|
case VT_I2:
|
|
pv = &propvarT.iVal;
|
|
break;
|
|
|
|
case VT_UI2:
|
|
pv = &propvarT.uiVal;
|
|
break;
|
|
|
|
case VT_I4:
|
|
pv = &propvarT.lVal;
|
|
break;
|
|
|
|
case VT_UI4:
|
|
pv = &propvarT.ulVal;
|
|
break;
|
|
|
|
case VT_R4:
|
|
pv = &propvarT.fltVal;
|
|
break;
|
|
|
|
case VT_R8:
|
|
pv = &propvarT.dblVal;
|
|
break;
|
|
|
|
case VT_CY:
|
|
pv = &propvarT.cyVal;
|
|
break;
|
|
|
|
case VT_DATE:
|
|
pv = &propvarT.date;
|
|
break;
|
|
|
|
case VT_BSTR:
|
|
pv = propvarT.bstrVal; // Pointer Copy
|
|
break;
|
|
|
|
case VT_BOOL:
|
|
pv = &propvarT.boolVal;
|
|
break;
|
|
|
|
case VT_ERROR:
|
|
pv = &propvarT.scode;
|
|
break;
|
|
|
|
case VT_I8:
|
|
pv = &propvarT.hVal;
|
|
break;
|
|
|
|
case VT_UI8:
|
|
pv = &propvarT.uhVal;
|
|
break;
|
|
|
|
case VT_CF:
|
|
pv = &propvarT.pclipdata;
|
|
break;
|
|
|
|
default:
|
|
hr = DISP_E_TYPEMISMATCH;
|
|
goto Exit;
|
|
}
|
|
|
|
// *Copy* the data into the SafeArray
|
|
hr = PrivSafeArrayPutElement( psa, (long*)&idx, const_cast<void*>(pv) );
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
Exit:
|
|
|
|
PropVariantClear( &propvarT );
|
|
return hr;
|
|
}
|
|
|