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.

1316 lines
31 KiB

  1. #include "priv.h"
  2. #pragma hdrstop
  3. // this is swiped from comctl32\mru.c
  4. #define SLOT_LOADED 0x01
  5. #define SLOT_USED 0x02
  6. typedef struct _SLOTITEMDATA
  7. {
  8. DWORD state;
  9. DWORD cb;
  10. BYTE *p;
  11. } SLOTITEMDATA;
  12. class CMruBase : public IMruDataList
  13. {
  14. public:
  15. CMruBase() : _cRef(1) {}
  16. // IUnknown methods
  17. STDMETHODIMP QueryInterface(REFIID riid, void **ppvOut);
  18. STDMETHODIMP_(ULONG) AddRef();
  19. STDMETHODIMP_(ULONG) Release();
  20. // IMruDataList (maybe?)
  21. STDMETHODIMP InitData(
  22. UINT uMax,
  23. MRULISTF flags,
  24. HKEY hKey,
  25. LPCWSTR pszSubKey,
  26. MRUDATALISTCOMPARE pfnCompare);
  27. STDMETHODIMP AddData(const BYTE *pData, DWORD cbData, DWORD *pdwSlot);
  28. STDMETHODIMP FindData(const BYTE *pData, DWORD cbData, int *piIndex);
  29. STDMETHODIMP GetData(int iIndex, BYTE *pData, DWORD cbData);
  30. STDMETHODIMP QueryInfo(int iIndex, DWORD *pdwSlot, DWORD *pcbData);
  31. STDMETHODIMP Delete(int iItem);
  32. protected:
  33. virtual ~CMruBase();
  34. HRESULT _GetItem(int iIndex, SLOTITEMDATA **ppitem);
  35. HRESULT _GetSlotItem(DWORD dwSlot, SLOTITEMDATA **ppitem);
  36. HRESULT _LoadItem(DWORD dwSlot);
  37. HRESULT _AddItem(DWORD dwSlot, const BYTE *pData, DWORD cbData);
  38. void _DeleteItem(DWORD dwSlot);
  39. HRESULT _UseEmptySlot(DWORD *pdwSlot);
  40. void _CheckUsedSlots();
  41. // virtuals that are optionally implemented
  42. virtual BOOL _IsEqual(SLOTITEMDATA *pitem, const BYTE *pData, DWORD cbData);
  43. virtual void _DeleteValue(LPCWSTR psz);
  44. // virtuals that must be implemented
  45. virtual HRESULT _InitSlots() = 0;
  46. virtual void _SaveSlots() = 0;
  47. virtual DWORD _UpdateSlots(int iIndex) = 0;
  48. virtual void _SlotString(DWORD dwSlot, LPWSTR psz, DWORD cch) = 0;
  49. virtual HRESULT _GetSlot(int iIndex, DWORD *pdwSlot) = 0;
  50. virtual HRESULT _RemoveSlot(int iIndex, DWORD *pdwSlot) = 0;
  51. protected:
  52. LONG _cRef;
  53. MRULISTF _flags;
  54. BOOL _fDirty;
  55. BOOL _fSlotsChecked;
  56. HKEY _hkMru;
  57. int _cMaxSlots;
  58. int _cUsedSlots;
  59. MRUDATALISTCOMPARE _pfnCompare;
  60. SLOTITEMDATA *_pItems;
  61. };
  62. class CMruLongList : public CMruBase
  63. {
  64. protected:
  65. virtual ~CMruLongList() { if (_rgdwSlots) { LocalFree(_rgdwSlots); _rgdwSlots = NULL; } }
  66. void _ImportShortList();
  67. virtual HRESULT _InitSlots();
  68. virtual void _SaveSlots();
  69. virtual DWORD _UpdateSlots(int iIndex);
  70. virtual void _SlotString(DWORD dwSlot, LPWSTR psz, DWORD cch);
  71. virtual HRESULT _GetSlot(int iIndex, DWORD *pdwSlot);
  72. virtual HRESULT _RemoveSlot(int iIndex, DWORD *pdwSlot);
  73. private:
  74. DWORD *_rgdwSlots;
  75. };
  76. STDMETHODIMP_(ULONG) CMruBase::AddRef()
  77. {
  78. return InterlockedIncrement(&_cRef);
  79. }
  80. #define szMRUEX TEXT("MRUListEx")
  81. #define szMRUEX_OLD TEXT("MRUList")
  82. STDMETHODIMP_(ULONG) CMruBase::Release()
  83. {
  84. if (InterlockedDecrement(&_cRef))
  85. return _cRef;
  86. _SaveSlots();
  87. delete this;
  88. return 0;
  89. }
  90. STDMETHODIMP CMruBase::QueryInterface(REFIID riid, void **ppvObj)
  91. {
  92. static const QITAB qit[] = {
  93. QITABENT(CMruBase, IMruDataList), // IID_IMruDataList
  94. { 0 },
  95. };
  96. return QISearch(this, qit, riid, ppvObj);
  97. }
  98. CMruBase::~CMruBase()
  99. {
  100. if (_hkMru)
  101. RegCloseKey(_hkMru);
  102. if (_pItems)
  103. {
  104. for (int i = 0; i < _cUsedSlots; i++)
  105. {
  106. if (_pItems[i].p)
  107. {
  108. LocalFree(_pItems[i].p);
  109. _pItems[i].p = NULL;
  110. }
  111. }
  112. LocalFree(_pItems);
  113. _pItems = NULL;
  114. }
  115. }
  116. class CMruShortList : public CMruBase
  117. {
  118. protected:
  119. virtual ~CMruShortList() { if (_rgchSlots) { LocalFree(_rgchSlots); _rgchSlots = NULL; } }
  120. virtual HRESULT _InitSlots();
  121. virtual void _SaveSlots();
  122. virtual DWORD _UpdateSlots(int iIndex);
  123. virtual void _SlotString(DWORD dwSlot, LPWSTR psz, DWORD cch);
  124. virtual HRESULT _GetSlot(int iIndex, DWORD *pdwSlot);
  125. virtual HRESULT _RemoveSlot(int iIndex, DWORD *pdwSlot);
  126. friend class CMruLongList;
  127. private:
  128. WCHAR *_rgchSlots;
  129. };
  130. HRESULT CMruShortList::_InitSlots()
  131. {
  132. HRESULT hr = E_OUTOFMEMORY;
  133. DWORD cb = (_cMaxSlots + 1) * sizeof(_rgchSlots[0]);
  134. _rgchSlots = (WCHAR *) LocalAlloc(LPTR, cb);
  135. if (_rgchSlots)
  136. {
  137. // Do we already have the new MRU Index?
  138. // Then validate it. You can never trust the registry not to be corrupted.
  139. // Must be at least the size of a DWORD
  140. // Must be a multiple of DWORD in length
  141. // Must end in a -1
  142. if (NOERROR == SHGetValue(_hkMru, NULL, szMRUEX_OLD, NULL, (LPBYTE)_rgchSlots, &cb))
  143. {
  144. ASSERT(!(cb % 2));
  145. _cUsedSlots = (cb / sizeof(_rgchSlots[0])) - 1;
  146. ASSERT(_rgchSlots[_cUsedSlots] == 0);
  147. }
  148. _rgchSlots[_cUsedSlots] = 0;
  149. hr = S_OK;
  150. }
  151. return hr;
  152. }
  153. void CMruShortList::_SaveSlots()
  154. {
  155. if (_fDirty)
  156. {
  157. SHSetValue(_hkMru, NULL, szMRUEX_OLD, REG_SZ, (BYTE *)_rgchSlots, sizeof(_rgchSlots[0]) * (_cUsedSlots + 1));
  158. _fDirty = FALSE;
  159. }
  160. }
  161. #define BASE_CHAR TEXT('a')
  162. void CMruShortList::_SlotString(DWORD dwSlot, LPWSTR psz, DWORD cch)
  163. {
  164. if (cch > 1)
  165. {
  166. psz[0] = (WCHAR) dwSlot + BASE_CHAR;
  167. psz[1] = 0;
  168. }
  169. }
  170. HRESULT CMruShortList::_GetSlot(int iIndex, DWORD *pdwSlot)
  171. {
  172. HRESULT hr = E_FAIL;
  173. if (iIndex < _cUsedSlots)
  174. {
  175. // its in our range of allocated slots
  176. if (_rgchSlots[iIndex] - BASE_CHAR < _cMaxSlots)
  177. {
  178. *pdwSlot = _rgchSlots[iIndex] - BASE_CHAR;
  179. _pItems[*pdwSlot].state |= SLOT_USED;
  180. hr = S_OK;
  181. }
  182. }
  183. return hr;
  184. }
  185. HRESULT CMruShortList::_RemoveSlot(int iIndex, DWORD *pdwSlot)
  186. {
  187. HRESULT hr = _GetSlot(iIndex, pdwSlot);
  188. if (SUCCEEDED(hr))
  189. {
  190. // MoveMemory() handles overlapping ranges
  191. MoveMemory(&_rgchSlots[iIndex], &_rgchSlots[iIndex+1], (_cUsedSlots - iIndex) * sizeof(_rgchSlots[0]));
  192. _cUsedSlots--;
  193. // unuse the slot
  194. _pItems->state &= ~SLOT_USED;
  195. _fDirty = TRUE;
  196. }
  197. return hr;
  198. }
  199. DWORD CMruShortList::_UpdateSlots(int iIndex)
  200. {
  201. // need to move this away
  202. DWORD dwSlot;
  203. DWORD cb = iIndex * sizeof(_rgchSlots[0]);
  204. if (iIndex != _cUsedSlots)
  205. dwSlot = _rgchSlots[iIndex] - BASE_CHAR;
  206. else
  207. {
  208. // we are at the end of the list
  209. // see if we can grow
  210. // find the first unused slot
  211. if (SUCCEEDED(_UseEmptySlot(&dwSlot)))
  212. {
  213. // need to move the terminator
  214. cb += sizeof(_rgchSlots[0]);
  215. }
  216. else
  217. {
  218. // dont move the the terminator
  219. // and dont move the last slot
  220. dwSlot = _rgchSlots[_cUsedSlots - 1] - BASE_CHAR;
  221. cb -= sizeof(_rgchSlots[0]);
  222. }
  223. }
  224. if (cb)
  225. {
  226. // MoveMemory() handles overlapping ranges
  227. MoveMemory(&_rgchSlots[1], &_rgchSlots[0], cb);
  228. _rgchSlots[0] = (WCHAR) dwSlot + BASE_CHAR;
  229. _fDirty = TRUE;
  230. }
  231. return dwSlot;
  232. }
  233. HRESULT CMruBase::InitData(
  234. UINT uMax,
  235. MRULISTF flags,
  236. HKEY hKey,
  237. LPCWSTR pszSubKey,
  238. MRUDATALISTCOMPARE pfnCompare)
  239. {
  240. HRESULT hr = E_FAIL;
  241. _flags = flags;
  242. _pfnCompare = pfnCompare;
  243. _cMaxSlots = uMax;
  244. if (pszSubKey)
  245. {
  246. RegCreateKeyEx(hKey, pszSubKey, 0L, NULL, 0, MAXIMUM_ALLOWED, NULL, &_hkMru, NULL);
  247. }
  248. else
  249. _hkMru = SHRegDuplicateHKey(hKey);
  250. if (_hkMru)
  251. {
  252. _pItems = (SLOTITEMDATA *) LocalAlloc(LPTR, sizeof(SLOTITEMDATA) * _cMaxSlots);
  253. if (_pItems)
  254. hr = _InitSlots();
  255. else
  256. hr = E_OUTOFMEMORY;
  257. }
  258. return hr;
  259. }
  260. void CMruBase::_CheckUsedSlots()
  261. {
  262. ASSERT(!_fSlotsChecked);
  263. DWORD dwSlot;
  264. for (int i = 0; i < _cUsedSlots; i++)
  265. {
  266. _GetSlot(i, &dwSlot);
  267. }
  268. _fSlotsChecked = TRUE;
  269. }
  270. HRESULT CMruBase::_AddItem(DWORD dwSlot, const BYTE *pData, DWORD cbData)
  271. {
  272. SLOTITEMDATA *pitem = &_pItems[dwSlot];
  273. WCHAR szSlot[12];
  274. _SlotString(dwSlot, szSlot, ARRAYSIZE(szSlot));
  275. HRESULT hr = E_OUTOFMEMORY;
  276. if (NOERROR == SHSetValue(_hkMru, NULL, szSlot, REG_BINARY, pData, cbData))
  277. {
  278. if (cbData >= pitem->cb || !pitem->p)
  279. {
  280. if (pitem->p)
  281. LocalFree(pitem->p);
  282. // Binary data has the size at the begining so we'll need a little extra room.
  283. pitem->p = (BYTE *)LocalAlloc(LPTR, cbData);
  284. }
  285. if (pitem->p)
  286. {
  287. pitem->cb = cbData;
  288. pitem->state = (SLOT_LOADED | SLOT_USED);
  289. memcpy(pitem->p, pData, cbData);
  290. hr = S_OK;
  291. }
  292. }
  293. return hr;
  294. }
  295. HRESULT CMruBase::AddData(const BYTE *pData, DWORD cbData, DWORD *pdwSlot)
  296. {
  297. HRESULT hr = E_FAIL;
  298. int iIndex;
  299. DWORD dwSlot;
  300. if (SUCCEEDED(FindData(pData, cbData, &iIndex)))
  301. {
  302. dwSlot = _UpdateSlots(iIndex);
  303. hr = S_OK;
  304. }
  305. else
  306. {
  307. dwSlot = _UpdateSlots(_cUsedSlots);
  308. hr = _AddItem(dwSlot, pData, cbData);
  309. }
  310. if (SUCCEEDED(hr) && pdwSlot)
  311. *pdwSlot = dwSlot;
  312. return hr;
  313. }
  314. BOOL CMruBase::_IsEqual(SLOTITEMDATA *pitem, const BYTE *pData, DWORD cbData)
  315. {
  316. BOOL fRet = FALSE;
  317. if (_pfnCompare)
  318. {
  319. fRet = (0 == _pfnCompare(pData, pitem->p, cbData));
  320. }
  321. else
  322. {
  323. switch (_flags & 0xf)
  324. {
  325. case MRULISTF_USE_MEMCMP:
  326. if (pitem->cb == cbData)
  327. fRet = (0 == memcmp(pData, pitem->p, min(cbData, pitem->cb)));
  328. break;
  329. case MRULISTF_USE_STRCMPIW:
  330. fRet = (0 == StrCmpIW((LPCWSTR)pData, (LPCWSTR)pitem->p));
  331. break;
  332. case MRULISTF_USE_STRCMPW:
  333. fRet = (0 == StrCmpW((LPCWSTR)pData, (LPCWSTR)pitem->p));
  334. break;
  335. case MRULISTF_USE_ILISEQUAL:
  336. fRet = ILIsEqual((LPCITEMIDLIST)pData, (LPCITEMIDLIST)pitem->p);
  337. break;
  338. }
  339. }
  340. return fRet;
  341. }
  342. HRESULT CMruBase::FindData(const BYTE *pData, DWORD cbData, int *piIndex)
  343. {
  344. HRESULT hr = E_FAIL;
  345. for (int iIndex = 0; iIndex < _cUsedSlots ; iIndex++)
  346. {
  347. SLOTITEMDATA *pitem;
  348. if (SUCCEEDED(_GetItem(iIndex, &pitem)))
  349. {
  350. if (_IsEqual(pitem, pData, cbData))
  351. {
  352. hr = S_OK;
  353. *piIndex = iIndex;
  354. break;
  355. }
  356. }
  357. }
  358. return hr;
  359. }
  360. HRESULT CMruBase::_LoadItem(DWORD dwSlot)
  361. {
  362. SLOTITEMDATA *pitem = &_pItems[dwSlot];
  363. DWORD cb;
  364. WCHAR szSlot[12];
  365. _SlotString(dwSlot, szSlot, ARRAYSIZE(szSlot));
  366. ASSERT(!(pitem->state & SLOT_LOADED));
  367. ASSERT(pitem->state & SLOT_USED);
  368. if (NOERROR == SHGetValue(_hkMru, NULL, szSlot, NULL, NULL, &cb) && cb)
  369. {
  370. // Binary data has the size at the begining so we'll need a little extra room.
  371. pitem->p = (BYTE *)LocalAlloc(LPTR, cb);
  372. if (pitem->p)
  373. {
  374. pitem->cb = cb;
  375. if (NOERROR != SHGetValue(_hkMru, NULL, szSlot, NULL, pitem->p, &cb))
  376. {
  377. LocalFree(pitem->p);
  378. pitem->p = NULL;
  379. }
  380. }
  381. }
  382. pitem->state |= SLOT_LOADED;
  383. return pitem->p ? S_OK : E_FAIL;
  384. }
  385. HRESULT CMruBase::_GetSlotItem(DWORD dwSlot, SLOTITEMDATA **ppitem)
  386. {
  387. HRESULT hr = S_OK;
  388. ASSERT(dwSlot < (DWORD)_cMaxSlots);
  389. if (!(_pItems[dwSlot].state & SLOT_LOADED))
  390. _LoadItem(dwSlot);
  391. if (_pItems[dwSlot].p)
  392. {
  393. *ppitem = &_pItems[dwSlot];
  394. return S_OK;
  395. }
  396. return E_OUTOFMEMORY;
  397. }
  398. HRESULT CMruBase::_GetItem(int iIndex, SLOTITEMDATA **ppitem)
  399. {
  400. DWORD dwSlot;
  401. HRESULT hr = _GetSlot(iIndex, &dwSlot);
  402. if (SUCCEEDED(hr))
  403. {
  404. hr = _GetSlotItem(dwSlot, ppitem);
  405. }
  406. return hr;
  407. }
  408. HRESULT CMruBase::GetData(int iIndex, BYTE *pData, DWORD cbData)
  409. {
  410. SLOTITEMDATA *pitem;
  411. HRESULT hr = _GetItem(iIndex, &pitem);
  412. if (SUCCEEDED(hr))
  413. {
  414. if (pitem->cb <= cbData)
  415. {
  416. memcpy(pData, pitem->p, min(cbData, pitem->cb));
  417. }
  418. else
  419. hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
  420. }
  421. return hr;
  422. }
  423. HRESULT CMruBase::QueryInfo(int iIndex, DWORD *pdwSlot, DWORD *pcbData)
  424. {
  425. DWORD dwSlot;
  426. HRESULT hr = _GetSlot(iIndex, &dwSlot);
  427. if (SUCCEEDED(hr))
  428. {
  429. if (pdwSlot)
  430. *pdwSlot = dwSlot;
  431. if (pcbData)
  432. {
  433. SLOTITEMDATA *pitem;
  434. hr = _GetSlotItem(dwSlot, &pitem);
  435. if (SUCCEEDED(hr))
  436. {
  437. *pcbData = pitem->cb;
  438. }
  439. }
  440. }
  441. return hr;
  442. }
  443. HRESULT CMruBase::_UseEmptySlot(DWORD *pdwSlot)
  444. {
  445. HRESULT hr = E_FAIL;
  446. if (!_fSlotsChecked)
  447. _CheckUsedSlots();
  448. for (DWORD dw = 0; dw < (DWORD) _cMaxSlots; dw++)
  449. {
  450. if (!(_pItems[dw].state & SLOT_USED))
  451. {
  452. _pItems[dw].state |= SLOT_USED;
  453. *pdwSlot = dw;
  454. _cUsedSlots++;
  455. hr = S_OK;
  456. break;
  457. }
  458. }
  459. return hr;
  460. }
  461. void CMruBase::_DeleteValue(LPCWSTR psz)
  462. {
  463. SHDeleteValue(_hkMru, NULL, psz);
  464. }
  465. void CMruBase::_DeleteItem(DWORD dwSlot)
  466. {
  467. ASSERT(dwSlot < (DWORD) _cMaxSlots);
  468. WCHAR szSlot[12];
  469. _SlotString(dwSlot, szSlot, ARRAYSIZE(szSlot));
  470. _DeleteValue(szSlot);
  471. if (_pItems[dwSlot].p)
  472. {
  473. LocalFree(_pItems[dwSlot].p);
  474. _pItems[dwSlot].p = NULL;
  475. }
  476. }
  477. HRESULT CMruBase::Delete(int iIndex)
  478. {
  479. DWORD dwSlot;
  480. HRESULT hr = _RemoveSlot(iIndex, &dwSlot);
  481. if (SUCCEEDED(hr))
  482. {
  483. _DeleteItem(dwSlot);
  484. }
  485. return hr;
  486. }
  487. void CMruLongList::_ImportShortList()
  488. {
  489. CMruShortList *pmru = new CMruShortList();
  490. if (pmru)
  491. {
  492. if (SUCCEEDED(pmru->InitData(_cMaxSlots, 0, _hkMru, NULL, NULL)))
  493. {
  494. // we need to walk the list
  495. DWORD dwSlot;
  496. SLOTITEMDATA *pitem;
  497. while (SUCCEEDED(pmru->_GetSlot(_cUsedSlots, &dwSlot))
  498. && SUCCEEDED(pmru->_GetSlotItem(dwSlot, &pitem)))
  499. {
  500. // we just copy to ourselves
  501. _AddItem(dwSlot, pitem->p, pitem->cb);
  502. pmru->_DeleteItem(dwSlot);
  503. // dont use _UpdateSlots() here
  504. _rgdwSlots[_cUsedSlots] = dwSlot;
  505. _cUsedSlots++;
  506. }
  507. _fDirty = TRUE;
  508. }
  509. pmru->Release();
  510. // wipe it out
  511. SHDeleteValue(_hkMru, NULL, szMRUEX_OLD);
  512. }
  513. }
  514. HRESULT CMruLongList::_InitSlots()
  515. {
  516. HRESULT hr = E_OUTOFMEMORY;
  517. DWORD cb = (_cMaxSlots + 1) * sizeof(_rgdwSlots[0]);
  518. _rgdwSlots = (DWORD *) LocalAlloc(LPTR, cb);
  519. if (_rgdwSlots)
  520. {
  521. // Do we already have the new MRU Index?
  522. // Then validate it. You can never trust the registry not to be corrupted.
  523. // Must be at least the size of a DWORD
  524. // Must be a multiple of DWORD in length
  525. // Must end in a -1
  526. if (NOERROR == SHGetValue(_hkMru, NULL, szMRUEX, NULL, (LPBYTE)_rgdwSlots, &cb))
  527. {
  528. ASSERT(!(cb % 4));
  529. _cUsedSlots = (cb / sizeof(_rgdwSlots[0])) - 1;
  530. ASSERT(_rgdwSlots[_cUsedSlots] == -1);
  531. }
  532. else
  533. {
  534. _ImportShortList();
  535. }
  536. _rgdwSlots[_cUsedSlots] = (DWORD)-1;
  537. hr = S_OK;
  538. }
  539. return hr;
  540. }
  541. void CMruLongList::_SaveSlots()
  542. {
  543. if (_fDirty)
  544. {
  545. SHSetValue(_hkMru, NULL, szMRUEX, REG_BINARY, (BYTE *)_rgdwSlots, sizeof(_rgdwSlots[0]) * (_cUsedSlots + 1));
  546. _fDirty = FALSE;
  547. }
  548. }
  549. void CMruLongList::_SlotString(DWORD dwSlot, LPWSTR psz, DWORD cch)
  550. {
  551. wnsprintfW(psz, cch, L"%d", dwSlot);
  552. }
  553. HRESULT CMruLongList::_GetSlot(int iIndex, DWORD *pdwSlot)
  554. {
  555. HRESULT hr = E_FAIL;
  556. ASSERT(iIndex < _cMaxSlots);
  557. if (iIndex < _cUsedSlots)
  558. {
  559. // its in our range of allocated slots
  560. if (_rgdwSlots[iIndex] < (DWORD) _cMaxSlots)
  561. {
  562. *pdwSlot = _rgdwSlots[iIndex];
  563. _pItems[*pdwSlot].state |= SLOT_USED;
  564. hr = S_OK;
  565. }
  566. }
  567. return hr;
  568. }
  569. HRESULT CMruLongList::_RemoveSlot(int iIndex, DWORD *pdwSlot)
  570. {
  571. HRESULT hr = _GetSlot(iIndex, pdwSlot);
  572. if (SUCCEEDED(hr))
  573. {
  574. // MoveMemory() handles overlapping ranges
  575. MoveMemory(&_rgdwSlots[iIndex], &_rgdwSlots[iIndex+1], (_cUsedSlots - iIndex) * sizeof(_rgdwSlots[0]));
  576. _cUsedSlots--;
  577. // unuse the slot
  578. _pItems->state &= ~SLOT_USED;
  579. _fDirty = TRUE;
  580. }
  581. return hr;
  582. }
  583. DWORD CMruLongList::_UpdateSlots(int iIndex)
  584. {
  585. // need to move this away
  586. DWORD dwSlot;
  587. DWORD cb = iIndex * sizeof(_rgdwSlots[0]);
  588. if (iIndex != _cUsedSlots)
  589. dwSlot = _rgdwSlots[iIndex];
  590. else
  591. {
  592. // we are at the end of the list
  593. // see if we can grow
  594. // find the first unused slot
  595. if (SUCCEEDED(_UseEmptySlot(&dwSlot)))
  596. {
  597. // need to move the terminator
  598. cb += sizeof(_rgdwSlots[0]);
  599. }
  600. else
  601. {
  602. // dont move the the terminator
  603. // and dont move the last slot
  604. dwSlot = _rgdwSlots[_cUsedSlots - 1];
  605. cb -= sizeof(_rgdwSlots[0]);
  606. }
  607. }
  608. if (cb)
  609. {
  610. // MoveMemory() handles overlapping ranges
  611. MoveMemory(&_rgdwSlots[1], &_rgdwSlots[0], cb);
  612. _rgdwSlots[0] = dwSlot;
  613. _fDirty = TRUE;
  614. }
  615. return dwSlot;
  616. }
  617. STDAPI CMruLongList_CreateInstance(IUnknown * punkOuter, IUnknown ** ppunk, LPCOBJECTINFO poi)
  618. {
  619. *ppunk = NULL;
  620. CMruLongList *p = new CMruLongList();
  621. if (p != NULL)
  622. {
  623. *ppunk = SAFECAST(p, IMruDataList *);
  624. return S_OK;
  625. }
  626. return E_OUTOFMEMORY;
  627. }
  628. class CMruPidlList;
  629. class CMruNode : public CMruLongList
  630. {
  631. public:
  632. CMruNode(CMruNode *pnodeParent, DWORD dwSlot);
  633. HRESULT GetNode(BOOL fCreate, LPCITEMIDLIST pidlChild, CMruNode **ppnode);
  634. HRESULT RemoveLeast(DWORD *pdwSlotLeast);
  635. HRESULT BindToSlot(DWORD dwSlot, IShellFolder **ppsf);
  636. HRESULT Clear(CMruPidlList *proot);
  637. CMruNode *GetParent()
  638. { if (_pnodeParent) _pnodeParent->AddRef(); return _pnodeParent;}
  639. HRESULT GetNodeSlot(DWORD *pdwNodeSlot)
  640. {
  641. DWORD cb = sizeof(*pdwNodeSlot);
  642. return NOERROR == SHGetValue(_hkMru, NULL, L"NodeSlot", NULL, pdwNodeSlot, pdwNodeSlot ? &cb : NULL) ? S_OK : E_FAIL;
  643. }
  644. HRESULT SetNodeSlot(DWORD dwNodeSlot)
  645. { return NOERROR == SHSetValue(_hkMru, NULL, L"NodeSlot", REG_DWORD, &dwNodeSlot, sizeof(dwNodeSlot)) ? S_OK : E_FAIL; }
  646. protected:
  647. CMruNode() {}
  648. virtual ~CMruNode();
  649. virtual BOOL _IsEqual(SLOTITEMDATA *pitem, const BYTE *pData, DWORD cbData);
  650. virtual void _DeleteValue(LPCWSTR psz);
  651. HRESULT _GetPidlSlot(LPCITEMIDLIST pidlChild, BOOL fCreate, DWORD *pdwKidSlot);
  652. HRESULT _CreateNode(DWORD dwSlot, CMruNode **ppnode);
  653. BOOL _InitLate();
  654. HRESULT _FindPidl(LPCITEMIDLIST pidl, int *piIndex)
  655. { return FindData((LPBYTE)pidl, pidl->mkid.cb + sizeof(pidl->mkid.cb), piIndex); }
  656. HRESULT _AddPidl(DWORD dwSlot, LPCITEMIDLIST pidl)
  657. { return _AddItem(dwSlot, (LPBYTE)pidl, pidl->mkid.cb + sizeof(pidl->mkid.cb)); }
  658. #ifdef DEBUG
  659. HRESULT _GetSlotName(DWORD dwSlot, LPWSTR psz, DWORD cch);
  660. #endif
  661. protected:
  662. DWORD _dwSlotSelf;
  663. CMruNode *_pnodeParent;
  664. IShellFolder *_psf;
  665. };
  666. class CMruPidlList : public CMruNode
  667. , public IMruPidlList
  668. {
  669. public:
  670. CMruPidlList() {}
  671. // IUnknown methods
  672. STDMETHODIMP QueryInterface(REFIID riid, void **ppvOut);
  673. STDMETHODIMP_(ULONG) AddRef()
  674. {
  675. return CMruBase::AddRef();
  676. }
  677. STDMETHODIMP_(ULONG) Release()
  678. {
  679. return CMruBase::Release();
  680. }
  681. // IMruPidlList
  682. STDMETHODIMP InitList(UINT uMax, HKEY hKey, LPCWSTR pszSubKey);
  683. STDMETHODIMP UsePidl(LPCITEMIDLIST pidl, DWORD *pdwSlot);
  684. STDMETHODIMP QueryPidl(LPCITEMIDLIST pidl, DWORD cSlots, DWORD *rgdwSlots, DWORD *pcSlotsFetched);
  685. STDMETHODIMP PruneKids(LPCITEMIDLIST pidl);
  686. HRESULT GetEmptySlot(DWORD *pdwSlot);
  687. void EmptyNodeSlot(DWORD dwNodeSlot);
  688. protected:
  689. ~CMruPidlList()
  690. {
  691. if (_rgbNodeSlots)
  692. {
  693. LocalFree(_rgbNodeSlots);
  694. _rgbNodeSlots = NULL;
  695. }
  696. if (_hMutex)
  697. CloseHandle(_hMutex);
  698. }
  699. BOOL _LoadNodeSlots();
  700. void _SaveNodeSlots();
  701. HRESULT _InitNodeSlots();
  702. protected:
  703. BYTE *_rgbNodeSlots;
  704. int _cUsedNodeSlots ;
  705. HANDLE _hMutex;
  706. };
  707. CMruNode::CMruNode(CMruNode *pnodeParent, DWORD dwSlot)
  708. : _pnodeParent(pnodeParent), _dwSlotSelf(dwSlot)
  709. {
  710. ASSERT(_cRef);
  711. _pnodeParent->AddRef();
  712. }
  713. CMruNode::~CMruNode()
  714. {
  715. if (_pnodeParent)
  716. _pnodeParent->Release();
  717. if (_psf)
  718. _psf->Release();
  719. }
  720. HRESULT CMruNode::BindToSlot(DWORD dwSlot, IShellFolder **ppsf)
  721. {
  722. SLOTITEMDATA *pitem;
  723. HRESULT hr = _GetSlotItem(dwSlot, &pitem);
  724. if (SUCCEEDED(hr))
  725. {
  726. hr = _psf->BindToObject((LPCITEMIDLIST)pitem->p, NULL, IID_PPV_ARG(IShellFolder, ppsf));
  727. }
  728. return hr;
  729. }
  730. #ifdef DEBUG
  731. HRESULT CMruNode::_GetSlotName(DWORD dwSlot, LPWSTR psz, DWORD cch)
  732. {
  733. SLOTITEMDATA *pitem;
  734. HRESULT hr = _GetSlotItem(dwSlot, &pitem);
  735. if (SUCCEEDED(hr))
  736. {
  737. hr = DisplayNameOf(_psf, (LPCITEMIDLIST)pitem->p, 0, psz, cch);
  738. }
  739. return hr;
  740. }
  741. #endif
  742. BOOL CMruNode::_IsEqual(SLOTITEMDATA *pitem, const BYTE *pData, DWORD cbData)
  743. {
  744. return S_OK == IShellFolder_CompareIDs(_psf, SHCIDS_CANONICALONLY, (LPCITEMIDLIST)pitem->p, (LPCITEMIDLIST)pData);
  745. }
  746. HRESULT CMruNode::_CreateNode(DWORD dwSlot, CMruNode **ppnode)
  747. {
  748. HRESULT hr = E_OUTOFMEMORY;
  749. CMruNode *pnode = new CMruNode(this, dwSlot);
  750. if (pnode)
  751. {
  752. WCHAR szSlot[12];
  753. _SlotString(dwSlot, szSlot, ARRAYSIZE(szSlot));
  754. hr = pnode->InitData(_cMaxSlots, 0, _hkMru, szSlot, NULL);
  755. if (SUCCEEDED(hr))
  756. *ppnode = pnode;
  757. else
  758. pnode->Release();
  759. }
  760. return hr;
  761. }
  762. BOOL CMruNode::_InitLate()
  763. {
  764. if (!_psf)
  765. {
  766. if (_pnodeParent)
  767. {
  768. _pnodeParent->BindToSlot(_dwSlotSelf, &_psf);
  769. #ifdef DEBUG
  770. WCHAR sz[MAX_PATH];
  771. if (SUCCEEDED(_pnodeParent->_GetSlotName(_dwSlotSelf, sz, ARRAYSIZE(sz))))
  772. SHSetValue(_hkMru, NULL, L"SlotName", REG_SZ, sz, CbFromCchW(lstrlen(sz) + 1));
  773. #endif
  774. }
  775. else
  776. SHGetDesktopFolder(&_psf);
  777. }
  778. return (_psf != NULL);
  779. }
  780. HRESULT CMruNode::_GetPidlSlot(LPCITEMIDLIST pidlChild, BOOL fCreate, DWORD *pdwKidSlot)
  781. {
  782. HRESULT hr = E_OUTOFMEMORY;
  783. LPITEMIDLIST pidlFirst = ILCloneFirst(pidlChild);
  784. if (pidlFirst)
  785. {
  786. int iIndex;
  787. if (SUCCEEDED(_FindPidl(pidlFirst, &iIndex)))
  788. {
  789. *pdwKidSlot = _UpdateSlots(iIndex);
  790. hr = S_OK;
  791. }
  792. else if (fCreate)
  793. {
  794. *pdwKidSlot = _UpdateSlots(_cUsedSlots);
  795. hr = _AddPidl(*pdwKidSlot, pidlFirst);
  796. }
  797. ILFree(pidlFirst);
  798. }
  799. return hr;
  800. }
  801. HRESULT CMruNode::GetNode(BOOL fCreate, LPCITEMIDLIST pidlChild, CMruNode **ppnode)
  802. {
  803. HRESULT hr = E_FAIL;
  804. if (ILIsEmpty(pidlChild))
  805. {
  806. *ppnode = this;
  807. AddRef();
  808. hr = S_OK;
  809. }
  810. else if (_InitLate())
  811. {
  812. DWORD dwKidSlot;
  813. hr = _GetPidlSlot(pidlChild, fCreate, &dwKidSlot);
  814. if (SUCCEEDED(hr))
  815. {
  816. // need to make another CMruNode
  817. CMruNode *pnode;
  818. hr = _CreateNode(dwKidSlot, &pnode);
  819. if (SUCCEEDED(hr))
  820. {
  821. // need to save so that this node
  822. // is updated so that it doesnt get
  823. // deleted from under us.
  824. _SaveSlots();
  825. hr = pnode->GetNode(fCreate, _ILNext(pidlChild), ppnode);
  826. pnode->Release();
  827. }
  828. }
  829. if (FAILED(hr) && !fCreate)
  830. {
  831. *ppnode = this;
  832. AddRef();
  833. hr = S_FALSE;
  834. }
  835. }
  836. return hr;
  837. }
  838. void CMruNode::_DeleteValue(LPCWSTR psz)
  839. {
  840. CMruBase::_DeleteValue(psz);
  841. SHDeleteKey(_hkMru, psz);
  842. }
  843. HRESULT CMruNode::RemoveLeast(DWORD *pdwSlotLeast)
  844. {
  845. // if this node has children
  846. // then we attempt to call RemoveLeast on them
  847. ASSERT(_cUsedSlots >= 0);
  848. HRESULT hr = S_FALSE;
  849. if (_cUsedSlots)
  850. {
  851. DWORD dwLocalSlot;
  852. hr = _GetSlot(_cUsedSlots - 1, &dwLocalSlot);
  853. if (SUCCEEDED(hr))
  854. {
  855. CMruNode *pnode;
  856. hr = _CreateNode(dwLocalSlot, &pnode);
  857. if (SUCCEEDED(hr))
  858. {
  859. hr = pnode->RemoveLeast(pdwSlotLeast);
  860. pnode->Release();
  861. }
  862. // S_FALSE means that this node needs
  863. // needs deleting. it is empty.
  864. if (hr == S_FALSE)
  865. {
  866. Delete(_cUsedSlots - 1);
  867. // if we still have kids, or have a NodeSlot
  868. // then we dont want to be deleted
  869. if (_cUsedSlots || SUCCEEDED(GetNodeSlot(NULL)))
  870. hr = S_OK;
  871. }
  872. }
  873. }
  874. else
  875. {
  876. // this is the empty node
  877. // delete me if you can
  878. ASSERT(!*pdwSlotLeast);
  879. GetNodeSlot(pdwSlotLeast);
  880. }
  881. return hr;
  882. }
  883. HRESULT CMruNode::Clear(CMruPidlList *proot)
  884. {
  885. DWORD dwLocalSlot;
  886. while (SUCCEEDED(_GetSlot(0, &dwLocalSlot)))
  887. {
  888. CMruNode *pnode;
  889. if (SUCCEEDED(_CreateNode(dwLocalSlot, &pnode)))
  890. {
  891. // tell the root about it
  892. DWORD dwNodeSlot;
  893. if (SUCCEEDED(pnode->GetNodeSlot(&dwNodeSlot)))
  894. proot->EmptyNodeSlot(dwNodeSlot);
  895. pnode->Clear(proot);
  896. pnode->Release();
  897. }
  898. Delete(0);
  899. }
  900. return S_OK;
  901. }
  902. class CSafeMutex
  903. {
  904. public:
  905. CSafeMutex() : _h(0) {}
  906. ~CSafeMutex() { if (_h) ReleaseMutex(_h); }
  907. HRESULT Enter(HANDLE hMutex)
  908. {
  909. // this is usually done on the UI thread
  910. // wait for half a second or dont bother
  911. HRESULT hr;
  912. DWORD dwWait = WaitForSingleObject(hMutex, 500);
  913. if (dwWait == WAIT_OBJECT_0)
  914. {
  915. _h = hMutex;
  916. hr = S_OK;
  917. }
  918. else
  919. hr = E_FAIL;
  920. return hr;
  921. }
  922. private:
  923. HANDLE _h;
  924. };
  925. HRESULT CMruPidlList::UsePidl(LPCITEMIDLIST pidl, DWORD *pdwSlot)
  926. {
  927. CSafeMutex sm;
  928. HRESULT hr = sm.Enter(_hMutex);
  929. if (SUCCEEDED(hr))
  930. {
  931. CMruNode *pnode;
  932. hr = GetNode(TRUE, pidl, &pnode);
  933. *pdwSlot = 0;
  934. if (SUCCEEDED(hr))
  935. {
  936. ASSERT(hr == S_OK);
  937. hr = pnode->GetNodeSlot(pdwSlot);
  938. if (FAILED(hr))
  939. {
  940. hr = GetEmptySlot(pdwSlot);
  941. if (SUCCEEDED(hr))
  942. {
  943. hr = pnode->SetNodeSlot(*pdwSlot);
  944. }
  945. }
  946. pnode->Release();
  947. }
  948. }
  949. return hr;
  950. }
  951. HRESULT CMruPidlList::QueryPidl(LPCITEMIDLIST pidl, DWORD cSlots, DWORD *rgdwSlots, DWORD *pcSlotsFetched)
  952. {
  953. CSafeMutex sm;
  954. HRESULT hr = sm.Enter(_hMutex);
  955. if (SUCCEEDED(hr))
  956. {
  957. CMruNode *pnode;
  958. hr = GetNode(FALSE, pidl, &pnode);
  959. *pcSlotsFetched = 0;
  960. if (SUCCEEDED(hr))
  961. {
  962. while (*pcSlotsFetched < cSlots && pnode)
  963. {
  964. CMruNode *pnodeParent = pnode->GetParent();
  965. if (SUCCEEDED(pnode->GetNodeSlot(&rgdwSlots[*pcSlotsFetched])))
  966. {
  967. (*pcSlotsFetched)++;
  968. }
  969. else if (hr == S_OK && !*pcSlotsFetched)
  970. {
  971. // we found the exact node
  972. // but we couldnt get the NodeSlot from it
  973. hr = S_FALSE;
  974. }
  975. pnode->Release();
  976. pnode = pnodeParent;
  977. }
  978. if (pnode)
  979. pnode->Release();
  980. }
  981. if (SUCCEEDED(hr) && !*pcSlotsFetched)
  982. hr = E_FAIL;
  983. }
  984. return hr;
  985. }
  986. HRESULT CMruPidlList::PruneKids(LPCITEMIDLIST pidl)
  987. {
  988. CSafeMutex sm;
  989. HRESULT hr = sm.Enter(_hMutex);
  990. if (SUCCEEDED(hr))
  991. {
  992. if (_LoadNodeSlots())
  993. {
  994. CMruNode *pnode;
  995. hr = GetNode(FALSE, pidl, &pnode);
  996. if (SUCCEEDED(hr))
  997. {
  998. if (hr == S_OK)
  999. {
  1000. hr = pnode->Clear(this);
  1001. }
  1002. else
  1003. hr = E_FAIL;
  1004. pnode->Release();
  1005. }
  1006. _SaveNodeSlots();
  1007. }
  1008. }
  1009. return hr;
  1010. }
  1011. STDMETHODIMP CMruPidlList::QueryInterface(REFIID riid, void **ppvObj)
  1012. {
  1013. static const QITAB qit[] = {
  1014. QITABENT(CMruPidlList, IMruPidlList), // IID_IMruDataList
  1015. { 0 },
  1016. };
  1017. return QISearch(this, qit, riid, ppvObj);
  1018. }
  1019. HRESULT CMruPidlList::InitList(UINT uMax, HKEY hKey, LPCWSTR pszSubKey)
  1020. {
  1021. HRESULT hr = InitData(uMax, 0, hKey, pszSubKey, NULL);
  1022. if (SUCCEEDED(hr))
  1023. {
  1024. hr = _InitNodeSlots();
  1025. if (SUCCEEDED(hr))
  1026. {
  1027. _hMutex = CreateMutex(NULL, FALSE, TEXT("Shell.CMruPidlList"));
  1028. if (!_hMutex)
  1029. hr = ResultFromLastError();
  1030. }
  1031. }
  1032. return hr;
  1033. }
  1034. BOOL CMruPidlList::_LoadNodeSlots()
  1035. {
  1036. DWORD cb = (_cMaxSlots) * sizeof(_rgbNodeSlots[0]);
  1037. if (NOERROR == SHGetValue(_hkMru, NULL, L"NodeSlots", NULL, _rgbNodeSlots , &cb))
  1038. {
  1039. _cUsedNodeSlots = (cb / sizeof(_rgbNodeSlots[0]));
  1040. return TRUE;
  1041. }
  1042. return FALSE;
  1043. }
  1044. void CMruPidlList::_SaveNodeSlots()
  1045. {
  1046. SHSetValue(_hkMru, NULL, L"NodeSlots", REG_BINARY, _rgbNodeSlots , _cUsedNodeSlots);
  1047. }
  1048. HRESULT CMruPidlList::_InitNodeSlots()
  1049. {
  1050. HRESULT hr = E_OUTOFMEMORY;
  1051. DWORD cb = (_cMaxSlots) * sizeof(_rgbNodeSlots[0]);
  1052. _rgbNodeSlots = (BYTE *) LocalAlloc(LPTR, cb);
  1053. if (_rgbNodeSlots)
  1054. {
  1055. _LoadNodeSlots();
  1056. _fDirty = TRUE;
  1057. _SaveNodeSlots();
  1058. hr = S_OK;
  1059. }
  1060. return hr;
  1061. }
  1062. void CMruPidlList::EmptyNodeSlot(DWORD dwNodeSlot)
  1063. {
  1064. ASSERT(dwNodeSlot <= (DWORD)_cMaxSlots);
  1065. _rgbNodeSlots[dwNodeSlot-1] = FALSE;
  1066. _fDirty = TRUE;
  1067. }
  1068. HRESULT CMruPidlList::GetEmptySlot(DWORD *pdwSlot)
  1069. {
  1070. HRESULT hr = E_FAIL;
  1071. *pdwSlot = 0;
  1072. if (_LoadNodeSlots())
  1073. {
  1074. if (_cUsedNodeSlots < _cMaxSlots)
  1075. {
  1076. // then we can just use the next most natural
  1077. // node slot
  1078. _rgbNodeSlots[_cUsedNodeSlots] = SLOT_USED;
  1079. *pdwSlot = ++_cUsedNodeSlots;
  1080. hr = S_OK;
  1081. }
  1082. else
  1083. {
  1084. // if we can find an empty in the list...
  1085. for (int i = 0; i < _cUsedNodeSlots; i++)
  1086. {
  1087. if (!(_rgbNodeSlots[i] & SLOT_USED))
  1088. {
  1089. _rgbNodeSlots[i] = SLOT_USED;
  1090. *pdwSlot = i+1;
  1091. hr = S_OK;
  1092. break;
  1093. }
  1094. }
  1095. if (FAILED(hr))
  1096. {
  1097. // we need to find the LRU slot
  1098. if (SUCCEEDED(RemoveLeast(pdwSlot)) && *pdwSlot)
  1099. hr = S_OK;
  1100. }
  1101. }
  1102. _SaveNodeSlots();
  1103. }
  1104. return hr;
  1105. }
  1106. STDAPI CMruPidlList_CreateInstance(IUnknown * punkOuter, IUnknown ** ppunk, LPCOBJECTINFO poi)
  1107. {
  1108. *ppunk = NULL;
  1109. CMruPidlList *p = new CMruPidlList();
  1110. if (p != NULL)
  1111. {
  1112. *ppunk = SAFECAST(p, IMruPidlList *);
  1113. return S_OK;
  1114. }
  1115. return E_OUTOFMEMORY;
  1116. }