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.

1027 lines
31 KiB

  1. /*
  2. * propstg.c - Property storage ADT
  3. */
  4. #include "priv.h"
  5. #include "propstg.h"
  6. #ifndef UNIX
  7. // SafeGetItemObject
  8. //
  9. // Since the GetItemObject member of IShellView was added late in the game
  10. // during Win95 development we have found at least one example (rnaui.dll)
  11. // of an application that built an IShellView with a NULL member for
  12. // GetItemObject. Fearing more applications that may have the same
  13. // problem, this wrapper function was added to catch bad apps like rnaui.
  14. // Thus, we check here for NULL before calling the member.
  15. //
  16. STDAPI SafeGetItemObject(LPSHELLVIEW psv, UINT uItem, REFIID riid, LPVOID *ppv)
  17. {
  18. #ifdef __cplusplus
  19. #error THIS_MUST_STAY_C
  20. // read the comment above
  21. #endif
  22. if (!psv->lpVtbl->GetItemObject)
  23. return E_FAIL;
  24. return (HRESULT)(psv->lpVtbl->GetItemObject(psv, uItem, riid, ppv));
  25. }
  26. #endif
  27. // This structure is a dictionary element. It maps a name to a propid.
  28. typedef struct
  29. {
  30. PROPID propid;
  31. WCHAR wszName[MAX_PATH];
  32. } DICTEL, * PDICTEL;
  33. // This structure is a propvariant element.
  34. typedef struct
  35. {
  36. PROPVARIANT propvar;
  37. DWORD dwFlags; // PEF_*
  38. } PROPEL, * PPROPEL;
  39. // Flags for PROPEL structure
  40. #define PEF_VALID 0x00000001
  41. #define PEF_DIRTY 0x00000002
  42. // This structure is the ADT for property storage
  43. typedef struct
  44. {
  45. DWORD cbSize;
  46. DWORD dwFlags;
  47. HDSA hdsaProps; // array of properties (indexed by propid)
  48. HDPA hdpaDict; // dictionary of names mapped to propid
  49. // (each element is a DICTEL)
  50. int idsaLastValid;
  51. } PROPSTG, * PPROPSTG;
  52. // The first two entries in hdsaProps are reserved. When we enumerate
  53. // thru the list, we skip these entries.
  54. #define PROPID_DICT 0
  55. #define PROPID_CODEPAGE 1
  56. #define IDSA_START 2
  57. #define CDSA_RESERVED 2
  58. BOOL IsValidHPROPSTG(HPROPSTG hstg)
  59. {
  60. PPROPSTG pstg = (PPROPSTG)hstg;
  61. return (IS_VALID_WRITE_PTR(pstg, PROPSTG) &&
  62. SIZEOF(*pstg) == pstg->cbSize &&
  63. NULL != pstg->hdsaProps &&
  64. NULL != pstg->hdpaDict);
  65. }
  66. #ifdef DEBUG
  67. BOOL IsValidPPROPSPEC(PROPSPEC * ppropspec)
  68. {
  69. return (ppropspec &&
  70. PRSPEC_PROPID == ppropspec->ulKind ||
  71. (PRSPEC_LPWSTR == ppropspec->ulKind &&
  72. IS_VALID_STRING_PTRW(ppropspec->DUMMYUNION_MEMBER(lpwstr), -1)));
  73. }
  74. #endif
  75. STDAPI PropStg_Create(OUT HPROPSTG * phstg, IN DWORD dwFlags)
  76. {
  77. HRESULT hres = STG_E_INVALIDPARAMETER;
  78. if (EVAL(IS_VALID_WRITE_PTR(phstg, HPROPSTG)))
  79. {
  80. PPROPSTG pstg = (PPROPSTG)LocalAlloc(LPTR, SIZEOF(*pstg));
  81. hres = STG_E_INSUFFICIENTMEMORY; // assume error
  82. if (pstg)
  83. {
  84. pstg->cbSize = SIZEOF(*pstg);
  85. pstg->dwFlags = dwFlags;
  86. pstg->idsaLastValid = PROPID_CODEPAGE;
  87. pstg->hdsaProps = DSA_Create(SIZEOF(PROPEL), 8);
  88. pstg->hdpaDict = DPA_Create(8);
  89. if (pstg->hdsaProps && pstg->hdpaDict)
  90. {
  91. // The first two propids are reserved, so insert
  92. // placeholders.
  93. PROPEL propel;
  94. propel.propvar.vt = VT_EMPTY;
  95. propel.dwFlags = 0;
  96. DSA_SetItem(pstg->hdsaProps, PROPID_DICT, &propel);
  97. DSA_SetItem(pstg->hdsaProps, PROPID_CODEPAGE, &propel);
  98. hres = S_OK;
  99. }
  100. else
  101. {
  102. // Clean up because something failed
  103. if (pstg->hdsaProps)
  104. {
  105. DSA_Destroy(pstg->hdsaProps);
  106. pstg->hdsaProps = NULL;
  107. }
  108. if (pstg->hdpaDict)
  109. {
  110. DPA_Destroy(pstg->hdpaDict);
  111. pstg->hdpaDict = NULL;
  112. }
  113. LocalFree(pstg);
  114. pstg = NULL;
  115. }
  116. }
  117. *phstg = (HPROPSTG)pstg;
  118. // Validate return values
  119. ASSERT((SUCCEEDED(hres) &&
  120. IS_VALID_WRITE_PTR(*phstg, PPROPSTG)) ||
  121. (FAILED(hres) && NULL == *phstg));
  122. }
  123. return hres;
  124. }
  125. STDAPI PropStg_Destroy(HPROPSTG hstg)
  126. {
  127. HRESULT hres = STG_E_INVALIDPARAMETER;
  128. if (EVAL(IS_VALID_HANDLE(hstg, PROPSTG)))
  129. {
  130. PPROPSTG pstg = (PPROPSTG)hstg;
  131. if (pstg->hdsaProps)
  132. {
  133. int cdsa = DSA_GetItemCount(pstg->hdsaProps) - CDSA_RESERVED;
  134. // The first two elements are not cleared, because they
  135. // are just place-holders.
  136. if (0 < cdsa)
  137. {
  138. PPROPEL ppropel = DSA_GetItemPtr(pstg->hdsaProps, IDSA_START);
  139. ASSERT(ppropel);
  140. while (0 < cdsa--)
  141. {
  142. PropVariantClear(&ppropel->propvar);
  143. ppropel++;
  144. }
  145. }
  146. DSA_Destroy(pstg->hdsaProps);
  147. pstg->hdsaProps = NULL;
  148. }
  149. if (pstg->hdpaDict)
  150. {
  151. int i, cel = DPA_GetPtrCount(pstg->hdpaDict);
  152. for (i = 0; i < cel; i++)
  153. {
  154. LocalFree(DPA_FastGetPtr(pstg->hdpaDict, i));
  155. }
  156. DPA_Destroy(pstg->hdpaDict);
  157. pstg->hdpaDict = NULL;
  158. }
  159. LocalFree(pstg);
  160. pstg = NULL;
  161. hres = S_OK;
  162. }
  163. return hres;
  164. }
  165. /*----------------------------------------------------------
  166. Purpose: Compare names
  167. Returns: standard -1, 0, 1
  168. Cond: --
  169. */
  170. int CALLBACK PropStg_Compare(IN LPVOID pv1, IN LPVOID pv2, IN LPARAM lParam)
  171. {
  172. LPCWSTR psz1 = pv1;
  173. LPCWSTR psz2 = pv2;
  174. // Case insensitive
  175. return StrCmpW(psz1, psz2);
  176. }
  177. /*----------------------------------------------------------
  178. Purpose: Returns TRUE if the property exists in this storage.
  179. If it does exist, the propid is returned.
  180. Returns: TRUE
  181. FALSE
  182. Cond: --
  183. */
  184. BOOL PropStg_PropertyExists(IN PPROPSTG pstg,
  185. IN const PROPSPEC * ppropspec,
  186. OUT PROPID * ppropid)
  187. {
  188. BOOL bRet;
  189. PPROPEL ppropel;
  190. HDSA hdsaProps;
  191. ASSERT(pstg);
  192. ASSERT(ppropspec);
  193. ASSERT(ppropid);
  194. hdsaProps = pstg->hdsaProps;
  195. switch (ppropspec->ulKind)
  196. {
  197. case PRSPEC_PROPID:
  198. *ppropid = ppropspec->DUMMYUNION_MEMBER(propid);
  199. bRet = (*ppropid < (PROPID)DSA_GetItemCount(hdsaProps));
  200. if (bRet)
  201. {
  202. ppropel = DSA_GetItemPtr(hdsaProps, *ppropid);
  203. bRet = (ppropel && IsFlagSet(ppropel->dwFlags, PEF_VALID));
  204. }
  205. break;
  206. case PRSPEC_LPWSTR:
  207. // Key off whether the name exists
  208. *ppropid = DPA_Search(pstg->hdpaDict, ppropspec->DUMMYUNION_MEMBER(lpwstr), 0, PropStg_Compare, 0, DPAS_SORTED);
  209. #ifdef DEBUG
  210. // Sanity check that the property actually exists
  211. ppropel = DSA_GetItemPtr(hdsaProps, *ppropid);
  212. ASSERT(-1 == *ppropid ||
  213. (ppropel && IsFlagSet(ppropel->dwFlags, PEF_VALID)));
  214. #endif
  215. bRet = (-1 != *ppropid);
  216. break;
  217. default:
  218. bRet = FALSE;
  219. break;
  220. }
  221. // Propid values 0 and 1 are reserved, as are values >= 0x80000000
  222. if (bRet && (0 == *ppropid || 1 == *ppropid || 0x80000000 <= *ppropid))
  223. bRet = FALSE;
  224. return bRet;
  225. }
  226. /*----------------------------------------------------------
  227. Purpose: Create a new propid and assign the given name to
  228. it.
  229. The propid is an index into hdsaProps.
  230. Returns: S_OK
  231. STG_E_INSUFFICIENTMEMORY
  232. Cond: --
  233. */
  234. HRESULT PropStg_NewPropid(IN PPROPSTG pstg,
  235. IN LPCWSTR pwsz,
  236. IN PROPID propidFirst,
  237. OUT PROPID * ppropid) OPTIONAL
  238. {
  239. HRESULT hres = STG_E_INVALIDPOINTER; // assume error
  240. DICTEL * pdictel;
  241. PROPID propid = (PROPID)-1;
  242. HDPA hdpa;
  243. ASSERT(pstg);
  244. ASSERT(ppropid);
  245. if (EVAL(IS_VALID_STRING_PTRW(pwsz, -1)))
  246. {
  247. hres = STG_E_INSUFFICIENTMEMORY; // assume error
  248. hdpa = pstg->hdpaDict;
  249. // The name shouldn't be in the list yet
  250. ASSERT(-1 == DPA_Search(hdpa, (LPVOID)pwsz, 0, PropStg_Compare, 0, DPAS_SORTED));
  251. pdictel = LocalAlloc(LPTR, SIZEOF(*pdictel));
  252. if (pdictel)
  253. {
  254. // Determine the propid for this
  255. PROPID propidNew = max(propidFirst, (PROPID)pstg->idsaLastValid + 1);
  256. pdictel->propid = propidNew;
  257. StrCpyNW(pdictel->wszName, pwsz, ARRAYSIZE(pdictel->wszName));
  258. if (-1 != DPA_AppendPtr(hdpa, pdictel))
  259. {
  260. // Sort it by name
  261. DPA_Sort(hdpa, PropStg_Compare, 0);
  262. hres = S_OK;
  263. propid = propidNew;
  264. }
  265. }
  266. }
  267. *ppropid = propid;
  268. return hres;
  269. }
  270. /*----------------------------------------------------------
  271. Purpose: Read a set of properties given their propids. If the propid
  272. doesn't exist in this property storage, then set the
  273. value type to VT_EMPTY but return success; unless
  274. all the properties in rgpropspec don't exist, in which
  275. case also return S_FALSE.
  276. Returns: S_OK
  277. S_FALSE
  278. STG_E_INVALIDPARAMETER
  279. STG_E_INSUFFICIENTMEMORY
  280. Cond: --
  281. */
  282. STDAPI PropStg_ReadMultiple(IN HPROPSTG hstg,
  283. IN ULONG cpspec,
  284. IN const PROPSPEC * rgpropspec,
  285. IN PROPVARIANT * rgpropvar)
  286. {
  287. HRESULT hres = STG_E_INVALIDPARAMETER;
  288. if (EVAL(IS_VALID_HANDLE(hstg, PROPSTG)) &&
  289. EVAL(IS_VALID_READ_BUFFER(rgpropspec, PROPSPEC, cpspec)) &&
  290. EVAL(IS_VALID_READ_BUFFER(rgpropvar, PROPVARIANT, cpspec)))
  291. {
  292. PPROPSTG pstg = (PPROPSTG)hstg;
  293. ULONG cpspecSav = cpspec;
  294. const PROPSPEC * ppropspec = rgpropspec;
  295. PROPVARIANT * ppropvar = rgpropvar;
  296. int idsa;
  297. BOOL bPropertyExists;
  298. ULONG cpspecIllegal = 0;
  299. hres = S_OK; // assume success
  300. if (0 < cpspec)
  301. {
  302. // Read the list of property specs
  303. while (0 < cpspec--)
  304. {
  305. bPropertyExists = PropStg_PropertyExists(pstg, ppropspec, (LPDWORD)&idsa);
  306. // Does this property exist?
  307. if ( !bPropertyExists )
  308. {
  309. // No
  310. ppropvar->vt = VT_ILLEGAL;
  311. cpspecIllegal++;
  312. }
  313. else
  314. {
  315. // Yes; is the element a valid property?
  316. PPROPEL ppropel = DSA_GetItemPtr(pstg->hdsaProps, idsa);
  317. ASSERT(ppropel);
  318. if (IsFlagSet(ppropel->dwFlags, PEF_VALID))
  319. {
  320. // Yes; copy the variant value
  321. hres = PropVariantCopy(ppropvar, &ppropel->propvar);
  322. }
  323. else
  324. {
  325. // No
  326. ppropvar->vt = VT_ILLEGAL;
  327. cpspecIllegal++;
  328. }
  329. }
  330. ppropspec++;
  331. ppropvar++;
  332. // Bail out of loop if something failed
  333. if (FAILED(hres))
  334. break;
  335. }
  336. // Are all the property specs illegal?
  337. if (cpspecIllegal == cpspecSav)
  338. {
  339. hres = S_FALSE; // yes
  340. }
  341. // Did anything fail above?
  342. if (FAILED(hres))
  343. {
  344. // Yes; clean up -- no properties will be retrieved
  345. FreePropVariantArray(cpspecSav, rgpropvar);
  346. }
  347. }
  348. }
  349. return hres;
  350. }
  351. /*----------------------------------------------------------
  352. Purpose: Add a set of property values given their propids.
  353. If the propid doesn't exist in this property storage,
  354. then add the propid as a legal ID and set the value.
  355. On error, some properties may or may not have been
  356. written.
  357. If pfn is non-NULL, this callback will get called
  358. to optionally "massage" the propvariant value or to
  359. validate it. The rules for the callback are:
  360. 1) It can change the value directly if it is not
  361. allocated
  362. 2) If the value is allocated, the callback must
  363. replace the pointer with a newly allocated
  364. buffer that it allocates. It must not try
  365. to free the value coming in, since it doesn't
  366. know how it was allocated. It must also use
  367. CoTaskMemAlloc to allocate its buffer.
  368. 3) If the callback returns an error, this function
  369. will stop writing properties and return that
  370. error.
  371. 4) If the callback returns S_FALSE, this function
  372. will not write that particular property and
  373. continue on to the next property. The function
  374. then returns S_FALSE once it is finished.
  375. Returns: S_OK
  376. S_FALSE
  377. STG_E_INVALIDPARAMETER
  378. STG_E_INSUFFICIENTMEMORY
  379. Cond: --
  380. */
  381. STDAPI PropStg_WriteMultipleEx(IN HPROPSTG hstg,
  382. IN ULONG cpspec,
  383. IN const PROPSPEC * rgpropspec,
  384. IN const PROPVARIANT * rgpropvar,
  385. IN PROPID propidFirst, OPTIONAL
  386. IN PFNPROPVARMASSAGE pfn, OPTIONAL
  387. IN LPARAM lParam) OPTIONAL
  388. {
  389. HRESULT hres = STG_E_INVALIDPARAMETER;
  390. if (EVAL(IS_VALID_HANDLE(hstg, PROPSTG)) &&
  391. EVAL(IS_VALID_READ_BUFFER(rgpropspec, PROPSPEC, cpspec)) &&
  392. EVAL(IS_VALID_READ_BUFFER(rgpropvar, PROPVARIANT, cpspec)))
  393. {
  394. PPROPSTG pstg = (PPROPSTG)hstg;
  395. const PROPSPEC * ppropspec = rgpropspec;
  396. const PROPVARIANT * ppropvar = rgpropvar;
  397. int idsa;
  398. PROPEL propel;
  399. BOOL bPropertyExists;
  400. BOOL bSkippedProperty = FALSE;
  401. if (0 == cpspec)
  402. {
  403. hres = S_OK;
  404. }
  405. else
  406. {
  407. // Write the list of property specs
  408. while (0 < cpspec--)
  409. {
  410. bPropertyExists = PropStg_PropertyExists(pstg, ppropspec, (LPDWORD)&idsa);
  411. hres = S_OK;
  412. // If this is an illegal variant type and yet a valid
  413. // property, then return an error. Otherwise, ignore it
  414. // and move on.
  415. if (VT_ILLEGAL == ppropvar->vt)
  416. {
  417. if (bPropertyExists)
  418. hres = STG_E_INVALIDPARAMETER;
  419. else
  420. goto NextDude;
  421. }
  422. if (SUCCEEDED(hres))
  423. {
  424. // Add the property. If it doesn't exist, add it.
  425. // Is this a propid or a name?
  426. switch (ppropspec->ulKind)
  427. {
  428. case PRSPEC_PROPID:
  429. idsa = ppropspec->DUMMYUNION_MEMBER(propid);
  430. break;
  431. case PRSPEC_LPWSTR:
  432. if ( !bPropertyExists )
  433. {
  434. hres = PropStg_NewPropid(pstg, ppropspec->DUMMYUNION_MEMBER(lpwstr),
  435. propidFirst, (LPDWORD)&idsa);
  436. }
  437. break;
  438. default:
  439. hres = STG_E_INVALIDNAME;
  440. break;
  441. }
  442. if (SUCCEEDED(hres))
  443. {
  444. PROPVARIANT propvarT;
  445. ASSERT(S_OK == hres); // we're assuming this on entry
  446. // Save a copy of the original in case the
  447. // callback changes it.
  448. CopyMemory(&propvarT, ppropvar, SIZEOF(propvarT));
  449. // How did the callback like it?
  450. if (pfn)
  451. hres = pfn(idsa, ppropvar, lParam);
  452. if (S_OK == hres)
  453. {
  454. // Fine; make a copy of the (possibly changed)
  455. // propvariant value
  456. hres = PropVariantCopy(&propel.propvar, ppropvar);
  457. if (SUCCEEDED(hres))
  458. {
  459. propel.dwFlags = PEF_VALID | PEF_DIRTY;
  460. hres = (DSA_SetItem(pstg->hdsaProps, idsa, &propel) ? S_OK : STG_E_INSUFFICIENTMEMORY);
  461. if (SUCCEEDED(hres) && idsa > pstg->idsaLastValid)
  462. {
  463. pstg->idsaLastValid = idsa;
  464. }
  465. }
  466. }
  467. else if (S_FALSE == hres)
  468. {
  469. bSkippedProperty = TRUE;
  470. }
  471. // Restore the propvariant value to its original
  472. // value. But first, did the callback allocate a
  473. // new buffer?
  474. if (propvarT.DUMMYUNION_MEMBER(pszVal) != ppropvar->DUMMYUNION_MEMBER(pszVal))
  475. {
  476. // Yes; clear it (this function is safe for
  477. // non-allocated values too).
  478. PropVariantClear((PROPVARIANT *)ppropvar);
  479. }
  480. // Restore
  481. CopyMemory((PROPVARIANT *)ppropvar, &propvarT, SIZEOF(*ppropvar));
  482. hres = S_OK;
  483. }
  484. }
  485. NextDude:
  486. ppropspec++;
  487. ppropvar++;
  488. // Bail out of loop if something failed
  489. if (FAILED(hres))
  490. break;
  491. }
  492. if (bSkippedProperty)
  493. hres = S_FALSE;
  494. }
  495. }
  496. return hres;
  497. }
  498. /*
  499. Purpose: Add a set of property values given their propids.
  500. If the propid doesn't exist in this property storage,
  501. then add the propid as a legal ID and set the value.
  502. On error, some properties may or may not have been
  503. written.
  504. Returns: S_OK
  505. STG_E_INVALIDPARAMETER
  506. STG_E_INSUFFICIENTMEMORY
  507. */
  508. STDAPI PropStg_WriteMultiple(IN HPROPSTG hstg,
  509. IN ULONG cpspec,
  510. IN const PROPSPEC * rgpropspec,
  511. IN const PROPVARIANT * rgpropvar,
  512. IN PROPID propidFirst) OPTIONAL
  513. {
  514. return PropStg_WriteMultipleEx(hstg, cpspec, rgpropspec, rgpropvar,
  515. propidFirst, NULL, 0);
  516. }
  517. STDAPI PropStg_DeleteMultiple(IN HPROPSTG hstg,
  518. IN ULONG cpspec,
  519. IN const PROPSPEC * rgpropspec)
  520. {
  521. HRESULT hres = STG_E_INVALIDPARAMETER;
  522. if (EVAL(IS_VALID_HANDLE(hstg, PROPSTG)) &&
  523. EVAL(IS_VALID_READ_BUFFER(rgpropspec, PROPSPEC, cpspec)))
  524. {
  525. PPROPSTG pstg = (PPROPSTG)hstg;
  526. const PROPSPEC * ppropspec = rgpropspec;
  527. HDSA hdsaProps = pstg->hdsaProps;
  528. PPROPEL ppropel;
  529. int idsa;
  530. int cdsa;
  531. hres = S_OK;
  532. if (0 < cpspec)
  533. {
  534. BOOL bDeletedLastValid = FALSE;
  535. // Delete the list of property specs
  536. while (0 < cpspec--)
  537. {
  538. if (PropStg_PropertyExists(pstg, ppropspec, (LPDWORD)&idsa))
  539. {
  540. // Delete the property. Zero out the existing
  541. // propel. Don't call DSA_DeleteItem, otherwise
  542. // we'll move the positions of any remaining
  543. // properties following this one, thus changing their
  544. // propids.
  545. ppropel = DSA_GetItemPtr(hdsaProps, idsa);
  546. ASSERT(ppropel);
  547. PropVariantClear(&ppropel->propvar);
  548. ppropel->dwFlags = 0;
  549. // Our idsaLastValid is messed up if we hit this
  550. // assert
  551. ASSERT(idsa <= pstg->idsaLastValid);
  552. if (idsa == pstg->idsaLastValid)
  553. bDeletedLastValid = TRUE;
  554. // Delete the names associated with the property
  555. // FEATURE (scotth): implement this
  556. }
  557. ppropspec++;
  558. }
  559. // Did we delete the property that was marked as the terminating
  560. // valid property in the list?
  561. if (bDeletedLastValid)
  562. {
  563. // Yes; go back and search for the new terminating index
  564. ppropel = DSA_GetItemPtr(hdsaProps, pstg->idsaLastValid);
  565. cdsa = pstg->idsaLastValid + 1 - CDSA_RESERVED;
  566. ASSERT(ppropel);
  567. while (0 < cdsa--)
  568. {
  569. if (IsFlagSet(ppropel->dwFlags, PEF_VALID))
  570. {
  571. pstg->idsaLastValid = cdsa - 1;
  572. break;
  573. }
  574. ppropel--;
  575. }
  576. if (0 == cdsa)
  577. pstg->idsaLastValid = PROPID_CODEPAGE;
  578. }
  579. // Since we didn't delete any items from hdsaProps (we freed
  580. // the variant value and zeroed it out), this structure
  581. // may have a bunch of unused elements at the end.
  582. // Compact now if necessary.
  583. // Do we have a bunch of trailing, empty elements?
  584. cdsa = DSA_GetItemCount(hdsaProps);
  585. if (cdsa > pstg->idsaLastValid + 1)
  586. {
  587. // Yes; compact. Start from the end and go backwards
  588. // so DSA_DeleteItem doesn't have to move memory blocks.
  589. for (idsa = cdsa-1; idsa > pstg->idsaLastValid; idsa--)
  590. {
  591. #ifdef DEBUG
  592. ppropel = DSA_GetItemPtr(hdsaProps, idsa);
  593. ASSERT(IsFlagClear(ppropel->dwFlags, PEF_VALID));
  594. #endif
  595. DSA_DeleteItem(hdsaProps, idsa);
  596. }
  597. }
  598. }
  599. }
  600. return hres;
  601. }
  602. /*----------------------------------------------------------
  603. Purpose: Marks the specified properties dirty or undirty, depending
  604. on the value of bDirty.
  605. Returns: S_OK
  606. STG_E_INVALIDPARAMETER
  607. STG_E_INSUFFICIENTMEMORY
  608. Cond: --
  609. */
  610. STDAPI PropStg_DirtyMultiple(IN HPROPSTG hstg,
  611. IN ULONG cpspec,
  612. IN const PROPSPEC * rgpropspec,
  613. IN BOOL bDirty)
  614. {
  615. HRESULT hres = STG_E_INVALIDPARAMETER;
  616. if (EVAL(IS_VALID_HANDLE(hstg, PROPSTG)) &&
  617. EVAL(IS_VALID_READ_BUFFER(rgpropspec, PROPSPEC, cpspec)))
  618. {
  619. PPROPSTG pstg = (PPROPSTG)hstg;
  620. const PROPSPEC * ppropspec = rgpropspec;
  621. HDSA hdsaProps = pstg->hdsaProps;
  622. PPROPEL ppropel;
  623. int idsa;
  624. hres = S_OK;
  625. if (0 < cpspec)
  626. {
  627. // Mark the list of property specs
  628. while (0 < cpspec--)
  629. {
  630. // Does it exist?
  631. if (PropStg_PropertyExists(pstg, ppropspec, (LPDWORD)&idsa))
  632. {
  633. // Yes; mark it
  634. ppropel = DSA_GetItemPtr(hdsaProps, idsa);
  635. ASSERT(ppropel);
  636. if (bDirty)
  637. {
  638. SetFlag(ppropel->dwFlags, PEF_DIRTY);
  639. }
  640. else
  641. {
  642. ClearFlag(ppropel->dwFlags, PEF_DIRTY);
  643. }
  644. }
  645. ppropspec++;
  646. }
  647. }
  648. }
  649. return hres;
  650. }
  651. /*----------------------------------------------------------
  652. Purpose: Marks or unmarks all the property values.
  653. Returns: S_OK
  654. STG_E_INVALIDPARAMETER
  655. STG_E_INSUFFICIENTMEMORY
  656. Cond: --
  657. */
  658. STDAPI PropStg_DirtyAll(IN HPROPSTG hstg,
  659. IN BOOL bDirty)
  660. {
  661. HRESULT hres = STG_E_INVALIDPARAMETER;
  662. if (EVAL(IS_VALID_HANDLE(hstg, PROPSTG)))
  663. {
  664. PPROPSTG pstg = (PPROPSTG)hstg;
  665. int cdsa = pstg->idsaLastValid + 1 - CDSA_RESERVED;
  666. hres = S_OK;
  667. if (0 < cdsa)
  668. {
  669. PPROPEL ppropel = DSA_GetItemPtr(pstg->hdsaProps, IDSA_START);
  670. ASSERT(ppropel);
  671. while (0 < cdsa--)
  672. {
  673. if (bDirty)
  674. SetFlag(ppropel->dwFlags, PEF_DIRTY);
  675. else
  676. ClearFlag(ppropel->dwFlags, PEF_DIRTY);
  677. ppropel++;
  678. }
  679. }
  680. }
  681. return hres;
  682. }
  683. /*----------------------------------------------------------
  684. Purpose: Returns S_OK if at least one property value is dirty
  685. in the storage. Otherwise, this function returns
  686. S_FALSE.
  687. Returns: S_OK if it is dirty
  688. S_FALSE if not
  689. STG_E_INVALIDPARAMETER
  690. STG_E_INSUFFICIENTMEMORY
  691. Cond: --
  692. */
  693. STDAPI PropStg_IsDirty(HPROPSTG hstg)
  694. {
  695. HRESULT hres = STG_E_INVALIDPARAMETER;
  696. if (EVAL(IS_VALID_HANDLE(hstg, PROPSTG)))
  697. {
  698. PPROPSTG pstg = (PPROPSTG)hstg;
  699. int cdsa = pstg->idsaLastValid + 1 - CDSA_RESERVED;
  700. hres = S_FALSE;
  701. if (0 < cdsa)
  702. {
  703. PPROPEL ppropel = DSA_GetItemPtr(pstg->hdsaProps, IDSA_START);
  704. ASSERT(ppropel);
  705. while (0 < cdsa--)
  706. {
  707. if (IsFlagSet(ppropel->dwFlags, PEF_DIRTY))
  708. {
  709. hres = S_OK;
  710. break;
  711. }
  712. ppropel++;
  713. }
  714. }
  715. }
  716. return hres;
  717. }
  718. /*----------------------------------------------------------
  719. Purpose: Enumerates thru the list of properties.
  720. Returns: S_OK
  721. STG_E_INVALIDPARAMETER
  722. STG_E_INSUFFICIENTMEMORY
  723. Cond: --
  724. */
  725. STDAPI PropStg_Enum(IN HPROPSTG hstg,
  726. IN DWORD dwFlags, // One of PSTGEF_
  727. IN PFNPROPSTGENUM pfnEnum,
  728. IN LPARAM lParam) OPTIONAL
  729. {
  730. HRESULT hres = STG_E_INVALIDPARAMETER;
  731. if (EVAL(IS_VALID_HANDLE(hstg, PROPSTG)) &&
  732. EVAL(IS_VALID_CODE_PTR(pfnEnum, PFNPROPSTGENUM)))
  733. {
  734. PPROPSTG pstg = (PPROPSTG)hstg;
  735. int cdsa = pstg->idsaLastValid + 1 - CDSA_RESERVED;
  736. DWORD dwFlagsPEF = 0;
  737. hres = S_OK;
  738. // Set the filter flags
  739. if (dwFlags & PSTGEF_DIRTY)
  740. SetFlag(dwFlagsPEF, PEF_DIRTY);
  741. if (0 < cdsa)
  742. {
  743. PPROPEL ppropel = DSA_GetItemPtr(pstg->hdsaProps, IDSA_START);
  744. int idsa = IDSA_START;
  745. ASSERT(ppropel);
  746. while (0 < cdsa--)
  747. {
  748. // Does it pass thru filter?
  749. if (IsFlagSet(ppropel->dwFlags, PEF_VALID) &&
  750. (0 == dwFlagsPEF || (dwFlagsPEF & ppropel->dwFlags)))
  751. {
  752. // Yes, call callback
  753. HRESULT hresT = pfnEnum(idsa, &ppropel->propvar, lParam);
  754. if (S_OK != hresT)
  755. {
  756. if (FAILED(hresT))
  757. hres = hresT;
  758. break; // stop enumeration
  759. }
  760. }
  761. ppropel++;
  762. idsa++;
  763. }
  764. }
  765. }
  766. return hres;
  767. }
  768. #ifdef DEBUG
  769. HRESULT CALLBACK PropStg_DumpVar(IN PROPID propid,
  770. IN PROPVARIANT * ppropvar,
  771. IN LPARAM lParam)
  772. {
  773. TCHAR sz[MAX_PATH];
  774. PPROPEL ppropel = (PPROPEL)ppropvar; // we're cheating here
  775. if (IsFlagSet(ppropel->dwFlags, PEF_DIRTY))
  776. wnsprintf(sz, ARRAYSIZE(sz), TEXT(" *id:%#lx\t%s"), propid, Dbg_GetVTName(ppropvar->vt));
  777. else
  778. wnsprintf(sz, ARRAYSIZE(sz), TEXT(" id:%#lx\t%s"), propid, Dbg_GetVTName(ppropvar->vt));
  779. switch (ppropvar->vt)
  780. {
  781. case VT_EMPTY:
  782. case VT_NULL:
  783. case VT_ILLEGAL:
  784. TraceMsg(TF_ALWAYS, " %s", sz);
  785. break;
  786. case VT_I2:
  787. case VT_I4:
  788. TraceMsg(TF_ALWAYS, " %s\t%d", sz, ppropvar->DUMMYUNION_MEMBER(lVal));
  789. break;
  790. case VT_UI1:
  791. TraceMsg(TF_ALWAYS, " %s\t%#02x '%c'", sz, ppropvar->DUMMYUNION_MEMBER(bVal), ppropvar->DUMMYUNION_MEMBER(bVal));
  792. break;
  793. case VT_UI2:
  794. TraceMsg(TF_ALWAYS, " %s\t%#04x", sz, ppropvar->DUMMYUNION_MEMBER(uiVal));
  795. break;
  796. case VT_UI4:
  797. TraceMsg(TF_ALWAYS, " %s\t%#08x", sz, ppropvar->DUMMYUNION_MEMBER(ulVal));
  798. break;
  799. case VT_LPSTR:
  800. TraceMsg(TF_ALWAYS, " %s\t\"%S\"", sz, Dbg_SafeStrA(ppropvar->DUMMYUNION_MEMBER(pszVal)));
  801. break;
  802. case VT_LPWSTR:
  803. TraceMsg(TF_ALWAYS, " %s\t\"%ls\"", sz, Dbg_SafeStrW(ppropvar->DUMMYUNION_MEMBER(pwszVal)));
  804. break;
  805. default:
  806. #if defined(_WIN64)
  807. TraceMsg(TF_ALWAYS, " %s\t0x%p", sz, (DWORD_PTR)ppropvar->DUMMYUNION_MEMBER(pszVal));
  808. #else
  809. TraceMsg(TF_ALWAYS, " s\t%#08lx", sz, (DWORD)ppropvar->DUMMYUNION_MEMBER(pszVal));
  810. #endif
  811. break;
  812. }
  813. return S_OK;
  814. }
  815. STDAPI PropStg_Dump(IN HPROPSTG hstg,
  816. IN DWORD dwFlags) // One of PSTGDF_
  817. {
  818. TraceMsg(TF_ALWAYS, " Property storage 0x%08lx = {", hstg);
  819. PropStg_Enum(hstg, 0, PropStg_DumpVar, 0);
  820. TraceMsg(TF_ALWAYS, " }");
  821. return NOERROR;
  822. }
  823. #endif