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.

649 lines
15 KiB

  1. #include "stdafx.h"
  2. #include <windows.h>
  3. #include <shlobj.h>
  4. #define NO_SHLWAPI_TPS
  5. #include <shlwapi.h>
  6. #include "idlist.h"
  7. #define VALIDATE_PIDL(pidl) // add debugging code behind this
  8. LPITEMIDLIST ILGetNext(LPCITEMIDLIST pidl)
  9. {
  10. if (pidl && pidl->mkid.cb)
  11. {
  12. VALIDATE_PIDL(pidl);
  13. return _ILNext(pidl);
  14. }
  15. return NULL;
  16. }
  17. UINT ILGetSize(LPCITEMIDLIST pidl)
  18. {
  19. UINT cbTotal = 0;
  20. if (pidl)
  21. {
  22. VALIDATE_PIDL(pidl);
  23. cbTotal += sizeof(pidl->mkid.cb); // Null terminator
  24. while (pidl->mkid.cb)
  25. {
  26. cbTotal += pidl->mkid.cb;
  27. pidl = _ILNext(pidl);
  28. }
  29. }
  30. return cbTotal;
  31. }
  32. #define CBIDL_MIN 256
  33. #define CBIDL_INCL 256
  34. LPITEMIDLIST ILCreate()
  35. {
  36. return (LPITEMIDLIST)CoTaskMemAlloc(CBIDL_MIN);
  37. }
  38. /*
  39. ** _ILResize
  40. *
  41. * PARAMETERS:
  42. * cbExtra is the amount to add to cbRequired if the block needs to grow,
  43. * or it is 0 if we want to resize to the exact size
  44. *
  45. * DESCRIPTION:
  46. *
  47. * RETURNS:
  48. *
  49. */
  50. LPITEMIDLIST _ILResize(LPITEMIDLIST pidl, UINT cbRequired, UINT cbExtra)
  51. {
  52. if (pidl == NULL)
  53. {
  54. pidl = (LPITEMIDLIST)CoTaskMemAlloc(cbRequired + cbExtra);
  55. }
  56. else
  57. {
  58. pidl = (LPITEMIDLIST)CoTaskMemRealloc(pidl, cbRequired + cbExtra);
  59. }
  60. return pidl;
  61. }
  62. LPITEMIDLIST ILFindLastID(LPCITEMIDLIST pidl)
  63. {
  64. LPCITEMIDLIST pidlLast = pidl;
  65. LPCITEMIDLIST pidlNext = pidl;
  66. VALIDATE_PIDL(pidl);
  67. if (pidl == NULL)
  68. return NULL;
  69. // Find the last one
  70. while (pidlNext->mkid.cb)
  71. {
  72. pidlLast = pidlNext;
  73. pidlNext = _ILNext(pidlLast);
  74. }
  75. return (LPITEMIDLIST)pidlLast;
  76. }
  77. BOOL ILRemoveLastID(LPITEMIDLIST pidl)
  78. {
  79. BOOL fRemoved = FALSE;
  80. if (pidl == NULL)
  81. return(FALSE);
  82. if (pidl->mkid.cb)
  83. {
  84. LPITEMIDLIST pidlLast = (LPITEMIDLIST)ILFindLastID(pidl);
  85. pidlLast->mkid.cb = 0; // Remove the last one, null-terminator
  86. fRemoved = TRUE;
  87. }
  88. return fRemoved;
  89. }
  90. LPITEMIDLIST ILClone(LPCITEMIDLIST pidl)
  91. {
  92. UINT cb = ILGetSize(pidl);
  93. LPITEMIDLIST pidlRet = (LPITEMIDLIST)CoTaskMemAlloc(cb);
  94. if (pidlRet)
  95. CopyMemory(pidlRet, pidl, cb);
  96. return pidlRet;
  97. }
  98. LPITEMIDLIST ILCloneFirst(LPCITEMIDLIST pidl)
  99. {
  100. UINT cb = pidl->mkid.cb+sizeof(pidl->mkid.cb);
  101. LPITEMIDLIST pidlRet = (LPITEMIDLIST)CoTaskMemAlloc(cb);
  102. if (pidlRet)
  103. {
  104. CopyMemory(pidlRet, pidl, pidl->mkid.cb);
  105. _ILNext(pidlRet)->mkid.cb = 0;
  106. }
  107. return pidlRet;
  108. }
  109. BOOL ILIsEqual(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  110. {
  111. BOOL bRet = FALSE;
  112. IShellFolder *psfDesktop;
  113. if (SUCCEEDED(SHGetDesktopFolder(&psfDesktop)))
  114. {
  115. bRet = psfDesktop->CompareIDs(0, pidl1, pidl2) == 0;
  116. psfDesktop->Release();
  117. }
  118. return bRet;
  119. }
  120. // test if
  121. // pidl1 is a parent of pidl2
  122. BOOL ILIsParent(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, BOOL fImmediate)
  123. {
  124. LPITEMIDLIST pidl2Prefix;
  125. UINT cb;
  126. LPCITEMIDLIST pidl1T;
  127. LPCITEMIDLIST pidl2T;
  128. VALIDATE_PIDL(pidl1);
  129. VALIDATE_PIDL(pidl2);
  130. if (!pidl1 || !pidl2)
  131. return FALSE;
  132. for (pidl1T = pidl1, pidl2T = pidl2; !ILIsEmpty(pidl1T);
  133. pidl1T = _ILNext(pidl1T), pidl2T = _ILNext(pidl2T))
  134. {
  135. // if pidl2 is shorter than pidl1, pidl1 can't be its parent.
  136. if (ILIsEmpty(pidl2T))
  137. return FALSE;
  138. }
  139. if (fImmediate)
  140. {
  141. // If fImmediate is TRUE, pidl2T should contain exactly one ID.
  142. if (ILIsEmpty(pidl2T) || !ILIsEmpty(_ILNext(pidl2T)))
  143. return FALSE;
  144. }
  145. //
  146. // Create a new IDList from a portion of pidl2, which contains the
  147. // same number of IDs as pidl1.
  148. //
  149. cb = (UINT)pidl2T - (UINT)pidl2;
  150. pidl2Prefix = (LPITEMIDLIST)CoTaskMemAlloc(cb + sizeof(pidl2->mkid.cb));
  151. if (pidl2Prefix)
  152. {
  153. CopyMemory(pidl2Prefix, pidl2, cb);
  154. *(USHORT *)((BYTE *)pidl2Prefix + cb) = 0;
  155. BOOL fRet = ILIsEqual(pidl1, pidl2Prefix);
  156. CoTaskMemFree(pidl2Prefix);
  157. return fRet;
  158. }
  159. return FALSE;
  160. }
  161. // this returns a pointer to the child id ie:
  162. // given pidlParent = \chicago\desktop
  163. // pidlChild = \chicago\desktop\foo\bar
  164. // the return will point to the ID that represents \foo\bar
  165. // NULL is returned if pidlParent is not a parent of pidlChild
  166. LPITEMIDLIST ILFindChild(LPCITEMIDLIST pidlParent, LPCITEMIDLIST pidlChild)
  167. {
  168. if (ILIsParent(pidlParent, pidlChild, FALSE))
  169. {
  170. while (!ILIsEmpty(pidlParent))
  171. {
  172. pidlChild = _ILNext(pidlChild);
  173. pidlParent = _ILNext(pidlParent);
  174. }
  175. return (LPITEMIDLIST)pidlChild;
  176. }
  177. return NULL;
  178. }
  179. LPITEMIDLIST ILCombine(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  180. {
  181. LPITEMIDLIST pidlNew;
  182. UINT cb1 = ILGetSize(pidl1) - sizeof(pidl1->mkid.cb);
  183. UINT cb2 = ILGetSize(pidl2);
  184. VALIDATE_PIDL(pidl1);
  185. VALIDATE_PIDL(pidl2);
  186. pidlNew = (LPITEMIDLIST)CoTaskMemAlloc(cb1 + cb2);
  187. if (pidlNew)
  188. {
  189. CopyMemory(pidlNew, pidl1, cb1);
  190. CopyMemory(((LPSTR)pidlNew) + cb1, pidl2, cb2);
  191. }
  192. return pidlNew;
  193. }
  194. LPITEMIDLIST ILCreateFromPath(LPCTSTR pszPath)
  195. {
  196. LPITEMIDLIST pidl = NULL;
  197. IShellFolder *psfDesktop;
  198. if (SUCCEEDED(SHGetDesktopFolder(&psfDesktop)))
  199. {
  200. ULONG cchEaten;
  201. WCHAR wszPath[MAX_PATH];
  202. SHTCharToUnicode(pszPath, wszPath, ARRAYSIZE(wszPath));
  203. psfDesktop->ParseDisplayName(NULL, NULL, wszPath, &cchEaten, &pidl, NULL);
  204. psfDesktop->Release();
  205. }
  206. return pidl;
  207. }
  208. //===========================================================================
  209. // IDLIST: Stream access
  210. // FEATURE: check bytes read on Read calls?
  211. //===========================================================================
  212. STDAPI ILLoadFromStream(IStream *pstm, LPITEMIDLIST * ppidl)
  213. {
  214. HRESULT hres;
  215. ULONG cb;
  216. // Delete the old one if any.
  217. if (*ppidl)
  218. {
  219. CoTaskMemFree(*ppidl);
  220. *ppidl = NULL;
  221. }
  222. // Read the size of the IDLIST
  223. cb = 0; // WARNING: We need to fill its HIWORD!
  224. hres = pstm->Read(&cb, sizeof(USHORT), NULL); // Yes, USHORT
  225. if (SUCCEEDED(hres) && cb)
  226. {
  227. // Create a IDLIST
  228. LPITEMIDLIST pidl = (LPITEMIDLIST)CoTaskMemAlloc(cb);
  229. if (pidl)
  230. {
  231. // Read its contents
  232. hres = pstm->Read(pidl, cb, NULL);
  233. if (SUCCEEDED(hres))
  234. {
  235. *ppidl = pidl;
  236. }
  237. else
  238. {
  239. CoTaskMemFree(pidl);
  240. }
  241. }
  242. else
  243. {
  244. hres = E_OUTOFMEMORY;
  245. }
  246. }
  247. return hres;
  248. }
  249. // FEATURE: check bytes written on Write calls?
  250. STDAPI ILSaveToStream(IStream *pstm, LPCITEMIDLIST pidl)
  251. {
  252. HRESULT hres;
  253. ULONG cb = ILGetSize(pidl);
  254. hres = pstm->Write(&cb, sizeof(USHORT), NULL); // Yes, USHORT
  255. if (SUCCEEDED(hres) && cb)
  256. {
  257. if (SUCCEEDED(hres))
  258. {
  259. hres = pstm->Write(pidl, cb, NULL);
  260. }
  261. }
  262. return hres;
  263. }
  264. #ifdef _HIDA
  265. //===========================================================================
  266. // IDLARRAY stuff
  267. //===========================================================================
  268. #define HIDA_GetPIDLFolder(pida) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[0])
  269. #define HIDA_GetPIDLItem(pida, i) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[i+1])
  270. HIDA HIDA_Create(LPCITEMIDLIST pidlFolder, UINT cidl, LPCITEMIDLIST * apidl)
  271. {
  272. HIDA hida;
  273. UINT i;
  274. UINT offset = sizeof(CIDA) + sizeof(UINT)*cidl;
  275. UINT cbTotal = offset + ILGetSize(pidlFolder);
  276. for (i=0; i<cidl ; i++) {
  277. cbTotal += ILGetSize(apidl[i]);
  278. }
  279. hida = GlobalAlloc(GPTR, cbTotal); // This MUST be GlobalAlloc!!!
  280. if (hida)
  281. {
  282. LPIDA pida = (LPIDA)hida; // no need to lock
  283. LPCITEMIDLIST pidlNext;
  284. pida->cidl = cidl;
  285. for (i=0, pidlNext=pidlFolder; ; pidlNext=apidl[i++])
  286. {
  287. UINT cbSize = ILGetSize(pidlNext);
  288. pida->aoffset[i] = offset;
  289. CopyMemory(((LPBYTE)pida)+offset, pidlNext, cbSize);
  290. offset += cbSize;
  291. if (i==cidl)
  292. break;
  293. }
  294. }
  295. return hida;
  296. }
  297. HIDA HIDA_Create2(LPVOID pida, UINT cb)
  298. {
  299. HIDA hida = GlobalAlloc(GPTR, cb);
  300. if (hida)
  301. {
  302. CopyMemory((LPIDA)hida, pida, cb); // no need to lock
  303. }
  304. return hida;
  305. }
  306. HIDA HIDA_Clone(HIDA hida)
  307. {
  308. UINT cbTotal = GlobalSize(hida);
  309. HIDA hidaCopy = GlobalAlloc(GPTR, cbTotal);
  310. if (hidaCopy)
  311. {
  312. LPIDA pida = (LPIDA)GlobalLock(hida);
  313. CopyMemory((LPIDA)hidaCopy, pida, cbTotal); // no need to lock
  314. GlobalUnlock(hida);
  315. }
  316. return hidaCopy;
  317. }
  318. UINT HIDA_GetCount(HIDA hida)
  319. {
  320. UINT count = 0;
  321. LPIDA pida = (LPIDA)GlobalLock(hida);
  322. if (pida) {
  323. count = pida->cidl;
  324. GlobalUnlock(hida);
  325. }
  326. return count;
  327. }
  328. UINT HIDA_GetIDList(HIDA hida, UINT i, LPITEMIDLIST pidlOut, UINT cbMax)
  329. {
  330. LPIDA pida = (LPIDA)GlobalLock(hida);
  331. if (pida)
  332. {
  333. LPCITEMIDLIST pidlFolder = HIDA_GetPIDLFolder(pida);
  334. LPCITEMIDLIST pidlItem = HIDA_GetPIDLItem(pida, i);
  335. UINT cbFolder = ILGetSize(pidlFolder)-sizeof(USHORT);
  336. UINT cbItem = ILGetSize(pidlItem);
  337. if (cbMax < cbFolder+cbItem) {
  338. if (pidlOut) {
  339. pidlOut->mkid.cb = 0;
  340. }
  341. } else {
  342. CopyMemory(pidlOut, pidlFolder, cbFolder);
  343. CopyMemory(((LPBYTE)pidlOut)+cbFolder, pidlItem, cbItem);
  344. }
  345. GlobalUnlock(hida);
  346. return (cbFolder+cbItem);
  347. }
  348. return 0;
  349. }
  350. //
  351. // This one reallocated pidl if necessary. NULL is valid to pass in as pidl.
  352. //
  353. LPITEMIDLIST HIDA_FillIDList(HIDA hida, UINT i, LPITEMIDLIST pidl)
  354. {
  355. UINT cbRequired = HIDA_GetIDList(hida, i, NULL, 0);
  356. pidl = _ILResize(pidl, cbRequired, 32); // extra 32-byte if we realloc
  357. if (pidl)
  358. {
  359. HIDA_GetIDList(hida, i, pidl, cbRequired);
  360. }
  361. return pidl;
  362. }
  363. LPCITEMIDLIST IDA_GetIDListPtr(LPIDA pida, UINT i)
  364. {
  365. if (i == (UINT)-1 || i < pida->cidl)
  366. {
  367. return HIDA_GetPIDLItem(pida, i);
  368. }
  369. return NULL;
  370. }
  371. LPITEMIDLIST IDA_ILClone(LPIDA pida, UINT i)
  372. {
  373. if (i < pida->cidl)
  374. return ILCombine(HIDA_GetPIDLFolder(pida), HIDA_GetPIDLItem(pida, i));
  375. return NULL;
  376. }
  377. LPITEMIDLIST HIDA_ILClone(HIDA hida, UINT i)
  378. {
  379. LPIDA pida = (LPIDA)GlobalLock(hida);
  380. if (pida)
  381. {
  382. LPITEMIDLIST pidl = IDA_ILClone(pida, i);
  383. GlobalUnlock(hida);
  384. return pidl;
  385. }
  386. return NULL;
  387. }
  388. void HIDA_ReleaseStgMedium(LPIDA pida, STGMEDIUM *pmedium)
  389. {
  390. if (pmedium->hGlobal && (pmedium->tymed==TYMED_HGLOBAL))
  391. {
  392. #ifdef DEBUG
  393. if (pida)
  394. {
  395. LPIDA pidaT = (LPIDA)GlobalLock(pmedium->hGlobal);
  396. GlobalUnlock(pmedium->hGlobal);
  397. }
  398. #endif
  399. GlobalUnlock(pmedium->hGlobal);
  400. }
  401. SHReleaseStgMedium(pmedium);
  402. }
  403. #endif // _HIDA
  404. #if 0
  405. //
  406. // This is a helper function to be called from within IShellFolder::CompareIDs.
  407. // When the first IDs of pidl1 and pidl2 are the (logically) same.
  408. //
  409. // Required:
  410. // psf && pidl1 && pidl2 && !ILEmpty(pidl1) && !ILEmpty(pidl2)
  411. //
  412. HRESULT ILCompareRelIDs(IShellFolder *psfParent, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  413. {
  414. HRESULT hres;
  415. LPCITEMIDLIST pidlRel1 = _ILNext(pidl1);
  416. LPCITEMIDLIST pidlRel2 = _ILNext(pidl2);
  417. if (ILIsEmpty(pidlRel1))
  418. {
  419. if (ILIsEmpty(pidlRel2)) {
  420. hres = 0;
  421. } else {
  422. hres = (HRESULT)-1;
  423. }
  424. }
  425. else
  426. {
  427. if (ILIsEmpty(pidlRel2))
  428. {
  429. hres = 1;
  430. }
  431. else
  432. {
  433. //
  434. // Neither pidlRel1 nor pidlRel2 is empty.
  435. // (1) Bind to the next level of the IShellFolder
  436. // (2) Call its CompareIDs to let it compare the rest of IDs.
  437. //
  438. // Notes: We should create pidlNext not from pidl2 but from pidl1
  439. // because fstreex.c may pass simple pidl2.
  440. //
  441. LPITEMIDLIST pidlNext = ILClone(pidl1);
  442. if (pidlNext)
  443. {
  444. IShellFolder *psfNext;
  445. _ILNext(pidlNext)->mkid.cb = 0;
  446. hres = psfParent->BindToObject(pidlNext, NULL,
  447. &IID_IShellFolder, &psfNext);
  448. if (SUCCEEDED(hres))
  449. {
  450. hres = psfNext->CompareIDs(0, pidlRel1, pidlRel2);
  451. psfNext->Release();
  452. }
  453. CoTaskMemFree(pidlNext);
  454. }
  455. else
  456. {
  457. hres = E_OUTOFMEMORY;
  458. }
  459. }
  460. }
  461. return hres;
  462. }
  463. void StrRetFormat(LPSTRRET pStrRet, LPCITEMIDLIST pidlRel, LPCSTR pszTemplate, LPCSTR pszAppend)
  464. {
  465. LPSTR pszRet;
  466. char szT[MAX_PATH];
  467. StrRetToStrN(szT, sizeof(szT), pStrRet, pidlRel);
  468. pszRet = ShellConstructMessageString(HINST_THISDLL, pszTemplate, pszAppend, szT);
  469. if (pszRet)
  470. {
  471. pStrRet->uType = STRRET_CSTR;
  472. lstrcpyn(pStrRet->cStr, pszRet, sizeof(pStrRet->cStr));
  473. Free(pszRet);
  474. }
  475. }
  476. //
  477. // Notes: This one passes SHGDN_FORMARSING to ISF::GetDisplayNameOf.
  478. //
  479. HRESULT ILGetRelDisplayName(IShellFolder *psf, LPSTRRET pStrRet,
  480. LPCITEMIDLIST pidlRel, LPCSTR pszName,
  481. LPCSTR pszTemplate)
  482. {
  483. HRESULT hres;
  484. LPITEMIDLIST pidlLeft = ILCloneFirst(pidlRel);
  485. if (pidlLeft)
  486. {
  487. IShellFolder *psfNext;
  488. hres = psf->BindToObject(pidlLeft, NULL, &IID_IShellFolder, &psfNext);
  489. if (SUCCEEDED(hres))
  490. {
  491. LPCITEMIDLIST pidlRight = _ILNext(pidlRel);
  492. hres = psfNext->GetDisplayNameOf(pidlRight, SHGDN_INFOLDER|SHGDN_FORPARSING, pStrRet);
  493. if (SUCCEEDED(hres))
  494. {
  495. if (pszTemplate)
  496. {
  497. StrRetFormat(pStrRet, pidlRight, pszTemplate, pszName);
  498. }
  499. else
  500. {
  501. hres = StrRetCatLeft(pszName, pStrRet, pidlRight);
  502. }
  503. }
  504. psfNext->Release();
  505. }
  506. CoTaskMemFree(pidlLeft);
  507. }
  508. else
  509. {
  510. hres = E_OUTOFMEMORY;
  511. }
  512. return hres;
  513. }
  514. LPITEMIDLIST ILAppendID(LPITEMIDLIST pidl, LPCSHITEMID pmkid, BOOL fAppend)
  515. {
  516. UINT cbUsed, cbRequired;
  517. // Create the ID list, if it is not given.
  518. if (!pidl)
  519. {
  520. pidl = ILCreate();
  521. if (!pidl)
  522. return NULL; // memory overflow
  523. }
  524. cbUsed = ILGetSize(pidl);
  525. cbRequired = cbUsed + pmkid->cb;
  526. pidl = _ILResize(pidl, cbRequired, CBIDL_INCL);
  527. if (!pidl)
  528. return NULL; // memory overflow
  529. if (fAppend)
  530. {
  531. // Append it.
  532. CopyMemory(_ILSkip(pidl, cbUsed-sizeof(pidl->mkid.cb)), pmkid, pmkid->cb);
  533. }
  534. else
  535. {
  536. // Put it at the top
  537. MoveMemory(_ILSkip(pidl, pmkid->cb), pidl, cbUsed);
  538. CopyMemory(pidl, pmkid, pmkid->cb);
  539. }
  540. // We must put zero-terminator because of LMEM_ZEROINIT.
  541. _ILSkip(pidl, cbRequired-sizeof(pidl->mkid.cb))->mkid.cb = 0;
  542. return pidl;
  543. }
  544. BOOL ILGetDisplayName(LPCITEMIDLIST pidl, LPSTR pszPath)
  545. {
  546. BOOL fSuccess = FALSE; // assume error
  547. STRRET srName;
  548. IShellFolder *psfDesktop;
  549. SHGetDesktopFolder(&psfDesktop);
  550. VALIDATE_PIDL(pidl);
  551. if (SUCCEEDED(psfDesktop->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &srName)))
  552. {
  553. StrRetToStrN(pszPath, MAX_PATH, &srName, pidl);
  554. fSuccess = TRUE;
  555. }
  556. return fSuccess;
  557. }
  558. #endif // not used