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.

1359 lines
37 KiB

  1. #include "shellprv.h"
  2. #include "ids.h"
  3. #include "datautil.h"
  4. #include "resource.h" // main symbols
  5. #include "cowsite.h" // CObjectWithSite
  6. #include "dpa.h" // CDPA
  7. class CStartMenuPin;
  8. //
  9. // PINENTRY - A single entry in the pin list
  10. //
  11. // The _liPos/_cbLink point back into the CPinList._pstmLink
  12. //
  13. class PINENTRY {
  14. public:
  15. LPITEMIDLIST _pidl;
  16. IShellLink * _psl; // a live IShellLink
  17. LARGE_INTEGER _liPos; // location of the shell link inside the stream
  18. DWORD _cbSize; // size of the buffer pointed to by _liPos
  19. HRESULT UpdateShellLink();
  20. void FreeShellLink()
  21. {
  22. _cbSize = 0;
  23. ATOMICRELEASE(_psl);
  24. }
  25. void Destruct()
  26. {
  27. ILFree(_pidl);
  28. FreeShellLink();
  29. }
  30. static BOOL DestroyCallback(PINENTRY *self, LPVOID)
  31. {
  32. self->Destruct();
  33. return TRUE;
  34. }
  35. };
  36. //
  37. // CPinList
  38. //
  39. class CPinList
  40. {
  41. public:
  42. CPinList() : _dsaEntries(NULL), _pstmLink(NULL) { }
  43. ~CPinList()
  44. {
  45. ATOMICRELEASE(_pstmLink);
  46. if (_dsaEntries)
  47. {
  48. _dsaEntries.DestroyCallbackEx(PINENTRY::DestroyCallback, (void *)NULL);
  49. }
  50. }
  51. BOOL Initialize() { return _dsaEntries.Create(4); }
  52. HRESULT Load(CStartMenuPin *psmpin);
  53. HRESULT Save(CStartMenuPin *psmpin);
  54. int AppendPidl(LPITEMIDLIST pidl)
  55. {
  56. PINENTRY entry = { pidl };
  57. return _dsaEntries.AppendItem(&entry);
  58. }
  59. PINENTRY *GetItemPtr(int i) { return _dsaEntries.GetItemPtr(i); }
  60. HRESULT SaveShellLink(PINENTRY *pentry, IStream *pstm);
  61. HRESULT LoadShellLink(PINENTRY *pentry, IShellLink **ppsl);
  62. HRESULT UpdateShellLink(PINENTRY *pentry) { return pentry->UpdateShellLink(); }
  63. PINENTRY *FindPidl(LPCITEMIDLIST pidl, int *pi);
  64. HRESULT ReplacePidl(LPCITEMIDLIST pidlOld, LPCITEMIDLIST pidlNew);
  65. private:
  66. struct ILWRITEINFO {
  67. IStream *pstmPidlWrite;
  68. IStream *pstmLinkWrite;
  69. CPinList *ppl;
  70. HRESULT hr;
  71. LPITEMIDLIST rgpidl[20]; // Must match ARRAYSIZE(c_rgcsidlRelative)
  72. };
  73. static BOOL ILWriteCallback(PINENTRY *pentry, ILWRITEINFO *pwi);
  74. CDSA<PINENTRY> _dsaEntries; // The items themselves
  75. IStream * _pstmLink; // PINENTRY._liPos points into this stream
  76. };
  77. class ATL_NO_VTABLE CStartMenuPin
  78. : public IShellExtInit
  79. , public IContextMenu
  80. , public IStartMenuPin
  81. , public CObjectWithSite
  82. , public CComObjectRootEx<CComSingleThreadModel>
  83. , public CComCoClass<CStartMenuPin, &CLSID_StartMenuPin>
  84. {
  85. public:
  86. ~CStartMenuPin();
  87. BEGIN_COM_MAP(CStartMenuPin)
  88. COM_INTERFACE_ENTRY(IShellExtInit)
  89. // Need to use COM_INTERFACE_ENTRY_IID for the interfaces
  90. // that don't have an idl
  91. COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu)
  92. COM_INTERFACE_ENTRY(IStartMenuPin)
  93. COM_INTERFACE_ENTRY(IObjectWithSite)
  94. END_COM_MAP()
  95. DECLARE_NO_REGISTRY()
  96. // *** IShellExtInit ***
  97. STDMETHODIMP Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdto, HKEY hkProgID);
  98. // *** IContextMenu ***
  99. STDMETHODIMP QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);
  100. STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO pici);
  101. STDMETHODIMP GetCommandString(UINT_PTR idCmd, UINT uType, UINT *pwRes, LPSTR pszName, UINT cchMax);
  102. // *** IStartMenuPin ***
  103. STDMETHODIMP EnumObjects(IEnumIDList **ppenum);
  104. STDMETHODIMP Modify(LPCITEMIDLIST pidlFrom, LPCITEMIDLIST pidlTo);
  105. STDMETHODIMP GetChangeCount(ULONG *pulOut);
  106. STDMETHODIMP IsPinnable(IDataObject *pdtobj, DWORD dwFlags, OPTIONAL LPITEMIDLIST *ppidl);
  107. STDMETHODIMP Resolve(HWND hwnd, DWORD dwFlags, LPCITEMIDLIST pidl, LPITEMIDLIST *ppidlResolved);
  108. // *** IObjectWithSite ***
  109. // Inherited from CObjectWithSite
  110. public:
  111. HRESULT SetChangeCount(ULONG ul);
  112. protected:
  113. BOOL _IsAcceptableTarget(LPCTSTR pszPath, DWORD dwAttrib, DWORD dwFlags);
  114. enum {
  115. IDM_PIN = 0,
  116. IDM_UNPIN = 1,
  117. IDM_MAX,
  118. };
  119. // These "seem" backwards, but remember: If the item is pinned,
  120. // then the command is "unpin". If the item is unpinned, then
  121. // the command is "pin".
  122. inline void _SetPinned() { _idmPinCmd = IDM_UNPIN; }
  123. inline void _SetUnpinned() { _idmPinCmd = IDM_PIN; }
  124. inline BOOL _IsPinned() const { return _idmPinCmd != IDM_PIN; }
  125. inline BOOL _DoPin() const { return _idmPinCmd == IDM_PIN; }
  126. inline BOOL _DoUnpin() const { return _idmPinCmd != IDM_PIN; }
  127. inline UINT _GetMenuStringID() const
  128. {
  129. COMPILETIME_ASSERT(IDS_STARTPIN_UNPINME == IDS_STARTPIN_PINME + IDM_UNPIN);
  130. return IDS_STARTPIN_PINME + _idmPinCmd;
  131. }
  132. static BOOL ILFreeCallback(LPITEMIDLIST pidl, void *)
  133. { ILFree(pidl); return TRUE; }
  134. HRESULT _ShouldAddMenu(UINT uFlags);
  135. HRESULT _InitPinRegStream();
  136. protected:
  137. IDataObject *_pdtobj;
  138. UINT _idmPinCmd; // Which command did we add?
  139. LPITEMIDLIST _pidl; // IContextMenu identity
  140. };
  141. #define REGSTR_PATH_STARTFAVS TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\StartPage")
  142. #define REGSTR_VAL_STARTFAVS TEXT("Favorites")
  143. #define REGSTR_VAL_STARTFAVCHANGES TEXT("FavoritesChanges")
  144. #define REGSTR_VAL_STARTFAVLINKS TEXT("FavoritesResolve")
  145. IStream *_OpenPinRegStream(DWORD grfMode)
  146. {
  147. return SHOpenRegStream2(HKEY_CURRENT_USER, REGSTR_PATH_STARTFAVS, REGSTR_VAL_STARTFAVS, grfMode);
  148. }
  149. IStream *_OpenLinksRegStream(DWORD grfMode)
  150. {
  151. return SHOpenRegStream2(HKEY_CURRENT_USER, REGSTR_PATH_STARTFAVS, REGSTR_VAL_STARTFAVLINKS, grfMode);
  152. }
  153. const LARGE_INTEGER c_li0 = { 0, 0 };
  154. const ULARGE_INTEGER& c_uli0 = (ULARGE_INTEGER&)c_li0;
  155. HRESULT IStream_GetPos(IStream *pstm, LARGE_INTEGER *pliPos)
  156. {
  157. return pstm->Seek(c_li0, STREAM_SEEK_CUR, (ULARGE_INTEGER*)pliPos);
  158. }
  159. HRESULT IStream_Copy(IStream *pstmFrom, IStream *pstmTo, DWORD cb)
  160. {
  161. ULARGE_INTEGER uliToCopy, uliCopied;
  162. uliToCopy.QuadPart = cb;
  163. HRESULT hr = pstmFrom->CopyTo(pstmTo, uliToCopy, NULL, &uliCopied);
  164. if (SUCCEEDED(hr) && uliToCopy.QuadPart != uliCopied.QuadPart)
  165. {
  166. hr = E_FAIL;
  167. }
  168. return hr;
  169. }
  170. class ATL_NO_VTABLE CStartMenuPinEnum
  171. : public IEnumIDList
  172. , public CComObjectRootEx<CComSingleThreadModel>
  173. , public CComCoClass<CStartMenuPinEnum>
  174. {
  175. public:
  176. ~CStartMenuPinEnum()
  177. {
  178. ATOMICRELEASE(_pstm);
  179. }
  180. BEGIN_COM_MAP(CStartMenuPinEnum)
  181. COM_INTERFACE_ENTRY(IEnumIDList)
  182. END_COM_MAP()
  183. /// *** IEnumIDList ***
  184. STDMETHODIMP Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched);
  185. STDMETHODIMP Skip(ULONG celt);
  186. STDMETHODIMP Reset();
  187. STDMETHODIMP Clone(IEnumIDList **ppenum);
  188. private:
  189. HRESULT _NextPidlFromStream(LPITEMIDLIST *ppidl);
  190. HRESULT _InitPinRegStream();
  191. private:
  192. HRESULT _hrLastEnum; // Result of last IEnumIDList::Next
  193. IStream * _pstm;
  194. };
  195. CStartMenuPin::~CStartMenuPin()
  196. {
  197. ILFree(_pidl);
  198. if (_pdtobj)
  199. _pdtobj->Release();
  200. }
  201. BOOL _IsLocalHardDisk(LPCTSTR pszPath)
  202. {
  203. // Reject CDs, floppies, network drives, etc.
  204. //
  205. int iDrive = PathGetDriveNumber(pszPath);
  206. if (iDrive < 0 || // reject UNCs
  207. RealDriveType(iDrive, /* fOkToHitNet = */ FALSE) != DRIVE_FIXED) // reject slow media
  208. {
  209. return FALSE;
  210. }
  211. return TRUE;
  212. }
  213. BOOL CStartMenuPin::_IsAcceptableTarget(LPCTSTR pszPath, DWORD dwAttrib, DWORD dwFlags)
  214. {
  215. // Regitems ("Internet" or "Email" for example) are acceptable
  216. // provided we aren't restricted to EXEs only.
  217. if (!(dwAttrib & SFGAO_FILESYSTEM))
  218. {
  219. return !(dwFlags & SMPINNABLE_EXEONLY);
  220. }
  221. // Otherwise, it's a file.
  222. // If requested, reject non-EXEs.
  223. // (Like the Start Menu, we treat MSC files as if they were EXEs)
  224. if (dwFlags & SMPINNABLE_EXEONLY)
  225. {
  226. LPCTSTR pszExt = PathFindExtension(pszPath);
  227. if (StrCmpIC(pszExt, TEXT(".EXE")) != 0 &&
  228. StrCmpIC(pszExt, TEXT(".MSC")) != 0)
  229. {
  230. return FALSE;
  231. }
  232. }
  233. // If requested, reject slow media
  234. if (dwFlags & SMPINNABLE_REJECTSLOWMEDIA)
  235. {
  236. if (!_IsLocalHardDisk(pszPath))
  237. {
  238. return FALSE;
  239. }
  240. // If it's a shortcut, then apply the same rule to the shortcut.
  241. if (PathIsLnk(pszPath))
  242. {
  243. BOOL fLocal = TRUE;
  244. IShellLink *psl;
  245. if (SUCCEEDED(LoadFromFile(CLSID_ShellLink, pszPath, IID_PPV_ARG(IShellLink, &psl))))
  246. {
  247. // IShellLink::GetPath returns S_FALSE if target is not a path
  248. TCHAR szPath[MAX_PATH];
  249. if (S_OK == psl->GetPath(szPath, ARRAYSIZE(szPath), NULL, 0))
  250. {
  251. fLocal = _IsLocalHardDisk(szPath);
  252. }
  253. psl->Release();
  254. }
  255. if (!fLocal)
  256. {
  257. return FALSE;
  258. }
  259. }
  260. }
  261. // All tests pass!
  262. return TRUE;
  263. }
  264. BOOL IsStartPanelOn()
  265. {
  266. SHELLSTATE ss = { 0 };
  267. SHGetSetSettings(&ss, SSF_STARTPANELON, FALSE);
  268. return ss.fStartPanelOn;
  269. }
  270. HRESULT CStartMenuPin::IsPinnable(IDataObject *pdtobj, DWORD dwFlags, OPTIONAL LPITEMIDLIST *ppidl)
  271. {
  272. HRESULT hr = S_FALSE;
  273. LPITEMIDLIST pidlRet = NULL;
  274. if (pdtobj && // must have a data object
  275. !SHRestricted(REST_NOSMPINNEDLIST) && // cannot be restricted
  276. IsStartPanelOn()) // start panel must be on
  277. {
  278. STGMEDIUM medium = {0};
  279. LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
  280. if (pida)
  281. {
  282. if (pida->cidl == 1)
  283. {
  284. pidlRet = IDA_FullIDList(pida, 0);
  285. if (pidlRet)
  286. {
  287. DWORD dwAttr = SFGAO_FILESYSTEM; // only SFGAO_FILESYSTEM is valid
  288. TCHAR szPath[MAX_PATH];
  289. if (SUCCEEDED(SHGetNameAndFlags(pidlRet, SHGDN_FORPARSING,
  290. szPath, ARRAYSIZE(szPath), &dwAttr)) &&
  291. _IsAcceptableTarget(szPath, dwAttr, dwFlags))
  292. {
  293. hr = S_OK;
  294. }
  295. }
  296. }
  297. HIDA_ReleaseStgMedium(pida, &medium);
  298. }
  299. }
  300. // Return pidlRet only if the call succeeded and the caller requested it
  301. if (hr != S_OK || !ppidl)
  302. {
  303. ILFree(pidlRet);
  304. pidlRet = NULL;
  305. }
  306. if (ppidl)
  307. {
  308. *ppidl = pidlRet;
  309. }
  310. return hr;
  311. }
  312. // Returns S_OK if should add, S_FALSE if not
  313. HRESULT CStartMenuPin::_ShouldAddMenu(UINT uFlags)
  314. {
  315. // "Pin" is never a default verb
  316. if (uFlags & CMF_DEFAULTONLY)
  317. return S_FALSE;
  318. HRESULT hr;
  319. // The context menu appears only for fast media
  320. //
  321. // If extended verbs are disabled, then show the menu only for EXEs
  322. DWORD dwFlags = SMPINNABLE_REJECTSLOWMEDIA;
  323. if (!(uFlags & CMF_EXTENDEDVERBS))
  324. {
  325. dwFlags |= SMPINNABLE_EXEONLY;
  326. }
  327. hr = IsPinnable(_pdtobj, dwFlags, &_pidl);
  328. if (S_OK == hr)
  329. {
  330. // If we are enclosed inside a shortcut, change our identity to the
  331. // enclosing shortcut.
  332. IPersistFile *ppf;
  333. if (SUCCEEDED(IUnknown_QueryService(_punkSite, SID_LinkSite, IID_PPV_ARG(IPersistFile, &ppf))))
  334. {
  335. LPOLESTR pszFile = NULL;
  336. if (ppf->GetCurFile(&pszFile) == S_OK && pszFile)
  337. {
  338. // ILCreateFromPathEx turns %USERPROFILE%\Desktop\foo.lnk
  339. // into CSIDL_DESKTOP\foo.lnk for us.
  340. LPITEMIDLIST pidl;
  341. if (SUCCEEDED(ILCreateFromPathEx(pszFile, NULL, ILCFP_FLAG_NORMAL, &pidl, NULL)))
  342. if (pidl)
  343. {
  344. ILFree(_pidl);
  345. _pidl = pidl;
  346. hr = S_OK;
  347. }
  348. CoTaskMemFree(pszFile);
  349. }
  350. ppf->Release();
  351. }
  352. }
  353. return hr;
  354. }
  355. // IShellExtInit::Initialize
  356. HRESULT CStartMenuPin::Initialize(LPCITEMIDLIST, IDataObject *pdtobj, HKEY)
  357. {
  358. IUnknown_Set((IUnknown **)&_pdtobj, pdtobj); // just grab this guy
  359. return S_OK;
  360. }
  361. // IContextMenu::QueryContextMenu
  362. HRESULT CStartMenuPin::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
  363. {
  364. HRESULT hr = _ShouldAddMenu(uFlags);
  365. if (S_OK == hr)
  366. {
  367. _SetUnpinned();
  368. // Determine whether this item is already on the Start Page or not.
  369. IEnumIDList *penum;
  370. hr = EnumObjects(&penum);
  371. if (SUCCEEDED(hr))
  372. {
  373. LPITEMIDLIST pidl;
  374. while (penum->Next(1, &pidl, NULL) == S_OK)
  375. {
  376. BOOL bSame = ILIsEqual(pidl, _pidl);
  377. ILFree(pidl);
  378. if (bSame)
  379. {
  380. _SetPinned();
  381. break;
  382. }
  383. }
  384. penum->Release();
  385. TCHAR szCommand[MAX_PATH];
  386. if (LoadString(g_hinst, _GetMenuStringID(), szCommand, ARRAYSIZE(szCommand)))
  387. {
  388. InsertMenu(hmenu, indexMenu, MF_STRING | MF_BYPOSITION,
  389. idCmdFirst + _idmPinCmd, szCommand);
  390. }
  391. hr = ResultFromShort(IDM_MAX);
  392. }
  393. }
  394. return hr;
  395. }
  396. const LPCTSTR c_rgpszVerb[] =
  397. {
  398. TEXT("pin"), // IDM_PIN
  399. TEXT("unpin"), // IDM_UNPIN
  400. };
  401. // *** IContextMenu::InvokeCommand
  402. HRESULT CStartMenuPin::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
  403. {
  404. LPCMINVOKECOMMANDINFOEX picix = reinterpret_cast<LPCMINVOKECOMMANDINFOEX>(pici);
  405. HRESULT hr = E_INVALIDARG;
  406. UINT idmCmd;
  407. if (IS_INTRESOURCE(pici->lpVerb))
  408. {
  409. idmCmd = PtrToInt(pici->lpVerb);
  410. }
  411. else
  412. {
  413. // Convert the string to an ID (or out of range if invalid)
  414. LPCTSTR pszVerb;
  415. #ifdef UNICODE
  416. WCHAR szVerb[MAX_PATH];
  417. if (pici->cbSize >= CMICEXSIZE_NT4 &&
  418. (pici->fMask & CMIC_MASK_UNICODE) &&
  419. picix->lpVerbW)
  420. {
  421. pszVerb = picix->lpVerbW;
  422. }
  423. else
  424. {
  425. SHAnsiToTChar(pici->lpVerb, szVerb, ARRAYSIZE(szVerb));
  426. pszVerb = szVerb;
  427. }
  428. #else
  429. pszVerb = pici->lpVerb;
  430. #endif
  431. for (idmCmd = 0; idmCmd < ARRAYSIZE(c_rgpszVerb); idmCmd++)
  432. {
  433. if (lstrcmpi(pszVerb, c_rgpszVerb[idmCmd]) == 0)
  434. {
  435. break;
  436. }
  437. }
  438. }
  439. if (idmCmd == _idmPinCmd)
  440. {
  441. if (_idmPinCmd == IDM_PIN)
  442. {
  443. hr = Modify(NULL, _pidl);
  444. }
  445. else
  446. {
  447. hr = Modify(_pidl, NULL);
  448. }
  449. }
  450. return hr;
  451. }
  452. // *** IContextMenu::GetCommandString
  453. HRESULT CStartMenuPin::GetCommandString(UINT_PTR idCmd, UINT uType, UINT *pwRes, LPSTR pszName, UINT cchMax)
  454. {
  455. TCHAR szBuf[MAX_PATH];
  456. LPCTSTR pszResult = NULL;
  457. switch (uType & ~GCS_UNICODE)
  458. {
  459. case GCS_VERBA:
  460. if (idCmd < ARRAYSIZE(c_rgpszVerb))
  461. {
  462. pszResult = c_rgpszVerb[idCmd];
  463. }
  464. break;
  465. case GCS_HELPTEXTA:
  466. if (idCmd < ARRAYSIZE(c_rgpszVerb))
  467. {
  468. COMPILETIME_ASSERT(IDS_STARTPIN_PINME_HELP + IDM_UNPIN == IDS_STARTPIN_UNPINME_HELP);
  469. if (LoadString(g_hinst, IDS_STARTPIN_PINME_HELP + (UINT)idCmd, szBuf, ARRAYSIZE(szBuf)))
  470. {
  471. pszResult = szBuf;
  472. }
  473. }
  474. break;
  475. }
  476. if (pszResult)
  477. {
  478. if (uType & GCS_UNICODE)
  479. {
  480. SHTCharToUnicode(pszResult, (LPWSTR)pszName, cchMax);
  481. }
  482. else
  483. {
  484. SHTCharToAnsi(pszResult, pszName, cchMax);
  485. }
  486. return S_OK;
  487. }
  488. return E_NOTIMPL;
  489. }
  490. PINENTRY *CPinList::FindPidl(LPCITEMIDLIST pidl, int *pi)
  491. {
  492. for (int i = _dsaEntries.GetItemCount() - 1; i >= 0; i--)
  493. {
  494. PINENTRY *pentry = _dsaEntries.GetItemPtr(i);
  495. if (ILIsEqual(pentry->_pidl, pidl))
  496. {
  497. if (pi)
  498. {
  499. *pi = i;
  500. }
  501. return pentry;
  502. }
  503. }
  504. return NULL;
  505. }
  506. HRESULT CPinList::ReplacePidl(LPCITEMIDLIST pidlOld, LPCITEMIDLIST pidlNew)
  507. {
  508. int i;
  509. PINENTRY *pentry = FindPidl(pidlOld, &i);
  510. if (pentry)
  511. {
  512. if (pidlNew == NULL) // Delete
  513. {
  514. pentry->Destruct();
  515. _dsaEntries.DeleteItem(i);
  516. return S_OK;
  517. }
  518. else
  519. if (IS_INTRESOURCE(pidlNew)) // Move
  520. {
  521. // Move the pidl from i to iPos
  522. PINENTRY entry = *pentry;
  523. int iPos = ((int)(INT_PTR)pidlNew) - 1;
  524. if (i < iPos)
  525. {
  526. // Moving down; others move up
  527. iPos--;
  528. // Must use MoveMemory because the memory blocks overlap
  529. MoveMemory(_dsaEntries.GetItemPtr(i),
  530. _dsaEntries.GetItemPtr(i+1),
  531. sizeof(PINENTRY) * (iPos-i));
  532. }
  533. else if (i > iPos)
  534. {
  535. // Moving up; others move down
  536. // Must use MoveMemory because the memory blocks overlap
  537. MoveMemory(_dsaEntries.GetItemPtr(iPos+1),
  538. _dsaEntries.GetItemPtr(iPos),
  539. sizeof(PINENTRY) * (i-iPos));
  540. }
  541. _dsaEntries.SetItem(iPos, &entry);
  542. return S_OK;
  543. }
  544. else // Replace
  545. {
  546. if (Pidl_Set(&pentry->_pidl, pidlNew))
  547. {
  548. // Failure to update the shell link is not fatal;
  549. // it just means we won't be able to repair the
  550. // shortcut if it breaks.
  551. pentry->UpdateShellLink();
  552. return S_OK;
  553. }
  554. else
  555. {
  556. return E_OUTOFMEMORY;
  557. }
  558. }
  559. }
  560. return HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
  561. }
  562. HRESULT CStartMenuPin::Modify(LPCITEMIDLIST pidlFrom, LPCITEMIDLIST pidlTo)
  563. {
  564. HRESULT hr;
  565. if(SHRestricted(REST_NOSMPINNEDLIST))
  566. return E_ACCESSDENIED;
  567. // Remap pidls to logical pidls (change CSIDL_DESKTOPDIRECTORY
  568. // to CSIDL_DESKTOP, etc.) so we don't get faked out when people
  569. // access objects sometimes directly on the desktop and sometimes
  570. // via their full filesystem name.
  571. LPITEMIDLIST pidlFromFree = NULL;
  572. LPITEMIDLIST pidlToFree = NULL;
  573. if (!IS_INTRESOURCE(pidlFrom))
  574. {
  575. pidlFromFree = SHLogILFromFSIL(pidlFrom);
  576. if (pidlFromFree) {
  577. pidlFrom = pidlFromFree;
  578. }
  579. }
  580. if (!IS_INTRESOURCE(pidlTo))
  581. {
  582. pidlToFree = SHLogILFromFSIL(pidlTo);
  583. if (pidlToFree) {
  584. pidlTo = pidlToFree;
  585. }
  586. }
  587. CPinList pl;
  588. hr = pl.Load(this);
  589. if (SUCCEEDED(hr))
  590. {
  591. if (SUCCEEDED(hr))
  592. {
  593. if (pidlFrom)
  594. {
  595. hr = pl.ReplacePidl(pidlFrom, pidlTo);
  596. }
  597. else if (pidlTo)
  598. {
  599. LPITEMIDLIST pidl = ILClone(pidlTo);
  600. if (pidl)
  601. {
  602. int iPos = pl.AppendPidl(pidl);
  603. if (iPos >= 0)
  604. {
  605. // Failure to update the shell link is not fatal;
  606. // it just means we won't be able to repair the
  607. // shortcut if it breaks.
  608. pl.GetItemPtr(iPos)->UpdateShellLink();
  609. }
  610. else
  611. {
  612. ILFree(pidl);
  613. hr = E_OUTOFMEMORY;
  614. }
  615. }
  616. else
  617. {
  618. hr = E_OUTOFMEMORY;
  619. }
  620. }
  621. else
  622. {
  623. // pidlFrom == pidlTo == NULL? What does that mean?
  624. hr = E_INVALIDARG;
  625. }
  626. if (SUCCEEDED(hr))
  627. {
  628. hr = pl.Save(this);
  629. }
  630. }
  631. }
  632. else
  633. {
  634. hr = E_OUTOFMEMORY; // could not create dpa
  635. }
  636. ILFree(pidlFromFree);
  637. ILFree(pidlToFree);
  638. return hr;
  639. }
  640. //
  641. // Find the pidl on the pin list and resolve the shortcut that
  642. // tracks it.
  643. //
  644. // Returns S_OK if the pidl changed and was resolved.
  645. // Returns S_FALSE if the pidl did not change.
  646. // Returns an error if the Resolve failed.
  647. //
  648. HRESULT CStartMenuPin::Resolve(HWND hwnd, DWORD dwFlags, LPCITEMIDLIST pidl, LPITEMIDLIST *ppidlResolved)
  649. {
  650. *ppidlResolved = NULL;
  651. if(SHRestricted(REST_NOSMPINNEDLIST))
  652. return E_ACCESSDENIED;
  653. // Remap pidls to logical pidls (change CSIDL_DESKTOPDIRECTORY
  654. // to CSIDL_DESKTOP, etc.) so we don't get faked out when people
  655. // access objects sometimes directly on the desktop and sometimes
  656. // via their full filesystem name.
  657. LPITEMIDLIST pidlFree = SHLogILFromFSIL(pidl);
  658. if (pidlFree) {
  659. pidl = pidlFree;
  660. }
  661. CPinList pl;
  662. HRESULT hr = pl.Load(this);
  663. if (SUCCEEDED(hr))
  664. {
  665. PINENTRY *pentry = pl.FindPidl(pidl, NULL);
  666. if (pentry)
  667. {
  668. IShellLink *psl;
  669. hr = pl.LoadShellLink(pentry, &psl);
  670. if (SUCCEEDED(hr))
  671. {
  672. hr = psl->Resolve(hwnd, dwFlags);
  673. if (hr == S_OK)
  674. {
  675. IPersistStream *pps;
  676. hr = psl->QueryInterface(IID_PPV_ARG(IPersistStream, &pps));
  677. if (SUCCEEDED(hr))
  678. {
  679. if (pps->IsDirty() == S_OK)
  680. {
  681. LPITEMIDLIST pidlNew;
  682. hr = psl->GetIDList(&pidlNew);
  683. if (SUCCEEDED(hr) && hr != S_OK)
  684. {
  685. // GetIDList returns S_FALSE on failure...
  686. hr = E_FAIL;
  687. }
  688. if (SUCCEEDED(hr))
  689. {
  690. ILFree(pentry->_pidl);
  691. pentry->_pidl = pidlNew;
  692. hr = SHILClone(pidlNew, ppidlResolved);
  693. }
  694. }
  695. pps->Release();
  696. }
  697. }
  698. else if (SUCCEEDED(hr))
  699. {
  700. // S_FALSE means "cancelled by user"
  701. hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
  702. }
  703. psl->Release();
  704. }
  705. }
  706. else
  707. {
  708. hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
  709. }
  710. if (hr == S_OK)
  711. {
  712. pl.Save(this); // if this fails, tough
  713. }
  714. }
  715. ILFree(pidlFree);
  716. return hr;
  717. }
  718. //
  719. // The target pidl has changed (or it's brand new). Create an IShellLink
  720. // around it so we can resolve it later.
  721. //
  722. HRESULT PINENTRY::UpdateShellLink()
  723. {
  724. ASSERT(_pidl);
  725. // Pitch the old link; it's useless now.
  726. FreeShellLink();
  727. HRESULT hr = SHCoCreateInstance(NULL, &CLSID_ShellLink, NULL, IID_PPV_ARG(IShellLink, &_psl));
  728. if (SUCCEEDED(hr))
  729. {
  730. hr = _psl->SetIDList(_pidl);
  731. if (FAILED(hr))
  732. {
  733. FreeShellLink(); // pitch it; it's no good
  734. }
  735. }
  736. return hr;
  737. }
  738. HRESULT CPinList::SaveShellLink(PINENTRY *pentry, IStream *pstm)
  739. {
  740. HRESULT hr;
  741. if (pentry->_psl)
  742. {
  743. // It's still in the form of an IShellLink.
  744. // Save it to the stream, then go back and update the size information.
  745. LARGE_INTEGER liPos, liPosAfter;
  746. DWORD cbSize = 0;
  747. IPersistStream *pps = NULL;
  748. if (SUCCEEDED(hr = IStream_GetPos(pstm, &liPos)) &&
  749. // Write a dummy DWORD; we will come back and patch it up later
  750. SUCCEEDED(hr = IStream_Write(pstm, &cbSize, sizeof(cbSize))) &&
  751. SUCCEEDED(hr = pentry->_psl->QueryInterface(IID_PPV_ARG(IPersistStream, &pps))))
  752. {
  753. if (SUCCEEDED(hr = pps->Save(pstm, TRUE)) &&
  754. SUCCEEDED(hr = IStream_GetPos(pstm, &liPosAfter)) &&
  755. SUCCEEDED(hr = pstm->Seek(liPos, STREAM_SEEK_SET, NULL)))
  756. {
  757. cbSize = liPosAfter.LowPart - liPos.LowPart - sizeof(DWORD);
  758. if (SUCCEEDED(hr = IStream_Write(pstm, &cbSize, sizeof(cbSize))) &&
  759. SUCCEEDED(hr = pstm->Seek(liPosAfter, STREAM_SEEK_SET, NULL)))
  760. {
  761. // Hooray! All got saved okay
  762. }
  763. }
  764. pps->Release();
  765. }
  766. }
  767. else
  768. {
  769. // It's just a reference back into our parent stream; copy it
  770. if (SUCCEEDED(hr = IStream_Write(pstm, &pentry->_cbSize, sizeof(pentry->_cbSize))))
  771. {
  772. // If _cbSize == 0 then _pstmLink might be NULL, so guard against it
  773. if (pentry->_cbSize)
  774. {
  775. if (SUCCEEDED(hr = _pstmLink->Seek(pentry->_liPos, STREAM_SEEK_SET, NULL)) &&
  776. SUCCEEDED(hr = IStream_Copy(_pstmLink, pstm, pentry->_cbSize)))
  777. {
  778. // Hooray! All got saved okay
  779. }
  780. }
  781. else
  782. {
  783. // Entry was blank - nothing to do, vacuous success
  784. }
  785. }
  786. }
  787. return hr;
  788. }
  789. HRESULT CPinList::LoadShellLink(PINENTRY *pentry, IShellLink **ppsl)
  790. {
  791. HRESULT hr;
  792. if (pentry->_psl)
  793. {
  794. hr = S_OK; // We already have the link
  795. }
  796. else if (pentry->_cbSize == 0)
  797. {
  798. hr = E_FAIL; // no link available
  799. }
  800. else
  801. { // gotta make it
  802. IPersistStream *pps;
  803. hr = SHCoCreateInstance(NULL, &CLSID_ShellLink, NULL, IID_PPV_ARG(IPersistStream, &pps));
  804. if (SUCCEEDED(hr))
  805. {
  806. if (SUCCEEDED(hr = _pstmLink->Seek(pentry->_liPos, STREAM_SEEK_SET, NULL)) &&
  807. SUCCEEDED(hr = pps->Load(_pstmLink)) &&
  808. SUCCEEDED(hr = pps->QueryInterface(IID_PPV_ARG(IShellLink, &pentry->_psl))))
  809. {
  810. // woo-hoo! All got loaded okay
  811. }
  812. pps->Release();
  813. }
  814. }
  815. *ppsl = pentry->_psl;
  816. if (SUCCEEDED(hr))
  817. {
  818. pentry->_psl->AddRef();
  819. hr = S_OK;
  820. }
  821. return hr;
  822. }
  823. HRESULT CStartMenuPin::GetChangeCount(ULONG *pulOut)
  824. {
  825. DWORD cb = sizeof(*pulOut);
  826. if (SHGetValue(HKEY_CURRENT_USER, REGSTR_PATH_STARTFAVS,
  827. REGSTR_VAL_STARTFAVCHANGES, NULL, pulOut, &cb) != ERROR_SUCCESS)
  828. {
  829. *pulOut = 0;
  830. }
  831. return S_OK;
  832. }
  833. HRESULT CStartMenuPin::SetChangeCount(ULONG ulChange)
  834. {
  835. SHSetValue(HKEY_CURRENT_USER, REGSTR_PATH_STARTFAVS,
  836. REGSTR_VAL_STARTFAVCHANGES, REG_DWORD, &ulChange,
  837. sizeof(ulChange));
  838. return S_OK;
  839. }
  840. //
  841. // We scan this list in order, so if there is a CSIDL that is a subdirectory
  842. // of another CSIDL, we must put the subdirectory first. For example,
  843. // CSIDL_PROGRAMS is typically a subdirectory of CSIDL_STARTMENU, so we
  844. // must put CSIDL_PROGRAMS first so we get the best match.
  845. //
  846. // Furthermore, directories pinned items are more likely to be found in
  847. // should come before less popular directories.
  848. //
  849. const int c_rgcsidlRelative[] = {
  850. // Most common: Start Menu stuff
  851. CSIDL_PROGRAMS, // Programs must come before StartMenu
  852. CSIDL_STARTMENU, // Programs must come before StartMenu
  853. // Next most common: My Documents stuff
  854. CSIDL_MYPICTURES, // MyXxx must come before Personal
  855. CSIDL_MYMUSIC, // MyXxx must come before Personal
  856. CSIDL_MYVIDEO, // MyXxx must come before Personal
  857. CSIDL_PERSONAL, // MyXxx must come before Personal
  858. CSIDL_COMMON_PROGRAMS, // Programs must come before StartMenu
  859. CSIDL_COMMON_STARTMENU, // Programs must come before StartMenu
  860. // Next most common: Desktop stuff
  861. CSIDL_DESKTOPDIRECTORY,
  862. CSIDL_COMMON_DESKTOPDIRECTORY,
  863. // Next most common: Program files stuff
  864. CSIDL_PROGRAM_FILES_COMMON, // ProgramFilesCommon must come before ProgramFiles
  865. CSIDL_PROGRAM_FILES, // ProgramFilesCommon must come before ProgramFiles
  866. CSIDL_PROGRAM_FILES_COMMONX86, // ProgramFilesCommon must come before ProgramFiles
  867. CSIDL_PROGRAM_FILESX86, // ProgramFilesCommon must come before ProgramFiles
  868. // Other stuff (less common)
  869. CSIDL_APPDATA,
  870. CSIDL_COMMON_APPDATA,
  871. CSIDL_SYSTEM,
  872. CSIDL_SYSTEMX86,
  873. CSIDL_WINDOWS,
  874. CSIDL_PROFILE, // Must come after all other profile-relative directories
  875. };
  876. BOOL CPinList::ILWriteCallback(PINENTRY *pentry, ILWRITEINFO *pwi)
  877. {
  878. BYTE csidl = CSIDL_DESKTOP; // Assume nothing interesting
  879. LPITEMIDLIST pidlWrite = pentry->_pidl; // Assume nothing interesting
  880. for (int i = 0; i < ARRAYSIZE(pwi->rgpidl); i++)
  881. {
  882. LPITEMIDLIST pidlT;
  883. if (pwi->rgpidl[i] &&
  884. (pidlT = ILFindChild(pwi->rgpidl[i], pentry->_pidl)))
  885. {
  886. csidl = (BYTE)c_rgcsidlRelative[i];
  887. pidlWrite = pidlT;
  888. break;
  889. }
  890. }
  891. if (SUCCEEDED(pwi->hr = IStream_Write(pwi->pstmPidlWrite, &csidl, sizeof(csidl))) &&
  892. SUCCEEDED(pwi->hr = IStream_WritePidl(pwi->pstmPidlWrite, pidlWrite)) &&
  893. SUCCEEDED(pwi->hr = pwi->ppl->SaveShellLink(pentry, pwi->pstmLinkWrite)))
  894. {
  895. // woo-hoo, all written successfully
  896. }
  897. return SUCCEEDED(pwi->hr);
  898. }
  899. #define CSIDL_END ((BYTE)0xFF)
  900. HRESULT CPinList::Save(CStartMenuPin *psmpin)
  901. {
  902. ILWRITEINFO wi;
  903. COMPILETIME_ASSERT(ARRAYSIZE(c_rgcsidlRelative) == ARRAYSIZE(wi.rgpidl));
  904. for (int i = 0; i < ARRAYSIZE(c_rgcsidlRelative); i++)
  905. {
  906. SHGetSpecialFolderLocation(NULL, c_rgcsidlRelative[i], &wi.rgpidl[i]);
  907. }
  908. wi.pstmPidlWrite = _OpenPinRegStream(STGM_WRITE);
  909. if (wi.pstmPidlWrite)
  910. {
  911. wi.pstmLinkWrite = _OpenLinksRegStream(STGM_WRITE);
  912. if (wi.pstmLinkWrite)
  913. {
  914. wi.hr = S_OK;
  915. wi.ppl = this;
  916. _dsaEntries.EnumCallbackEx(ILWriteCallback, &wi);
  917. if (SUCCEEDED(wi.hr))
  918. {
  919. BYTE csidlEnd = CSIDL_END;
  920. wi.hr = IStream_Write(wi.pstmPidlWrite, &csidlEnd, sizeof(csidlEnd));
  921. }
  922. if (FAILED(wi.hr))
  923. {
  924. wi.pstmPidlWrite->SetSize(c_uli0);
  925. wi.pstmLinkWrite->SetSize(c_uli0);
  926. }
  927. wi.pstmLinkWrite->Release();
  928. }
  929. wi.pstmPidlWrite->Release();
  930. }
  931. else
  932. {
  933. wi.hr = E_ACCESSDENIED; // Most common reason is lack of write permission
  934. }
  935. for (i = 0; i < ARRAYSIZE(c_rgcsidlRelative); i++)
  936. {
  937. ILFree(wi.rgpidl[i]);
  938. }
  939. // Bump the change count so people can detect and refresh
  940. ULONG ulChange;
  941. psmpin->GetChangeCount(&ulChange);
  942. psmpin->SetChangeCount(ulChange + 1);
  943. // Notify everyone that the pin list changed
  944. SHChangeDWORDAsIDList dwidl;
  945. dwidl.cb = SIZEOF(dwidl) - SIZEOF(dwidl.cbZero);
  946. dwidl.dwItem1 = SHCNEE_PINLISTCHANGED;
  947. dwidl.dwItem2 = 0;
  948. dwidl.cbZero = 0;
  949. SHChangeNotify(SHCNE_EXTENDED_EVENT, SHCNF_FLUSH, (LPCITEMIDLIST)&dwidl, NULL);
  950. return wi.hr;
  951. }
  952. HRESULT CPinList::Load(CStartMenuPin *psmpin)
  953. {
  954. HRESULT hr;
  955. if (Initialize())
  956. {
  957. IEnumIDList *penum;
  958. hr = psmpin->EnumObjects(&penum);
  959. if (SUCCEEDED(hr))
  960. {
  961. LPITEMIDLIST pidl;
  962. while (penum->Next(1, &pidl, NULL) == S_OK)
  963. {
  964. if (AppendPidl(pidl) < 0)
  965. {
  966. ILFree(pidl);
  967. hr = E_OUTOFMEMORY;
  968. break;
  969. }
  970. }
  971. penum->Release();
  972. }
  973. if (SUCCEEDED(hr))
  974. {
  975. //
  976. // Now read the persisted shortcuts.
  977. //
  978. _pstmLink = _OpenLinksRegStream(STGM_READ);
  979. if (_pstmLink)
  980. {
  981. for (int i = 0; i < _dsaEntries.GetItemCount(); i++)
  982. {
  983. PINENTRY *pentry = _dsaEntries.GetItemPtr(i);
  984. LARGE_INTEGER liSeek = { 0, 0 };
  985. if (SUCCEEDED(hr = IStream_Read(_pstmLink, &liSeek.LowPart, sizeof(liSeek.LowPart))) && // read size
  986. SUCCEEDED(hr = IStream_GetPos(_pstmLink, &pentry->_liPos)) && // read current pos
  987. SUCCEEDED(hr = _pstmLink->Seek(liSeek, STREAM_SEEK_CUR, NULL))) // skip over link
  988. {
  989. pentry->_cbSize = liSeek.LowPart; // set this only on success
  990. }
  991. else
  992. {
  993. break;
  994. }
  995. }
  996. }
  997. // If we encountered an error,
  998. // then throw all the shortcuts away because they are
  999. // probably corrupted.
  1000. if (FAILED(hr))
  1001. {
  1002. for (int i = 0; i < _dsaEntries.GetItemCount(); i++)
  1003. {
  1004. _dsaEntries.GetItemPtr(i)->FreeShellLink();
  1005. }
  1006. }
  1007. // Problems reading the persisted shortcuts are ignored
  1008. // since they are merely advisory.
  1009. hr = S_OK;
  1010. }
  1011. }
  1012. else
  1013. {
  1014. hr = E_OUTOFMEMORY;
  1015. }
  1016. return hr;
  1017. }
  1018. //
  1019. // Reading a pidl from a stream is a dangerous proposition because
  1020. // a corrupted pidl can cause a shell extension to go haywire.
  1021. //
  1022. // A pinned item is stored in the stream in the form
  1023. //
  1024. // [byte:csidl] [dword:cbPidl] [size_is(cbPidl):pidl]
  1025. //
  1026. // With the special csidl = -1 indicating the end of the list.
  1027. //
  1028. // We use a byte for the csidl so a corrupted stream won't accidentally
  1029. // pass "CSIDL_FLAG_CREATE" as a csidl to SHGetSpecialFolderLocation.
  1030. HRESULT CStartMenuPinEnum::_NextPidlFromStream(LPITEMIDLIST *ppidl)
  1031. {
  1032. BYTE csidl;
  1033. HRESULT hr = IStream_Read(_pstm, &csidl, sizeof(csidl));
  1034. if (SUCCEEDED(hr))
  1035. {
  1036. if (csidl == CSIDL_END)
  1037. {
  1038. hr = S_FALSE; // end of enumeration
  1039. }
  1040. else
  1041. {
  1042. LPITEMIDLIST pidlRoot;
  1043. hr = SHGetSpecialFolderLocation(NULL, csidl, &pidlRoot);
  1044. if (SUCCEEDED(hr))
  1045. {
  1046. LPITEMIDLIST pidl;
  1047. hr = IStream_ReadPidl(_pstm, &pidl);
  1048. if (SUCCEEDED(hr))
  1049. {
  1050. hr = SHILCombine(pidlRoot, pidl, ppidl);
  1051. ILFree(pidl);
  1052. }
  1053. ILFree(pidlRoot);
  1054. }
  1055. }
  1056. }
  1057. return hr;
  1058. }
  1059. // *** IEnumIDList::Next
  1060. HRESULT CStartMenuPinEnum::Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched)
  1061. {
  1062. HRESULT hr;
  1063. ASSERT(celt > 0);
  1064. // If there was an error or EOF on the last call to IEnumIDList::Next,
  1065. // then that result is sticky. Once an enumeration has errored, it stays
  1066. // in the error state; once it has reached EOF, it stays at EOF. The
  1067. // only way to clear the state is to perform a Reset().
  1068. if (_hrLastEnum != S_OK)
  1069. {
  1070. return _hrLastEnum;
  1071. }
  1072. if (!_pstm)
  1073. {
  1074. _pstm = _OpenPinRegStream(STGM_READ);
  1075. }
  1076. if (_pstm)
  1077. {
  1078. rgelt[0] = NULL;
  1079. hr = _NextPidlFromStream(rgelt);
  1080. }
  1081. else
  1082. {
  1083. hr = S_FALSE; // No stream therefore no items
  1084. }
  1085. if (pceltFetched)
  1086. {
  1087. *pceltFetched = hr == S_OK ? 1 : 0;
  1088. }
  1089. // Remember the return code for next time. If an error occured or EOF,
  1090. // then free the memory used for enumeration.
  1091. _hrLastEnum = hr;
  1092. if (_hrLastEnum != S_OK)
  1093. {
  1094. ATOMICRELEASE(_pstm);
  1095. }
  1096. return hr;
  1097. }
  1098. // *** IEnumIDList::Skip
  1099. HRESULT CStartMenuPinEnum::Skip(ULONG)
  1100. {
  1101. return E_NOTIMPL;
  1102. }
  1103. // *** IEnumIDList::Reset
  1104. HRESULT CStartMenuPinEnum::Reset()
  1105. {
  1106. _hrLastEnum = S_OK;
  1107. ATOMICRELEASE(_pstm);
  1108. return S_OK;
  1109. }
  1110. // *** IEnumIDList::Clone
  1111. STDMETHODIMP CStartMenuPinEnum::Clone(IEnumIDList **ppenum)
  1112. {
  1113. *ppenum = NULL;
  1114. return E_NOTIMPL;
  1115. }
  1116. // *** IStartMenuPin::EnumObjects
  1117. STDMETHODIMP CStartMenuPin::EnumObjects(IEnumIDList **ppenum)
  1118. {
  1119. _InitPinRegStream();
  1120. *ppenum = NULL;
  1121. return CStartMenuPinEnum::CreateInstance(ppenum);
  1122. }
  1123. STDAPI CStartMenuPin_CreateInstance(IUnknown* punkOuter, REFIID riid, void** ppunk)
  1124. {
  1125. return CStartMenuPin::_CreatorClass::CreateInstance(punkOuter, riid, ppunk);
  1126. }
  1127. // *** IStartMenuPin::_InitPinRegStream
  1128. //
  1129. // If the pin list has not yet been created, then create a default one.
  1130. //
  1131. static LPCTSTR c_rgszDefaultPin[] = {
  1132. TEXT("shell:::{2559a1f4-21d7-11d4-bdaf-00c04f60b9f0}"), // CLSID_AutoCMClientInet
  1133. TEXT("shell:::{2559a1f5-21d7-11d4-bdaf-00c04f60b9f0}"), // CLSID_AutoCMClientMail
  1134. #ifdef NOTYET
  1135. // NOTE! Before you turn this on, make sure wmp is installed on ia64
  1136. TEXT("shell:::{2559a1f2-21d7-11d4-bdaf-00c04f60b9f0}"), // CLSID_AutoCMClientMedia
  1137. #endif
  1138. };
  1139. HRESULT CStartMenuPin::_InitPinRegStream()
  1140. {
  1141. HRESULT hr = S_OK;
  1142. if(SHRestricted(REST_NOSMPINNEDLIST))
  1143. return hr; //Nothing to initialize.
  1144. IStream *pstm = _OpenPinRegStream(STGM_READ);
  1145. BOOL fEmpty = pstm == NULL || SHIsEmptyStream(pstm);
  1146. ATOMICRELEASE(pstm);
  1147. if (fEmpty)
  1148. {
  1149. // Create a default pin list
  1150. CPinList pl;
  1151. // Do not call pl.Load() because that will recurse back into us!
  1152. if (pl.Initialize())
  1153. {
  1154. for (int i = 0; i < ARRAYSIZE(c_rgszDefaultPin); i++)
  1155. {
  1156. LPITEMIDLIST pidl = ILCreateFromPath(c_rgszDefaultPin[i]);
  1157. if (pidl && pl.AppendPidl(pidl) < 0)
  1158. {
  1159. ILFree(pidl);
  1160. }
  1161. }
  1162. hr = pl.Save(this);
  1163. }
  1164. else
  1165. {
  1166. hr = E_OUTOFMEMORY;
  1167. }
  1168. }
  1169. return hr;
  1170. }