Source code of Windows XP (NT5)
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.

2574 lines
68 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. // Try to open the property storage
  174. // We have to open exclusive, no matter how the parent was opened.
  175. hr = _ppropsetstgContainer->Open( FMTID_PropertyBag,
  176. FORCE_EXCLUSIVE(_grfMode) & ~STGM_CREATE,
  177. &ppropstg );
  178. if( STG_E_FILENOTFOUND == hr && FILE_OPEN == dwDisposition )
  179. {
  180. // It didn't exist, and we're supposed to remedy that.
  181. hr = _ppropsetstgContainer->Create( FMTID_PropertyBag, NULL,
  182. PROPSETFLAG_CASE_SENSITIVE | PROPSETFLAG_NONSIMPLE,
  183. FORCE_EXCLUSIVE(_grfMode) | STGM_CREATE,
  184. &ppropstg );
  185. }
  186. else if( STG_E_FILENOTFOUND == hr && FILE_OPEN_IF == dwDisposition )
  187. {
  188. // This is an expected error.
  189. propSuppressExitErrors();
  190. }
  191. if( FAILED(hr) ) goto Exit;
  192. // Verify that this is a Unicode property set
  193. propspecCodePage.ulKind = PRSPEC_PROPID;
  194. propspecCodePage.propid = PID_CODEPAGE;
  195. hr = ppropstg->ReadMultiple( 1, &propspecCodePage, &propvarCodePage );
  196. if( FAILED(hr) ) goto Exit;
  197. if( VT_I2 != propvarCodePage.vt
  198. ||
  199. CP_WINUNICODE != propvarCodePage.iVal )
  200. {
  201. propDbg(( DEB_ERROR, "Non-unicode codepage found in supposed property bag (0x%x)\n",
  202. propvarCodePage.iVal ));
  203. hr = STG_E_INVALIDHEADER;
  204. goto Exit;
  205. }
  206. // Also verify that the property set is non-simple and case-sensitive
  207. hr = ppropstg->Stat( &statpropsetstg );
  208. if( FAILED(hr) ) goto Exit;
  209. if( !(PROPSETFLAG_CASE_SENSITIVE & statpropsetstg.grfFlags)
  210. ||
  211. !(PROPSETFLAG_NONSIMPLE & statpropsetstg.grfFlags) )
  212. {
  213. propDbg(( DEB_ERROR, "Supposed property bag isn't case sensitive or isn't non-simple (0x%x)\n",
  214. statpropsetstg.grfFlags ));
  215. hr = STG_E_INVALIDHEADER;
  216. goto Exit;
  217. }
  218. _ppropstg = ppropstg;
  219. ppropstg = NULL;
  220. }
  221. // Even if we already have an open IPropertyStorage, we may not have yet read the
  222. // Locale ID (this happens in the case where the CPropertyBagEx(IPropertyStorage*)
  223. // constructor is used).
  224. if( !_fLcidInitialized )
  225. {
  226. hr = GetLCID();
  227. if( FAILED(hr) ) goto Exit;
  228. }
  229. // ----
  230. // Exit
  231. // ----
  232. hr = S_OK;
  233. Exit:
  234. DfpVerify( 0 == RELEASE_INTERFACE(ppropstg) );
  235. return(hr);
  236. } // CPropertyBagEx::OpenPropStg
  237. //+----------------------------------------------------------------------------
  238. //
  239. // Method: GetLCID
  240. //
  241. // Get the LocalID from the property set represented by _ppropstg.
  242. //
  243. //+----------------------------------------------------------------------------
  244. HRESULT
  245. CPropertyBagEx::GetLCID()
  246. {
  247. HRESULT hr = S_OK;
  248. PROPSPEC propspecLCID;
  249. PROPVARIANT propvarLCID;
  250. propITrace( "CPropertyBagEx::GetLCID" );
  251. propspecLCID.ulKind = PRSPEC_PROPID;
  252. propspecLCID.propid = PID_LOCALE;
  253. if( SUCCEEDED( hr = _ppropstg->ReadMultiple( 1, &propspecLCID, &propvarLCID ))
  254. &&
  255. VT_UI4 == propvarLCID.vt )
  256. {
  257. _lcid = propvarLCID.uiVal;
  258. }
  259. else if( S_FALSE == hr )
  260. {
  261. _lcid = GetUserDefaultLCID();
  262. }
  263. PropVariantClear( &propvarLCID );
  264. return( hr );
  265. } // CPropertyBagEx::GetLCID()
  266. //+----------------------------------------------------------------------------
  267. //
  268. // Method: IUnknown methods
  269. //
  270. // If we have a parent (_ppropsetstgParent), we forward all calls to there.
  271. // Otherwise, we implement all calls here. Whether or not we have a parent
  272. // depends on which of the two constructors is called. In NFF, NSS, and docfile,
  273. // we have a parent. When creating a standalone property bag implementation
  274. // (in StgCreate/OpenStorageOnHandle), we have no such parent.
  275. //
  276. //+----------------------------------------------------------------------------
  277. HRESULT STDMETHODCALLTYPE
  278. CPropertyBagEx::QueryInterface(
  279. /* [in] */ REFIID riid,
  280. /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject)
  281. {
  282. // Do we have a parent?
  283. if( NULL != _ppropsetstgContainer )
  284. {
  285. // Yes, refer the call
  286. DfpAssert( 0 == _cRefs );
  287. return( _ppropsetstgContainer->QueryInterface( riid, ppvObject ));
  288. }
  289. else
  290. {
  291. // No, we have no parent.
  292. if( IID_IPropertyBagEx == riid
  293. ||
  294. IID_IUnknown == riid )
  295. {
  296. AddRef();
  297. *ppvObject = static_cast<IPropertyBagEx*>(this);
  298. return( S_OK );
  299. }
  300. else if( IID_IPropertyBag == riid )
  301. {
  302. AddRef();
  303. *ppvObject = static_cast<IPropertyBag*>(this);
  304. return( S_OK );
  305. }
  306. }
  307. return( E_NOINTERFACE );
  308. }
  309. ULONG STDMETHODCALLTYPE
  310. CPropertyBagEx::AddRef( void)
  311. {
  312. // Do we have a parent?
  313. if( NULL != _ppropsetstgContainer )
  314. {
  315. // Yes. The parent does the ref-counting
  316. DfpAssert( 0 == _cRefs );
  317. return( _ppropsetstgContainer->AddRef() );
  318. }
  319. else
  320. {
  321. // No. We don't have a parent, and we must do the ref-counting.
  322. LONG lRefs = InterlockedIncrement( &_cRefs );
  323. return( lRefs );
  324. }
  325. }
  326. ULONG STDMETHODCALLTYPE
  327. CPropertyBagEx::Release( void)
  328. {
  329. // Do we have a parent?
  330. if( NULL != _ppropsetstgContainer )
  331. {
  332. // Yes. The container does the ref-counting
  333. DfpAssert( 0 == _cRefs );
  334. return( _ppropsetstgContainer->Release() );
  335. }
  336. else
  337. {
  338. // No. We don't have a parent, and we must do the ref-counting.
  339. LONG lRefs = InterlockedDecrement( &_cRefs );
  340. if( 0 == lRefs )
  341. {
  342. // Only in this case (where we have no parent) do we release
  343. // the IBlockingLock
  344. RELEASE_INTERFACE( _pBlockingLock );
  345. delete this;
  346. }
  347. return( lRefs );
  348. }
  349. }
  350. //+----------------------------------------------------------------------------
  351. //
  352. // Method: Read/Write (IPropertyBag)
  353. //
  354. // These methods simply thunk to ReadMultiple/WriteMultiple.
  355. //
  356. //+----------------------------------------------------------------------------
  357. HRESULT STDMETHODCALLTYPE
  358. CPropertyBagEx::Read(
  359. /* [in] */ LPCOLESTR pszPropName,
  360. /* [out][in] */ VARIANT __RPC_FAR *pVar,
  361. /* [in] */ IErrorLog __RPC_FAR *pErrorLog)
  362. {
  363. HRESULT hr=S_OK, hr2=S_OK;
  364. PROPVARIANT *ppropvar, propvarTmp;
  365. ppropvar = reinterpret_cast<PROPVARIANT*>(pVar);
  366. propvarTmp = *ppropvar;
  367. hr = ReadMultiple(1, &pszPropName, &propvarTmp, pErrorLog );
  368. if( !FAILED( hr ) )
  369. {
  370. hr2 = ImplicitPropVariantToVariantChangeType( ppropvar, &propvarTmp, _lcid );
  371. PropVariantClear( &propvarTmp );
  372. }
  373. if( FAILED(hr2) )
  374. return hr2;
  375. else
  376. return hr;
  377. } // CPropertyBagEx::Read
  378. HRESULT STDMETHODCALLTYPE
  379. CPropertyBagEx::Write(
  380. /* [in] */ LPCOLESTR pszPropName,
  381. /* [in] */ VARIANT __RPC_FAR *pVar)
  382. {
  383. if( !IsVariantType( pVar->vt ))
  384. return( STG_E_INVALIDPARAMETER );
  385. return( WriteMultiple(1, &pszPropName, reinterpret_cast<PROPVARIANT*>(pVar) ));
  386. }
  387. //+----------------------------------------------------------------------------
  388. //
  389. // Method: CPropertyBagEx::ReadMultiple (IPropertyBagEx)
  390. //
  391. // This method calls down to IPropertyStorage::ReadMultiple. It has the
  392. // additional behaviour of coercing the property types into those specified
  393. // by the caller.
  394. //
  395. //+----------------------------------------------------------------------------
  396. HRESULT STDMETHODCALLTYPE
  397. CPropertyBagEx::ReadMultiple(
  398. /* [in] */ ULONG cprops,
  399. /* [size_is][in] */ LPCOLESTR const __RPC_FAR rgoszPropNames[ ],
  400. /* [size_is][out][in] */ PROPVARIANT __RPC_FAR rgpropvar[ ],
  401. /* [in] */ IErrorLog __RPC_FAR *pErrorLog)
  402. {
  403. HRESULT hr = S_OK;
  404. ULONG i;
  405. PROPSPEC *rgpropspec = NULL; // PERF: use TStackBuffer
  406. PROPVARIANT *rgpropvarRead = NULL;
  407. BOOL fAtLeastOnePropertyRead = FALSE;
  408. propXTrace( "CPropertyBagEx::ReadMultiple" );
  409. _pBlockingLock->Lock( INFINITE );
  410. // ----------
  411. // Validation
  412. // ----------
  413. if (0 == cprops)
  414. {
  415. hr = S_OK;
  416. goto Exit;
  417. }
  418. if (S_OK != (hr = ValidateInRGLPOLESTR( cprops, rgoszPropNames )))
  419. goto Exit;
  420. if (S_OK != (hr = ValidateOutRGPROPVARIANT( cprops, rgpropvar )))
  421. goto Exit;
  422. propTraceParameters(( "%d, 0x%x, 0x%x, 0x%x", cprops, rgoszPropNames, rgpropvar, pErrorLog ));
  423. // --------------
  424. // Initialization
  425. // --------------
  426. // Open the underlying property set if it exists.
  427. hr = OpenPropStg( FILE_OPEN_IF );
  428. if( STG_E_FILENOTFOUND == hr )
  429. {
  430. // The property storage for this bag doesn't even exist. So we simulate
  431. // the reading non-extant properties by setting the vt's to emtpy and returning
  432. // s_false.
  433. for( ULONG i = 0; i < cprops; i++ )
  434. rgpropvar[i].vt = VT_EMPTY;
  435. hr = S_FALSE;
  436. goto Exit;
  437. }
  438. else if( FAILED(hr) )
  439. goto Exit;
  440. // The property set existed and is now open.
  441. // Alloc propspec & propvar arrays.
  442. // PERF: Make these stack-based for typical-sized reads.
  443. rgpropspec = reinterpret_cast<PROPSPEC*>
  444. ( CoTaskMemAlloc( cprops * sizeof(PROPSPEC) ));
  445. if( NULL == rgpropspec )
  446. {
  447. hr = E_OUTOFMEMORY;
  448. goto Exit;
  449. }
  450. rgpropvarRead = reinterpret_cast<PROPVARIANT*>
  451. ( CoTaskMemAlloc( cprops * sizeof(PROPVARIANT) ));
  452. if( NULL == rgpropvarRead )
  453. {
  454. hr = E_OUTOFMEMORY;
  455. goto Exit;
  456. }
  457. // Put the names into the propspecs.
  458. for( i = 0; i < cprops; i++ )
  459. {
  460. PropVariantInit( &rgpropvarRead[i] );
  461. rgpropspec[i].ulKind = PRSPEC_LPWSTR;
  462. rgpropspec[i].lpwstr = const_cast<LPOLESTR>(rgoszPropNames[i]);
  463. }
  464. // ----------------------------
  465. // Read & coerce the properties
  466. // ----------------------------
  467. // Read the properties into the local PropVar array
  468. hr = _ppropstg->ReadMultiple(cprops, rgpropspec, rgpropvarRead );
  469. if( FAILED(hr) ) goto Exit;
  470. if( S_FALSE != hr )
  471. fAtLeastOnePropertyRead = TRUE;
  472. // Coerce the properties as necessary
  473. for( i = 0; i < cprops; i++ )
  474. {
  475. // If the caller requested a type (something other than VT_EMPTY), and the
  476. // requested type isn't the same as the read type, then a coercion is necessary.
  477. if( VT_EMPTY != rgpropvar[i].vt && rgpropvarRead[i].vt != rgpropvar[i].vt )
  478. {
  479. PROPVARIANT propvar;
  480. PropVariantInit( &propvar );
  481. // Does this property require conversion
  482. // (i.e. to a VT_UNKNOWN/DISPATCH)?
  483. if( PropertyRequiresConversion( rgpropvar[i].vt )
  484. &&
  485. ( VT_STORED_OBJECT == rgpropvarRead[i].vt
  486. ||
  487. VT_STREAMED_OBJECT == rgpropvarRead[i].vt
  488. )
  489. )
  490. {
  491. // Load the object from the stream/storage in rgpropvarRead
  492. // into propvar.
  493. propvar = rgpropvar[i];
  494. hr = LoadObject( &propvar, &rgpropvarRead[i] );
  495. if( FAILED(hr) )
  496. goto Exit;
  497. }
  498. else
  499. {
  500. // Coerce the property in rgpropvarRead into propvar, according
  501. // to the caller-requested type (which is specified in rgpropvar).
  502. hr = PropVariantChangeType( &propvar, &rgpropvarRead[i],
  503. _lcid, 0, rgpropvar[i].vt );
  504. if( FAILED(hr) )
  505. {
  506. propDbg(( DEB_ERROR, "CPropertyBagEx(0x%x), PropVariantChangeType"
  507. "(0x%x,0x%x,%d,%d,%d) returned %08x\n",
  508. this, &propvar, &rgpropvarRead[i], _lcid, 0, rgpropvar[i].vt, hr ));
  509. goto Exit;
  510. }
  511. }
  512. // Get rid of the value original read, and keep the coerced value.
  513. PropVariantClear( &rgpropvarRead[i] );
  514. rgpropvarRead[i] = propvar;
  515. }
  516. } // for( i = 0; i < cprops; i++ )
  517. // No errors from here to the end.
  518. // Copy the PropVars from the local array to the caller's
  519. for( i = 0; i < cprops; i++ )
  520. rgpropvar[i] = rgpropvarRead[i];
  521. // ----
  522. // Exit
  523. // ----
  524. CoTaskMemFree( rgpropvarRead );
  525. rgpropvarRead = NULL;
  526. if( SUCCEEDED(hr) )
  527. hr = fAtLeastOnePropertyRead ? S_OK : S_FALSE;
  528. Exit:
  529. // We needn't free the rgpropspec[*].lpwstr members, they point
  530. // into rgszPropName
  531. if( NULL != rgpropspec )
  532. CoTaskMemFree( rgpropspec );
  533. if( NULL != rgpropvarRead )
  534. {
  535. for( i = 0; i < cprops; i++ )
  536. PropVariantClear( &rgpropvarRead[i] );
  537. CoTaskMemFree( rgpropvarRead );
  538. }
  539. _pBlockingLock->Unlock();
  540. return( hr );
  541. } // CPropertyBagEx::ReadMultiple
  542. //+----------------------------------------------------------------------------
  543. //
  544. // Method: CPropertyBagEx::LoadObject
  545. //
  546. // This method loads an IUnknown or IDispatch object from its persisted state
  547. // in a stream or storage (by QI-ing for IPersistStream or IPersistStorage,
  548. // respectively).
  549. //
  550. //+----------------------------------------------------------------------------
  551. HRESULT
  552. CPropertyBagEx::LoadObject( OUT PROPVARIANT *ppropvarOut, IN PROPVARIANT *ppropvarIn ) const
  553. {
  554. HRESULT hr = S_OK;
  555. IUnknown *punk = NULL;
  556. IPersistStorage *pPersistStg = NULL;
  557. IPersistStream *pPersistStm = NULL;
  558. propITrace( "CPropertyBagEx::LoadObject" );
  559. DfpAssert( VT_STREAMED_OBJECT == ppropvarIn->vt || VT_STORED_OBJECT == ppropvarIn->vt );
  560. DfpAssert( VT_UNKNOWN == ppropvarOut->vt || VT_DISPATCH == ppropvarOut->vt );
  561. punk = ppropvarOut->punkVal;
  562. DfpAssert( reinterpret_cast<void*>(&ppropvarOut->punkVal)
  563. ==
  564. reinterpret_cast<void*>(&ppropvarOut->pdispVal) );
  565. // --------------------------
  566. // Read in an IPersistStorage
  567. // --------------------------
  568. if( VT_STORED_OBJECT == ppropvarIn->vt )
  569. {
  570. DfpAssert( NULL != ppropvarIn->pStorage );
  571. // Get an IUnknown if the caller didn't provide one.
  572. if( NULL == punk )
  573. {
  574. STATSTG statstg;
  575. hr = ppropvarIn->pStorage->Stat( &statstg, STATFLAG_NONAME );
  576. if( FAILED(hr) ) goto Exit;
  577. hr = CoCreateInstance( statstg.clsid, NULL, CLSCTX_ALL, IID_IUnknown,
  578. reinterpret_cast<void**>(&punk) );
  579. if( FAILED(hr) ) goto Exit;
  580. }
  581. // QI for IPersistStorage
  582. hr = punk->QueryInterface( IID_IPersistStorage, reinterpret_cast<void**>(&pPersistStg) );
  583. if( FAILED(hr) )
  584. {
  585. propDbg(( DEB_ERROR, "Caller didn't provide IPersistStorage in IProeprtyBagEx::ReadMultiple (%08x)", hr ));
  586. goto Exit;
  587. }
  588. // IPersist::Load the storage
  589. hr = pPersistStg->Load( ppropvarIn->pStorage );
  590. if( FAILED(hr) )
  591. {
  592. propDbg(( DEB_ERROR, "Failed IPersistStorage::Load in IPropretyBagEx::ReadMultiple (%08x)", hr ));
  593. goto Exit;
  594. }
  595. } // if( VT_STORED_OBJECT == ppropvarIn->vt )
  596. // -------------------------
  597. // Read in an IPersistStream
  598. // -------------------------
  599. else
  600. {
  601. DfpAssert( VT_STREAMED_OBJECT == ppropvarIn->vt );
  602. DfpAssert( NULL != ppropvarIn->pStream );
  603. CLSID clsid;
  604. ULONG cbRead;
  605. // Skip over the clsid
  606. hr = ppropvarIn->pStream->Read( &clsid, sizeof(clsid), &cbRead );
  607. if( FAILED(hr) ) goto Exit;
  608. if( sizeof(clsid) != cbRead )
  609. {
  610. hr = STG_E_INVALIDHEADER;
  611. propDbg(( DEB_ERROR, "Clsid missing in VT_STREAMED_OBJECT in IPropertyBagEx::ReadMultiple (%d bytes)",
  612. cbRead ));
  613. goto Exit;
  614. }
  615. // Get an IUnknown if the caller didn't provide one.
  616. if( NULL == punk )
  617. {
  618. hr = CoCreateInstance( clsid, NULL, CLSCTX_ALL, IID_IUnknown,
  619. reinterpret_cast<void**>(&punk) );
  620. if( FAILED(hr) ) goto Exit;
  621. }
  622. // QI for the IPersistStream
  623. hr = punk->QueryInterface( IID_IPersistStream, reinterpret_cast<void**>(&pPersistStm) );
  624. if( FAILED(hr) )
  625. {
  626. propDbg(( DEB_ERROR, "Caller didn't provide IPersistStream in IPropertyBagEx::ReadMultiple (%08x)", hr ));
  627. goto Exit;
  628. }
  629. // Load the remainder of the stream
  630. IFDBG( ULONG cRefs = GetRefCount( ppropvarIn->pStream ));
  631. hr = pPersistStm->Load( ppropvarIn->pStream );
  632. if( FAILED(hr) )
  633. {
  634. propDbg(( DEB_ERROR, "Failed IPersistStream::Load in IPropertyBagEx::ReadMultiple (%08x)", hr ));
  635. goto Exit;
  636. }
  637. DfpAssert( GetRefCount( ppropvarIn->pStream ) == cRefs );
  638. } // if( VT_STORED_OBJECT == ppropvarIn->vt ) ... else
  639. // ----
  640. // Exit
  641. // ----
  642. ppropvarOut->punkVal = punk;
  643. punk = NULL;
  644. hr = S_OK;
  645. Exit:
  646. RELEASE_INTERFACE( pPersistStg );
  647. RELEASE_INTERFACE( pPersistStm );
  648. RELEASE_INTERFACE( punk );
  649. return( hr );
  650. } // CPropertyBagEx::LoadObject
  651. //+----------------------------------------------------------------------------
  652. //
  653. // Method: CPropertyBagEx::WriteMultiple (IPropertyBagEx)
  654. //
  655. // This method calls down to IPropertyStorage::WriteMultiple. Additionally,
  656. // this method is atomic. So if the WriteMultiple fails, the IPropertyStorage
  657. // is reverted and reopened.
  658. //
  659. //+----------------------------------------------------------------------------
  660. HRESULT STDMETHODCALLTYPE
  661. CPropertyBagEx::WriteMultiple(
  662. /* [in] */ ULONG cprops,
  663. /* [size_is][in] */ LPCOLESTR const __RPC_FAR rgoszPropNames[ ],
  664. /* [size_is][in] */ const PROPVARIANT __RPC_FAR rgpropvar[ ])
  665. {
  666. HRESULT hr = S_OK;
  667. ULONG i;
  668. PROPSPEC *prgpropspec = NULL;
  669. BOOL fInterfaces = FALSE;
  670. _pBlockingLock->Lock( INFINITE );
  671. CStackPropVarArray rgpropvarCopy;
  672. propXTrace( "CPropertyBagEx::WriteMultiple" );
  673. hr = rgpropvarCopy.Init( cprops );
  674. if( FAILED(hr) ) goto Exit;
  675. // ----------
  676. // Validation
  677. // ----------
  678. if (0 == cprops)
  679. {
  680. hr = S_OK;
  681. goto Exit;
  682. }
  683. if (S_OK != (hr = ValidateInRGLPOLESTR( cprops, rgoszPropNames )))
  684. goto Exit;
  685. if (S_OK != (hr = ValidateInRGPROPVARIANT( cprops, rgpropvar )))
  686. goto Exit;
  687. propTraceParameters(("%lu, 0x%x, 0x%x", cprops, rgoszPropNames, rgpropvar));
  688. // --------------
  689. // Initialization
  690. // --------------
  691. // Open the property storage if it isn't already, creating if necessary.
  692. hr = OpenPropStg( FILE_OPEN );
  693. if( FAILED(hr) ) goto Exit;
  694. // PERF: Create a local array of propspecs for the WriteMultiple call
  695. prgpropspec = reinterpret_cast<PROPSPEC*>( CoTaskMemAlloc( cprops * sizeof(PROPSPEC) ));
  696. if( NULL == prgpropspec )
  697. {
  698. hr = E_OUTOFMEMORY;
  699. goto Exit;
  700. }
  701. // Set up the PROPSPEC and PROPVARIANT arrays to be written.
  702. for( i = 0; i < cprops; i++ )
  703. {
  704. // Point the propspecs at the caller-provided names
  705. prgpropspec[i].ulKind = PRSPEC_LPWSTR;
  706. prgpropspec[i].lpwstr = const_cast<LPOLESTR>(rgoszPropNames[i]);
  707. // Make a copy of the input PropVariant array, converting VT_UNKNOWN and
  708. // VT_DISPATCH into VT_EMPTY. We'll handle these after the initial write.
  709. rgpropvarCopy[i] = rgpropvar[i];
  710. if( PropertyRequiresConversion( rgpropvarCopy[i].vt ))
  711. {
  712. if( NULL == rgpropvarCopy[i].punkVal )
  713. {
  714. hr = E_INVALIDARG;
  715. goto Exit;
  716. }
  717. // We'll handle this particular property after the initial WriteMultiple.
  718. fInterfaces = TRUE;
  719. PropVariantInit( &rgpropvarCopy[i] );
  720. }
  721. }
  722. // --------------------
  723. // Write the properties
  724. // --------------------
  725. // Write all the properties, though the VT_UNKNOWN and VT_DISPATCH have been
  726. // switched to VT_EMPTY.
  727. hr = _ppropstg->WriteMultiple(cprops, prgpropspec, rgpropvarCopy, PID_FIRST_USABLE );
  728. if( FAILED(hr) ) goto Exit;
  729. // Now write the VT_UNKNOWN and VT_DISPATCH properties individually, converting
  730. // first to VT_STREAMED_OBJECT or VT_STORED_OBJECT.
  731. if( fInterfaces )
  732. {
  733. hr = WriteObjects( cprops, prgpropspec, rgpropvar );
  734. if( FAILED(hr) ) goto Exit;
  735. }
  736. // ----
  737. // Exit
  738. // ----
  739. hr = S_OK;
  740. Exit:
  741. // We don't need to delete the lpwstr fields, they just point to the caller-provided
  742. // names.
  743. if( NULL != prgpropspec )
  744. CoTaskMemFree( prgpropspec );
  745. // We don't need to clear rgpropvarCopy; it wasn't a deep copy.
  746. _pBlockingLock->Unlock();
  747. return( hr );
  748. } // CPropertyBagEx::WriteMultiple
  749. //+----------------------------------------------------------------------------
  750. //
  751. // Method: CPropertyBagEx::WriteObjects
  752. //
  753. // This method writes VT_UNKNOWN and VT_DISPATCH properties. It scans the
  754. // input rgpropvar array for such properties. For each that it finds, it QIs
  755. // for IPersistStorage or IPersistStream (in that order), creates a
  756. // VT_STORED_OBJECT/VT_STREAMED_OBJECT property (respectively), and persists the object
  757. // to that property.
  758. //
  759. //+----------------------------------------------------------------------------
  760. HRESULT
  761. CPropertyBagEx::WriteObjects( IN ULONG cprops,
  762. IN const PROPSPEC rgpropspec[],
  763. IN const PROPVARIANT rgpropvar[] )
  764. {
  765. HRESULT hr = S_OK;
  766. ULONG i;
  767. propITrace( "CPropertyBagEx::WriteObjects" );
  768. for( i = 0; i < cprops; i++ )
  769. {
  770. // Is this a type we need to handle?
  771. if( PropertyRequiresConversion( rgpropvar[i].vt ))
  772. {
  773. hr = WriteOneObject( &rgpropspec[i], &rgpropvar[i] );
  774. if( FAILED(hr) ) goto Exit;
  775. }
  776. }
  777. hr = S_OK;
  778. Exit:
  779. return( hr );
  780. } // CPropertyBagEx::WriteObjects
  781. //+----------------------------------------------------------------------------
  782. //
  783. //
  784. //
  785. //+----------------------------------------------------------------------------
  786. HRESULT
  787. CPropertyBagEx::WriteOneObject( const IN PROPSPEC *ppropspec, const IN PROPVARIANT *ppropvar )
  788. {
  789. HRESULT hr = S_OK;
  790. PROPVARIANT propvarNew;
  791. IPersistStorage *pPersistStg = NULL;
  792. IPersistStream *pPersistStm = NULL;
  793. IUnknown *punk = NULL;
  794. PropVariantInit( &propvarNew );
  795. DfpAssert( PropertyRequiresConversion( ppropvar->vt ));
  796. DfpAssert( reinterpret_cast<const void*const*>(&ppropvar->punkVal)
  797. ==
  798. reinterpret_cast<const void*const*>(&ppropvar->pdispVal)
  799. &&
  800. reinterpret_cast<const void*const*>(&ppropvar->pdispVal)
  801. ==
  802. reinterpret_cast<const void*const*>(&ppropvar->ppdispVal)
  803. &&
  804. reinterpret_cast<const void*const*>(&ppropvar->ppdispVal)
  805. ==
  806. reinterpret_cast<const void*const*>(&ppropvar->ppunkVal) );
  807. // --------------------
  808. // Look for an IPersist
  809. // --------------------
  810. // Get the IUnknown pointer (good for both VT_UNKNOWN and VT_DISPATCH).
  811. if( VT_BYREF & ppropvar->vt )
  812. punk = *ppropvar->ppunkVal;
  813. else
  814. punk = ppropvar->punkVal;
  815. DfpAssert( NULL != punk );
  816. // QI for IPersistStorage, then IPersistStream. If we find one or the other,
  817. // set up propvarNew in preparation for a write.
  818. hr = punk->QueryInterface( IID_IPersistStorage, reinterpret_cast<void**>(&pPersistStg) );
  819. if( SUCCEEDED(hr) )
  820. {
  821. propvarNew.vt = VT_STORED_OBJECT;
  822. }
  823. else if( E_NOINTERFACE == hr )
  824. {
  825. hr = punk->QueryInterface( IID_IPersistStream, reinterpret_cast<void**>(&pPersistStm) );
  826. if( SUCCEEDED(hr) )
  827. propvarNew.vt = VT_STREAMED_OBJECT;
  828. }
  829. if( FAILED(hr) )
  830. {
  831. propDbg(( DEB_WARN, "Couldn't find an IPersist interface in IPropertyBagEx::WriteMultiple" ));
  832. goto Exit;
  833. }
  834. // ----------------------------------
  835. // Create the stream/storage property
  836. // ----------------------------------
  837. // Write this empty VT_STREAMED/STORED_OBJECT (has a NULL pstm/pstg value). This will cause the
  838. // property set to create a new stream/storage.
  839. hr = _ppropstg->WriteMultiple( 1, ppropspec, &propvarNew, PID_FIRST_USABLE );
  840. if( FAILED(hr) )
  841. {
  842. propDbg(( DEB_ERROR, "Couldn't write %d for interface in IPropertyBagEx", propvarNew.vt ));
  843. goto Exit;
  844. }
  845. // Get an interface pointer for that new stream/storage.
  846. hr = _ppropstg->ReadMultiple( 1, ppropspec, &propvarNew );
  847. if( FAILED(hr) || S_FALSE == hr )
  848. {
  849. propDbg(( DEB_ERROR, "Couldn't read stream/storage back for interface in IPropertyBagEx::WriteMultiple" ));
  850. if( !FAILED(hr) )
  851. hr = STG_E_WRITEFAULT;
  852. goto Exit;
  853. }
  854. // --------------------
  855. // Persist the property
  856. // --------------------
  857. if( NULL != pPersistStg )
  858. {
  859. CLSID clsid;
  860. DfpAssert( VT_STORED_OBJECT == propvarNew.vt );
  861. // Set the clsid
  862. hr = pPersistStg->GetClassID( &clsid );
  863. if( E_NOTIMPL == hr )
  864. clsid = CLSID_NULL;
  865. else if( FAILED(hr) )
  866. goto Exit;
  867. hr = propvarNew.pStorage->SetClass( clsid );
  868. if( FAILED(hr) ) goto Exit;
  869. // Persist to VT_STORED_OBJECT
  870. hr = pPersistStg->Save( propvarNew.pStorage, FALSE /* Not necessarily same as load */ );
  871. if( FAILED(hr) ) goto Exit;
  872. hr = pPersistStg->SaveCompleted( propvarNew.pStorage );
  873. if( FAILED(hr) ) goto Exit;
  874. }
  875. else
  876. {
  877. CLSID clsid;
  878. ULONG cbWritten;
  879. DfpAssert( VT_STREAMED_OBJECT == propvarNew.vt );
  880. DfpAssert( NULL != pPersistStm );
  881. #if 1 == DBG
  882. // This stream should be at its start.
  883. {
  884. CULargeInteger culi;
  885. hr = propvarNew.pStream->Seek( CLargeInteger(0), STREAM_SEEK_CUR, &culi );
  886. if( FAILED(hr) ) goto Exit;
  887. DfpAssert( CULargeInteger(0) == culi );
  888. }
  889. #endif // #if 1 == DBG
  890. // Write the clsid
  891. DfpAssert( NULL != pPersistStm );
  892. hr = pPersistStm->GetClassID( &clsid );
  893. if( E_NOTIMPL == hr )
  894. clsid = CLSID_NULL;
  895. else if( FAILED(hr) )
  896. goto Exit;
  897. hr = propvarNew.pStream->Write( &clsid, sizeof(clsid), &cbWritten );
  898. if( FAILED(hr) || sizeof(clsid) != cbWritten )
  899. goto Exit;
  900. // Persist to VT_STREAMED_OBJECT
  901. IFDBG( ULONG cRefs = GetRefCount( propvarNew.pStream ));
  902. hr = pPersistStm->Save( propvarNew.pStream, TRUE /* Clear dirty flag */ );
  903. if( FAILED(hr) ) goto Exit;
  904. DfpAssert( GetRefCount( propvarNew.pStream ) == cRefs );
  905. }
  906. Exit:
  907. RELEASE_INTERFACE(pPersistStg);
  908. RELEASE_INTERFACE(pPersistStm);
  909. PropVariantClear( &propvarNew );
  910. return( hr );
  911. } // CPropertyBagEx::WriteOneObject
  912. //+----------------------------------------------------------------------------
  913. //
  914. // Method: CPropertyBagEx::DeleteMultiple (IPropertyBagEx)
  915. //
  916. //+----------------------------------------------------------------------------
  917. HRESULT STDMETHODCALLTYPE
  918. CPropertyBagEx::DeleteMultiple( /*[in]*/ ULONG cprops,
  919. /*[in]*/ LPCOLESTR const rgoszPropNames[],
  920. /*[in]*/ DWORD dwReserved )
  921. {
  922. HRESULT hr = S_OK;
  923. IEnumSTATPROPBAG *penum = NULL;
  924. STATPROPBAG statpropbag = { NULL };
  925. PROPSPEC *prgpropspec = NULL;
  926. ULONG i;
  927. // --------------
  928. // Initialization
  929. // --------------
  930. propXTrace( "CPropertyBagEx::DeleteMultiple" );
  931. _pBlockingLock->Lock( INFINITE );
  932. // Validate the input
  933. if (S_OK != (hr = ValidateInRGLPOLESTR( cprops, rgoszPropNames )))
  934. goto Exit;
  935. if( 0 != dwReserved )
  936. {
  937. hr = STG_E_INVALIDPARAMETER;
  938. goto Exit;
  939. }
  940. propTraceParameters(( "%d, 0x%x", cprops, rgoszPropNames ));
  941. // If it's not already open, open the property storage now. If it doesn't exist,
  942. // then our mission is accomplished; the properties don't exist.
  943. hr = OpenPropStg( FILE_OPEN_IF ); // Open only if it already exists
  944. if( STG_E_FILENOTFOUND == hr )
  945. {
  946. hr = S_OK;
  947. goto Exit;
  948. }
  949. else if( FAILED(hr) )
  950. goto Exit;
  951. // Create an array of PROPSPECs, and load with the caller-provided
  952. // names.
  953. prgpropspec = reinterpret_cast<PROPSPEC*>
  954. ( CoTaskMemAlloc( cprops * sizeof(PROPSPEC) ));
  955. if( NULL == prgpropspec )
  956. {
  957. hr = E_OUTOFMEMORY;
  958. goto Exit;
  959. }
  960. for( i = 0; i < cprops; i++ )
  961. {
  962. prgpropspec[i].ulKind = PRSPEC_LPWSTR;
  963. prgpropspec[i].lpwstr = const_cast<LPOLESTR>(rgoszPropNames[i]);
  964. }
  965. // ---------------------
  966. // Delete the properties
  967. // ---------------------
  968. hr = _ppropstg->DeleteMultiple( cprops, prgpropspec );
  969. if( FAILED(hr) ) goto Exit;
  970. // ----
  971. // Exit
  972. // ----
  973. hr = S_OK;
  974. Exit:
  975. CoTaskMemFree( prgpropspec );
  976. _pBlockingLock->Unlock();
  977. return( hr );
  978. } // CPropertyBagEx::DeleteMultiple
  979. //+----------------------------------------------------------------------------
  980. //
  981. // Method: CPropertyBagEx::Open (IPropertyBagEx)
  982. //
  983. // This method reads a VT_VERSIONED_STREAM from the underlying property set,
  984. // and returns the IStream pointer in *ppUnk. If the property doesn't already
  985. // exist, and guidPropertyType is not GUID_NULL, then an empty property is
  986. // created first. An empty property can also be created over an existing
  987. // one by specifying OPENPROPERTY_OVERWRITE.
  988. //
  989. //+----------------------------------------------------------------------------
  990. HRESULT STDMETHODCALLTYPE
  991. CPropertyBagEx::Open(
  992. /* [in] */ IUnknown __RPC_FAR *pUnkOuter,
  993. /* [in] */ LPCOLESTR pwszPropName,
  994. /* [in] */ GUID guidPropertyType,
  995. /* [in] */ DWORD dwFlags,
  996. /* [in] */ REFIID riid,
  997. /* [out] */ IUnknown __RPC_FAR *__RPC_FAR *ppUnk)
  998. {
  999. HRESULT hr = S_OK;
  1000. PROPVARIANT propvar;
  1001. IUnknown *pUnk = NULL;
  1002. DBGBUF(buf1);
  1003. DBGBUF(buf2);
  1004. // --------------
  1005. // Initialization
  1006. // --------------
  1007. propXTrace( "CPropertyBagEx::Open" );
  1008. PropVariantInit( &propvar );
  1009. _pBlockingLock->Lock( INFINITE );
  1010. // Validation
  1011. GEN_VDATEPTRIN_LABEL( pUnkOuter, IUnknown*, E_INVALIDARG, Exit, hr );
  1012. if (S_OK != (hr = ValidateInRGLPOLESTR( 1, &pwszPropName )))
  1013. goto Exit;
  1014. GEN_VDATEREADPTRIN_LABEL( &riid, IID*, E_INVALIDARG, Exit, hr );
  1015. propTraceParameters(( "0x%x, %ws, %s, 0x%x, %s",
  1016. pUnkOuter, pwszPropName, DbgFmtId(guidPropertyType,buf1),dwFlags, DbgFmtId(riid,buf2) ));
  1017. // Initialize out-parms
  1018. *ppUnk = NULL;
  1019. // We don't support aggregation
  1020. if( NULL != pUnkOuter )
  1021. {
  1022. propDbg(( DEB_ERROR, "CPropertyBagEx(0x%x)::Open doesn't support pUnkOuter\n", this ));
  1023. hr = E_NOTIMPL;
  1024. goto Exit;
  1025. }
  1026. // Check for unsupported flags
  1027. if( 0 != (~OPENPROPERTY_OVERWRITE & dwFlags) )
  1028. {
  1029. propDbg(( DEB_ERROR, "CPropertyBagEx(0x%x)::Open, invalid dwFlags (0x%x)\n", this, dwFlags ));
  1030. hr = E_NOTIMPL;
  1031. goto Exit;
  1032. }
  1033. // We don't support anything but IID_IStream
  1034. if( IID_IStream != riid )
  1035. {
  1036. hr = E_NOTIMPL;
  1037. goto Exit;
  1038. }
  1039. // -----------------
  1040. // Read the property
  1041. // -----------------
  1042. // Attempt to read the property
  1043. hr = ReadMultiple( 1, &pwszPropName, &propvar, NULL );
  1044. // If the property doesn't exist but we were given a valid guid, create a new one.
  1045. if( S_FALSE == hr && GUID_NULL != guidPropertyType )
  1046. {
  1047. VERSIONEDSTREAM VersionedStream;
  1048. PROPVARIANT propvarCreate;
  1049. // Write a new property speicifying NULL for the stream
  1050. VersionedStream.guidVersion = guidPropertyType;
  1051. VersionedStream.pStream = NULL;
  1052. propvarCreate.vt = VT_VERSIONED_STREAM;
  1053. propvarCreate.pVersionedStream = &VersionedStream;
  1054. hr = WriteMultiple( 1, &pwszPropName, &propvarCreate );
  1055. if( FAILED(hr) ) goto Exit;
  1056. // Read the property back, getting an IStream*
  1057. hr = ReadMultiple( 1, &pwszPropName, &propvar, NULL );
  1058. }
  1059. if( FAILED(hr) ) goto Exit;
  1060. // Is the type or guidPropertyType wrong? (If the caller specified GUID_NULL,
  1061. // then any guidVersion is OK.)
  1062. if( VT_VERSIONED_STREAM != propvar.vt
  1063. ||
  1064. GUID_NULL != guidPropertyType
  1065. &&
  1066. guidPropertyType != propvar.pVersionedStream->guidVersion )
  1067. {
  1068. if( OPENPROPERTY_OVERWRITE & dwFlags )
  1069. {
  1070. // Delete the existing property
  1071. hr = DeleteMultiple( 1, &pwszPropName, 0 );
  1072. if( FAILED(hr) ) goto Exit;
  1073. // Recursive call
  1074. // PERF: Optimize this so that we don't have to re-do the parameter checking.
  1075. hr = Open( pUnkOuter, pwszPropName, guidPropertyType, dwFlags & ~OPENPROPERTY_OVERWRITE, riid, ppUnk );
  1076. } // if( OPENPROPERTY_OVERWRITE & dwFlags )
  1077. else
  1078. {
  1079. propDbg(( DEB_ERROR, "CPropertyBagEx(0x%x)::Open couldn't overwrite existing property - %ws, %d\n",
  1080. this, pwszPropName, propvar.vt ));
  1081. hr = STG_E_FILEALREADYEXISTS;
  1082. } // if( OPENPROPERTY_OVERWRITE & dwFlags ) ... else
  1083. // Unconditional goto Exit; we either just called Open recursively to do the work,
  1084. // or set an error.
  1085. goto Exit;
  1086. }
  1087. // We have a good property, QI for the riid and we're done
  1088. DfpAssert( IID_IStream == riid );
  1089. hr = propvar.pVersionedStream->pStream->QueryInterface( IID_IUnknown, reinterpret_cast<void**>(&pUnk) );
  1090. if( FAILED(hr) )
  1091. {
  1092. pUnk = NULL;
  1093. goto Exit;
  1094. }
  1095. // ----
  1096. // Exit
  1097. // ----
  1098. *ppUnk = pUnk;
  1099. pUnk = NULL;
  1100. hr = S_OK;
  1101. Exit:
  1102. if( NULL != pUnk )
  1103. pUnk->Release();
  1104. PropVariantClear( &propvar );
  1105. _pBlockingLock->Unlock();
  1106. return( hr );
  1107. }
  1108. //+----------------------------------------------------------------------------
  1109. //
  1110. // Method: CPropertyBagEx::Enum (IPropertyBagEx)
  1111. //
  1112. // This method returns an enumerator of properties in a bag. If
  1113. // ENUMPROPERTY_MASK is set in dwFlags, poszPropNameMask is treated as a
  1114. // prefix, and the enumerator returns all those properties which match
  1115. // that prefix.
  1116. //
  1117. //+----------------------------------------------------------------------------
  1118. HRESULT STDMETHODCALLTYPE
  1119. CPropertyBagEx::Enum(
  1120. /* [in] */ LPCOLESTR poszPropNameMask,
  1121. /* [in] */ DWORD dwFlags,
  1122. /* [out] */ IEnumSTATPROPBAG __RPC_FAR *__RPC_FAR *ppenum)
  1123. {
  1124. HRESULT hr = S_OK;
  1125. CEnumSTATPROPBAG *penum = NULL;
  1126. propXTrace( "CPropertyBagEx::Enum" );
  1127. _pBlockingLock->Lock( INFINITE );
  1128. // Validate inputs
  1129. if (NULL != poszPropNameMask && S_OK != (hr = ValidateInRGLPOLESTR( 1, &poszPropNameMask )))
  1130. goto Exit;
  1131. GEN_VDATEPTROUT_LABEL( ppenum, IEnumSTATPROPBAG*, E_INVALIDARG, Exit, hr );
  1132. if( 0 != dwFlags )
  1133. {
  1134. propDbg(( DEB_ERROR, "CPropertyBagEx(0x%x)::Enum - invalid dwFlags (%08x)\n", this, dwFlags ));
  1135. hr = E_INVALIDARG;
  1136. goto Exit;
  1137. }
  1138. propTraceParameters(( "%ws, 0x%08x, 0x%x", poszPropNameMask, dwFlags, ppenum ));
  1139. // Initialize output
  1140. *ppenum = NULL;
  1141. // Open the property storage, if it exists
  1142. hr = OpenPropStg( FILE_OPEN_IF );
  1143. if( STG_E_FILENOTFOUND == hr )
  1144. hr = S_OK;
  1145. else if( FAILED(hr) )
  1146. goto Exit;
  1147. // Create an enumerator
  1148. penum = new CEnumSTATPROPBAG( _pBlockingLock );
  1149. if( NULL == penum )
  1150. {
  1151. hr = E_OUTOFMEMORY;
  1152. goto Exit;
  1153. }
  1154. // And initialize it
  1155. hr = penum->Init( _ppropstg, poszPropNameMask, dwFlags ); // _ppropstg could be NULL
  1156. if( FAILED(hr) ) goto Exit;
  1157. // ----
  1158. // Exit
  1159. // ----
  1160. hr = S_OK;
  1161. *ppenum = static_cast<IEnumSTATPROPBAG*>( penum );
  1162. penum = NULL;
  1163. Exit:
  1164. if( NULL != penum )
  1165. penum->Release();
  1166. _pBlockingLock->Unlock();
  1167. return( hr );
  1168. } // CPropertyBagEx::Enum
  1169. //+----------------------------------------------------------------------------
  1170. //
  1171. // Method: CPropertyBagEx::Commit
  1172. //
  1173. //+----------------------------------------------------------------------------
  1174. HRESULT
  1175. CPropertyBagEx::Commit( DWORD grfCommitFlags )
  1176. {
  1177. HRESULT hr = S_OK;
  1178. propITrace( "CPropertyBagEx::Commit" );
  1179. propTraceParameters(( "%08x", grfCommitFlags ));
  1180. // As an optimization, we only take the lock if we're really going to
  1181. // do the commit.
  1182. if( NULL != _ppropstg )
  1183. {
  1184. _pBlockingLock->Lock( INFINITE );
  1185. if( NULL != _ppropstg && IsWriteable() )
  1186. {
  1187. hr = _ppropstg->Commit( grfCommitFlags );
  1188. }
  1189. _pBlockingLock->Unlock();
  1190. }
  1191. return( hr );
  1192. } // CPropertyBagEx::Commit
  1193. //+----------------------------------------------------------------------------
  1194. //
  1195. // Method: CPropertyBagEx::ShutDown
  1196. //
  1197. //+----------------------------------------------------------------------------
  1198. HRESULT
  1199. CPropertyBagEx::ShutDown()
  1200. {
  1201. HRESULT hr = S_OK;
  1202. propITrace( "CPropertyBagEx::ShutDown" );
  1203. if( NULL != _pBlockingLock )
  1204. _pBlockingLock->Lock( INFINITE );
  1205. // Release the property storage that holds the bag. It is because
  1206. // of this call that we need this separate ShutDown method; we can't
  1207. // release this interface in the destructor, since it may be reverted
  1208. // by that point.
  1209. DfpVerify( 0 == RELEASE_INTERFACE(_ppropstg) );
  1210. // We didn't AddRef these, so we don't need to release them.
  1211. if( NULL != _ppropsetstgContainer )
  1212. _ppropsetstgContainer = NULL;
  1213. if( NULL != _pBlockingLock )
  1214. {
  1215. _pBlockingLock->Unlock();
  1216. _pBlockingLock = NULL;
  1217. }
  1218. return( hr );
  1219. } // CPropertyBagEx::ShutDown
  1220. //+----------------------------------------------------------------------------
  1221. //
  1222. // Method: CSTATPROPBAGArray::Init
  1223. //
  1224. //+----------------------------------------------------------------------------
  1225. HRESULT
  1226. CSTATPROPBAGArray::Init( IPropertyStorage *ppropstg, const OLECHAR *poszPrefix, DWORD dwFlags )
  1227. {
  1228. HRESULT hr = S_OK;
  1229. propITrace( "CSTATPROPBAGArray::Init" );
  1230. propTraceParameters(( "0x%x, %ws", ppropstg, poszPrefix ));
  1231. _pBlockingLock->Lock( INFINITE );
  1232. // Keep the IPropertyBagEx::Enum flags
  1233. _dwFlags = dwFlags;
  1234. // Copy the prefix string
  1235. if( NULL == poszPrefix )
  1236. _poszPrefix = NULL;
  1237. else
  1238. {
  1239. _poszPrefix = reinterpret_cast<OLECHAR*>
  1240. ( CoTaskMemAlloc( ( (ocslen(poszPrefix)+1) * sizeof(OLECHAR) )));
  1241. if( NULL == _poszPrefix )
  1242. {
  1243. hr = E_OUTOFMEMORY;
  1244. goto Exit;
  1245. }
  1246. ocscpy( _poszPrefix, poszPrefix );
  1247. }
  1248. // If we were given an IPropertyStorage, enum it. Otherwise, we'll leave
  1249. // _penum NULL and always return 0 for *pcFetched.
  1250. if( NULL != ppropstg )
  1251. {
  1252. hr = ppropstg->Enum( &_penum );
  1253. if( FAILED(hr) ) goto Exit;
  1254. }
  1255. hr = S_OK;
  1256. Exit:
  1257. _pBlockingLock->Unlock();
  1258. return( hr );
  1259. } // CSTATPROPBAGArray::Init
  1260. //+----------------------------------------------------------------------------
  1261. //
  1262. // Method: CSTATPROPBAGArray::AddRef/Release
  1263. //
  1264. //+----------------------------------------------------------------------------
  1265. ULONG
  1266. CSTATPROPBAGArray::AddRef()
  1267. {
  1268. return( InterlockedIncrement( &_cReferences ));
  1269. }
  1270. ULONG
  1271. CSTATPROPBAGArray::Release()
  1272. {
  1273. LONG lRet = InterlockedDecrement( &_cReferences );
  1274. if( 0 == lRet )
  1275. delete this;
  1276. return( 0 > lRet ? 0 : lRet );
  1277. }
  1278. //+----------------------------------------------------------------------------
  1279. //
  1280. // Method: CSTATPROPBAGArray::NextAt
  1281. //
  1282. // This method gets the *pcFetched STATPROPBAG structures in the
  1283. // enumeration starting from index iNext. This is implemented using
  1284. // the IEnumSTATPROPSTG enumerator in _penum.
  1285. //
  1286. //+----------------------------------------------------------------------------
  1287. HRESULT
  1288. CSTATPROPBAGArray::NextAt( ULONG iNext, STATPROPBAG *prgstatpropbag, ULONG *pcFetched )
  1289. {
  1290. HRESULT hr = S_OK;
  1291. STATPROPSTG statpropstg = { NULL };
  1292. ULONG iMatch = 0;
  1293. ULONG iFetched = 0;
  1294. propITrace( "CSTATPROPBAGArray::NextAt" );
  1295. propTraceParameters(( "%d, 0x%x, 0x%x", iNext, prgstatpropbag, pcFetched ));
  1296. _pBlockingLock->Lock( INFINITE );
  1297. // If there's nothing to enumerate, then we're done
  1298. if( NULL == _penum )
  1299. {
  1300. hr = S_FALSE;
  1301. *pcFetched = 0;
  1302. goto Exit;
  1303. }
  1304. // Reset the IEnumSTATPROPBAGTG index (doesn't reload, just resets the index).
  1305. hr = _penum->Reset();
  1306. if( FAILED(hr) ) goto Exit;
  1307. // Walk the IEnumSTATPROPBAGTG enumerator, looking for matches.
  1308. hr = _penum->Next( 1, &statpropstg, NULL );
  1309. while( S_OK == hr && iFetched < *pcFetched )
  1310. {
  1311. // Does this property have a name (all properties in a bag must have
  1312. // a name)?
  1313. if( NULL != statpropstg.lpwstrName )
  1314. {
  1315. // Yes, we have a name. Are we enumerating all properties (in which case
  1316. // _poszPrefix is NULL), or does this property name match the prefix?
  1317. if( NULL == _poszPrefix
  1318. ||
  1319. statpropstg.lpwstrName == ocsstr( statpropstg.lpwstrName, _poszPrefix )
  1320. ||
  1321. !ocscmp( statpropstg.lpwstrName, _poszPrefix ) )
  1322. {
  1323. // Yes, this property matches the prefix and is therefore part
  1324. // of this enumeration. But is this the index into the enumeration
  1325. // that we're looking for?
  1326. if( iNext == iMatch )
  1327. {
  1328. // Yes, everything matches, and we have a property that should
  1329. // be returned.
  1330. prgstatpropbag[ iFetched ].lpwstrName = statpropstg.lpwstrName;
  1331. statpropstg.lpwstrName = NULL;
  1332. prgstatpropbag[ iFetched ].vt = statpropstg.vt;
  1333. // GUID is not current supported by the enumeration
  1334. prgstatpropbag[ iFetched ].guidPropertyType = GUID_NULL;
  1335. // Show that we're now looking for the i+1 index
  1336. iNext++;
  1337. iFetched++;
  1338. }
  1339. // Increment the number of property matches we've had.
  1340. // (iMatch will increment until it equals iNext, after
  1341. // which the two will always both increment and remain equal).
  1342. iMatch++;
  1343. } // if( NULL == _poszPrefix ...
  1344. } // if( NULL != statpropstg.lpwstrName )
  1345. CoTaskMemFree( statpropstg.lpwstrName );
  1346. statpropstg.lpwstrName = NULL;
  1347. hr = _penum->Next( 1, &statpropstg, NULL );
  1348. } // while( S_OK == hr && iFetched < *pcFetched )
  1349. // Did we get an error on a _penum->Next call?
  1350. if( FAILED(hr) ) goto Exit;
  1351. // If we reached this point, there was no error. Determine if
  1352. // OK or FALSE should be returned.
  1353. if( iFetched == *pcFetched )
  1354. hr = S_OK;
  1355. else
  1356. hr = S_FALSE;
  1357. *pcFetched = iFetched;
  1358. // ----
  1359. // Exit
  1360. // ----
  1361. Exit:
  1362. CoTaskMemFree( statpropstg.lpwstrName );
  1363. _pBlockingLock->Unlock();
  1364. return( hr );
  1365. } // CSTATPROPBAGArray::NextAt
  1366. //+----------------------------------------------------------------------------
  1367. //
  1368. // Method: CEnumSTATPROPBAG::CEnumSTATPROPBAG (copy constructor)
  1369. //
  1370. //+----------------------------------------------------------------------------
  1371. CEnumSTATPROPBAG::CEnumSTATPROPBAG( const CEnumSTATPROPBAG &Other )
  1372. {
  1373. propDbg(( DEB_ITRACE, "CEnumSTATPROPBAG::CEnumSTATPROPBAG (copy constructor)" ));
  1374. Other._pBlockingLock->Lock( INFINITE );
  1375. new(this) CEnumSTATPROPBAG( Other._pBlockingLock );
  1376. _iarray = Other._iarray;
  1377. Other._parray->AddRef();
  1378. _parray = Other._parray;
  1379. Other._pBlockingLock->Unlock();
  1380. }
  1381. //+----------------------------------------------------------------------------
  1382. //
  1383. // Method: CEnumSTATPROPBAG::~CEnumSTATPROPBAG
  1384. //
  1385. //+----------------------------------------------------------------------------
  1386. CEnumSTATPROPBAG::~CEnumSTATPROPBAG()
  1387. {
  1388. propDbg(( DEB_ITRACE, "CEnumSTATPROPBAG::~CEnumSTATPROPBAG\n" ));
  1389. _pBlockingLock->Release();
  1390. if( NULL != _parray )
  1391. _parray->Release();
  1392. }
  1393. //+----------------------------------------------------------------------------
  1394. //
  1395. // Method: CEnumSTATPROPBAG::Init
  1396. //
  1397. //+----------------------------------------------------------------------------
  1398. HRESULT
  1399. CEnumSTATPROPBAG::Init( IPropertyStorage *ppropstg, LPCOLESTR poszPrefix, DWORD dwFlags )
  1400. {
  1401. HRESULT hr = S_OK;
  1402. propITrace( "CEnumSTATPROPBAG::Init" );
  1403. propTraceParameters(( "0x%x, %ws", ppropstg, poszPrefix ));
  1404. // Create a STATPROPBAG Array
  1405. _parray = new CSTATPROPBAGArray( _pBlockingLock );
  1406. if( NULL == _parray )
  1407. {
  1408. hr = E_OUTOFMEMORY;
  1409. goto Exit;
  1410. }
  1411. // Load the array from the IPropertyStorage
  1412. hr = _parray->Init( ppropstg, poszPrefix, dwFlags );
  1413. if( FAILED(hr) ) goto Exit;
  1414. hr = S_OK;
  1415. Exit:
  1416. return( hr );
  1417. } // CEnumSTATPROPBAG::Init
  1418. //+----------------------------------------------------------------------------
  1419. //
  1420. // Method: CEnumSTATPROPBAG::QueryInterface/AddRef/Release
  1421. //
  1422. //+----------------------------------------------------------------------------
  1423. HRESULT STDMETHODCALLTYPE
  1424. CEnumSTATPROPBAG::QueryInterface( REFIID riid, void **ppvObject )
  1425. {
  1426. HRESULT hr = S_OK;
  1427. // Validate the inputs
  1428. VDATEREADPTRIN( &riid, IID );
  1429. VDATEPTROUT( ppvObject, void* );
  1430. *ppvObject = NULL;
  1431. if( IID_IEnumSTATPROPBAG == riid || IID_IUnknown == riid )
  1432. {
  1433. *ppvObject = static_cast<IEnumSTATPROPBAG*>(this);
  1434. AddRef();
  1435. }
  1436. else
  1437. hr = E_NOINTERFACE;
  1438. return(hr);
  1439. } // CEnumSTATPROPBAG::QueryInterface
  1440. ULONG STDMETHODCALLTYPE
  1441. CEnumSTATPROPBAG::AddRef()
  1442. {
  1443. return( InterlockedIncrement( &_cRefs ));
  1444. }
  1445. ULONG STDMETHODCALLTYPE
  1446. CEnumSTATPROPBAG::Release()
  1447. {
  1448. LONG lRet = InterlockedDecrement( &_cRefs );
  1449. if( 0 == lRet )
  1450. delete this;
  1451. return( 0 > lRet ? 0 : lRet );
  1452. }
  1453. //+----------------------------------------------------------------------------
  1454. //
  1455. // Method: CEnumSTATPROPBAG::Next
  1456. //
  1457. //+----------------------------------------------------------------------------
  1458. HRESULT STDMETHODCALLTYPE
  1459. CEnumSTATPROPBAG::Next( ULONG celt, STATPROPBAG *rgelt, ULONG *pceltFetched )
  1460. {
  1461. HRESULT hr = S_OK;
  1462. ULONG celtFetched = celt;
  1463. propXTrace( "CEnumSTATPROPBAG::Next" );
  1464. _pBlockingLock->Lock( INFINITE );
  1465. // Validate the inputs
  1466. if (NULL == pceltFetched)
  1467. {
  1468. if (celt != 1)
  1469. return(STG_E_INVALIDPARAMETER);
  1470. }
  1471. else
  1472. {
  1473. VDATEPTROUT( pceltFetched, ULONG );
  1474. *pceltFetched = 0;
  1475. }
  1476. propTraceParameters(( "%d, 0x%x, 0x%x", celt, rgelt, pceltFetched ));
  1477. // Get the next set of stat structures
  1478. hr = _parray->NextAt( _iarray, rgelt, &celtFetched );
  1479. if( FAILED(hr) ) goto Exit;
  1480. // Advance the index
  1481. _iarray += celtFetched;
  1482. // Return the count to the caller
  1483. if( NULL != pceltFetched )
  1484. *pceltFetched = celtFetched;
  1485. hr = celtFetched == celt ? S_OK : S_FALSE;
  1486. Exit:
  1487. _pBlockingLock->Unlock();
  1488. return( hr );
  1489. } // CEnumSTATPROPBAG::Next
  1490. //+----------------------------------------------------------------------------
  1491. //
  1492. // Method: CEnumSTATPROPBAG::Reset
  1493. //
  1494. //+----------------------------------------------------------------------------
  1495. HRESULT STDMETHODCALLTYPE
  1496. CEnumSTATPROPBAG::Reset( )
  1497. {
  1498. HRESULT hr = S_OK;
  1499. propXTrace( "CEnumSTATPROPBAG::Reset" );
  1500. _pBlockingLock->Lock( INFINITE );
  1501. _iarray = 0;
  1502. _pBlockingLock->Unlock();
  1503. return( hr );
  1504. } // CEnumSTATPROPBAG::Reset
  1505. //+----------------------------------------------------------------------------
  1506. //
  1507. // Method: CEnumSTATPROPBAG::Skip
  1508. //
  1509. //+----------------------------------------------------------------------------
  1510. HRESULT STDMETHODCALLTYPE
  1511. CEnumSTATPROPBAG::Skip( ULONG celt )
  1512. {
  1513. HRESULT hr = S_OK;
  1514. STATPROPBAG statpropbag = { NULL };
  1515. propXTrace( "CEnumSTATPROPBAG::Skip" );
  1516. propTraceParameters( ("%d", celt ));
  1517. _pBlockingLock->Lock( INFINITE );
  1518. while( celt )
  1519. {
  1520. ULONG cFetched = 1;
  1521. hr = _parray->NextAt( _iarray, &statpropbag, &cFetched );
  1522. CoTaskMemFree( statpropbag.lpwstrName );
  1523. statpropbag.lpwstrName = NULL;
  1524. if( FAILED(hr) )
  1525. goto Exit;
  1526. else if( S_FALSE == hr )
  1527. break;
  1528. else
  1529. {
  1530. _iarray++;
  1531. hr = S_OK;
  1532. --celt;
  1533. }
  1534. }
  1535. Exit:
  1536. CoTaskMemFree( statpropbag.lpwstrName );
  1537. _pBlockingLock->Unlock();
  1538. return( hr );
  1539. } // CEnumSTATPROPBAG::Skip
  1540. //+----------------------------------------------------------------------------
  1541. //
  1542. // Method: CEnumSTATPROPBAG::Clone
  1543. //
  1544. //+----------------------------------------------------------------------------
  1545. HRESULT STDMETHODCALLTYPE
  1546. CEnumSTATPROPBAG::Clone( IEnumSTATPROPBAG **ppenum )
  1547. {
  1548. HRESULT hr = S_OK;
  1549. CEnumSTATPROPBAG *penum = NULL;
  1550. propXTrace( "CEnumSTATPROPBAG::Clone" );
  1551. // Validate the input
  1552. VDATEPTROUT( ppenum, IEnumSTATPROPSTG* );
  1553. *ppenum = NULL;
  1554. propTraceParameters(( "0x%x", ppenum ));
  1555. penum = new CEnumSTATPROPBAG( *this );
  1556. if( NULL == penum )
  1557. {
  1558. hr = E_OUTOFMEMORY;
  1559. goto Exit;
  1560. }
  1561. *ppenum = static_cast<IEnumSTATPROPBAG*>(penum);
  1562. penum = NULL;
  1563. Exit:
  1564. if( NULL != penum )
  1565. delete penum ;
  1566. return( hr );
  1567. } // CEnumSTATPROPBAG::Clone
  1568. //
  1569. // Add this functionality to chgtype.cxx when chgtype.cxx is finalized.
  1570. //
  1571. //
  1572. // These are the Property Variant types that need to be
  1573. // dumbed down to the VARIANT types.
  1574. //
  1575. struct {
  1576. VARTYPE vtSrc, vtDest;
  1577. } const ImplicitCoercionLookup[] = {
  1578. // Src Dest
  1579. {VT_I8, VT_I4},
  1580. {VT_UI8, VT_UI4},
  1581. {VT_LPSTR, VT_BSTR},
  1582. {VT_LPWSTR, VT_BSTR},
  1583. {VT_FILETIME, VT_DATE},
  1584. {VT_BLOB, VT_ARRAY|VT_UI1},
  1585. {VT_STREAM, VT_UNKNOWN},
  1586. {VT_STREAMED_OBJECT, VT_UNKNOWN},
  1587. {VT_STORAGE, VT_UNKNOWN},
  1588. {VT_STORED_OBJECT, VT_UNKNOWN},
  1589. {VT_BLOB_OBJECT, VT_ARRAY|VT_UI1},
  1590. {VT_CF, VT_ARRAY|VT_UI1},
  1591. {VT_CLSID, VT_BSTR},
  1592. };
  1593. HRESULT
  1594. ImplicitPropVariantToVariantChangeType(
  1595. PROPVARIANT *pDest, // Omit the hungarian to make
  1596. const PROPVARIANT *pSrc, // the code look cleaner.
  1597. LCID lcid )
  1598. {
  1599. HRESULT hr=S_OK;
  1600. VARTYPE vtCoerce;
  1601. VARTYPE vtType;
  1602. int i;
  1603. //
  1604. // Safe arrays are only built from old VARIANT types.
  1605. // They are easy so get them out of the way.
  1606. //
  1607. if( VT_ARRAY & pSrc->vt )
  1608. {
  1609. return PropVariantCopy( pDest, pSrc );
  1610. }
  1611. vtType = pSrc->vt & VT_TYPEMASK;
  1612. vtCoerce = VT_EMPTY;
  1613. for(i=0; i<ELEMENTS(ImplicitCoercionLookup); i++)
  1614. {
  1615. if( ImplicitCoercionLookup[i].vtSrc == vtType )
  1616. {
  1617. vtCoerce = ImplicitCoercionLookup[i].vtDest;
  1618. break;
  1619. }
  1620. }
  1621. if(VT_VECTOR & pSrc->vt)
  1622. {
  1623. if( VT_EMPTY == vtCoerce )
  1624. vtCoerce = pSrc->vt & VT_TYPEMASK;
  1625. return HrPropVarVECTORToSAFEARRAY( pDest, pSrc, lcid, vtCoerce);
  1626. }
  1627. if( VT_EMPTY == vtCoerce )
  1628. hr = PropVariantCopy( pDest, pSrc ); // Optimization.
  1629. else
  1630. hr = PropVariantChangeType( pDest, pSrc, lcid, 0, vtCoerce );
  1631. return hr;
  1632. }
  1633. HRESULT
  1634. HrPropVarVECTORToSAFEARRAY(
  1635. PROPVARIANT *pDest,
  1636. const PROPVARIANT *pSrc,
  1637. LCID lcid,
  1638. VARTYPE vtCoerce )
  1639. {
  1640. DWORD i;
  1641. HRESULT hr = S_OK;
  1642. SAFEARRAY *psaT=NULL;
  1643. SAFEARRAYBOUND sabound;
  1644. PROPVARIANT propvarT1, propvarT2;
  1645. PropVariantInit( &propvarT1 );
  1646. PropVariantInit( &propvarT2 );
  1647. PROPASSERT (VT_VECTOR & pSrc->vt);
  1648. VARTYPE vt;
  1649. vt = pSrc->vt & VT_TYPEMASK;
  1650. // Create the SafeArray
  1651. sabound.lLbound = 0;
  1652. sabound.cElements = pSrc->cac.cElems;
  1653. psaT = PrivSafeArrayCreate(vtCoerce, 1, &sabound );
  1654. if(psaT == NULL)
  1655. {
  1656. hr = E_OUTOFMEMORY;
  1657. goto Error;
  1658. }
  1659. // Convert single typed Vectors
  1660. // Pull each element from the Vector,
  1661. // Change it's type, per requested.
  1662. // Put it into the Array.
  1663. if(VT_VARIANT != vt)
  1664. {
  1665. for(i=0; i<pSrc->cac.cElems; i++)
  1666. {
  1667. hr = LoadPropVariantFromVectorElem( &propvarT1, pSrc, i );
  1668. if( FAILED(hr) )
  1669. goto Error;
  1670. //
  1671. // PERF: Performance
  1672. // If the pSrc->vt == vtCoerce, then we can skip the ChangeType
  1673. // and the following clean. And go directly to "Put" of T1.
  1674. //
  1675. hr = PropVariantChangeType( &propvarT2, &propvarT1, lcid, 0, vtCoerce );
  1676. PropVariantClear( &propvarT1 );
  1677. if( FAILED(hr) )
  1678. goto Error;
  1679. hr = PutPropVariantDataIntoSafeArray( psaT, &propvarT2, i);
  1680. PropVariantClear( &propvarT2 );
  1681. if( FAILED(hr) )
  1682. goto Error;
  1683. }
  1684. }
  1685. // Convert Vectors of Variants
  1686. // Do Implisit Coercion of each Variant into an new Variant
  1687. // Put the new Variant into the Array.
  1688. else
  1689. {
  1690. for(i=0; i<pSrc->cac.cElems; i++)
  1691. {
  1692. hr = ImplicitPropVariantToVariantChangeType(
  1693. &propvarT2, &pSrc->capropvar.pElems[i], lcid );
  1694. if( FAILED(hr) )
  1695. goto Error;
  1696. hr = PrivSafeArrayPutElement( psaT, (long*)&i, (void*)&propvarT2 );
  1697. PropVariantClear( &propvarT2 );
  1698. if( FAILED(hr) )
  1699. goto Error;
  1700. }
  1701. }
  1702. pDest->vt = VT_ARRAY | vtCoerce;
  1703. pDest->parray = psaT;
  1704. psaT = NULL;
  1705. Error:
  1706. if(NULL != psaT)
  1707. PrivSafeArrayDestroy( psaT );
  1708. return hr;
  1709. }
  1710. //-----------------------------------------------------------------
  1711. // Dup routines
  1712. //-----------------------------------------------------------------
  1713. LPSTR
  1714. PropDupStr( const LPSTR lpstr )
  1715. {
  1716. int cch;
  1717. if( NULL == lpstr )
  1718. return NULL;
  1719. else
  1720. {
  1721. cch = lstrlenA( lpstr ) + 1;
  1722. return (LPSTR)AllocAndCopy( cch*sizeof(CHAR), lpstr );
  1723. }
  1724. }
  1725. LPWSTR
  1726. PropDupWStr( const LPWSTR lpwstr )
  1727. {
  1728. int cch;
  1729. if( NULL == lpwstr )
  1730. return NULL;
  1731. else
  1732. {
  1733. cch = lstrlenW( lpwstr ) + 1;
  1734. return (LPWSTR)AllocAndCopy( cch*sizeof(WCHAR), lpwstr );
  1735. }
  1736. }
  1737. LPCLSID
  1738. PropDupCLSID( const LPCLSID pclsid )
  1739. {
  1740. return (LPCLSID)AllocAndCopy( sizeof(CLSID), pclsid);
  1741. }
  1742. CLIPDATA*
  1743. PropDupClipData( const CLIPDATA* pclipdata )
  1744. {
  1745. CLIPDATA* pcdNew=NULL;
  1746. CLIPDATA* pclipdataNew=NULL;
  1747. PVOID pvMem=NULL;
  1748. pcdNew = new CLIPDATA;
  1749. pvMem = AllocAndCopy( CBPCLIPDATA( *pclipdata ), pclipdata->pClipData);
  1750. if( NULL == pvMem || NULL == pcdNew )
  1751. goto Error;
  1752. pcdNew->cbSize = pclipdata->cbSize;
  1753. pcdNew->ulClipFmt = pclipdata->ulClipFmt;
  1754. pcdNew->pClipData = (BYTE*)pvMem;
  1755. pclipdataNew = pcdNew;
  1756. pcdNew = NULL;
  1757. pvMem = NULL;
  1758. Error:
  1759. if( NULL != pcdNew )
  1760. delete pcdNew;
  1761. if( NULL != pvMem )
  1762. delete pvMem;
  1763. return pclipdataNew;
  1764. }
  1765. //------------------------------------------------------------------------
  1766. //
  1767. // LoadPropVariantFromVectorElem()
  1768. // This routine will load a provided PropVariant from an element of a
  1769. // provided SafeArray at the provided index.
  1770. // All the PropVariant Vector types are supported.
  1771. //
  1772. //------------------------------------------------------------------------
  1773. HRESULT
  1774. LoadPropVariantFromVectorElem(
  1775. PROPVARIANT *pDest,
  1776. const PROPVARIANT *pSrc,
  1777. int idx)
  1778. {
  1779. VARTYPE vt;
  1780. HRESULT hr=S_OK;
  1781. vt = pSrc->vt & VT_TYPEMASK;
  1782. switch( vt )
  1783. {
  1784. case VT_I1:
  1785. pDest->cVal = pSrc->cac.pElems[idx];
  1786. break;
  1787. case VT_UI1:
  1788. pDest->bVal = pSrc->caub.pElems[idx];
  1789. break;
  1790. case VT_I2:
  1791. pDest->iVal = pSrc->cai.pElems[idx];
  1792. break;
  1793. case VT_UI2:
  1794. pDest->uiVal = pSrc->caui.pElems[idx];
  1795. break;
  1796. case VT_I4:
  1797. pDest->lVal = pSrc->cal.pElems[idx];
  1798. break;
  1799. case VT_UI4:
  1800. pDest->ulVal = pSrc->caul.pElems[idx];
  1801. break;
  1802. case VT_R4:
  1803. pDest->fltVal = pSrc->caflt.pElems[idx];
  1804. break;
  1805. case VT_R8:
  1806. pDest->dblVal = pSrc->cadbl.pElems[idx];
  1807. break;
  1808. case VT_CY:
  1809. pDest->cyVal = pSrc->cacy.pElems[idx];
  1810. break;
  1811. case VT_DATE:
  1812. pDest->date = pSrc->cadate.pElems[idx];
  1813. break;
  1814. case VT_BSTR:
  1815. if( NULL == pSrc->cabstr.pElems[idx] )
  1816. pDest->bstrVal = NULL;
  1817. else
  1818. {
  1819. pDest->bstrVal = PrivSysAllocString( pSrc->cabstr.pElems[idx] );
  1820. if( NULL == pDest->bstrVal)
  1821. {
  1822. hr = E_OUTOFMEMORY;
  1823. goto Error;
  1824. }
  1825. }
  1826. break;
  1827. case VT_BOOL:
  1828. pDest->boolVal = pSrc->cabool.pElems[idx];
  1829. break;
  1830. case VT_ERROR:
  1831. pDest->scode = pSrc->cascode.pElems[idx];
  1832. break;
  1833. case VT_I8:
  1834. pDest->hVal = pSrc->cah.pElems[idx];
  1835. break;
  1836. case VT_UI8:
  1837. pDest->uhVal = pSrc->cauh.pElems[idx];
  1838. break;
  1839. // String Copy
  1840. case VT_LPSTR:
  1841. if( NULL == pSrc->calpstr.pElems[idx] )
  1842. pDest->pszVal = NULL;
  1843. else
  1844. {
  1845. pDest->pszVal = PropDupStr( pSrc->calpstr.pElems[idx] );
  1846. if( NULL == pDest->pszVal)
  1847. {
  1848. hr = E_OUTOFMEMORY;
  1849. goto Error;
  1850. }
  1851. }
  1852. break;
  1853. // Wide String Copy
  1854. case VT_LPWSTR:
  1855. if( NULL == pSrc->calpwstr.pElems[idx] )
  1856. pDest->pwszVal = NULL;
  1857. else
  1858. {
  1859. pDest->pwszVal = PropDupWStr( pSrc->calpwstr.pElems[idx] );
  1860. if( NULL == pDest->pwszVal)
  1861. {
  1862. hr = E_OUTOFMEMORY;
  1863. goto Error;
  1864. }
  1865. }
  1866. break;
  1867. case VT_FILETIME:
  1868. pDest->filetime = pSrc->cafiletime.pElems[idx];
  1869. break;
  1870. //
  1871. // The Variant takes a pointer to a CLIPDATA but the
  1872. // vector is an array of CLIPDATA structs.
  1873. //
  1874. case VT_CF:
  1875. pDest->pclipdata = PropDupClipData(&pSrc->caclipdata.pElems[idx]);
  1876. if( NULL == pDest->pclipdata)
  1877. {
  1878. hr = E_OUTOFMEMORY;
  1879. goto Error;
  1880. }
  1881. break;
  1882. //
  1883. // The Variant takes a pointer to a CLSID but the
  1884. // vector is an array of CLSID structs.
  1885. //
  1886. case VT_CLSID:
  1887. pDest->puuid = PropDupCLSID(&pSrc->cauuid.pElems[idx]);
  1888. if( NULL == pDest->puuid)
  1889. {
  1890. hr = E_OUTOFMEMORY;
  1891. goto Error;
  1892. }
  1893. break;
  1894. default:
  1895. return DISP_E_TYPEMISMATCH;
  1896. }
  1897. pDest->vt = vt;
  1898. Error:
  1899. return hr;
  1900. }
  1901. //------------------------------------------------------------------------
  1902. //
  1903. // PutPropVariantDataIntoSafeArray
  1904. // This will take the data part of a propvariant and "Put" it into the
  1905. // a SafeArray at the provided index.
  1906. // Only the insection of PROPVARIANT vector types with old VARIANT types
  1907. // are supported.
  1908. //
  1909. //------------------------------------------------------------------------
  1910. // PERF: Reimplement this without using the PropVariantCopy
  1911. HRESULT
  1912. PutPropVariantDataIntoSafeArray(
  1913. SAFEARRAY *psa,
  1914. const PROPVARIANT *pSrc,
  1915. int idx)
  1916. {
  1917. VARTYPE vt;
  1918. HRESULT hr=S_OK;
  1919. PROPVARIANT propvarT;
  1920. const void *pv=NULL;
  1921. vt = pSrc->vt & VT_TYPEMASK;
  1922. PROPASSERT(vt == pSrc->vt);
  1923. PropVariantInit( &propvarT );
  1924. hr = PropVariantCopy( &propvarT, pSrc );
  1925. if( FAILED( hr ) ) goto Exit;
  1926. switch( vt )
  1927. {
  1928. case VT_I1:
  1929. pv = &propvarT.cVal;
  1930. break;
  1931. case VT_UI1:
  1932. pv = &propvarT.bVal;
  1933. break;
  1934. case VT_I2:
  1935. pv = &propvarT.iVal;
  1936. break;
  1937. case VT_UI2:
  1938. pv = &propvarT.uiVal;
  1939. break;
  1940. case VT_I4:
  1941. pv = &propvarT.lVal;
  1942. break;
  1943. case VT_UI4:
  1944. pv = &propvarT.ulVal;
  1945. break;
  1946. case VT_R4:
  1947. pv = &propvarT.fltVal;
  1948. break;
  1949. case VT_R8:
  1950. pv = &propvarT.dblVal;
  1951. break;
  1952. case VT_CY:
  1953. pv = &propvarT.cyVal;
  1954. break;
  1955. case VT_DATE:
  1956. pv = &propvarT.date;
  1957. break;
  1958. case VT_BSTR:
  1959. pv = propvarT.bstrVal; // Pointer Copy
  1960. break;
  1961. case VT_BOOL:
  1962. pv = &propvarT.boolVal;
  1963. break;
  1964. case VT_ERROR:
  1965. pv = &propvarT.scode;
  1966. break;
  1967. case VT_I8:
  1968. pv = &propvarT.hVal;
  1969. break;
  1970. case VT_UI8:
  1971. pv = &propvarT.uhVal;
  1972. break;
  1973. case VT_CF:
  1974. pv = &propvarT.pclipdata;
  1975. break;
  1976. default:
  1977. hr = DISP_E_TYPEMISMATCH;
  1978. goto Exit;
  1979. }
  1980. // *Copy* the data into the SafeArray
  1981. hr = PrivSafeArrayPutElement( psa, (long*)&idx, const_cast<void*>(pv) );
  1982. if( FAILED(hr) ) goto Exit;
  1983. Exit:
  1984. PropVariantClear( &propvarT );
  1985. return hr;
  1986. }