Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2151 lines
60 KiB

  1. //+============================================================================
  2. //
  3. // File: bag.cxx
  4. //
  5. // This file implements the IPropertyBagEx implementation used
  6. // in docfile, NSS (Native Structured Storage) and NFF (NTFS
  7. // Flat-File). IPropertyBagEx is required to be QI-able
  8. // from IStorage, so CPropertyBagEx is instantiated as a
  9. // data member of the various IStorage implementations.
  10. //
  11. // History:
  12. //
  13. // 3/10/98 MikeHill - Don't create a property set until the bag
  14. // is modified
  15. // - Add a constructor & IUnknown so that this class can
  16. // be instantiated standalone (used by
  17. // StgCreate/OpenStorageOnHandle).
  18. // - Added dbg tracing.
  19. // - Removed Commit-after-writes.
  20. // - Chagned from STATPROPS structure to STATPROPBAG.
  21. // - Added parameter validation.
  22. // - Added a ShutDown method.
  23. // 5/6/98 MikeHill
  24. // - Split IPropertyBag from IPropertyBagEx, new BagEx
  25. // DeleteMultiple method.
  26. // - Added support for VT_UNKNOWN/VT_DISPATCH.
  27. // - Beefed up dbg outs.
  28. // - Use CoTaskMem rather than new/delete.
  29. // 5/18/98 MikeHill
  30. // - Moved some initialization from the constructors
  31. // to a new Init method.
  32. // - Disallow non-Variant types in IPropertyBag::Write.
  33. // 6/11/98 MikeHill
  34. // - Add the new reserved parameter to DeleteMultiple.
  35. //
  36. //+============================================================================
  37. #include <pch.cxx>
  38. #include "chgtype.hxx"
  39. #define FORCE_EXCLUSIVE(g) ((g & ~STGM_SHARE_MASK) | STGM_SHARE_EXCLUSIVE)
  40. //
  41. // Add this prototype to chgtype.hxx when chgtype.hxx is finalized.
  42. //
  43. HRESULT ImplicitPropVariantToVariantChangeType(
  44. PROPVARIANT *pDest,
  45. const PROPVARIANT *pSrc,
  46. LCID lcid );
  47. HRESULT HrPropVarVECTORToSAFEARRAY(
  48. PROPVARIANT *pDest,
  49. const PROPVARIANT *pSrc,
  50. LCID lcid,
  51. VARTYPE vtCoerce );
  52. HRESULT LoadPropVariantFromVectorElem(
  53. PROPVARIANT *pDest,
  54. const PROPVARIANT *pSrc,
  55. int idx);
  56. HRESULT PutPropVariantDataIntoSafeArray(
  57. SAFEARRAY *psa,
  58. const PROPVARIANT *pSrc,
  59. int idx);
  60. //+----------------------------------------------------------------------------
  61. //
  62. // Method: CPropertyBagEx
  63. //
  64. //+----------------------------------------------------------------------------
  65. // Normal constructor
  66. CPropertyBagEx::CPropertyBagEx( DWORD grfMode )
  67. {
  68. HRESULT hr = S_OK;
  69. propITrace( "CPropertyBagEx::CPropertyBagEx(IPropertySetStorage)" );
  70. propTraceParameters(( "0x%x", grfMode ));
  71. // mask out the stgm_transacted bit; the bag is
  72. // interpreted as part of the parent storage, and should therefore
  73. // be in the storage's transaction set.
  74. _grfMode = grfMode & ~STGM_TRANSACTED;
  75. _lcid = LOCALE_NEUTRAL;
  76. _fLcidInitialized = FALSE;
  77. _ppropstg = NULL;
  78. _ppropsetstgContainer = NULL;
  79. _pBlockingLock = NULL;
  80. // We won't use this ref-count, we'll rely on _ppropsetstgContainer
  81. _cRefs = 0;
  82. } // CPropertyBagEx::CPropertyBagEx(grfMode)
  83. // Constructor for building an IPropertyBagEx on an IPropertyStorage
  84. CPropertyBagEx::CPropertyBagEx( DWORD grfMode,
  85. IPropertyStorage *ppropstg,
  86. IBlockingLock *pBlockingLock )
  87. {
  88. HRESULT hr = S_OK;
  89. propITrace( "CPropertyBagEx::CPropertyBagEx(IPropertyStorage)" );
  90. propTraceParameters(( "0x%x, 0x%x", ppropstg, pBlockingLock ));
  91. new(this) CPropertyBagEx( grfMode );
  92. Init (NULL, pBlockingLock);
  93. // We addref and keep the IPropertyStorage
  94. _ppropstg = ppropstg;
  95. _ppropstg->AddRef();
  96. // We keep and addref the BlockingLock
  97. _pBlockingLock->AddRef();
  98. // We also keep our own ref-count
  99. _cRefs = 1;
  100. } // CPropertyBagEx::CPropertyBagEx(grfMode, IPropertyStorage*, IBlockingLock*)
  101. //+----------------------------------------------------------------------------
  102. //
  103. // Method: CPropertyBagEx::Init (non-interface method)
  104. //
  105. // This method is used by the caller to provide the IPropertySetStorage
  106. // (and IBlockingLock) that is to contain the property bag.
  107. // This must be called after the simple grfMode-based constructor.
  108. //
  109. // This method does *not* addref these interfaces.
  110. //
  111. //+----------------------------------------------------------------------------
  112. void
  113. CPropertyBagEx::Init( IPropertySetStorage *ppropsetstg, IBlockingLock *pBlockingLock )
  114. {
  115. DfpAssert( NULL == _ppropsetstgContainer
  116. &&
  117. NULL == _pBlockingLock
  118. &&
  119. NULL == _ppropstg );
  120. _ppropsetstgContainer = ppropsetstg;
  121. _pBlockingLock = pBlockingLock;
  122. }
  123. //+----------------------------------------------------------------------------
  124. //
  125. // Method: ~CPropertyBagEx
  126. //
  127. //+----------------------------------------------------------------------------
  128. CPropertyBagEx::~CPropertyBagEx()
  129. {
  130. #if DBG
  131. IStorageTest *ptest = NULL;
  132. HRESULT hr = S_OK;
  133. if( NULL != _ppropstg )
  134. {
  135. hr = _ppropstg->QueryInterface( IID_IStorageTest, reinterpret_cast<void**>(&ptest) );
  136. if( SUCCEEDED(hr) )
  137. {
  138. hr = ptest->IsDirty();
  139. DfpAssert( S_FALSE == hr || E_NOINTERFACE == hr );
  140. RELEASE_INTERFACE(ptest);
  141. }
  142. }
  143. #endif // #if DBG
  144. ShutDown();
  145. }
  146. //+----------------------------------------------------------------------------
  147. //
  148. // Method: OpenPropStg
  149. //
  150. // Open the bag's IPropertyStorage. If FILE_OPEN_IF is specified, we open
  151. // it only if it already exists. if FILE_OPEN is specified, we'll create it
  152. // if necessary.
  153. //
  154. //+----------------------------------------------------------------------------
  155. HRESULT
  156. CPropertyBagEx::OpenPropStg( DWORD dwDisposition )
  157. {
  158. HRESULT hr = S_OK;
  159. IPropertyStorage *ppropstg = NULL;
  160. PROPVARIANT propvarCodePage;
  161. PROPSPEC propspecCodePage;
  162. STATPROPSETSTG statpropsetstg;
  163. propITrace( "CPropertyBagEx::OpenPropStg" );
  164. propTraceParameters(( "%s", FILE_OPEN_IF == dwDisposition
  165. ? "OpenIf"
  166. : (FILE_OPEN == dwDisposition ? "Open" : "Unknown disposition")
  167. ));
  168. DfpAssert( FILE_OPEN == dwDisposition || FILE_OPEN_IF == dwDisposition );
  169. PropVariantInit( &propvarCodePage );
  170. // Does the IPropertyStorage need to be opened?
  171. if( NULL == _ppropstg )
  172. {
  173. // Ensure we have a _ppropsetstgContainer
  174. if( NULL == _ppropsetstgContainer )
  175. {
  176. hr = HRESULT_FROM_WIN32( ERROR_INTERNAL_ERROR );
  177. goto Exit;
  178. }
  179. // Try to open the property storage
  180. // We have to open exclusive, no matter how the parent was opened.
  181. hr = _ppropsetstgContainer->Open( FMTID_PropertyBag,
  182. FORCE_EXCLUSIVE(_grfMode) & ~STGM_CREATE,
  183. &ppropstg );
  184. if( STG_E_FILENOTFOUND == hr && FILE_OPEN == dwDisposition )
  185. {
  186. // It didn't exist, and we're supposed to remedy that.
  187. hr = _ppropsetstgContainer->Create( FMTID_PropertyBag, NULL,
  188. PROPSETFLAG_CASE_SENSITIVE | PROPSETFLAG_NONSIMPLE,
  189. FORCE_EXCLUSIVE(_grfMode) | STGM_CREATE,
  190. &ppropstg );
  191. }
  192. else if( STG_E_FILENOTFOUND == hr && FILE_OPEN_IF == dwDisposition )
  193. {
  194. // This is an expected error.
  195. propSuppressExitErrors();
  196. }
  197. if( FAILED(hr) ) goto Exit;
  198. // Verify that this is a Unicode property set
  199. propspecCodePage.ulKind = PRSPEC_PROPID;
  200. propspecCodePage.propid = PID_CODEPAGE;
  201. hr = ppropstg->ReadMultiple( 1, &propspecCodePage, &propvarCodePage );
  202. if( FAILED(hr) ) goto Exit;
  203. if( VT_I2 != propvarCodePage.vt
  204. ||
  205. CP_WINUNICODE != propvarCodePage.iVal )
  206. {
  207. propDbg(( DEB_ERROR, "Non-unicode codepage found in supposed property bag (0x%x)\n",
  208. propvarCodePage.iVal ));
  209. hr = STG_E_INVALIDHEADER;
  210. goto Exit;
  211. }
  212. // Also verify that the property set is non-simple and case-sensitive
  213. hr = ppropstg->Stat( &statpropsetstg );
  214. if( FAILED(hr) ) goto Exit;
  215. if( !(PROPSETFLAG_CASE_SENSITIVE & statpropsetstg.grfFlags)
  216. ||
  217. !(PROPSETFLAG_NONSIMPLE & statpropsetstg.grfFlags) )
  218. {
  219. propDbg(( DEB_ERROR, "Supposed property bag isn't case sensitive or isn't non-simple (0x%x)\n",
  220. statpropsetstg.grfFlags ));
  221. hr = STG_E_INVALIDHEADER;
  222. goto Exit;
  223. }
  224. _ppropstg = ppropstg;
  225. ppropstg = NULL;
  226. }
  227. // Even if we already have an open IPropertyStorage, we may not have yet read the
  228. // Locale ID (this happens in the case where the CPropertyBagEx(IPropertyStorage*)
  229. // constructor is used).
  230. if( !_fLcidInitialized )
  231. {
  232. hr = GetLCID();
  233. if( FAILED(hr) ) goto Exit;
  234. }
  235. // ----
  236. // Exit
  237. // ----
  238. hr = S_OK;
  239. Exit:
  240. DfpVerify( 0 == RELEASE_INTERFACE(ppropstg) );
  241. return(hr);
  242. } // CPropertyBagEx::OpenPropStg
  243. //+----------------------------------------------------------------------------
  244. //
  245. // Method: GetLCID
  246. //
  247. // Get the LocalID from the property set represented by _ppropstg.
  248. //
  249. //+----------------------------------------------------------------------------
  250. HRESULT
  251. CPropertyBagEx::GetLCID()
  252. {
  253. HRESULT hr = S_OK;
  254. PROPSPEC propspecLCID;
  255. PROPVARIANT propvarLCID;
  256. propITrace( "CPropertyBagEx::GetLCID" );
  257. propspecLCID.ulKind = PRSPEC_PROPID;
  258. propspecLCID.propid = PID_LOCALE;
  259. PropVariantInit( &propvarLCID );
  260. if( SUCCEEDED( hr = _ppropstg->ReadMultiple( 1, &propspecLCID, &propvarLCID ))
  261. &&
  262. VT_UI4 == propvarLCID.vt )
  263. {
  264. _lcid = propvarLCID.uiVal;
  265. }
  266. else if( S_FALSE == hr )
  267. {
  268. _lcid = GetUserDefaultLCID();
  269. }
  270. _fLcidInitialized = TRUE;
  271. PropVariantClear( &propvarLCID );
  272. return( hr );
  273. } // CPropertyBagEx::GetLCID()
  274. //+----------------------------------------------------------------------------
  275. //
  276. // Method: IUnknown methods
  277. //
  278. // If we have a parent (_ppropsetstgParent), we forward all calls to there.
  279. // Otherwise, we implement all calls here. Whether or not we have a parent
  280. // depends on which of the two constructors is called. In NFF, NSS, and docfile,
  281. // we have a parent. When creating a standalone property bag implementation
  282. // (in StgCreate/OpenStorageOnHandle), we have no such parent.
  283. //
  284. //+----------------------------------------------------------------------------
  285. HRESULT STDMETHODCALLTYPE
  286. CPropertyBagEx::QueryInterface(
  287. /* [in] */ REFIID riid,
  288. /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject)
  289. {
  290. // Do we have a parent?
  291. if( NULL != _ppropsetstgContainer )
  292. {
  293. // Yes, refer the call
  294. DfpAssert( 0 == _cRefs );
  295. return( _ppropsetstgContainer->QueryInterface( riid, ppvObject ));
  296. }
  297. else
  298. {
  299. // No, we have no parent.
  300. if( IID_IPropertyBagEx == riid
  301. ||
  302. IID_IUnknown == riid )
  303. {
  304. AddRef();
  305. *ppvObject = static_cast<IPropertyBagEx*>(this);
  306. return( S_OK );
  307. }
  308. else if( IID_IPropertyBag == riid )
  309. {
  310. AddRef();
  311. *ppvObject = static_cast<IPropertyBag*>(this);
  312. return( S_OK );
  313. }
  314. }
  315. return( E_NOINTERFACE );
  316. }
  317. ULONG STDMETHODCALLTYPE
  318. CPropertyBagEx::AddRef( void)
  319. {
  320. // Do we have a parent?
  321. if( NULL != _ppropsetstgContainer )
  322. {
  323. // Yes. The parent does the ref-counting
  324. DfpAssert( 0 == _cRefs );
  325. return( _ppropsetstgContainer->AddRef() );
  326. }
  327. else
  328. {
  329. // No. We don't have a parent, and we must do the ref-counting.
  330. LONG lRefs = InterlockedIncrement( &_cRefs );
  331. return( lRefs );
  332. }
  333. }
  334. ULONG STDMETHODCALLTYPE
  335. CPropertyBagEx::Release( void)
  336. {
  337. // Do we have a parent?
  338. if( NULL != _ppropsetstgContainer )
  339. {
  340. // Yes. The container does the ref-counting
  341. DfpAssert( 0 == _cRefs );
  342. return( _ppropsetstgContainer->Release() );
  343. }
  344. else
  345. {
  346. // No. We don't have a parent, and we must do the ref-counting.
  347. LONG lRefs = InterlockedDecrement( &_cRefs );
  348. if( 0 == lRefs )
  349. {
  350. // Only in this case (where we have no parent) do we release
  351. // the IBlockingLock
  352. RELEASE_INTERFACE( _pBlockingLock );
  353. delete this;
  354. }
  355. return( lRefs );
  356. }
  357. }
  358. //+----------------------------------------------------------------------------
  359. //
  360. // Method: Read/Write (IPropertyBag)
  361. //
  362. // These methods simply thunk to ReadMultiple/WriteMultiple.
  363. //
  364. //+----------------------------------------------------------------------------
  365. HRESULT STDMETHODCALLTYPE
  366. CPropertyBagEx::Read(
  367. /* [in] */ LPCOLESTR pszPropName,
  368. /* [out][in] */ VARIANT __RPC_FAR *pVar,
  369. /* [in] */ IErrorLog __RPC_FAR *pErrorLog)
  370. {
  371. HRESULT hr=S_OK, hr2=S_OK;
  372. PROPVARIANT *ppropvar, propvarTmp;
  373. // We'll read the property into propvarTmp. Initialize it to
  374. // *pVar, since it is an in/out parameter (pVar->VarType holds the
  375. // caller's desired type).
  376. ppropvar = reinterpret_cast<PROPVARIANT*>(pVar);
  377. propvarTmp = *ppropvar;
  378. // Call down to ReadMultiple to read this one property.
  379. hr = ReadMultiple(1, &pszPropName, &propvarTmp, pErrorLog );
  380. // If that worked, coerce the property into the type desired by the caller.
  381. // ReadMultiple already tries to do a coercion, but IPropertyBag::Read
  382. // supports different types than IPropertyBagEx::ReadMultiple(??).
  383. if( !FAILED( hr ) )
  384. {
  385. hr2 = ImplicitPropVariantToVariantChangeType( ppropvar, &propvarTmp, _lcid );
  386. PropVariantClear( &propvarTmp );
  387. }
  388. if( FAILED(hr2) )
  389. return hr2;
  390. else
  391. return hr;
  392. } // CPropertyBagEx::Read
  393. HRESULT STDMETHODCALLTYPE
  394. CPropertyBagEx::Write(
  395. /* [in] */ LPCOLESTR pszPropName,
  396. /* [in] */ VARIANT __RPC_FAR *pVar)
  397. {
  398. if( !IsVariantType( pVar->vt ))
  399. return( STG_E_INVALIDPARAMETER );
  400. return( WriteMultiple(1, &pszPropName, reinterpret_cast<PROPVARIANT*>(pVar) ));
  401. }
  402. //+----------------------------------------------------------------------------
  403. //
  404. // Method: CPropertyBagEx::ReadMultiple (IPropertyBagEx)
  405. //
  406. // This method calls down to IPropertyStorage::ReadMultiple. It has the
  407. // additional behaviour of coercing the property types into those specified
  408. // by the caller.
  409. //
  410. //+----------------------------------------------------------------------------
  411. HRESULT STDMETHODCALLTYPE
  412. CPropertyBagEx::ReadMultiple(
  413. /* [in] */ ULONG cprops,
  414. /* [size_is][in] */ LPCOLESTR const __RPC_FAR rgoszPropNames[ ],
  415. /* [size_is][out][in] */ PROPVARIANT __RPC_FAR rgpropvar[ ],
  416. /* [in] */ IErrorLog __RPC_FAR *pErrorLog)
  417. {
  418. HRESULT hr = S_OK;
  419. ULONG i;
  420. PROPSPEC *rgpropspec = NULL; // PERF: use TStackBuffer
  421. PROPVARIANT *rgpropvarRead = NULL;
  422. BOOL fAtLeastOnePropertyRead = FALSE;
  423. propXTrace( "CPropertyBagEx::ReadMultiple" );
  424. _pBlockingLock->Lock( INFINITE );
  425. // ----------
  426. // Validation
  427. // ----------
  428. if (0 == cprops)
  429. {
  430. hr = S_OK;
  431. goto Exit;
  432. }
  433. if (S_OK != (hr = ValidateInRGLPOLESTR( cprops, rgoszPropNames )))
  434. goto Exit;
  435. if (S_OK != (hr = ValidateOutRGPROPVARIANT( cprops, rgpropvar )))
  436. goto Exit;
  437. propTraceParameters(( "%d, 0x%x, 0x%x, 0x%x", cprops, rgoszPropNames, rgpropvar, pErrorLog ));
  438. // --------------
  439. // Initialization
  440. // --------------
  441. // Open the underlying property set if it exists.
  442. hr = OpenPropStg( FILE_OPEN_IF );
  443. if( STG_E_FILENOTFOUND == hr )
  444. {
  445. // The property storage for this bag doesn't even exist. So we simulate
  446. // the reading non-extant properties by setting the vt's to emtpy and returning
  447. // s_false.
  448. for( i = 0; i < cprops; i++ )
  449. rgpropvar[i].vt = VT_EMPTY;
  450. hr = S_FALSE;
  451. goto Exit;
  452. }
  453. else if( FAILED(hr) )
  454. goto Exit;
  455. // The property set existed and is now open.
  456. // Alloc propspec & propvar arrays.
  457. // PERF: Make these stack-based for typical-sized reads.
  458. rgpropspec = reinterpret_cast<PROPSPEC*>
  459. ( CoTaskMemAlloc( cprops * sizeof(PROPSPEC) ));
  460. if( NULL == rgpropspec )
  461. {
  462. hr = E_OUTOFMEMORY;
  463. goto Exit;
  464. }
  465. rgpropvarRead = reinterpret_cast<PROPVARIANT*>
  466. ( CoTaskMemAlloc( cprops * sizeof(PROPVARIANT) ));
  467. if( NULL == rgpropvarRead )
  468. {
  469. hr = E_OUTOFMEMORY;
  470. goto Exit;
  471. }
  472. // Put the names into the propspecs.
  473. for( i = 0; i < cprops; i++ )
  474. {
  475. PropVariantInit( &rgpropvarRead[i] );
  476. rgpropspec[i].ulKind = PRSPEC_LPWSTR;
  477. rgpropspec[i].lpwstr = const_cast<LPOLESTR>(rgoszPropNames[i]);
  478. }
  479. // ----------------------------
  480. // Read & coerce the properties
  481. // ----------------------------
  482. // Read the properties into the local PropVar array
  483. hr = _ppropstg->ReadMultiple(cprops, rgpropspec, rgpropvarRead );
  484. if( FAILED(hr) ) goto Exit;
  485. if( S_FALSE != hr )
  486. fAtLeastOnePropertyRead = TRUE;
  487. // Coerce the properties as necessary
  488. for( i = 0; i < cprops; i++ )
  489. {
  490. // If the caller requested a type (something other than VT_EMPTY), and the
  491. // requested type isn't the same as the read type, then a coercion is necessary.
  492. if( VT_EMPTY != rgpropvar[i].vt && rgpropvarRead[i].vt != rgpropvar[i].vt )
  493. {
  494. PROPVARIANT propvar;
  495. PropVariantInit( &propvar );
  496. // Does this property require conversion
  497. // (i.e. to a VT_UNKNOWN/DISPATCH)?
  498. if( PropertyRequiresConversion( rgpropvar[i].vt )
  499. &&
  500. ( VT_STORED_OBJECT == rgpropvarRead[i].vt
  501. ||
  502. VT_STREAMED_OBJECT == rgpropvarRead[i].vt
  503. )
  504. )
  505. {
  506. // Load the object from the stream/storage in rgpropvarRead
  507. // into propvar.
  508. propvar = rgpropvar[i];
  509. hr = LoadObject( &propvar, &rgpropvarRead[i] );
  510. if( FAILED(hr) )
  511. goto Exit;
  512. }
  513. else
  514. {
  515. // Coerce the property in rgpropvarRead into propvar, according
  516. // to the caller-requested type (which is specified in rgpropvar).
  517. hr = PropVariantChangeType( &propvar, &rgpropvarRead[i],
  518. _lcid, 0, rgpropvar[i].vt );
  519. if( FAILED(hr) )
  520. {
  521. propDbg(( DEB_ERROR, "CPropertyBagEx(0x%x), PropVariantChangeType"
  522. "(0x%x,0x%x,%d,%d,%d) returned %08x\n",
  523. this, &propvar, &rgpropvarRead[i], _lcid, 0, rgpropvar[i].vt, hr ));
  524. goto Exit;
  525. }
  526. }
  527. // Get rid of the value original read, and keep the coerced value.
  528. PropVariantClear( &rgpropvarRead[i] );
  529. rgpropvarRead[i] = propvar;
  530. }
  531. } // for( i = 0; i < cprops; i++ )
  532. // No errors from here to the end.
  533. // Copy the PropVars from the local array to the caller's
  534. for( i = 0; i < cprops; i++ )
  535. rgpropvar[i] = rgpropvarRead[i];
  536. // ----
  537. // Exit
  538. // ----
  539. CoTaskMemFree( rgpropvarRead );
  540. rgpropvarRead = NULL;
  541. if( SUCCEEDED(hr) )
  542. hr = fAtLeastOnePropertyRead ? S_OK : S_FALSE;
  543. Exit:
  544. // We needn't free the rgpropspec[*].lpwstr members, they point
  545. // into rgszPropName
  546. if( NULL != rgpropspec )
  547. CoTaskMemFree( rgpropspec );
  548. if( NULL != rgpropvarRead )
  549. {
  550. for( i = 0; i < cprops; i++ )
  551. PropVariantClear( &rgpropvarRead[i] );
  552. CoTaskMemFree( rgpropvarRead );
  553. }
  554. _pBlockingLock->Unlock();
  555. return( hr );
  556. } // CPropertyBagEx::ReadMultiple
  557. //+----------------------------------------------------------------------------
  558. //
  559. // Method: CPropertyBagEx::LoadObject
  560. //
  561. // This method loads an IUnknown or IDispatch object from its persisted state
  562. // in a stream or storage (by QI-ing for IPersistStream or IPersistStorage,
  563. // respectively).
  564. //
  565. //+----------------------------------------------------------------------------
  566. HRESULT
  567. CPropertyBagEx::LoadObject( OUT PROPVARIANT *ppropvarOut, IN PROPVARIANT *ppropvarIn ) const
  568. {
  569. HRESULT hr = S_OK;
  570. IUnknown *punk = NULL;
  571. IPersistStorage *pPersistStg = NULL;
  572. IPersistStream *pPersistStm = NULL;
  573. propITrace( "CPropertyBagEx::LoadObject" );
  574. DfpAssert( VT_STREAMED_OBJECT == ppropvarIn->vt || VT_STORED_OBJECT == ppropvarIn->vt );
  575. DfpAssert( VT_UNKNOWN == ppropvarOut->vt || VT_DISPATCH == ppropvarOut->vt );
  576. punk = ppropvarOut->punkVal;
  577. DfpAssert( reinterpret_cast<void*>(&ppropvarOut->punkVal)
  578. ==
  579. reinterpret_cast<void*>(&ppropvarOut->pdispVal) );
  580. // --------------------------
  581. // Read in an IPersistStorage
  582. // --------------------------
  583. if( VT_STORED_OBJECT == ppropvarIn->vt )
  584. {
  585. DfpAssert( NULL != ppropvarIn->pStorage );
  586. // Get an IUnknown if the caller didn't provide one.
  587. if( NULL == punk )
  588. {
  589. STATSTG statstg;
  590. hr = ppropvarIn->pStorage->Stat( &statstg, STATFLAG_NONAME );
  591. if( FAILED(hr) ) goto Exit;
  592. hr = CoCreateInstance( statstg.clsid, NULL, CLSCTX_ALL, IID_IUnknown,
  593. reinterpret_cast<void**>(&punk) );
  594. if( FAILED(hr) ) goto Exit;
  595. }
  596. // QI for IPersistStorage
  597. hr = punk->QueryInterface( IID_IPersistStorage, reinterpret_cast<void**>(&pPersistStg) );
  598. if( FAILED(hr) )
  599. {
  600. propDbg(( DEB_ERROR, "Caller didn't provide IPersistStorage in IProeprtyBagEx::ReadMultiple (%08x)", hr ));
  601. goto Exit;
  602. }
  603. // IPersist::Load the storage
  604. hr = pPersistStg->Load( ppropvarIn->pStorage );
  605. if( FAILED(hr) )
  606. {
  607. propDbg(( DEB_ERROR, "Failed IPersistStorage::Load in IPropretyBagEx::ReadMultiple (%08x)", hr ));
  608. goto Exit;
  609. }
  610. } // if( VT_STORED_OBJECT == ppropvarIn->vt )
  611. // -------------------------
  612. // Read in an IPersistStream
  613. // -------------------------
  614. else
  615. {
  616. DfpAssert( VT_STREAMED_OBJECT == ppropvarIn->vt );
  617. DfpAssert( NULL != ppropvarIn->pStream );
  618. CLSID clsid;
  619. ULONG cbRead;
  620. // Skip over the clsid
  621. hr = ppropvarIn->pStream->Read( &clsid, sizeof(clsid), &cbRead );
  622. if( FAILED(hr) ) goto Exit;
  623. if( sizeof(clsid) != cbRead )
  624. {
  625. hr = STG_E_INVALIDHEADER;
  626. propDbg(( DEB_ERROR, "Clsid missing in VT_STREAMED_OBJECT in IPropertyBagEx::ReadMultiple (%d bytes)",
  627. cbRead ));
  628. goto Exit;
  629. }
  630. // Get an IUnknown if the caller didn't provide one.
  631. if( NULL == punk )
  632. {
  633. hr = CoCreateInstance( clsid, NULL, CLSCTX_ALL, IID_IUnknown,
  634. reinterpret_cast<void**>(&punk) );
  635. if( FAILED(hr) ) goto Exit;
  636. }
  637. // QI for the IPersistStream
  638. hr = punk->QueryInterface( IID_IPersistStream, reinterpret_cast<void**>(&pPersistStm) );
  639. if( FAILED(hr) )
  640. {
  641. propDbg(( DEB_ERROR, "Caller didn't provide IPersistStream in IPropertyBagEx::ReadMultiple (%08x)", hr ));
  642. goto Exit;
  643. }
  644. // Load the remainder of the stream
  645. IFDBG( ULONG cRefs = GetRefCount( ppropvarIn->pStream ));
  646. hr = pPersistStm->Load( ppropvarIn->pStream );
  647. if( FAILED(hr) )
  648. {
  649. propDbg(( DEB_ERROR, "Failed IPersistStream::Load in IPropertyBagEx::ReadMultiple (%08x)", hr ));
  650. goto Exit;
  651. }
  652. DfpAssert( GetRefCount( ppropvarIn->pStream ) == cRefs );
  653. } // if( VT_STORED_OBJECT == ppropvarIn->vt ) ... else
  654. // ----
  655. // Exit
  656. // ----
  657. ppropvarOut->punkVal = punk;
  658. punk = NULL;
  659. hr = S_OK;
  660. Exit:
  661. RELEASE_INTERFACE( pPersistStg );
  662. RELEASE_INTERFACE( pPersistStm );
  663. RELEASE_INTERFACE( punk );
  664. return( hr );
  665. } // CPropertyBagEx::LoadObject
  666. //+----------------------------------------------------------------------------
  667. //
  668. // Method: CPropertyBagEx::WriteMultiple (IPropertyBagEx)
  669. //
  670. // This method calls down to IPropertyStorage::WriteMultiple. Additionally,
  671. // this method is atomic. So if the WriteMultiple fails, the IPropertyStorage
  672. // is reverted and reopened.
  673. //
  674. //+----------------------------------------------------------------------------
  675. HRESULT STDMETHODCALLTYPE
  676. CPropertyBagEx::WriteMultiple(
  677. /* [in] */ ULONG cprops,
  678. /* [size_is][in] */ LPCOLESTR const __RPC_FAR rgoszPropNames[ ],
  679. /* [size_is][in] */ const PROPVARIANT __RPC_FAR rgpropvar[ ])
  680. {
  681. HRESULT hr = S_OK;
  682. ULONG i;
  683. PROPSPEC *prgpropspec = NULL;
  684. BOOL fInterfaces = FALSE;
  685. _pBlockingLock->Lock( INFINITE );
  686. CStackPropVarArray rgpropvarCopy;
  687. propXTrace( "CPropertyBagEx::WriteMultiple" );
  688. hr = rgpropvarCopy.Init( cprops );
  689. if( FAILED(hr) ) goto Exit;
  690. // ----------
  691. // Validation
  692. // ----------
  693. if (0 == cprops)
  694. {
  695. hr = S_OK;
  696. goto Exit;
  697. }
  698. if (S_OK != (hr = ValidateInRGLPOLESTR( cprops, rgoszPropNames )))
  699. goto Exit;
  700. if (S_OK != (hr = ValidateInRGPROPVARIANT( cprops, rgpropvar )))
  701. goto Exit;
  702. propTraceParameters(("%lu, 0x%x, 0x%x", cprops, rgoszPropNames, rgpropvar));
  703. // --------------
  704. // Initialization
  705. // --------------
  706. // Open the property storage if it isn't already, creating if necessary.
  707. hr = OpenPropStg( FILE_OPEN );
  708. if( FAILED(hr) ) goto Exit;
  709. // PERF: Create a local array of propspecs for the WriteMultiple call
  710. prgpropspec = reinterpret_cast<PROPSPEC*>( CoTaskMemAlloc( cprops * sizeof(PROPSPEC) ));
  711. if( NULL == prgpropspec )
  712. {
  713. hr = E_OUTOFMEMORY;
  714. goto Exit;
  715. }
  716. // Set up the PROPSPEC and PROPVARIANT arrays to be written.
  717. for( i = 0; i < cprops; i++ )
  718. {
  719. // Point the propspecs at the caller-provided names
  720. prgpropspec[i].ulKind = PRSPEC_LPWSTR;
  721. prgpropspec[i].lpwstr = const_cast<LPOLESTR>(rgoszPropNames[i]);
  722. // Make a copy of the input PropVariant array, converting VT_UNKNOWN and
  723. // VT_DISPATCH into VT_EMPTY. We'll handle these after the initial write.
  724. rgpropvarCopy[i] = rgpropvar[i];
  725. if( PropertyRequiresConversion( rgpropvarCopy[i].vt ))
  726. {
  727. if( NULL == rgpropvarCopy[i].punkVal )
  728. {
  729. hr = E_INVALIDARG;
  730. goto Exit;
  731. }
  732. // We'll handle this particular property after the initial WriteMultiple.
  733. fInterfaces = TRUE;
  734. PropVariantInit( &rgpropvarCopy[i] );
  735. }
  736. }
  737. // --------------------
  738. // Write the properties
  739. // --------------------
  740. // Write all the properties, though the VT_UNKNOWN and VT_DISPATCH have been
  741. // switched to VT_EMPTY (their names will be written, but no value will be written
  742. // for them).
  743. hr = _ppropstg->WriteMultiple(cprops, prgpropspec, rgpropvarCopy, PID_FIRST_USABLE );
  744. if( FAILED(hr) ) goto Exit;
  745. // Now write the VT_UNKNOWN and VT_DISPATCH properties individually, converting
  746. // first to VT_STREAMED_OBJECT or VT_STORED_OBJECT.
  747. if( fInterfaces )
  748. {
  749. hr = WriteObjects( cprops, prgpropspec, rgpropvar );
  750. if( FAILED(hr) ) goto Exit;
  751. }
  752. // ----
  753. // Exit
  754. // ----
  755. hr = S_OK;
  756. Exit:
  757. // We don't need to delete the lpwstr fields, they just point to the caller-provided
  758. // names.
  759. if( NULL != prgpropspec )
  760. CoTaskMemFree( prgpropspec );
  761. // We don't need to clear rgpropvarCopy; it wasn't a deep copy.
  762. _pBlockingLock->Unlock();
  763. return( hr );
  764. } // CPropertyBagEx::WriteMultiple
  765. //+----------------------------------------------------------------------------
  766. //
  767. // Method: CPropertyBagEx::WriteObjects
  768. //
  769. // This method writes VT_UNKNOWN and VT_DISPATCH properties. It scans the
  770. // input rgpropvar array for such properties. For each that it finds, it QIs
  771. // for IPersistStorage or IPersistStream (in that order), creates a
  772. // VT_STORED_OBJECT/VT_STREAMED_OBJECT property (respectively), and persists the object
  773. // to that property.
  774. //
  775. //+----------------------------------------------------------------------------
  776. HRESULT
  777. CPropertyBagEx::WriteObjects( IN ULONG cprops,
  778. IN const PROPSPEC rgpropspec[],
  779. IN const PROPVARIANT rgpropvar[] )
  780. {
  781. HRESULT hr = S_OK;
  782. ULONG i;
  783. propITrace( "CPropertyBagEx::WriteObjects" );
  784. for( i = 0; i < cprops; i++ )
  785. {
  786. // Is this a type we need to handle?
  787. if( PropertyRequiresConversion( rgpropvar[i].vt ))
  788. {
  789. hr = WriteOneObject( &rgpropspec[i], &rgpropvar[i] );
  790. if( FAILED(hr) ) goto Exit;
  791. }
  792. }
  793. hr = S_OK;
  794. Exit:
  795. return( hr );
  796. } // CPropertyBagEx::WriteObjects
  797. //+----------------------------------------------------------------------------
  798. //
  799. // Method: WriteOneObject
  800. //
  801. // This method takes an input propvar that holds an IUnknown or IDispatch,
  802. // and writes it out to the property set in a stored object
  803. // (if the unknown/dispatch supports IPersistStorage) or a streamed object
  804. // (if it supports IPersistStream).
  805. //
  806. //+----------------------------------------------------------------------------
  807. HRESULT
  808. CPropertyBagEx::WriteOneObject( const IN PROPSPEC *ppropspec, const IN PROPVARIANT *ppropvar )
  809. {
  810. HRESULT hr = S_OK;
  811. PROPVARIANT propvarNew;
  812. IPersistStorage *pPersistStg = NULL;
  813. IPersistStream *pPersistStm = NULL;
  814. IUnknown *punk = NULL;
  815. PropVariantInit( &propvarNew );
  816. DfpAssert( PropertyRequiresConversion( ppropvar->vt ));
  817. DfpAssert( reinterpret_cast<const void*const*>(&ppropvar->punkVal)
  818. ==
  819. reinterpret_cast<const void*const*>(&ppropvar->pdispVal)
  820. &&
  821. reinterpret_cast<const void*const*>(&ppropvar->pdispVal)
  822. ==
  823. reinterpret_cast<const void*const*>(&ppropvar->ppdispVal)
  824. &&
  825. reinterpret_cast<const void*const*>(&ppropvar->ppdispVal)
  826. ==
  827. reinterpret_cast<const void*const*>(&ppropvar->ppunkVal) );
  828. // --------------------
  829. // Look for an IPersist
  830. // --------------------
  831. // Get the IUnknown pointer (good for both VT_UNKNOWN and VT_DISPATCH).
  832. if( VT_BYREF & ppropvar->vt )
  833. punk = *ppropvar->ppunkVal;
  834. else
  835. punk = ppropvar->punkVal;
  836. DfpAssert( NULL != punk );
  837. // QI for IPersistStorage, then IPersistStream. If we find one or the other,
  838. // set up propvarNew in preparation for a write. We give preference to IPersistStorage,
  839. // because if the punk supports both, we should give it the opportunity for the more
  840. // functional of the two persistence mechanisms.
  841. hr = punk->QueryInterface( IID_IPersistStorage, reinterpret_cast<void**>(&pPersistStg) );
  842. if( SUCCEEDED(hr) )
  843. {
  844. propvarNew.vt = VT_STORED_OBJECT;
  845. }
  846. else if( E_NOINTERFACE == hr )
  847. {
  848. hr = punk->QueryInterface( IID_IPersistStream, reinterpret_cast<void**>(&pPersistStm) );
  849. if( SUCCEEDED(hr) )
  850. propvarNew.vt = VT_STREAMED_OBJECT;
  851. }
  852. if( FAILED(hr) )
  853. {
  854. propDbg(( DEB_WARN, "Couldn't find an IPersist interface in IPropertyBagEx::WriteMultiple" ));
  855. goto Exit;
  856. }
  857. // ----------------------------------
  858. // Create the stream/storage property
  859. // ----------------------------------
  860. // Write this empty VT_STREAMED/STORED_OBJECT (has a NULL pstm/pstg value). This will cause the
  861. // property set to create a new stream/storage.
  862. hr = _ppropstg->WriteMultiple( 1, ppropspec, &propvarNew, PID_FIRST_USABLE );
  863. if( FAILED(hr) )
  864. {
  865. propDbg(( DEB_ERROR, "Couldn't write %d for interface in IPropertyBagEx", propvarNew.vt ));
  866. goto Exit;
  867. }
  868. // Get an interface pointer for that new stream/storage.
  869. hr = _ppropstg->ReadMultiple( 1, ppropspec, &propvarNew );
  870. if( FAILED(hr) || S_FALSE == hr )
  871. {
  872. propDbg(( DEB_ERROR, "Couldn't read stream/storage back for interface in IPropertyBagEx::WriteMultiple" ));
  873. if( !FAILED(hr) )
  874. hr = STG_E_WRITEFAULT;
  875. goto Exit;
  876. }
  877. // --------------------
  878. // Persist the property
  879. // --------------------
  880. // IPersistStorage case
  881. if( NULL != pPersistStg )
  882. {
  883. CLSID clsid;
  884. DfpAssert( VT_STORED_OBJECT == propvarNew.vt );
  885. // Set the clsid
  886. hr = pPersistStg->GetClassID( &clsid );
  887. if( E_NOTIMPL == hr )
  888. clsid = CLSID_NULL;
  889. else if( FAILED(hr) )
  890. goto Exit;
  891. hr = propvarNew.pStorage->SetClass( clsid );
  892. if( FAILED(hr) ) goto Exit;
  893. // Persist to VT_STORED_OBJECT
  894. hr = pPersistStg->Save( propvarNew.pStorage, FALSE /* Not necessarily same as load */ );
  895. if( FAILED(hr) ) goto Exit;
  896. hr = pPersistStg->SaveCompleted( propvarNew.pStorage );
  897. if( FAILED(hr) ) goto Exit;
  898. }
  899. // IPersistStream case
  900. else
  901. {
  902. CLSID clsid;
  903. ULONG cbWritten;
  904. DfpAssert( VT_STREAMED_OBJECT == propvarNew.vt );
  905. DfpAssert( NULL != pPersistStm );
  906. #if 1 == DBG
  907. // This stream should be at its start.
  908. {
  909. CULargeInteger culi;
  910. hr = propvarNew.pStream->Seek( CLargeInteger(0), STREAM_SEEK_CUR, &culi );
  911. if( FAILED(hr) ) goto Exit;
  912. DfpAssert( CULargeInteger(0) == culi );
  913. }
  914. #endif // #if 1 == DBG
  915. // Write the clsid
  916. DfpAssert( NULL != pPersistStm );
  917. hr = pPersistStm->GetClassID( &clsid );
  918. if( E_NOTIMPL == hr )
  919. clsid = CLSID_NULL;
  920. else if( FAILED(hr) )
  921. goto Exit;
  922. hr = propvarNew.pStream->Write( &clsid, sizeof(clsid), &cbWritten );
  923. if( FAILED(hr) || sizeof(clsid) != cbWritten )
  924. goto Exit;
  925. // Persist to VT_STREAMED_OBJECT
  926. IFDBG( ULONG cRefs = GetRefCount( propvarNew.pStream ));
  927. hr = pPersistStm->Save( propvarNew.pStream, TRUE /* Clear dirty flag */ );
  928. if( FAILED(hr) ) goto Exit;
  929. DfpAssert( GetRefCount( propvarNew.pStream ) == cRefs );
  930. }
  931. Exit:
  932. RELEASE_INTERFACE(pPersistStg);
  933. RELEASE_INTERFACE(pPersistStm);
  934. PropVariantClear( &propvarNew );
  935. return( hr );
  936. } // CPropertyBagEx::WriteOneObject
  937. //+----------------------------------------------------------------------------
  938. //
  939. // Method: CPropertyBagEx::DeleteMultiple (IPropertyBagEx)
  940. //
  941. //+----------------------------------------------------------------------------
  942. HRESULT STDMETHODCALLTYPE
  943. CPropertyBagEx::DeleteMultiple( /*[in]*/ ULONG cprops,
  944. /*[in]*/ LPCOLESTR const rgoszPropNames[],
  945. /*[in]*/ DWORD dwReserved )
  946. {
  947. HRESULT hr = S_OK;
  948. IEnumSTATPROPBAG *penum = NULL;
  949. STATPROPBAG statpropbag = { NULL };
  950. PROPSPEC *prgpropspec = NULL;
  951. ULONG i;
  952. // --------------
  953. // Initialization
  954. // --------------
  955. propXTrace( "CPropertyBagEx::DeleteMultiple" );
  956. _pBlockingLock->Lock( INFINITE );
  957. // Validate the input
  958. if (S_OK != (hr = ValidateInRGLPOLESTR( cprops, rgoszPropNames )))
  959. goto Exit;
  960. if( 0 != dwReserved )
  961. {
  962. hr = STG_E_INVALIDPARAMETER;
  963. goto Exit;
  964. }
  965. propTraceParameters(( "%d, 0x%x", cprops, rgoszPropNames ));
  966. // If it's not already open, open the property storage now. If it doesn't exist,
  967. // then our mission is accomplished; the properties don't exist.
  968. hr = OpenPropStg( FILE_OPEN_IF ); // Open only if it already exists
  969. if( STG_E_FILENOTFOUND == hr )
  970. {
  971. hr = S_OK;
  972. goto Exit;
  973. }
  974. else if( FAILED(hr) )
  975. goto Exit;
  976. // Create an array of PROPSPECs, and load with the caller-provided
  977. // names.
  978. prgpropspec = reinterpret_cast<PROPSPEC*>
  979. ( CoTaskMemAlloc( cprops * sizeof(PROPSPEC) ));
  980. if( NULL == prgpropspec )
  981. {
  982. hr = E_OUTOFMEMORY;
  983. goto Exit;
  984. }
  985. for( i = 0; i < cprops; i++ )
  986. {
  987. prgpropspec[i].ulKind = PRSPEC_LPWSTR;
  988. prgpropspec[i].lpwstr = const_cast<LPOLESTR>(rgoszPropNames[i]);
  989. }
  990. // ---------------------
  991. // Delete the properties
  992. // ---------------------
  993. hr = _ppropstg->DeleteMultiple( cprops, prgpropspec );
  994. if( FAILED(hr) ) goto Exit;
  995. // ----
  996. // Exit
  997. // ----
  998. hr = S_OK;
  999. Exit:
  1000. CoTaskMemFree( prgpropspec );
  1001. _pBlockingLock->Unlock();
  1002. return( hr );
  1003. } // CPropertyBagEx::DeleteMultiple
  1004. //+----------------------------------------------------------------------------
  1005. //
  1006. // Method: CPropertyBagEx::Open (IPropertyBagEx)
  1007. //
  1008. // This method reads a VT_VERSIONED_STREAM from the underlying property set,
  1009. // and returns the IStream pointer in *ppUnk. If the property doesn't already
  1010. // exist, and guidPropertyType is not GUID_NULL, then an empty property is
  1011. // created first. An empty property can also be created over an existing
  1012. // one by specifying OPENPROPERTY_OVERWRITE.
  1013. //
  1014. //+----------------------------------------------------------------------------
  1015. HRESULT STDMETHODCALLTYPE
  1016. CPropertyBagEx::Open(
  1017. /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
  1018. /* [in] */ LPCOLESTR pwszPropName,
  1019. /* [in] */ GUID guidPropertyType,
  1020. /* [in] */ DWORD dwFlags,
  1021. /* [in] */ REFIID riid,
  1022. /* [out] */ IUnknown __RPC_FAR *__RPC_FAR *ppUnk)
  1023. {
  1024. HRESULT hr = S_OK;
  1025. PROPVARIANT propvar;
  1026. IUnknown *pUnk = NULL;
  1027. DBGBUF(buf1);
  1028. DBGBUF(buf2);
  1029. // --------------
  1030. // Initialization
  1031. // --------------
  1032. propXTrace( "CPropertyBagEx::Open" );
  1033. PropVariantInit( &propvar );
  1034. _pBlockingLock->Lock( INFINITE );
  1035. // Validation
  1036. GEN_VDATEPTRIN_LABEL( pUnkOuter, IUnknown*, E_INVALIDARG, Exit, hr );
  1037. if (S_OK != (hr = ValidateInRGLPOLESTR( 1, &pwszPropName )))
  1038. goto Exit;
  1039. GEN_VDATEREADPTRIN_LABEL( &riid, IID*, E_INVALIDARG, Exit, hr );
  1040. propTraceParameters(( "0x%x, %ws, %s, 0x%x, %s",
  1041. pUnkOuter, pwszPropName, DbgFmtId(guidPropertyType,buf1),dwFlags, DbgFmtId(riid,buf2) ));
  1042. // Initialize out-parms
  1043. *ppUnk = NULL;
  1044. // We don't support aggregation
  1045. if( NULL != pUnkOuter )
  1046. {
  1047. propDbg(( DEB_ERROR, "CPropertyBagEx(0x%x)::Open doesn't support pUnkOuter\n", this ));
  1048. hr = E_NOTIMPL;
  1049. goto Exit;
  1050. }
  1051. // Check for unsupported flags
  1052. if( 0 != (~OPENPROPERTY_OVERWRITE & dwFlags) )
  1053. {
  1054. propDbg(( DEB_ERROR, "CPropertyBagEx(0x%x)::Open, invalid dwFlags (0x%x)\n", this, dwFlags ));
  1055. hr = E_NOTIMPL;
  1056. goto Exit;
  1057. }
  1058. // We don't support anything but IID_IStream
  1059. if( IID_IStream != riid )
  1060. {
  1061. hr = E_NOTIMPL;
  1062. goto Exit;
  1063. }
  1064. // -----------------
  1065. // Read the property
  1066. // -----------------
  1067. // Attempt to read the property
  1068. hr = ReadMultiple( 1, &pwszPropName, &propvar, NULL );
  1069. // If the property doesn't exist but we were given a valid guid, create a new one.
  1070. if( S_FALSE == hr && GUID_NULL != guidPropertyType )
  1071. {
  1072. VERSIONEDSTREAM VersionedStream;
  1073. PROPVARIANT propvarCreate;
  1074. // Write a new property speicifying NULL for the stream
  1075. VersionedStream.guidVersion = guidPropertyType;
  1076. VersionedStream.pStream = NULL;
  1077. propvarCreate.vt = VT_VERSIONED_STREAM;
  1078. propvarCreate.pVersionedStream = &VersionedStream;
  1079. hr = WriteMultiple( 1, &pwszPropName, &propvarCreate );
  1080. if( FAILED(hr) ) goto Exit;
  1081. // Read the property back, getting an IStream*
  1082. hr = ReadMultiple( 1, &pwszPropName, &propvar, NULL );
  1083. }
  1084. if( FAILED(hr) ) goto Exit;
  1085. // Is the type or guidPropertyType wrong? (If the caller specified GUID_NULL,
  1086. // then any guidVersion is OK.)
  1087. if( VT_VERSIONED_STREAM != propvar.vt
  1088. ||
  1089. GUID_NULL != guidPropertyType
  1090. &&
  1091. guidPropertyType != propvar.pVersionedStream->guidVersion )
  1092. {
  1093. if( OPENPROPERTY_OVERWRITE & dwFlags )
  1094. {
  1095. // Delete the existing property
  1096. hr = DeleteMultiple( 1, &pwszPropName, 0 );
  1097. if( FAILED(hr) ) goto Exit;
  1098. // Recursive call
  1099. // PERF: Optimize this so that we don't have to re-do the parameter checking.
  1100. hr = Open( pUnkOuter, pwszPropName, guidPropertyType, dwFlags & ~OPENPROPERTY_OVERWRITE, riid, ppUnk );
  1101. } // if( OPENPROPERTY_OVERWRITE & dwFlags )
  1102. else
  1103. {
  1104. propDbg(( DEB_ERROR, "CPropertyBagEx(0x%x)::Open couldn't overwrite existing property - %ws, %d\n",
  1105. this, pwszPropName, propvar.vt ));
  1106. hr = STG_E_FILEALREADYEXISTS;
  1107. } // if( OPENPROPERTY_OVERWRITE & dwFlags ) ... else
  1108. // Unconditional goto Exit; we either just called Open recursively to do the work,
  1109. // or set an error.
  1110. goto Exit;
  1111. }
  1112. // We have a good property, QI for the riid and we're done
  1113. DfpAssert( IID_IStream == riid );
  1114. hr = propvar.pVersionedStream->pStream->QueryInterface( IID_IUnknown, reinterpret_cast<void**>(&pUnk) );
  1115. if( FAILED(hr) )
  1116. {
  1117. pUnk = NULL;
  1118. goto Exit;
  1119. }
  1120. // ----
  1121. // Exit
  1122. // ----
  1123. *ppUnk = pUnk;
  1124. pUnk = NULL;
  1125. hr = S_OK;
  1126. Exit:
  1127. if( NULL != pUnk )
  1128. pUnk->Release();
  1129. PropVariantClear( &propvar );
  1130. _pBlockingLock->Unlock();
  1131. return( hr );
  1132. }
  1133. //+----------------------------------------------------------------------------
  1134. //
  1135. // Method: CPropertyBagEx::Enum (IPropertyBagEx)
  1136. //
  1137. // This method returns an enumerator of properties in a bag.
  1138. //
  1139. //+----------------------------------------------------------------------------
  1140. HRESULT STDMETHODCALLTYPE
  1141. CPropertyBagEx::Enum(
  1142. /* [in] */ LPCOLESTR poszPropNameMask,
  1143. /* [in] */ DWORD dwFlags,
  1144. /* [out] */ IEnumSTATPROPBAG __RPC_FAR *__RPC_FAR *ppenum)
  1145. {
  1146. HRESULT hr = S_OK;
  1147. CEnumSTATPROPBAG *penum = NULL;
  1148. propXTrace( "CPropertyBagEx::Enum" );
  1149. _pBlockingLock->Lock( INFINITE );
  1150. // Validate inputs
  1151. if (NULL != poszPropNameMask && S_OK != (hr = ValidateInRGLPOLESTR( 1, &poszPropNameMask )))
  1152. goto Exit;
  1153. GEN_VDATEPTROUT_LABEL( ppenum, IEnumSTATPROPBAG*, E_INVALIDARG, Exit, hr );
  1154. // Flags aren't used yet.
  1155. if( 0 != dwFlags )
  1156. {
  1157. propDbg(( DEB_ERROR, "CPropertyBagEx(0x%x)::Enum - invalid dwFlags (%08x)\n", this, dwFlags ));
  1158. hr = E_INVALIDARG;
  1159. goto Exit;
  1160. }
  1161. propTraceParameters(( "%ws, 0x%08x, 0x%x", poszPropNameMask, dwFlags, ppenum ));
  1162. // Initialize output
  1163. *ppenum = NULL;
  1164. // Open the property storage, if it exists. If it doesn't,
  1165. // we'll still carry on and create an enumerator, it just won't have
  1166. // anything in it.
  1167. hr = OpenPropStg( FILE_OPEN_IF );
  1168. if( STG_E_FILENOTFOUND == hr )
  1169. hr = S_OK;
  1170. else if( FAILED(hr) )
  1171. goto Exit;
  1172. // Create an enumerator
  1173. penum = new CEnumSTATPROPBAG( _pBlockingLock );
  1174. if( NULL == penum )
  1175. {
  1176. hr = E_OUTOFMEMORY;
  1177. goto Exit;
  1178. }
  1179. // And initialize it
  1180. hr = penum->Init( _ppropstg, poszPropNameMask, dwFlags ); // _ppropstg could be NULL
  1181. if( FAILED(hr) ) goto Exit;
  1182. // ----
  1183. // Exit
  1184. // ----
  1185. hr = S_OK;
  1186. *ppenum = static_cast<IEnumSTATPROPBAG*>( penum );
  1187. penum = NULL;
  1188. Exit:
  1189. if( NULL != penum )
  1190. penum->Release();
  1191. _pBlockingLock->Unlock();
  1192. return( hr );
  1193. } // CPropertyBagEx::Enum
  1194. //+----------------------------------------------------------------------------
  1195. //
  1196. // Method: CPropertyBagEx::Commit
  1197. //
  1198. //+----------------------------------------------------------------------------
  1199. HRESULT
  1200. CPropertyBagEx::Commit( DWORD grfCommitFlags )
  1201. {
  1202. HRESULT hr = S_OK;
  1203. propITrace( "CPropertyBagEx::Commit" );
  1204. propTraceParameters(( "%08x", grfCommitFlags ));
  1205. // As an optimization, we only take the lock if we're really going to
  1206. // do the commit.
  1207. if( NULL != _ppropstg )
  1208. {
  1209. _pBlockingLock->Lock( INFINITE );
  1210. // We have to re-check if _ppropstg is NULL, since it
  1211. // could have changed while we were taking the lock.
  1212. if( NULL != _ppropstg && IsWriteable() )
  1213. {
  1214. hr = _ppropstg->Commit( grfCommitFlags );
  1215. }
  1216. _pBlockingLock->Unlock();
  1217. }
  1218. return( hr );
  1219. } // CPropertyBagEx::Commit
  1220. //+----------------------------------------------------------------------------
  1221. //
  1222. // Method: CPropertyBagEx::ShutDown
  1223. //
  1224. // Release the _ppropstg and clean up.
  1225. //
  1226. //+----------------------------------------------------------------------------
  1227. HRESULT
  1228. CPropertyBagEx::ShutDown()
  1229. {
  1230. HRESULT hr = S_OK;
  1231. propITrace( "CPropertyBagEx::ShutDown" );
  1232. if( NULL != _pBlockingLock )
  1233. _pBlockingLock->Lock( INFINITE );
  1234. // Release the property storage that holds the bag. It is because
  1235. // of this call that we need this separate ShutDown method; we can't
  1236. // release this interface in the destructor, since it may be reverted
  1237. // by that point.
  1238. DfpVerify( 0 == RELEASE_INTERFACE(_ppropstg) );
  1239. // We didn't AddRef these, so we don't need to release them.
  1240. if( NULL != _ppropsetstgContainer )
  1241. _ppropsetstgContainer = NULL;
  1242. if( NULL != _pBlockingLock )
  1243. {
  1244. _pBlockingLock->Unlock();
  1245. _pBlockingLock = NULL;
  1246. }
  1247. return( hr );
  1248. } // CPropertyBagEx::ShutDown
  1249. //+----------------------------------------------------------------------------
  1250. //
  1251. // Method: CSTATPROPBAGArray::Init
  1252. //
  1253. //+----------------------------------------------------------------------------
  1254. HRESULT
  1255. CSTATPROPBAGArray::Init( IPropertyStorage *ppropstg, const OLECHAR *poszPrefix, DWORD dwFlags )
  1256. {
  1257. HRESULT hr = S_OK;
  1258. propITrace( "CSTATPROPBAGArray::Init" );
  1259. propTraceParameters(( "0x%x, %ws", ppropstg, poszPrefix ));
  1260. _pBlockingLock->Lock( INFINITE );
  1261. // Keep the IPropertyBagEx::Enum flags
  1262. _dwFlags = dwFlags;
  1263. // Copy the prefix string
  1264. if( NULL == poszPrefix )
  1265. // There isn't a prefix string
  1266. _poszPrefix = NULL;
  1267. else
  1268. {
  1269. // Dup the prefix string
  1270. ULONG cbPrefix = (ocslen(poszPrefix)+1) * sizeof(OLECHAR);
  1271. _poszPrefix = reinterpret_cast<OLECHAR*> ( CoTaskMemAlloc( cbPrefix ));
  1272. if( NULL == _poszPrefix )
  1273. {
  1274. hr = E_OUTOFMEMORY;
  1275. goto Exit;
  1276. }
  1277. //ocscpy( _poszPrefix, poszPrefix );
  1278. CopyMemory( _poszPrefix, poszPrefix, cbPrefix );
  1279. }
  1280. // If we were given an IPropertyStorage, enum it. Otherwise, we'll leave
  1281. // _penum NULL and always return 0 for *pcFetched.
  1282. if( NULL != ppropstg )
  1283. {
  1284. hr = ppropstg->Enum( &_penum );
  1285. if( FAILED(hr) ) goto Exit;
  1286. }
  1287. hr = S_OK;
  1288. Exit:
  1289. _pBlockingLock->Unlock();
  1290. return( hr );
  1291. } // CSTATPROPBAGArray::Init
  1292. //+----------------------------------------------------------------------------
  1293. //
  1294. // Method: CSTATPROPBAGArray::AddRef/Release
  1295. //
  1296. //+----------------------------------------------------------------------------
  1297. ULONG
  1298. CSTATPROPBAGArray::AddRef()
  1299. {
  1300. return( InterlockedIncrement( &_cReferences ));
  1301. }
  1302. ULONG
  1303. CSTATPROPBAGArray::Release()
  1304. {
  1305. LONG lRet = InterlockedDecrement( &_cReferences );
  1306. if( 0 == lRet )
  1307. delete this;
  1308. return( 0 > lRet ? 0 : lRet );
  1309. }
  1310. //+----------------------------------------------------------------------------
  1311. //
  1312. // Method: CSTATPROPBAGArray::NextAt
  1313. //
  1314. // This method gets the *pcFetched STATPROPBAG structures in the
  1315. // enumeration starting from index iNext. This is implemented using
  1316. // the IEnumSTATPROPSTG enumerator in _penum.
  1317. //
  1318. //+----------------------------------------------------------------------------
  1319. HRESULT
  1320. CSTATPROPBAGArray::NextAt( ULONG iNext, STATPROPBAG *prgstatpropbag, ULONG *pcFetched )
  1321. {
  1322. HRESULT hr = S_OK;
  1323. STATPROPSTG statpropstg = { NULL };
  1324. ULONG iMatch = 0;
  1325. ULONG iFetched = 0;
  1326. propITrace( "CSTATPROPBAGArray::NextAt" );
  1327. propTraceParameters(( "%d, 0x%x, 0x%x", iNext, prgstatpropbag, pcFetched ));
  1328. _pBlockingLock->Lock( INFINITE );
  1329. // If there's nothing to enumerate, then we're done
  1330. if( NULL == _penum )
  1331. {
  1332. hr = S_FALSE;
  1333. *pcFetched = 0;
  1334. goto Exit;
  1335. }
  1336. // Reset the IEnumSTATPROPBAGTG index (doesn't reload, just resets the index).
  1337. hr = _penum->Reset();
  1338. if( FAILED(hr) ) goto Exit;
  1339. // Walk the IEnumSTATPROPBAGTG enumerator, looking for matches.
  1340. hr = _penum->Next( 1, &statpropstg, NULL );
  1341. while( S_OK == hr && iFetched < *pcFetched )
  1342. {
  1343. // Does this property have a name (all properties in a bag must have
  1344. // a name)?
  1345. if( NULL != statpropstg.lpwstrName )
  1346. {
  1347. // Yes, we have a name. Are we enumerating all properties (in which case
  1348. // _poszPrefix is NULL), or does this property name match the prefix?
  1349. if( NULL == _poszPrefix
  1350. ||
  1351. statpropstg.lpwstrName == ocsstr( statpropstg.lpwstrName, _poszPrefix )
  1352. ||
  1353. !ocscmp( statpropstg.lpwstrName, _poszPrefix ) )
  1354. {
  1355. // Yes, this property matches the prefix and is therefore part
  1356. // of this enumeration. But is this the index into the enumeration
  1357. // that we're looking for?
  1358. if( iNext == iMatch )
  1359. {
  1360. // Yes, everything matches, and we have a property that should
  1361. // be returned.
  1362. // *Move* the name from statpropstg to the output array.
  1363. prgstatpropbag[ iFetched ].lpwstrName = statpropstg.lpwstrName;
  1364. statpropstg.lpwstrName = NULL;
  1365. prgstatpropbag[ iFetched ].vt = statpropstg.vt;
  1366. // GUID is not currently supported by the enumeration
  1367. prgstatpropbag[ iFetched ].guidPropertyType = GUID_NULL;
  1368. // Show that we're now looking for the i+1 index
  1369. iNext++;
  1370. iFetched++;
  1371. }
  1372. // Increment the number of property matches we've had.
  1373. // (iMatch will increment until it equals iNext, after
  1374. // which the two will always both increment and remain equal).
  1375. iMatch++;
  1376. } // if( NULL == _poszPrefix ...
  1377. } // if( NULL != statpropstg.lpwstrName )
  1378. CoTaskMemFree( statpropstg.lpwstrName ); // (May be NULL)
  1379. statpropstg.lpwstrName = NULL;
  1380. hr = _penum->Next( 1, &statpropstg, NULL );
  1381. } // while( S_OK == hr && iFetched < *pcFetched )
  1382. // Did we get an error on a _penum->Next call?
  1383. if( FAILED(hr) ) goto Exit;
  1384. // If we reached this point, there was no error. Determine if
  1385. // OK or FALSE should be returned.
  1386. if( iFetched == *pcFetched )
  1387. hr = S_OK;
  1388. else
  1389. hr = S_FALSE;
  1390. *pcFetched = iFetched;
  1391. // ----
  1392. // Exit
  1393. // ----
  1394. Exit:
  1395. // Cleanup on error
  1396. CoTaskMemFree( statpropstg.lpwstrName );
  1397. _pBlockingLock->Unlock();
  1398. return( hr );
  1399. } // CSTATPROPBAGArray::NextAt
  1400. //+----------------------------------------------------------------------------
  1401. //
  1402. // Method: CEnumSTATPROPBAG::CEnumSTATPROPBAG (copy constructor)
  1403. //
  1404. //+----------------------------------------------------------------------------
  1405. CEnumSTATPROPBAG::CEnumSTATPROPBAG( const CEnumSTATPROPBAG &Other )
  1406. {
  1407. propDbg(( DEB_ITRACE, "CEnumSTATPROPBAG::CEnumSTATPROPBAG (copy constructor)" ));
  1408. Other._pBlockingLock->Lock( INFINITE );
  1409. new(this) CEnumSTATPROPBAG( Other._pBlockingLock );
  1410. _iarray = Other._iarray;
  1411. Other._parray->AddRef();
  1412. _parray = Other._parray;
  1413. Other._pBlockingLock->Unlock();
  1414. }
  1415. //+----------------------------------------------------------------------------
  1416. //
  1417. // Method: CEnumSTATPROPBAG::~CEnumSTATPROPBAG
  1418. //
  1419. //+----------------------------------------------------------------------------
  1420. CEnumSTATPROPBAG::~CEnumSTATPROPBAG()
  1421. {
  1422. propDbg(( DEB_ITRACE, "CEnumSTATPROPBAG::~CEnumSTATPROPBAG\n" ));
  1423. _pBlockingLock->Release();
  1424. if( NULL != _parray )
  1425. _parray->Release();
  1426. }
  1427. //+----------------------------------------------------------------------------
  1428. //
  1429. // Method: CEnumSTATPROPBAG::Init
  1430. //
  1431. //+----------------------------------------------------------------------------
  1432. HRESULT
  1433. CEnumSTATPROPBAG::Init( IPropertyStorage *ppropstg, LPCOLESTR poszPrefix, DWORD dwFlags )
  1434. {
  1435. HRESULT hr = S_OK;
  1436. propITrace( "CEnumSTATPROPBAG::Init" );
  1437. propTraceParameters(( "0x%x, %ws", ppropstg, poszPrefix ));
  1438. // Create a STATPROPBAG Array
  1439. _parray = new CSTATPROPBAGArray( _pBlockingLock );
  1440. if( NULL == _parray )
  1441. {
  1442. hr = E_OUTOFMEMORY;
  1443. goto Exit;
  1444. }
  1445. // Load the array from the IPropertyStorage
  1446. hr = _parray->Init( ppropstg, poszPrefix, dwFlags );
  1447. if( FAILED(hr) ) goto Exit;
  1448. hr = S_OK;
  1449. Exit:
  1450. return( hr );
  1451. } // CEnumSTATPROPBAG::Init
  1452. //+----------------------------------------------------------------------------
  1453. //
  1454. // Method: CEnumSTATPROPBAG::QueryInterface/AddRef/Release
  1455. //
  1456. //+----------------------------------------------------------------------------
  1457. HRESULT STDMETHODCALLTYPE
  1458. CEnumSTATPROPBAG::QueryInterface( REFIID riid, void **ppvObject )
  1459. {
  1460. HRESULT hr = S_OK;
  1461. // Validate the inputs
  1462. VDATEREADPTRIN( &riid, IID );
  1463. VDATEPTROUT( ppvObject, void* );
  1464. *ppvObject = NULL;
  1465. // Get the interface
  1466. if( IID_IEnumSTATPROPBAG == riid || IID_IUnknown == riid )
  1467. {
  1468. *ppvObject = static_cast<IEnumSTATPROPBAG*>(this);
  1469. AddRef();
  1470. }
  1471. else
  1472. hr = E_NOINTERFACE;
  1473. return(hr);
  1474. } // CEnumSTATPROPBAG::QueryInterface
  1475. ULONG STDMETHODCALLTYPE
  1476. CEnumSTATPROPBAG::AddRef()
  1477. {
  1478. return( InterlockedIncrement( &_cRefs ));
  1479. }
  1480. ULONG STDMETHODCALLTYPE
  1481. CEnumSTATPROPBAG::Release()
  1482. {
  1483. LONG lRet = InterlockedDecrement( &_cRefs );
  1484. if( 0 == lRet )
  1485. delete this;
  1486. return( 0 > lRet ? 0 : lRet );
  1487. }
  1488. //+----------------------------------------------------------------------------
  1489. //
  1490. // Method: CEnumSTATPROPBAG::Next
  1491. //
  1492. //+----------------------------------------------------------------------------
  1493. HRESULT STDMETHODCALLTYPE
  1494. CEnumSTATPROPBAG::Next( ULONG celt, STATPROPBAG *rgelt, ULONG *pceltFetched )
  1495. {
  1496. HRESULT hr = S_OK;
  1497. ULONG celtFetched = celt;
  1498. propXTrace( "CEnumSTATPROPBAG::Next" );
  1499. // Validate the inputs
  1500. if (NULL == pceltFetched)
  1501. {
  1502. if (celt != 1)
  1503. {
  1504. hr = STG_E_INVALIDPARAMETER;
  1505. goto Exit;
  1506. }
  1507. }
  1508. else
  1509. {
  1510. VDATEPTROUT( pceltFetched, ULONG );
  1511. *pceltFetched = 0;
  1512. }
  1513. propTraceParameters(( "%d, 0x%x, 0x%x", celt, rgelt, pceltFetched ));
  1514. _pBlockingLock->Lock( INFINITE );
  1515. // Get the next set of stat structures
  1516. hr = _parray->NextAt( _iarray, rgelt, &celtFetched );
  1517. if( FAILED(hr) ) goto Exit;
  1518. // Advance the index
  1519. _iarray += celtFetched;
  1520. // Return the count to the caller
  1521. if( NULL != pceltFetched )
  1522. *pceltFetched = celtFetched;
  1523. hr = celtFetched == celt ? S_OK : S_FALSE;
  1524. Exit:
  1525. _pBlockingLock->Unlock();
  1526. return( hr );
  1527. } // CEnumSTATPROPBAG::Next
  1528. //+----------------------------------------------------------------------------
  1529. //
  1530. // Method: CEnumSTATPROPBAG::Reset
  1531. //
  1532. //+----------------------------------------------------------------------------
  1533. HRESULT STDMETHODCALLTYPE
  1534. CEnumSTATPROPBAG::Reset( )
  1535. {
  1536. HRESULT hr = S_OK;
  1537. propXTrace( "CEnumSTATPROPBAG::Reset" );
  1538. _pBlockingLock->Lock( INFINITE );
  1539. _iarray = 0;
  1540. _pBlockingLock->Unlock();
  1541. return( hr );
  1542. } // CEnumSTATPROPBAG::Reset
  1543. //+----------------------------------------------------------------------------
  1544. //
  1545. // Method: CEnumSTATPROPBAG::Skip
  1546. //
  1547. //+----------------------------------------------------------------------------
  1548. HRESULT STDMETHODCALLTYPE
  1549. CEnumSTATPROPBAG::Skip( ULONG celt )
  1550. {
  1551. HRESULT hr = S_OK;
  1552. STATPROPBAG statpropbag = { NULL };
  1553. propXTrace( "CEnumSTATPROPBAG::Skip" );
  1554. propTraceParameters( ("%d", celt ));
  1555. _pBlockingLock->Lock( INFINITE );
  1556. // Loop until we've skipped celt items (or run into the end)
  1557. while( celt )
  1558. {
  1559. ULONG cFetched = 1;
  1560. // Get the next item.
  1561. hr = _parray->NextAt( _iarray, &statpropbag, &cFetched );
  1562. // Free the name
  1563. CoTaskMemFree( statpropbag.lpwstrName );
  1564. statpropbag.lpwstrName = NULL;
  1565. // Did the NextAt work?
  1566. if( FAILED(hr) )
  1567. // No, it failed
  1568. goto Exit;
  1569. else if( S_FALSE == hr )
  1570. // It worked, but there's nothing left
  1571. break;
  1572. else
  1573. {
  1574. // We got an entry
  1575. _iarray++;
  1576. hr = S_OK;
  1577. --celt;
  1578. }
  1579. }
  1580. Exit:
  1581. CoTaskMemFree( statpropbag.lpwstrName );
  1582. _pBlockingLock->Unlock();
  1583. return( hr );
  1584. } // CEnumSTATPROPBAG::Skip
  1585. //+----------------------------------------------------------------------------
  1586. //
  1587. // Method: CEnumSTATPROPBAG::Clone
  1588. //
  1589. //+----------------------------------------------------------------------------
  1590. HRESULT STDMETHODCALLTYPE
  1591. CEnumSTATPROPBAG::Clone( IEnumSTATPROPBAG **ppenum )
  1592. {
  1593. HRESULT hr = S_OK;
  1594. CEnumSTATPROPBAG *penum = NULL;
  1595. propXTrace( "CEnumSTATPROPBAG::Clone" );
  1596. // Validate the input
  1597. VDATEPTROUT( ppenum, IEnumSTATPROPSTG* );
  1598. *ppenum = NULL;
  1599. propTraceParameters(( "0x%x", ppenum ));
  1600. // Instantiated & initialize a new enumerator
  1601. penum = new CEnumSTATPROPBAG( *this );
  1602. if( NULL == penum )
  1603. {
  1604. hr = E_OUTOFMEMORY;
  1605. goto Exit;
  1606. }
  1607. // Set the out parm
  1608. *ppenum = static_cast<IEnumSTATPROPBAG*>(penum);
  1609. penum = NULL;
  1610. Exit:
  1611. if( NULL != penum )
  1612. delete penum ;
  1613. return( hr );
  1614. } // CEnumSTATPROPBAG::Clone