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.

1190 lines
32 KiB

  1. #include "dpastuff.h"
  2. //
  3. // The ORDERITEM structure is exposed via the IOrderList interface.
  4. // ORDERITEM2 contains our private hidden fields.
  5. //
  6. // The extra fields contain information about the cached icon location.
  7. //
  8. // ftModified is the modify-time on the pidl, which is used to detect
  9. // whether the cache needs to be refreshed.
  10. //
  11. // If ftModified is nonzero, then { pwszIcon, iIconIndex, pidlTarget }
  12. // describe the icon that should be displayed for the item.
  13. //
  14. // If pwszIcon is nonzero, then the item is a shortcut with a custom
  15. // icon. pwszIcon points to the file name for the icon, iIconIndex
  16. // is the icon index within the pwszIcon file.
  17. //
  18. // If pidlTarget is nonzero, then the item is a shortcut with a default
  19. // icon. pidlTarget is the target pidl, whose icon we should use.
  20. //
  21. typedef struct ORDERITEM2 {
  22. ORDERITEM oi; // part that clients see - must come first
  23. DWORD dwFlags; // User defined flags.
  24. LPWSTR pwszIcon; // for cacheing the icon location
  25. int iIconIndex; // for cacheing the icon location
  26. LPITEMIDLIST pidlTarget; // use the icon for this pidl
  27. } ORDERITEM2, *PORDERITEM2;
  28. int CALLBACK OrderItem_Compare(LPVOID pv1, LPVOID pv2, LPARAM lParam)
  29. {
  30. PORDERITEM poi1 = (PORDERITEM)pv1;
  31. PORDERITEM poi2 = (PORDERITEM)pv2;
  32. PORDERINFO poinfo = (PORDERINFO)lParam;
  33. int nRet;
  34. if (!poinfo)
  35. {
  36. ASSERT(FALSE);
  37. return 0;
  38. }
  39. switch (poinfo->dwSortBy)
  40. {
  41. case OI_SORTBYNAME:
  42. {
  43. // Make sure they're both non-null
  44. //
  45. if ( poi1->pidl && poi2->pidl )
  46. {
  47. HRESULT hres = poinfo->psf->CompareIDs(0, poi1->pidl, poi2->pidl);
  48. nRet = (short)HRESULT_CODE(hres);
  49. }
  50. else
  51. {
  52. if ( poi1->pidl == poi2->pidl )
  53. nRet = 0;
  54. else
  55. nRet = ((UINT_PTR)poi1->pidl < (UINT_PTR)poi2->pidl ? -1 : 1);
  56. }
  57. break;
  58. }
  59. case OI_SORTBYORDINAL:
  60. if (poi1->nOrder == poi2->nOrder)
  61. nRet = 0;
  62. else
  63. // do unsigned compare so -1 goes to end of list
  64. nRet = ((UINT)poi1->nOrder < (UINT)poi2->nOrder ? -1 : 1);
  65. break;
  66. default:
  67. ASSERT_MSG(0, "Bad dwSortBy passed to OrderItem_Compare");
  68. nRet = 0;
  69. break;
  70. }
  71. return nRet;
  72. }
  73. void OrderItem_FreeIconInfo(PORDERITEM poi)
  74. {
  75. PORDERITEM2 poi2 = CONTAINING_RECORD(poi, ORDERITEM2, oi);
  76. if (poi2->pwszIcon)
  77. {
  78. LPWSTR pwszIcon = poi2->pwszIcon;
  79. poi2->pwszIcon = NULL;
  80. LocalFree(pwszIcon);
  81. }
  82. if (poi2->pidlTarget)
  83. {
  84. LPITEMIDLIST pidl = poi2->pidlTarget;
  85. poi2->pidlTarget = NULL;
  86. ILFree(pidl);
  87. }
  88. }
  89. LPVOID CALLBACK OrderItem_Merge(UINT uMsg, LPVOID pvDst, LPVOID pvSrc, LPARAM lParam)
  90. {
  91. PORDERITEM2 poi2Dst = CONTAINING_RECORD(pvDst, ORDERITEM2, oi);
  92. PORDERITEM2 poi2Src = CONTAINING_RECORD(pvSrc, ORDERITEM2, oi);
  93. PORDERINFO poinfo = (PORDERINFO)lParam;
  94. LPVOID pvRet = pvDst;
  95. switch (uMsg)
  96. {
  97. case DPAMM_MERGE:
  98. // Transfer the order field
  99. poi2Dst->oi.nOrder = poi2Src->oi.nOrder;
  100. // Propagate any cached icon information too...
  101. if (poi2Src->pwszIcon || poi2Src->pidlTarget)
  102. {
  103. // To avoid useless allocation, we transfer the cache across
  104. // instead of copying it.
  105. if (poinfo->psf2 &&
  106. poinfo->psf2->CompareIDs(SHCIDS_ALLFIELDS, poi2Dst->oi.pidl, poi2Src->oi.pidl) == S_OK)
  107. {
  108. OrderItem_FreeIconInfo(&poi2Dst->oi);
  109. CopyMemory((LPBYTE)poi2Dst + sizeof(ORDERITEM),
  110. (LPBYTE)poi2Src + sizeof(ORDERITEM),
  111. sizeof(ORDERITEM2) - sizeof(ORDERITEM));
  112. ZeroMemory((LPBYTE)poi2Src + sizeof(ORDERITEM),
  113. sizeof(ORDERITEM2) - sizeof(ORDERITEM));
  114. }
  115. }
  116. break;
  117. case DPAMM_DELETE:
  118. case DPAMM_INSERT:
  119. // Don't need to implement this
  120. ASSERT(0);
  121. pvRet = NULL;
  122. break;
  123. }
  124. return pvRet;
  125. }
  126. int OrderItem_UpdatePos(LPVOID p, LPVOID pData)
  127. {
  128. PORDERITEM poi = (PORDERITEM)p;
  129. if (-1 == poi->nOrder)
  130. {
  131. poi->nOrder = (int)(INT_PTR)pData;
  132. }
  133. else if ((int)(INT_PTR)pData >= poi->nOrder)
  134. {
  135. poi->nOrder++;
  136. }
  137. return 1;
  138. }
  139. // OrderList_Merge sorts hdpaNew to match hdpaOld order,
  140. // putting any items in hdpaNew that were not in hdpaOld
  141. // at position iInsertPos (-1 means end of list).
  142. //
  143. // Assumes hdpaOld is already sorted by sort order in lParam (OI_SORTBYNAME by default)
  144. // (if hdpaOld is specified)
  145. //
  146. void OrderList_Merge(HDPA hdpaNew, HDPA hdpaOld, int iInsertPos, LPARAM lParam,
  147. LPFNORDERMERGENOMATCH pfn, LPVOID pvParam)
  148. {
  149. PORDERINFO poinfo = (PORDERINFO)lParam;
  150. BOOL fMergeOnly = FALSE;
  151. if (poinfo->dwSortBy == OI_MERGEBYNAME)
  152. {
  153. poinfo->dwSortBy = OI_SORTBYNAME;
  154. fMergeOnly = TRUE;
  155. }
  156. // hdpaNew has not been sorted, sort by name
  157. DPA_Sort(hdpaNew, OrderItem_Compare, lParam);
  158. BOOL fForceNoMatch = FALSE;
  159. if (FAILED(poinfo->psf->QueryInterface(IID_IShellFolder2, (LPVOID *)&poinfo->psf2))) {
  160. // 239390: Network Connections folder doesn't implement QI correctly. Its psf
  161. // fails QI for IID_IShellFolder2, but doesn't null out ppvObj. So do it for them.
  162. poinfo->psf2 = NULL;
  163. }
  164. // Copy order preferences over from old list to new list
  165. if (hdpaOld)
  166. {
  167. DPA_Merge(hdpaNew, hdpaOld, DPAM_SORTED | DPAM_NORMAL, OrderItem_Compare, OrderItem_Merge, lParam);
  168. // If we're waiting for the notify from a drag&drop operation,
  169. // update the new items (they will have a -1) to the insert position.
  170. if (-1 != iInsertPos)
  171. {
  172. DPA_EnumCallback(hdpaNew, OrderItem_UpdatePos, (LPVOID)(INT_PTR)iInsertPos);
  173. }
  174. if (poinfo->dwSortBy != OI_SORTBYORDINAL && !fMergeOnly)
  175. {
  176. poinfo->dwSortBy = OI_SORTBYORDINAL;
  177. DPA_Sort(hdpaNew, OrderItem_Compare, lParam);
  178. }
  179. }
  180. else
  181. fForceNoMatch = TRUE;
  182. // If the caller passed a NoMatch callback, then call it with
  183. // each item that is not matched.
  184. if (pfn)
  185. {
  186. for (int i = DPA_GetPtrCount(hdpaNew)-1 ; i >= 0 ; i--)
  187. {
  188. PORDERITEM poi = (PORDERITEM)DPA_FastGetPtr(hdpaNew, i);
  189. // Does this item have order information?
  190. if (iInsertPos == poi->nOrder ||
  191. -1 == poi->nOrder ||
  192. fForceNoMatch)
  193. {
  194. // No; Then pass to the "No Match" callback
  195. pfn(pvParam, poi->pidl);
  196. }
  197. }
  198. }
  199. ATOMICRELEASE(poinfo->psf2);
  200. OrderList_Reorder(hdpaNew);
  201. }
  202. // OrderList_Reorder refreshes the order info
  203. void OrderList_Reorder(HDPA hdpa)
  204. {
  205. int i;
  206. for (i = DPA_GetPtrCount(hdpa)-1 ; i >= 0 ; i--)
  207. {
  208. PORDERITEM poi = (PORDERITEM)DPA_FastGetPtr(hdpa, i);
  209. poi->nOrder = i;
  210. }
  211. }
  212. BOOL OrderList_Append(HDPA hdpa, LPITEMIDLIST pidl, int nOrder)
  213. {
  214. PORDERITEM poi = OrderItem_Create(pidl, nOrder);
  215. if (poi)
  216. {
  217. if (-1 != DPA_AppendPtr(hdpa, poi))
  218. return TRUE;
  219. OrderItem_Free(poi, FALSE); //don't free pidl because caller will do it
  220. }
  221. return FALSE;
  222. }
  223. // This differes from DPA_Clone in that it allocates new items!
  224. HDPA OrderList_Clone(HDPA hdpa)
  225. {
  226. HDPA hdpaNew = NULL;
  227. if (EVAL(hdpa))
  228. {
  229. hdpaNew = DPA_Create(DPA_GetPtrCount(hdpa));
  230. if (hdpaNew)
  231. {
  232. int i;
  233. for (i = 0 ; i < DPA_GetPtrCount(hdpa) ; i++)
  234. {
  235. PORDERITEM poi = (PORDERITEM)DPA_FastGetPtr(hdpa, i);
  236. LPITEMIDLIST pidl = ILClone(poi->pidl);
  237. if (pidl)
  238. {
  239. if (!OrderList_Append(hdpaNew, pidl, poi->nOrder))
  240. {
  241. ILFree(pidl);
  242. }
  243. }
  244. }
  245. }
  246. }
  247. return hdpaNew;
  248. }
  249. // Does not clone the pidl but will free it.
  250. // Does not addref the psf nor release it.
  251. PORDERITEM OrderItem_Create(LPITEMIDLIST pidl, int nOrder)
  252. {
  253. PORDERITEM2 poi = (PORDERITEM2)LocalAlloc(LPTR, SIZEOF(ORDERITEM2));
  254. if (poi)
  255. {
  256. poi->oi.pidl = pidl;
  257. poi->oi.nOrder = nOrder;
  258. return &poi->oi;
  259. }
  260. return NULL;
  261. }
  262. void OrderItem_Free(PORDERITEM poi, BOOL fKillPidls /* = TRUE */)
  263. {
  264. if (fKillPidls)
  265. ILFree(poi->pidl);
  266. OrderItem_FreeIconInfo(poi);
  267. LocalFree(poi);
  268. }
  269. int OrderItem_FreeItem(LPVOID p, LPVOID pData)
  270. {
  271. PORDERITEM poi = (PORDERITEM)p;
  272. OrderItem_Free(poi, (BOOL)(INT_PTR)pData);
  273. return 1;
  274. }
  275. void OrderList_Destroy(HDPA* phdpa, BOOL fKillPidls /* = fTrue */)
  276. {
  277. if (*phdpa) {
  278. DPA_DestroyCallback(*phdpa, OrderItem_FreeItem, (LPVOID) (INT_PTR)fKillPidls);
  279. *phdpa = NULL;
  280. }
  281. }
  282. //
  283. // Return values:
  284. //
  285. // S_OK - icon obtained successfully
  286. // S_FALSE - icon not obtained, don't waste time trying
  287. // E_FAIL - no cached icon, need to do more work
  288. //
  289. HRESULT OrderItem_GetSystemImageListIndexFromCache(PORDERITEM poi,
  290. IShellFolder *psf, int *piOut)
  291. {
  292. PORDERITEM2 poi2 = CONTAINING_RECORD(poi, ORDERITEM2, oi);
  293. IShellFolder *psfT;
  294. LPCITEMIDLIST pidlItem;
  295. HRESULT hr;
  296. // Do we have a cached icon location?
  297. if (poi2->pwszIcon)
  298. {
  299. *piOut = 0;
  300. // Validate Path existance.
  301. if (PathFileExistsW(poi2->pwszIcon))
  302. {
  303. *piOut = Shell_GetCachedImageIndex(poi2->pwszIcon, poi2->iIconIndex, GIL_PERINSTANCE);
  304. }
  305. return (*piOut > 0)? S_OK : E_FAIL;
  306. }
  307. // Do we have a cached pidlTarget?
  308. if (poi2->pidlTarget)
  309. {
  310. hr = SHBindToIDListParent(poi2->pidlTarget, IID_IShellFolder, (void**)&psfT, &pidlItem);
  311. if (SUCCEEDED(hr))
  312. {
  313. // Make sure the pidl exsists before binding. because the bind does succeed if it does not exist.
  314. DWORD dwAttrib = SFGAO_VALIDATE;
  315. hr = psfT->GetAttributesOf(1, (LPCITEMIDLIST*)&pidlItem, &dwAttrib);
  316. if (SUCCEEDED(hr))
  317. {
  318. *piOut = SHMapPIDLToSystemImageListIndex(psfT, pidlItem, NULL);
  319. }
  320. psfT->Release();
  321. return hr;
  322. }
  323. // Bind failed - shortcut target was deleted
  324. // Keep the cache valid because we don't want to whack the disk
  325. // all the time only to discover it's busted.
  326. return E_FAIL;
  327. }
  328. return E_FAIL;
  329. }
  330. DWORD OrderItem_GetFlags(PORDERITEM poi)
  331. {
  332. PORDERITEM2 poi2 = CONTAINING_RECORD(poi, ORDERITEM2, oi);
  333. return poi2->dwFlags;
  334. }
  335. void OrderItem_SetFlags(PORDERITEM poi, DWORD dwFlags)
  336. {
  337. PORDERITEM2 poi2 = CONTAINING_RECORD(poi, ORDERITEM2, oi);
  338. poi2->dwFlags = dwFlags;
  339. }
  340. int OrderItem_GetSystemImageListIndex(PORDERITEM poi, IShellFolder *psf, BOOL fUseCache)
  341. {
  342. PORDERITEM2 poi2 = CONTAINING_RECORD(poi, ORDERITEM2, oi);
  343. HRESULT hr;
  344. int iBitmap;
  345. DWORD dwAttr;
  346. if (fUseCache)
  347. {
  348. hr = OrderItem_GetSystemImageListIndexFromCache(poi, psf, &iBitmap);
  349. if (SUCCEEDED(hr))
  350. {
  351. return iBitmap;
  352. }
  353. else
  354. {
  355. goto Fallback;
  356. }
  357. }
  358. else
  359. {
  360. //
  361. // Free any pointers we cached previously
  362. //
  363. if (poi2->pidlTarget)
  364. {
  365. ILFree(poi2->pidlTarget);
  366. poi2->pidlTarget = NULL;
  367. }
  368. Str_SetPtr(&poi2->pwszIcon, NULL);
  369. }
  370. //
  371. // Go find the icon.
  372. //
  373. ASSERT(poi2->pidlTarget == NULL);
  374. ASSERT(poi2->pwszIcon == NULL);
  375. //
  376. // Is this item shortcutlike at all?
  377. //
  378. dwAttr = SFGAO_LINK;
  379. hr = psf->GetAttributesOf(1, (LPCITEMIDLIST*)&poi->pidl, &dwAttr);
  380. if (FAILED(hr) || !(dwAttr & SFGAO_LINK))
  381. goto Fallback; // not a shortcut; use the fallback
  382. //
  383. // Must go for ANSI version first because client might not support
  384. // UNICODE.
  385. //
  386. // FEATURE - should QI for IExtractIcon to see if we get GIL_DONTCACHE
  387. // back.
  388. IShellLinkA *pslA;
  389. hr = psf->GetUIObjectOf(NULL, 1, (LPCITEMIDLIST*)&poi->pidl,
  390. IID_IShellLinkA, 0, (LPVOID *)&pslA);
  391. if (FAILED(hr))
  392. goto Fallback;
  393. //
  394. // If there's a UNICODE version, that's even better.
  395. //
  396. IShellLinkW *pslW;
  397. WCHAR wszIconPath[MAX_PATH];
  398. hr = pslA->QueryInterface(IID_IShellLinkW, (LPVOID *)&pslW);
  399. if (SUCCEEDED(hr))
  400. {
  401. hr = pslW->GetIconLocation(wszIconPath, ARRAYSIZE(wszIconPath), &poi2->iIconIndex);
  402. pslW->Release();
  403. }
  404. else
  405. {
  406. // Only IShellLinkA supported. Thunk to UNICODE manually.
  407. CHAR szIconPath[ARRAYSIZE(wszIconPath)];
  408. hr = pslA->GetIconLocation(szIconPath, ARRAYSIZE(szIconPath), &poi2->iIconIndex);
  409. if (SUCCEEDED(hr))
  410. SHAnsiToUnicode(szIconPath, wszIconPath, ARRAYSIZE(wszIconPath));
  411. }
  412. // If we have a custom icon path, then save that
  413. if (SUCCEEDED(hr) && wszIconPath[0])
  414. {
  415. Str_SetPtr(&poi2->pwszIcon, wszIconPath);
  416. }
  417. else
  418. {
  419. // No icon path, get the target instead
  420. pslA->GetIDList(&poi2->pidlTarget);
  421. if (IsURLChild(poi2->pidlTarget, TRUE))
  422. {
  423. // If this is a url, we want to go to the "Fallback" case. The reason for this
  424. // is that the fallback case will go through
  425. // where we will end up with the generic icon for .url files
  426. ILFree(poi2->pidlTarget);
  427. poi2->pidlTarget = NULL;
  428. pslA->Release();
  429. goto Fallback;
  430. }
  431. }
  432. pslA->Release();
  433. //
  434. // Aw-right, the cache is all loaded up. Let's try that again.
  435. //
  436. hr = OrderItem_GetSystemImageListIndexFromCache(poi, psf, &iBitmap);
  437. if (hr == S_OK)
  438. {
  439. return iBitmap;
  440. }
  441. Fallback:
  442. return SHMapPIDLToSystemImageListIndex(psf, poi->pidl, NULL);
  443. }
  444. // Header for file menu streams
  445. //
  446. // The file menu stream consists of an IOSTREAMHEADER followed by
  447. // a DPA_SaveStream of the order DPA. Each item in the DPA consists
  448. // of an OISTREAMITEM.
  449. //
  450. // To keep roaming profiles working between NT4 (IE4) and NT5 (IE5),
  451. // the dwVersion used by NT5 must be the same as that used by NT4.
  452. // I.e., it must be 2.
  453. typedef struct tagOISTREAMHEADER
  454. {
  455. DWORD cbSize; // Size of header
  456. DWORD dwVersion; // Version of header
  457. } OISTREAMHEADER;
  458. #define OISTREAMHEADER_VERSION 2
  459. //
  460. // Each item in a persisted order DPA consists of an OISTREAMITEM
  461. // followed by additional goo. All pidls stored include the
  462. // terminating (USHORT)0.
  463. //
  464. // IE4:
  465. // OISTREAMITEM
  466. // pidl - the item itself
  467. //
  468. // IE5 - shortcut has custom icon
  469. // OISTREAMITEM
  470. // pidl - the item itself (last-modify time implied)
  471. // <optional padding> - for WCHAR alignment
  472. // dwFlags - User defined Flags
  473. // dwStringLen - Length of the icon path
  474. // UNICODEZ iconpath - icon path
  475. // iIconIndex - icon index
  476. //
  477. // IE5 - shortcut takes its icon from another pidl
  478. // OISTREAMITEM
  479. // pidl - the item itself (last-modify time implied)
  480. // <optional padding> - for WCHAR alignment
  481. // dwFlags - User defined Flags
  482. // (DWORD)0 - null string indicates "no custom icon"
  483. // pidlTarget - use the icon for this pidl
  484. //
  485. typedef struct tagOISTREAMITEM
  486. {
  487. DWORD cbSize; // Size including trailing goo
  488. int nOrder; // User-specified order
  489. // variable-sized trailing goo comes here.
  490. //
  491. // See above for description of trailing goo.
  492. } OISTREAMITEM;
  493. #define CB_OISTREAMITEM (sizeof(OISTREAMITEM))
  494. //
  495. // Save a component of the orderitem to the stream. If an error has
  496. // already occurred on the stream, *phrRc contains the old error code,
  497. // and we write nothing.
  498. //
  499. // If pstm == NULL, then we are not actually writing anything. We are
  500. // merely doing a dry run.
  501. //
  502. // Otherwise, *phrRc accumulates the number of bytes actually written,
  503. // or receives an error code on failure.
  504. //
  505. void
  506. OrderItem_SaveSubitemToStream(IStream *pstm, LPCVOID pvData, ULONG cb, HRESULT* phrRc)
  507. {
  508. HRESULT hres;
  509. if (SUCCEEDED(*phrRc))
  510. {
  511. if (pstm)
  512. {
  513. hres = IStream_Write(pstm, (LPVOID)pvData, cb);
  514. if (SUCCEEDED(hres))
  515. {
  516. *phrRc += cb; // successful write - accumulate
  517. }
  518. else
  519. {
  520. *phrRc = hres; // error - return error code
  521. }
  522. }
  523. else
  524. {
  525. *phrRc += cb; // no output stream - accumulate
  526. }
  527. }
  528. }
  529. //
  530. // This worker function (1) computes the numer of bytes we will actually
  531. // write out, and (2) actually writes it if pstm != NULL.
  532. //
  533. // Return value is the number of bytes written (or would have been
  534. // written), or a COM error code on failure.
  535. //
  536. const BYTE c_Zeros[2] = { 0 }; // a bunch of zeros
  537. HRESULT
  538. OrderItem_SaveToStreamWorker(PORDERITEM2 poi2, OISTREAMITEM *posi,
  539. IStream *pstm, IShellFolder2 *psf2)
  540. {
  541. HRESULT hrRc = 0; // no bytes, no error
  542. ASSERT(poi2->oi.pidl);
  543. //
  544. // First comes the header.
  545. //
  546. OrderItem_SaveSubitemToStream(pstm, posi, CB_OISTREAMITEM, &hrRc);
  547. //
  548. // Then the pidl.
  549. //
  550. // We're assuming this is an immediate child pidl. If it's not,
  551. // the pidl is being truncated!
  552. ASSERT(0 == _ILNext(poi2->oi.pidl)->mkid.cb);
  553. OrderItem_SaveSubitemToStream(pstm, poi2->oi.pidl,
  554. poi2->oi.pidl->mkid.cb + sizeof(USHORT),
  555. &hrRc);
  556. // Insert padding to get back to WCHAR alignment.
  557. if (hrRc % sizeof(WCHAR))
  558. {
  559. OrderItem_SaveSubitemToStream(pstm, &c_Zeros, 1, &hrRc);
  560. }
  561. OrderItem_SaveSubitemToStream(pstm, &poi2->dwFlags, sizeof(DWORD), &hrRc);
  562. //
  563. // If we haven't barfed yet and the IShellFolder supports identity
  564. // and there is icon information, then save it.
  565. //
  566. if (SUCCEEDED(hrRc) && psf2 && (poi2->pwszIcon || poi2->pidlTarget))
  567. {
  568. // Optional icon is present.
  569. if (poi2->pwszIcon)
  570. {
  571. // UNICODEZ path
  572. DWORD cbString = (lstrlenW(poi2->pwszIcon) + 1) * sizeof(WCHAR);
  573. // Save the String len
  574. OrderItem_SaveSubitemToStream(pstm, &cbString,
  575. sizeof(DWORD) , &hrRc);
  576. OrderItem_SaveSubitemToStream(pstm, poi2->pwszIcon,
  577. (lstrlenW(poi2->pwszIcon) + 1) * sizeof(WCHAR), &hrRc);
  578. // icon index
  579. OrderItem_SaveSubitemToStream(pstm, &poi2->iIconIndex,
  580. sizeof(poi2->iIconIndex), &hrRc);
  581. }
  582. else
  583. {
  584. DWORD cbString = 0;
  585. OrderItem_SaveSubitemToStream(pstm, &cbString, sizeof(DWORD), &hrRc);
  586. // pidlTarget
  587. OrderItem_SaveSubitemToStream(pstm, poi2->pidlTarget,
  588. ILGetSize(poi2->pidlTarget), &hrRc);
  589. }
  590. }
  591. return hrRc;
  592. }
  593. HRESULT
  594. CALLBACK
  595. OrderItem_SaveToStream(DPASTREAMINFO * pinfo, IStream * pstm, LPVOID pvData)
  596. {
  597. PORDERITEM2 poi2 = (PORDERITEM2)pinfo->pvItem;
  598. HRESULT hres = S_FALSE;
  599. IShellFolder2 *psf2 = (IShellFolder2 *)pvData;
  600. if (poi2->oi.pidl)
  601. {
  602. OISTREAMITEM osi;
  603. // First a dry run to compute the size of this item.
  604. hres = OrderItem_SaveToStreamWorker(poi2, NULL, NULL, psf2);
  605. // Nothing actually got written, so this should always succeed.
  606. ASSERT(SUCCEEDED(hres));
  607. osi.cbSize = hres;
  608. osi.nOrder = poi2->oi.nOrder;
  609. // Now write it out for real
  610. hres = OrderItem_SaveToStreamWorker(poi2, &osi, pstm, psf2);
  611. // On success, we must return exactly S_OK or DPA will blow us off
  612. if (SUCCEEDED(hres))
  613. hres = S_OK;
  614. }
  615. return hres;
  616. }
  617. //
  618. // Check if a pidl we read out of a stream is a simple child pidl.
  619. // The pidl must be exactly cb bytes in length.
  620. // The pointer is known to be valid;
  621. // we just want to check that the contents are good, too.
  622. //
  623. BOOL
  624. IsValidPersistedChildPidl(LPCITEMIDLIST pidl, UINT cb)
  625. {
  626. // Must have at least room for one byte of pidl plus the terminating
  627. // zero.
  628. if (cb < 1 + sizeof(USHORT))
  629. return FALSE;
  630. // Make sure size is at least what it's supposed to be.
  631. if (pidl->mkid.cb + sizeof(USHORT) > cb)
  632. return FALSE;
  633. // Make sure there's a zero right after it.
  634. pidl = _ILNext(pidl);
  635. return pidl->mkid.cb == 0;
  636. }
  637. //
  638. // Just like ILGetSize, but returns (UINT)-1 if the pidl is corrupt.
  639. // We use (UINT)-1 as the return value because it will be bigger than
  640. // the buffer size we eventually compare it against.
  641. UINT SafeILGetSize(LPCITEMIDLIST pidl)
  642. {
  643. __try
  644. {
  645. return ILGetSize(pidl);
  646. }
  647. _except (EXCEPTION_EXECUTE_HANDLER)
  648. {
  649. }
  650. return (UINT)-1;
  651. }
  652. HRESULT
  653. CALLBACK
  654. OrderItem_LoadFromStream(DPASTREAMINFO * pinfo, IStream * pstm, LPVOID /*pvData*/)
  655. {
  656. HRESULT hres;
  657. OISTREAMITEM osi;
  658. hres = IStream_Read(pstm, &osi, CB_OISTREAMITEM);
  659. if (SUCCEEDED(hres))
  660. {
  661. ASSERT(CB_OISTREAMITEM < osi.cbSize);
  662. if (CB_OISTREAMITEM < osi.cbSize)
  663. {
  664. UINT cb = osi.cbSize - CB_OISTREAMITEM;
  665. LPITEMIDLIST pidl = IEILCreate(cb);
  666. if ( !pidl )
  667. hres = E_OUTOFMEMORY;
  668. else
  669. {
  670. hres = IStream_Read(pstm, pidl, cb);
  671. if (SUCCEEDED(hres) && IsValidPersistedChildPidl(pidl, cb))
  672. {
  673. PORDERITEM poi = OrderItem_Create(pidl, osi.nOrder);
  674. if (poi)
  675. {
  676. PORDERITEM2 poi2 = CONTAINING_RECORD(poi, ORDERITEM2, oi);
  677. pinfo->pvItem = poi;
  678. // cbPos = offset to trailing gunk after pidl
  679. UINT cbPos = pidl->mkid.cb + sizeof(USHORT);
  680. cbPos = ROUNDUP(cbPos, sizeof(WCHAR));
  681. // Do we have a DWORD hanging off the end of the pidl? This should be the flags.
  682. if (cb >= cbPos + sizeof(DWORD))
  683. {
  684. poi2->dwFlags = *(UNALIGNED DWORD*)((LPBYTE)pidl + cbPos);
  685. }
  686. // Make sure there's at least a WCHAR to test against.
  687. if (cb >= cbPos + sizeof(WCHAR) + 2 * sizeof(DWORD))
  688. {
  689. DWORD cbString = *(UNALIGNED DWORD*)((LPBYTE)pidl + cbPos + sizeof(DWORD));
  690. LPWSTR pwszIcon = (LPWSTR)((LPBYTE)pidl + cbPos + 2 * sizeof(DWORD));
  691. // Do we have a string lenght?
  692. if (pwszIcon && cbString != 0)
  693. {
  694. // Yes, then this is a string not a pidl. We want to make sure this is a
  695. // fully qualified path.
  696. if (IS_VALID_STRING_PTRW(pwszIcon, cbString) &&
  697. !PathIsRelative(pwszIcon))
  698. {
  699. poi2->pwszIcon = StrDup(pwszIcon);
  700. pwszIcon += lstrlenW(pwszIcon) + 1;
  701. poi2->iIconIndex = *(UNALIGNED int *)pwszIcon;
  702. }
  703. }
  704. else
  705. {
  706. // A string length of zero is
  707. LPITEMIDLIST pidlTarget = (LPITEMIDLIST)(pwszIcon);
  708. // We want to write
  709. // cbPos + sizeof(WCHAR) + SafeILGetSize(pidlTarget) <= cb
  710. // but SafeILGetSize returns (UINT)-1 on error, so we need
  711. // to do some algebra to avoid overflows
  712. if (SafeILGetSize(pidlTarget) <= cb - cbPos - 2 * sizeof(DWORD))
  713. {
  714. poi2->pidlTarget = ILClone(pidlTarget);
  715. }
  716. }
  717. }
  718. hres = E_OUTOFMEMORY;
  719. // pidl Contains extranious information. Take the hit of stripping it so that
  720. // our working set doesn't bloat.
  721. LPITEMIDLIST pidlNew = ILClone(poi2->oi.pidl);
  722. if (pidlNew)
  723. {
  724. ILFree(poi2->oi.pidl);
  725. poi2->oi.pidl = pidlNew;
  726. hres = S_OK;
  727. }
  728. }
  729. else
  730. hres = E_OUTOFMEMORY;
  731. }
  732. else
  733. hres = E_FAIL;
  734. // Cleanup
  735. if (FAILED(hres))
  736. ILFree(pidl);
  737. }
  738. }
  739. else
  740. hres = E_FAIL;
  741. }
  742. ASSERT((S_OK == hres && pinfo->pvItem) || FAILED(hres));
  743. return hres;
  744. }
  745. HRESULT OrderList_LoadFromStream(IStream* pstm, HDPA * phdpa, IShellFolder * psfParent)
  746. {
  747. HDPA hdpa = NULL;
  748. OISTREAMHEADER oish;
  749. ASSERT(phdpa);
  750. ASSERT(pstm);
  751. // Read the header for more info
  752. if (SUCCEEDED(IStream_Read(pstm, &oish, sizeof(oish))) &&
  753. sizeof(oish) == oish.cbSize)
  754. {
  755. // Load the stream. (Should be ordered by name.)
  756. DPA_LoadStream(&hdpa, OrderItem_LoadFromStream, pstm, psfParent);
  757. // if this is the wrong version, throw away the pidls.
  758. // we go through the load anyways to make suret he read pointer is set right
  759. if (OISTREAMHEADER_VERSION != oish.dwVersion)
  760. OrderList_Destroy(&hdpa, TRUE);
  761. }
  762. *phdpa = hdpa;
  763. return (NULL != hdpa) ? S_OK : E_FAIL;
  764. }
  765. HRESULT OrderList_SaveToStream(IStream* pstm, HDPA hdpaSave, IShellFolder *psf)
  766. {
  767. HRESULT hres = E_OUTOFMEMORY;
  768. OISTREAMHEADER oish;
  769. HDPA hdpa;
  770. // Clone the array and sort by name for the purpose of persisting it
  771. hdpa = DPA_Clone(hdpaSave, NULL);
  772. if (hdpa)
  773. {
  774. ORDERINFO oinfo = {0};
  775. #ifdef DEBUG
  776. // use QI to help track down leaks
  777. if (psf)
  778. EVAL(SUCCEEDED(psf->QueryInterface(IID_IShellFolder, (LPVOID *)&oinfo.psf)));
  779. #else
  780. oinfo.psf = psf;
  781. if (psf)
  782. oinfo.psf->AddRef();
  783. #endif
  784. oinfo.dwSortBy = OI_SORTBYNAME;
  785. DPA_Sort(hdpa, OrderItem_Compare, (LPARAM)&oinfo);
  786. // Save the header
  787. oish.cbSize = sizeof(oish);
  788. oish.dwVersion = OISTREAMHEADER_VERSION;
  789. hres = IStream_Write(pstm, &oish, sizeof(oish));
  790. if (SUCCEEDED(hres))
  791. {
  792. if (psf)
  793. oinfo.psf->QueryInterface(IID_IShellFolder2, (LPVOID *)&oinfo.psf2);
  794. hres = DPA_SaveStream(hdpa, OrderItem_SaveToStream, pstm, oinfo.psf2);
  795. ATOMICRELEASE(oinfo.psf2);
  796. }
  797. ATOMICRELEASE(oinfo.psf);
  798. DPA_Destroy(hdpa);
  799. }
  800. return hres;
  801. }
  802. /////////////
  803. //
  804. // COrderList impl for export to channel installer
  805. //
  806. class COrderList : public IPersistFolder,
  807. public IOrderList2
  808. {
  809. public:
  810. virtual STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj);
  811. virtual STDMETHODIMP_(ULONG) AddRef(void);
  812. virtual STDMETHODIMP_(ULONG) Release(void);
  813. // IPersistFolder
  814. virtual STDMETHODIMP GetClassID(CLSID *pClassID);
  815. virtual STDMETHODIMP Initialize(LPCITEMIDLIST pidl);
  816. // IOrderList
  817. virtual STDMETHODIMP GetOrderList(HDPA * phdpa);
  818. virtual STDMETHODIMP SetOrderList(HDPA hdpa, IShellFolder *psf);
  819. virtual STDMETHODIMP FreeOrderList(HDPA hdpa);
  820. virtual STDMETHODIMP SortOrderList(HDPA hdpa, DWORD dw);
  821. virtual STDMETHODIMP AllocOrderItem(PORDERITEM * ppoi, LPCITEMIDLIST pidl);
  822. virtual STDMETHODIMP FreeOrderItem(PORDERITEM poi);
  823. // IOrderList 2
  824. virtual STDMETHODIMP LoadFromStream(IStream* pstm, HDPA* hdpa, IShellFolder* psf);
  825. virtual STDMETHODIMP SaveToStream(IStream* pstm, HDPA hdpa);
  826. protected:
  827. COrderList(IUnknown* punkOuter, LPCOBJECTINFO poi);
  828. friend IUnknown * COrderList_Create();
  829. COrderList();
  830. ~COrderList();
  831. int _cRef;
  832. IShellFolder *_psf;
  833. LPITEMIDLIST _pidl;
  834. LPITEMIDLIST _pidlFavorites;
  835. };
  836. COrderList::COrderList()
  837. {
  838. _cRef = 1;
  839. DllAddRef();
  840. }
  841. COrderList::~COrderList()
  842. {
  843. ILFree(_pidl);
  844. ILFree(_pidlFavorites);
  845. ATOMICRELEASE(_psf);
  846. DllRelease();
  847. }
  848. IUnknown * COrderList_Create()
  849. {
  850. COrderList * pcol = new COrderList;
  851. if (pcol)
  852. {
  853. return SAFECAST(pcol, IPersistFolder*);
  854. }
  855. return NULL;
  856. }
  857. STDAPI COrderList_CreateInstance(IUnknown * pUnkOuter, IUnknown ** punk, LPCOBJECTINFO poi)
  858. {
  859. *punk = COrderList_Create();
  860. return *punk ? S_OK : E_OUTOFMEMORY;
  861. }
  862. ULONG COrderList::AddRef()
  863. {
  864. _cRef++;
  865. return _cRef;
  866. }
  867. ULONG COrderList::Release()
  868. {
  869. ASSERT(_cRef > 0);
  870. _cRef--;
  871. if (_cRef > 0)
  872. return _cRef;
  873. delete this;
  874. return 0;
  875. }
  876. HRESULT COrderList::QueryInterface(REFIID riid, void **ppvObj)
  877. {
  878. static const QITAB qit[] = {
  879. QITABENT(COrderList, IPersistFolder),
  880. QITABENT(COrderList, IOrderList),
  881. QITABENTMULTI(COrderList, IOrderList2, IOrderList),
  882. { 0 },
  883. };
  884. return QISearch(this, qit, riid, ppvObj);
  885. }
  886. HRESULT COrderList::GetClassID(CLSID *pClassID)
  887. {
  888. *pClassID = CLSID_OrderListExport;
  889. return S_OK;
  890. }
  891. // This is the directory setup wants to re-order
  892. HRESULT COrderList::Initialize(LPCITEMIDLIST pidl)
  893. {
  894. if (!_pidlFavorites)
  895. {
  896. SHGetSpecialFolderLocation(NULL, CSIDL_FAVORITES, &_pidlFavorites);
  897. if (!_pidlFavorites)
  898. return E_OUTOFMEMORY;
  899. }
  900. if (!pidl || !ILIsParent(_pidlFavorites, pidl, FALSE))
  901. return E_INVALIDARG;
  902. // Initialize can be called multiple times
  903. ATOMICRELEASE(_psf);
  904. Pidl_Set(&_pidl, pidl);
  905. if (_pidl)
  906. IEBindToObject(_pidl, &_psf);
  907. if (!_psf)
  908. return E_OUTOFMEMORY;
  909. return S_OK;
  910. }
  911. HRESULT COrderList_GetOrderList(HDPA * phdpa, LPCITEMIDLIST pidl, IShellFolder * psf)
  912. {
  913. IStream* pstm = OpenPidlOrderStream((LPCITEMIDLIST)CSIDL_FAVORITES, pidl, REG_SUBKEY_FAVORITESA, STGM_READ);
  914. if (pstm)
  915. {
  916. HRESULT hres = OrderList_LoadFromStream(pstm, phdpa, psf);
  917. pstm->Release();
  918. return hres;
  919. }
  920. *phdpa = NULL;
  921. return E_OUTOFMEMORY;
  922. }
  923. HRESULT COrderList::GetOrderList(HDPA * phdpa)
  924. {
  925. HRESULT hres = E_FAIL;
  926. *phdpa = NULL;
  927. if (_psf)
  928. hres = COrderList_GetOrderList(phdpa, _pidl, _psf);
  929. return hres;
  930. }
  931. HRESULT COrderList_SetOrderList(HDPA hdpa, LPCITEMIDLIST pidl, IShellFolder *psf)
  932. {
  933. IStream* pstm = OpenPidlOrderStream((LPCITEMIDLIST)CSIDL_FAVORITES, pidl, REG_SUBKEY_FAVORITESA, STGM_WRITE);
  934. if (EVAL(pstm))
  935. {
  936. HRESULT hres = OrderList_SaveToStream(pstm, hdpa, psf);
  937. pstm->Release();
  938. return hres;
  939. }
  940. return E_OUTOFMEMORY;
  941. }
  942. HRESULT COrderList::SetOrderList(HDPA hdpa, IShellFolder *psf)
  943. {
  944. if (!_psf)
  945. return E_FAIL;
  946. return COrderList_SetOrderList(hdpa, _pidl, psf);
  947. }
  948. HRESULT COrderList::FreeOrderList(HDPA hdpa)
  949. {
  950. OrderList_Destroy(&hdpa);
  951. return S_OK;
  952. }
  953. HRESULT COrderList::SortOrderList(HDPA hdpa, DWORD dw)
  954. {
  955. if (OI_SORTBYNAME != dw && OI_SORTBYORDINAL != dw)
  956. return E_INVALIDARG;
  957. if (!_psf)
  958. return E_FAIL;
  959. ORDERINFO oinfo;
  960. oinfo.dwSortBy = dw;
  961. oinfo.psf = _psf;
  962. #ifdef DEBUG
  963. oinfo.psf2 = (IShellFolder2 *)INVALID_HANDLE_VALUE; // force fault if someone uses it
  964. #endif
  965. DPA_Sort(hdpa, OrderItem_Compare, (LPARAM)&oinfo);
  966. return S_OK;
  967. }
  968. HRESULT COrderList::AllocOrderItem(PORDERITEM * ppoi, LPCITEMIDLIST pidl)
  969. {
  970. LPITEMIDLIST pidlClone = ILClone(pidl);
  971. *ppoi = NULL;
  972. if (pidlClone)
  973. {
  974. *ppoi = OrderItem_Create(pidlClone, -1);
  975. if (*ppoi)
  976. return S_OK;
  977. ILFree(pidlClone);
  978. }
  979. return E_OUTOFMEMORY;
  980. }
  981. HRESULT COrderList::FreeOrderItem(PORDERITEM poi)
  982. {
  983. OrderItem_Free(poi);
  984. return S_OK;
  985. }
  986. // IOrderList2::LoadFromStream
  987. STDMETHODIMP COrderList::LoadFromStream(IStream* pstm, HDPA* phdpa, IShellFolder* psf)
  988. {
  989. ASSERT(_psf == NULL);
  990. _psf = psf;
  991. if (_psf)
  992. _psf->AddRef();
  993. return OrderList_LoadFromStream(pstm, phdpa, _psf);
  994. }
  995. // IOrderList2::SaveToStream
  996. STDMETHODIMP COrderList::SaveToStream(IStream* pstm, HDPA hdpa)
  997. {
  998. return OrderList_SaveToStream(pstm, hdpa, _psf);
  999. }