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

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