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.

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