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

1322 lines
33 KiB

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