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.

882 lines
22 KiB

  1. #include "pch.h"
  2. #include "thisdll.h"
  3. #include "wmwrap.h"
  4. #include "MediaProp.h"
  5. #include "ids.h"
  6. #include <streams.h>
  7. #include <drmexternals.h>
  8. #define IsEqualSCID(a, b) (((a).pid == (b).pid) && IsEqualIID((a).fmtid, (b).fmtid) )
  9. class CMediaPropStgEnum : public IEnumSTATPROPSTG
  10. {
  11. public:
  12. CMediaPropStgEnum(const COLMAP **pprops, ULONG cprop, ULONG pos);
  13. // IUnknown
  14. STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
  15. STDMETHODIMP_(ULONG) AddRef();
  16. STDMETHODIMP_(ULONG) Release();
  17. // IEnumSTATPROPSTG
  18. STDMETHODIMP Next(ULONG celt, STATPROPSTG *rgelt, ULONG *pceltFetched);
  19. STDMETHODIMP Skip(ULONG celt);
  20. STDMETHODIMP Reset();
  21. STDMETHODIMP Clone(IEnumSTATPROPSTG **ppenum);
  22. STDMETHODIMP Init(BOOL *pbAvailable);
  23. protected:
  24. ~CMediaPropStgEnum();
  25. private:
  26. LONG _cRef;
  27. ULONG _pos, _size;
  28. const COLMAP **_pprops;
  29. BOOL *_pbAvailable;
  30. };
  31. /*
  32. The way this works:
  33. The PropSetStg has a fixed set of PropStorages. When it gets created,
  34. it makes an authoritative PropStg for each fmtid. Thereafter, it defers
  35. Open() requests to the appropriate PropStg.
  36. This PropStg then marks itself opened and makes a copy of itself
  37. (marked NON_AUTH) which can be abused as the caller sees fit.
  38. The copy will call the original when it's time to commit and will also
  39. notify the original when it is closed.
  40. The current implementation requires that STGM_SHARE_EXCLUSIVE be specified
  41. when opening a propstg.
  42. If you plan on creating this class yourself (which you probably shouldn't),
  43. you must also call Init() first and confirm that it succeeds.
  44. */
  45. STDMETHODIMP_(ULONG) CMediaPropStgEnum::AddRef()
  46. {
  47. return InterlockedIncrement(&_cRef);
  48. }
  49. STDMETHODIMP_(ULONG) CMediaPropStgEnum::Release()
  50. {
  51. if (InterlockedDecrement(&_cRef))
  52. return _cRef;
  53. delete this;
  54. return 0;
  55. }
  56. STDMETHODIMP CMediaPropStgEnum::QueryInterface(REFIID riid, void **ppv)
  57. {
  58. static const QITAB qit[] = {
  59. QITABENT(CMediaPropStgEnum, IEnumSTATPROPSTG),
  60. { 0 },
  61. };
  62. return QISearch(this, qit, riid, ppv);
  63. }
  64. // IEnum
  65. STDMETHODIMP CMediaPropStgEnum::Next(ULONG celt, STATPROPSTG *rgelt, ULONG *pceltFetched)
  66. {
  67. ULONG cFetched = 0;
  68. // While there is still room for more in rgelt, and we haven't exhausted our supply...
  69. while ((cFetched < celt) && (_pos < _size))
  70. {
  71. // Don't enumerate VT_EMPTY values.
  72. // e.g. if track# doesn't exist for this storage, don't enum it. (_pbAvailable).
  73. // Also don't enumerate aliased properties (.bEnumerate)
  74. if (_pbAvailable[_pos] && _pprops[_pos]->bEnumerate)
  75. {
  76. ZeroMemory(&rgelt[cFetched], sizeof(STATPROPSTG));
  77. rgelt[cFetched].lpwstrName = NULL;
  78. rgelt[cFetched].propid = _pprops[_pos]->pscid->pid;
  79. rgelt[cFetched].vt = _pprops[_pos]->vt;
  80. cFetched++;
  81. }
  82. _pos++; // increment our position in our internal list of properties.
  83. }
  84. if (pceltFetched)
  85. *pceltFetched = cFetched;
  86. return cFetched == celt ? S_OK : S_FALSE;
  87. }
  88. STDMETHODIMP CMediaPropStgEnum::Skip(ULONG celt)
  89. {
  90. HRESULT hr;
  91. if (_pos + celt > _size)
  92. {
  93. hr = S_FALSE;
  94. _pos = _size;
  95. }
  96. else
  97. {
  98. hr = S_OK;
  99. _pos += celt;
  100. }
  101. return hr;
  102. }
  103. STDMETHODIMP CMediaPropStgEnum::Reset()
  104. {
  105. _pos = 0;
  106. return S_OK;
  107. }
  108. STDMETHODIMP CMediaPropStgEnum::Clone(IEnumSTATPROPSTG **ppenum)
  109. {
  110. HRESULT hr = STG_E_INSUFFICIENTMEMORY;
  111. CMediaPropStgEnum *penum = new CMediaPropStgEnum(_pprops, _size, _pos);
  112. if (penum)
  113. {
  114. hr = penum->Init(_pbAvailable);
  115. if (SUCCEEDED(hr))
  116. {
  117. hr = penum->QueryInterface(IID_PPV_ARG(IEnumSTATPROPSTG, ppenum));
  118. }
  119. penum->Release();
  120. }
  121. return hr;
  122. }
  123. // pbAvailable array must be of size _size.
  124. STDMETHODIMP CMediaPropStgEnum::Init(BOOL *pbAvailable)
  125. {
  126. HRESULT hr = E_FAIL;
  127. if (pbAvailable)
  128. {
  129. _pbAvailable = (BOOL*)CoTaskMemAlloc(sizeof(BOOL) * _size);
  130. if (_pbAvailable)
  131. {
  132. // Copy the values.
  133. CopyMemory(_pbAvailable, pbAvailable, sizeof(BOOL) * _size);
  134. hr = S_OK;
  135. }
  136. else
  137. {
  138. hr = STG_E_INSUFFICIENTMEMORY;
  139. }
  140. }
  141. return hr;
  142. }
  143. CMediaPropStgEnum::CMediaPropStgEnum(const COLMAP **pprops, ULONG cprops, ULONG pos) :
  144. _cRef(1), _pprops(pprops), _size(cprops), _pos(pos)
  145. {
  146. DllAddRef();
  147. }
  148. CMediaPropStgEnum::~CMediaPropStgEnum()
  149. {
  150. if (_pbAvailable)
  151. {
  152. CoTaskMemFree(_pbAvailable);
  153. }
  154. DllRelease();
  155. }
  156. //
  157. // CMediaPropStg methods
  158. //
  159. // IUnknown
  160. STDMETHODIMP CMediaPropStorage::QueryInterface(REFIID riid, void **ppv)
  161. {
  162. static const QITAB qit[] = {
  163. QITABENT(CMediaPropStorage, IPropertyStorage),
  164. QITABENT(CMediaPropStorage, IQueryPropertyFlags),
  165. { 0 },
  166. };
  167. return QISearch(this, qit, riid, ppv);
  168. }
  169. STDMETHODIMP_(ULONG) CMediaPropStorage::AddRef()
  170. {
  171. return InterlockedIncrement(&_cRef);
  172. }
  173. STDMETHODIMP_(ULONG) CMediaPropStorage::Release()
  174. {
  175. if (InterlockedDecrement(&_cRef))
  176. return _cRef;
  177. delete this;
  178. return 0;
  179. }
  180. //IPropertyStorage
  181. STDMETHODIMP CMediaPropStorage::ReadMultiple(ULONG cpspec, const PROPSPEC rgpspec[], PROPVARIANT rgvar[])
  182. {
  183. EnterCriticalSection(_pcs);
  184. const COLMAP *pcmap;
  185. PROPVARIANT *pvar;
  186. LONG celt = 0;
  187. for (ULONG i = 0; i < cpspec; i++)
  188. {
  189. if (SUCCEEDED(LookupProp(&rgpspec[i], &pcmap, &pvar, NULL, NULL, FALSE)) && pvar->vt != VT_EMPTY)
  190. {
  191. celt++;
  192. // This copy can only fail for a bad type in pvar, but the results we get from LookupProp
  193. // should always be valid variants.
  194. PropVariantCopy(&rgvar[i], pvar);
  195. }
  196. else
  197. {
  198. PropVariantInit(&rgvar[i]);
  199. }
  200. }
  201. LeaveCriticalSection(_pcs);
  202. return celt ? S_OK : S_FALSE;
  203. }
  204. /**
  205. * Provide some more suitable errors, so docprop can tell the user what happened.
  206. */
  207. HRESULT _WMToStgWriteErrorCode(HRESULT hrIn)
  208. {
  209. HRESULT hr;
  210. switch (hrIn)
  211. {
  212. case NS_E_FILE_WRITE:
  213. // Probably because of a lock violation.
  214. // Ideally the WMSDK would pass back a more descriptive error.
  215. hr = STG_E_LOCKVIOLATION;
  216. break;
  217. default:
  218. hr = STG_E_WRITEFAULT;
  219. }
  220. return hr;
  221. }
  222. STDMETHODIMP CMediaPropStorage::WriteMultiple(ULONG cpspec, PROPSPEC const rgpspec[], const PROPVARIANT rgvar[], PROPID propidNameFirst)
  223. {
  224. const COLMAP *pcmap;
  225. PROPVARIANT *pvar, *pvarWrite;
  226. BOOL *pbDirty;
  227. ULONG celt = 0;
  228. EnterCriticalSection(_pcs);
  229. // fail if we're readonly
  230. HRESULT hr = STG_E_ACCESSDENIED;
  231. if (_dwMode & (STGM_WRITE | STGM_READWRITE))
  232. {
  233. hr = S_OK;
  234. for (ULONG i = 0; i < cpspec; i++)
  235. {
  236. if (!IsSpecialProperty(&rgpspec[i]) && SUCCEEDED(LookupProp(&rgpspec[i], &pcmap, &pvar, &pvarWrite, &pbDirty, FALSE)))
  237. {
  238. if (SUCCEEDED(PropVariantCopy(pvarWrite, &rgvar[i]))) // Could fail if we're given bad propvariant
  239. {
  240. celt++;
  241. *pbDirty = TRUE;
  242. }
  243. }
  244. }
  245. if (IsDirectMode() && celt)
  246. {
  247. hr = DoCommit(STGC_OVERWRITE, &_ftLastCommit, _pvarProps, _pbDirtyFlags);
  248. if (FAILED(hr))
  249. {
  250. //
  251. _ppsAuthority->CopyPropStorageData(_pvarProps);
  252. for (ULONG i=0; i<_cNumProps; i++)
  253. {
  254. _pbDirtyFlags[i]=FALSE;
  255. }
  256. hr = _WMToStgWriteErrorCode(hr);
  257. }
  258. }
  259. if (SUCCEEDED(hr))
  260. {
  261. hr = (celt == cpspec) ? S_OK : S_FALSE;
  262. }
  263. }
  264. LeaveCriticalSection(_pcs);
  265. return hr;
  266. }
  267. STDMETHODIMP CMediaPropStorage::DeleteMultiple(ULONG cpspec, PROPSPEC const rgpspec[])
  268. {
  269. const COLMAP *pcmap;
  270. PROPVARIANT *pvar, *pvarWrite;
  271. BOOL *pbDirty;
  272. ULONG celt = 0;
  273. EnterCriticalSection(_pcs);
  274. for (ULONG i = 0; i < cpspec; i++)
  275. {
  276. if (!IsSpecialProperty(&rgpspec[i]) && SUCCEEDED(LookupProp(&rgpspec[i], &pcmap, &pvar, &pvarWrite, &pbDirty, FALSE)))
  277. {
  278. celt++;
  279. *pbDirty = TRUE;
  280. PropVariantInit(pvarWrite);
  281. }
  282. }
  283. if (IsDirectMode() && celt)
  284. {
  285. DoCommit(STGC_OVERWRITE, &_ftLastCommit, _pvarProps, _pbDirtyFlags);
  286. }
  287. LeaveCriticalSection(_pcs);
  288. return celt == cpspec? S_OK : S_FALSE;
  289. }
  290. STDMETHODIMP CMediaPropStorage::ReadPropertyNames(ULONG cpspec, PROPID const rgpropid[], LPWSTR rglpwstrName[])
  291. {
  292. ULONG celt = 0;
  293. PROPSPEC spec;
  294. EnterCriticalSection(_pcs);
  295. spec.ulKind = PRSPEC_PROPID;
  296. for (ULONG i = 0; i < cpspec; i++)
  297. {
  298. rglpwstrName[i] = NULL;
  299. spec.propid = rgpropid[i];
  300. PROPVARIANT *pvar;
  301. const COLMAP *pcmap;
  302. if (SUCCEEDED(LookupProp(&spec, &pcmap, &pvar, NULL, NULL, FALSE)))
  303. {
  304. if (pcmap && SUCCEEDED(SHStrDup(pcmap->pszName, &rglpwstrName[i])))
  305. {
  306. celt++;
  307. }
  308. }
  309. }
  310. LeaveCriticalSection(_pcs);
  311. return celt ? S_OK : S_FALSE;
  312. }
  313. STDMETHODIMP CMediaPropStorage::WritePropertyNames(ULONG cpspec, PROPID const rgpropid[], LPWSTR const rglpwstrName[])
  314. {
  315. return E_NOTIMPL;
  316. }
  317. STDMETHODIMP CMediaPropStorage::DeletePropertyNames(ULONG cpspec, PROPID const rgpropid[])
  318. {
  319. return E_NOTIMPL;
  320. }
  321. STDMETHODIMP CMediaPropStorage::SetClass(REFCLSID clsid)
  322. {
  323. return E_NOTIMPL;
  324. }
  325. STDMETHODIMP CMediaPropStorage::Commit(DWORD grfCommitFlags)
  326. {
  327. HRESULT hr = S_OK;
  328. EnterCriticalSection(_pcs);
  329. if (!IsDirectMode())
  330. {
  331. hr = DoCommit(grfCommitFlags, &_ftLastCommit, _pvarChangedProps, _pbDirtyFlags);
  332. if (SUCCEEDED(hr))
  333. {
  334. for (ULONG i = 0;i < _cNumProps; i++)
  335. {
  336. if (_pbDirtyFlags[i])
  337. {
  338. _pbDirtyFlags[i] = FALSE;
  339. PropVariantCopy(&_pvarProps[i], &_pvarChangedProps[i]);
  340. }
  341. }
  342. }
  343. }
  344. LeaveCriticalSection(_pcs);
  345. return hr;
  346. }
  347. STDMETHODIMP CMediaPropStorage::Revert()
  348. {
  349. EnterCriticalSection(_pcs);
  350. if (!IsDirectMode())
  351. {
  352. for (ULONG i = 0; i < _cNumProps; i++)
  353. {
  354. if (_pbDirtyFlags[i])
  355. {
  356. _pbDirtyFlags[i] = FALSE;
  357. // Should never fail, _pvarProps[i] always has valid type
  358. PropVariantCopy(&_pvarChangedProps[i], &_pvarProps[i]);
  359. }
  360. }
  361. }
  362. LeaveCriticalSection(_pcs);
  363. return S_OK;
  364. }
  365. STDMETHODIMP CMediaPropStorage::Enum(IEnumSTATPROPSTG **ppenum)
  366. {
  367. EnterCriticalSection(_pcs);
  368. // Ensure slow properties have been extracted, since we won't know whether to enumerate
  369. // them if their values are still VT_EMPTY;
  370. HRESULT hr = _EnsureSlowPropertiesLoaded();
  371. if (SUCCEEDED(hr))
  372. {
  373. // Make the availability array - if a property value is set to VT_EMPTY, we
  374. // will not enumerate it.
  375. BOOL *pbAvailable = (BOOL*)CoTaskMemAlloc(sizeof(BOOL) * _cNumProps);
  376. if (pbAvailable)
  377. {
  378. for (UINT i = 0; i < _cNumProps; i++)
  379. {
  380. pbAvailable[i] = (_pvarProps[i].vt != VT_EMPTY);
  381. }
  382. CMediaPropStgEnum *penum = new CMediaPropStgEnum(_ppcmPropInfo, _cNumProps, 0);
  383. if (penum)
  384. {
  385. hr = penum->Init(pbAvailable);
  386. if (SUCCEEDED(hr))
  387. {
  388. hr = penum->QueryInterface(IID_PPV_ARG(IEnumSTATPROPSTG, ppenum));
  389. }
  390. penum->Release();
  391. }
  392. CoTaskMemFree(pbAvailable);
  393. }
  394. }
  395. LeaveCriticalSection(_pcs);
  396. return hr;
  397. }
  398. STDMETHODIMP CMediaPropStorage::Stat(STATPROPSETSTG *pstatpropstg)
  399. {
  400. ZeroMemory(pstatpropstg, sizeof(STATPROPSETSTG));
  401. pstatpropstg->fmtid = _fmtid;
  402. return S_OK;
  403. }
  404. STDMETHODIMP CMediaPropStorage::SetTimes(FILETIME const *pctime, FILETIME const *patime, FILETIME const *pmtime)
  405. {
  406. return E_NOTIMPL;
  407. }
  408. // This only returns the SHCOLSTATE_SLOW flag currently.
  409. STDMETHODIMP CMediaPropStorage::GetFlags(const PROPSPEC *pspec, SHCOLSTATEF *pcsFlags)
  410. {
  411. const COLMAP *pPInfo;
  412. PROPVARIANT *pvar;
  413. *pcsFlags = 0;
  414. EnterCriticalSection(_pcs);
  415. HRESULT hr = LookupProp(pspec, &pPInfo, &pvar, NULL, NULL, TRUE); // TRUE -> so it doesn't populate slow props
  416. if (SUCCEEDED(hr) && _IsSlowProperty(pPInfo))
  417. {
  418. *pcsFlags |= SHCOLSTATE_SLOW;
  419. }
  420. LeaveCriticalSection(_pcs);
  421. return hr;
  422. }
  423. CMediaPropStorage::CMediaPropStorage(CMediaPropSetStg *ppssParent, CMediaPropStorage *ppsAuthority, REFFMTID fmtid, const COLMAP **ppcmPropInfo, DWORD cNumProps, DWORD dwMode, CRITICAL_SECTION *pcs) :
  424. _cRef(1), _ppssParent(ppssParent), _ppsAuthority(ppsAuthority), _fmtid(fmtid), _ppcmPropInfo(ppcmPropInfo), _cNumProps(cNumProps), _dwMode(dwMode), _pcs(pcs), _bRetrievedSlowProperties(0)
  425. {
  426. // init our Authority info and column metadata
  427. _authLevel = ppsAuthority ? NON_AUTH : AUTH;
  428. if (ppsAuthority)
  429. _ppsAuthority->AddRef();
  430. ASSERT ((_ppsAuthority && _authLevel== NON_AUTH) || (_ppsAuthority==NULL && _authLevel== AUTH));
  431. SYSTEMTIME stime;
  432. GetSystemTime(&stime);
  433. SystemTimeToFileTime(&stime, &_ftLastCommit);
  434. _state = CLOSED;
  435. PropVariantInit(&_varCodePage);
  436. _varCodePage.vt = VT_I2;
  437. _varCodePage.iVal = (SHORT)CP_WINUNICODE;
  438. // Allocate our Property arrays
  439. _pvarProps = (PROPVARIANT*)CoTaskMemAlloc(sizeof(*_pvarProps) * _cNumProps);
  440. if (_pvarProps)
  441. {
  442. for (ULONG i = 0; i < _cNumProps; i++)
  443. {
  444. PropVariantInit(&_pvarProps[i]);
  445. }
  446. if (_ppsAuthority)
  447. _ppsAuthority->CopyPropStorageData(_pvarProps);
  448. if (IsDirectMode())
  449. {
  450. _pvarChangedProps = NULL;
  451. }
  452. else
  453. {
  454. _pvarChangedProps = (PROPVARIANT*)CoTaskMemAlloc(sizeof(*_pvarChangedProps) * _cNumProps);
  455. if (_pvarChangedProps)
  456. {
  457. for (ULONG i = 0; i < _cNumProps; i++)
  458. {
  459. PropVariantInit(&_pvarChangedProps[i]);
  460. }
  461. }
  462. }
  463. _pbDirtyFlags = (BOOL*)CoTaskMemAlloc(sizeof(*_pbDirtyFlags) * _cNumProps);
  464. if (_pbDirtyFlags)
  465. ZeroMemory(_pbDirtyFlags, sizeof(*_pbDirtyFlags) * _cNumProps);
  466. }
  467. DllAddRef();
  468. }
  469. CMediaPropStorage::~CMediaPropStorage()
  470. {
  471. ASSERT(_state==CLOSED);
  472. if (_authLevel == NON_AUTH)
  473. {
  474. ASSERT(_ppsAuthority);
  475. _ppsAuthority->OnClose();
  476. _ppsAuthority->Release();
  477. }
  478. for (ULONG i = 0; i < _cNumProps; i++)
  479. {
  480. PropVariantClear(&_pvarProps[i]);
  481. }
  482. CoTaskMemFree(_pvarProps);
  483. if (_pvarChangedProps)
  484. {
  485. for (ULONG i = 0; i < _cNumProps; i++)
  486. {
  487. PropVariantClear(&_pvarChangedProps[i]);
  488. }
  489. CoTaskMemFree(_pvarChangedProps);
  490. }
  491. CoTaskMemFree(_pbDirtyFlags);
  492. PropVariantClear(&_varCodePage);
  493. DllRelease();
  494. }
  495. HRESULT CMediaPropStorage::Open(DWORD dwShareMode, DWORD dwOpenMode, IPropertyStorage **ppPropStg)
  496. {
  497. // require STGM_SHARE_EXCLUSIVE
  498. if (!(dwShareMode & STGM_SHARE_EXCLUSIVE))
  499. return E_FAIL;
  500. HRESULT hr;
  501. CMediaPropStorage *pps = new CMediaPropStorage(NULL, this, _fmtid, _ppcmPropInfo, _cNumProps, dwOpenMode, _pcs);
  502. if (pps)
  503. {
  504. hr = pps->QueryInterface(IID_PPV_ARG(IPropertyStorage, ppPropStg));
  505. pps->Release();
  506. if (SUCCEEDED(hr))
  507. _state = OPENED_DENYALL;
  508. }
  509. else
  510. hr = STG_E_INSUFFICIENTMEMORY;
  511. return hr;
  512. }
  513. HRESULT CMediaPropStorage::_EnsureSlowPropertiesLoaded()
  514. {
  515. if (!_bRetrievedSlowProperties)
  516. {
  517. HRESULT hr;
  518. _bRetrievedSlowProperties = TRUE;
  519. if (_authLevel == NON_AUTH)
  520. {
  521. ASSERT(_ppsAuthority);
  522. hr = _ppsAuthority->_EnsureSlowPropertiesLoaded();
  523. if (SUCCEEDED(hr))
  524. {
  525. // We have some new values... recopy them.
  526. hr = _ppsAuthority->CopyPropStorageData(_pvarProps);
  527. }
  528. }
  529. else
  530. {
  531. hr = _ppssParent->_PopulateSlowProperties();
  532. }
  533. _hrSlowProps = hr;
  534. }
  535. return _hrSlowProps;
  536. }
  537. BOOL CMediaPropStorage::_IsSlowProperty(const COLMAP *pPInfo)
  538. {
  539. if (_authLevel == NON_AUTH)
  540. {
  541. ASSERT(_ppsAuthority);
  542. return (_ppsAuthority->_IsSlowProperty(pPInfo));
  543. }
  544. else
  545. {
  546. return (_ppssParent->_IsSlowProperty(pPInfo));
  547. }
  548. }
  549. HRESULT CMediaPropStorage::CopyPropStorageData(PROPVARIANT *pvarProps)
  550. {
  551. ASSERT(_authLevel == AUTH);
  552. for (ULONG i = 0; i < _cNumProps; i++)
  553. {
  554. // Check for VT_EMPTY, because this may be the second time
  555. // we're copying properties (because of slow properties)
  556. if (pvarProps[i].vt == VT_EMPTY)
  557. PropVariantCopy(&pvarProps[i], &_pvarProps[i]);
  558. }
  559. return S_OK;
  560. }
  561. void CMediaPropStorage::OnClose()
  562. {
  563. ASSERT(_authLevel == AUTH);
  564. _state = CLOSED;
  565. }
  566. void CMediaPropStorage::_ResetPropStorage()
  567. {
  568. for (ULONG i = 0; i < _cNumProps; i++)
  569. {
  570. PropVariantClear(&_pvarProps[i]);
  571. }
  572. }
  573. HRESULT CMediaPropStorage::SetProperty(PROPSPEC *ppspec, PROPVARIANT *pvar)
  574. {
  575. PROPVARIANT *pvarRead, *pvarWrite;
  576. const COLMAP *pcmap;
  577. if (SUCCEEDED(LookupProp(ppspec, &pcmap, &pvarRead, &pvarWrite, NULL, TRUE)) && pvarWrite)
  578. {
  579. return PropVariantCopy(pvarRead, pvar);//We can write to this pointer because we're populating the store with initial data
  580. }
  581. return E_FAIL;
  582. }
  583. /**
  584. * Provides a peek to the current value requested. Does _not_ triggered a call to PopulateSlowProperties
  585. * if the property is slow and hasn't been populated.
  586. */
  587. HRESULT CMediaPropStorage::QuickLookup(PROPSPEC *pspec, PROPVARIANT **ppvar)
  588. {
  589. const COLMAP *pcmap;
  590. return LookupProp(pspec, &pcmap, ppvar, NULL, NULL, TRUE);
  591. }
  592. //
  593. // On Success, returns a pointer to the COLMAP struct for this prop and a pointer to the propvariant
  594. // holding the data.
  595. //
  596. // handles special proids and knows about STGM_DIRECT (if in direct mode readdata == writedata)
  597. //
  598. // ppvarWriteData and ppbDirty are optional and may be NULL
  599. // If pspec refers to a special property, then pvarWriteData and ppbDirty are set to null (if they are supplied)
  600. //
  601. HRESULT CMediaPropStorage::LookupProp(const PROPSPEC *pspec, const COLMAP **ppcmName, PROPVARIANT **ppvarReadData, PROPVARIANT **ppvarWriteData, BOOL **ppbDirty, BOOL bPropertySet)
  602. {
  603. if (IsSpecialProperty(pspec))
  604. {
  605. *ppvarReadData = NULL;
  606. switch (pspec->propid)
  607. {
  608. case 0:
  609. return E_FAIL;//we don't support a dictionary
  610. case 1:
  611. //return the codepage property
  612. *ppvarReadData = &_varCodePage;
  613. *ppcmName = NULL;
  614. if (ppvarWriteData)
  615. *ppvarWriteData = NULL;
  616. if (ppbDirty)
  617. *ppbDirty = NULL;
  618. return S_OK;
  619. default:
  620. return E_NOTIMPL;
  621. }
  622. }
  623. ULONG iPos = -1;
  624. switch (pspec->ulKind)
  625. {
  626. case PRSPEC_LPWSTR:
  627. for (ULONG i = 0; i < _cNumProps; i++)
  628. {
  629. if (StrCmpW(_ppcmPropInfo[i]->pszName, pspec->lpwstr) == 0)
  630. {
  631. iPos = i;
  632. break;
  633. }
  634. }
  635. break;
  636. case PRSPEC_PROPID:
  637. for (i = 0; i < _cNumProps; i++)
  638. {
  639. if (_ppcmPropInfo[i]->pscid->pid == pspec->propid)
  640. {
  641. iPos = i;
  642. break;
  643. }
  644. }
  645. break;
  646. default:
  647. return E_UNEXPECTED;
  648. }
  649. if (iPos == -1)
  650. return STG_E_INVALIDPARAMETER;// Not found
  651. *ppcmName = _ppcmPropInfo[iPos];
  652. HRESULT hr = S_OK;
  653. // We're checking several things here, before asking to load slow properties:
  654. // 1) We need to make sure we're not setting a value in our internal list of props - or else we could get in a loop
  655. // 2) We need to check that slow properties have not yet been retrieved
  656. // 3) We need to check that the property asked for is slow
  657. // 4) We need to check that its current value is VT_EMPTY, since it could have been populated with the fast properties
  658. if (!bPropertySet && !_bRetrievedSlowProperties && _IsSlowProperty(*ppcmName) && (_pvarProps[iPos].vt == VT_EMPTY) )
  659. {
  660. hr = _EnsureSlowPropertiesLoaded();
  661. }
  662. if (SUCCEEDED(hr))
  663. {
  664. if (IsDirectMode())
  665. {
  666. *ppvarReadData = &_pvarProps[iPos];
  667. if (ppvarWriteData)
  668. *ppvarWriteData = *ppvarReadData;
  669. }
  670. else if (_pbDirtyFlags[iPos])
  671. {
  672. *ppvarReadData = &_pvarChangedProps[iPos];
  673. if (ppvarWriteData)
  674. *ppvarWriteData = &_pvarChangedProps[iPos];
  675. }
  676. else
  677. {
  678. *ppvarReadData = &_pvarProps[iPos];
  679. if (ppvarWriteData)
  680. *ppvarWriteData = &_pvarChangedProps[iPos];
  681. }
  682. if (ppbDirty)
  683. {
  684. *ppbDirty = &_pbDirtyFlags[iPos];
  685. }
  686. }
  687. return hr;
  688. }
  689. //flushes changes made to the actual Music file. Works in both transacted mode and direct mode
  690. HRESULT CMediaPropStorage::DoCommit(DWORD grfCommitFlags, FILETIME *ftLastCommit, PROPVARIANT *pVarProps, BOOL *pbDirtyFlags)
  691. {
  692. if (_authLevel == NON_AUTH)
  693. return _ppsAuthority->DoCommit(grfCommitFlags, ftLastCommit, pVarProps, pbDirtyFlags);
  694. //Flush out changes
  695. switch (grfCommitFlags)
  696. {
  697. case STGC_DEFAULT:
  698. case STGC_OVERWRITE:
  699. break;
  700. case STGC_ONLYIFCURRENT:
  701. if (CompareFileTime(&_ftLastCommit, ftLastCommit) ==1)
  702. return STG_E_NOTCURRENT;
  703. break;
  704. default:
  705. return STG_E_INVALIDPARAMETER;
  706. }
  707. HRESULT hr = _ppssParent->FlushChanges(_fmtid, _cNumProps, _ppcmPropInfo, pVarProps, pbDirtyFlags);
  708. if (SUCCEEDED(hr))
  709. {
  710. if (!IsDirectMode())
  711. {
  712. for (ULONG i = 0; i < _cNumProps; i++)
  713. {
  714. PropVariantCopy(&_pvarProps[i], &pVarProps[i]);
  715. }
  716. }
  717. _ftLastCommit = *ftLastCommit;
  718. }
  719. return hr;
  720. }
  721. BOOL CMediaPropStorage::IsDirectMode()
  722. {
  723. // Backwards logic because STGM_DIRECT == 0x0
  724. return (STGM_TRANSACTED & _dwMode) ? FALSE : TRUE;
  725. }
  726. BOOL CMediaPropStorage::IsSpecialProperty(const PROPSPEC *pspec)
  727. {
  728. return (pspec->propid < 2 || pspec->propid > 0x7fffffff) ? TRUE : FALSE;
  729. }