1 /*
2  * ReactOS Explorer
3  *
4  * Copyright 2014 Giannis Adamopoulos
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20 #include "shellmenu.h"
21 
22 #include "CMergedFolder.h"
23 
24 WINE_DEFAULT_DEBUG_CHANNEL(CStartMenu);
25 
26 //#define TEST_TRACKPOPUPMENU_SUBMENUS
27 
28 
29 /* NOTE: The following constants *MUST NOT* be changed because
30          they're hardcoded and need to be the exact values
31          in order to get the start menu to work! */
32 #define IDM_RUN                     401
33 #define IDM_LOGOFF                  402
34 #define IDM_UNDOCKCOMPUTER          410
35 #define IDM_TASKBARANDSTARTMENU     413
36 #define IDM_LASTSTARTMENU_SEPARATOR 450
37 #define IDM_DOCUMENTS               501
38 #define IDM_HELPANDSUPPORT          503
39 #define IDM_PROGRAMS                504
40 #define IDM_CONTROLPANEL            505
41 #define IDM_SHUTDOWN                506
42 #define IDM_FAVORITES               507
43 #define IDM_SETTINGS                508
44 #define IDM_PRINTERSANDFAXES        510
45 #define IDM_SEARCH                  520
46 #define IDM_SYNCHRONIZE             553
47 #define IDM_NETWORKCONNECTIONS      557
48 #define IDM_DISCONNECT              5000
49 #define IDM_SECURITY                5001
50 
51 /*
52  * TODO:
53  * 1. append the start menu contents from all users
54  * 2. implement the context menu for start menu entries (programs, control panel, network connetions, printers)
55  * 3. filter out programs folder from the shell folder part of the start menu
56  * 4. showing the programs start menu is SLOW compared to windows. this needs some investigation
57  */
58 
59 class CShellMenuCallback :
60     public CComObjectRootEx<CComMultiThreadModelNoCS>,
61     public IShellMenuCallback
62 {
63 private:
64     HWND m_hwndTray;
65     CComPtr<IShellMenu> m_pShellMenu;
66     CComPtr<IBandSite> m_pBandSite;
67     CComPtr<IDeskBar> m_pDeskBar;
68     CComPtr<ITrayPriv> m_pTrayPriv;
69     CComPtr<IShellFolder> m_psfPrograms;
70 
71     LPITEMIDLIST m_pidlPrograms;
72 
73     HRESULT OnInitMenu()
74     {
75         HMENU hmenu;
76         HRESULT hr;
77 
78         if (m_pTrayPriv.p)
79             return S_OK;
80 
81         hr = IUnknown_GetSite(m_pDeskBar, IID_PPV_ARG(ITrayPriv, &m_pTrayPriv));
82         if (FAILED_UNEXPECTEDLY(hr))
83             return hr;
84 
85         hr = IUnknown_GetWindow(m_pTrayPriv, &m_hwndTray);
86         if (FAILED_UNEXPECTEDLY(hr))
87             return hr;
88 
89         hr = m_pTrayPriv->AppendMenu(&hmenu);
90         if (FAILED_UNEXPECTEDLY(hr))
91             return hr;
92 
93         hr = m_pShellMenu->SetMenu(hmenu, NULL, SMSET_BOTTOM);
94         if (FAILED_UNEXPECTEDLY(hr))
95         {
96             DestroyMenu(hmenu);
97             return hr;
98         }
99 
100         return hr;
101     }
102 
103     HRESULT OnGetInfo(LPSMDATA psmd, SMINFO *psminfo)
104     {
105         int iconIndex = 0;
106 
107         switch (psmd->uId)
108         {
109             // Smaller "24x24" icons used for the start menu
110             // The bitmaps are still 32x32, but the image is centered
111         case IDM_FAVORITES: iconIndex = -IDI_SHELL_FAVOTITES; break;
112         case IDM_SEARCH: iconIndex = -IDI_SHELL_SEARCH1; break;
113         case IDM_HELPANDSUPPORT: iconIndex = -IDI_SHELL_HELP2; break;
114         case IDM_LOGOFF: iconIndex = -IDI_SHELL_LOGOFF1; break;
115         case IDM_PROGRAMS:  iconIndex = -IDI_SHELL_PROGRAMS_FOLDER1; break;
116         case IDM_DOCUMENTS: iconIndex = -IDI_SHELL_RECENT_DOCUMENTS1; break;
117         case IDM_RUN: iconIndex = -IDI_SHELL_RUN1; break;
118         case IDM_SHUTDOWN: iconIndex = -IDI_SHELL_SHUTDOWN1; break;
119         case IDM_SETTINGS: iconIndex = -IDI_SHELL_CONTROL_PANEL1; break;
120         case IDM_MYDOCUMENTS: iconIndex = -IDI_SHELL_MY_DOCUMENTS; break;
121         case IDM_MYPICTURES: iconIndex = -IDI_SHELL_MY_PICTURES; break;
122 
123         case IDM_CONTROLPANEL: iconIndex = -IDI_SHELL_CONTROL_PANEL; break;
124         case IDM_NETWORKCONNECTIONS: iconIndex = -IDI_SHELL_NETWORK_CONNECTIONS2; break;
125         case IDM_PRINTERSANDFAXES: iconIndex = -IDI_SHELL_PRINTER2; break;
126         case IDM_TASKBARANDSTARTMENU: iconIndex = -IDI_SHELL_TSKBAR_STARTMENU; break;
127         //case IDM_SECURITY: iconIndex = -21; break;
128         //case IDM_SYNCHRONIZE: iconIndex = -21; break;
129         //case IDM_DISCONNECT: iconIndex = -21; break;
130         //case IDM_UNDOCKCOMPUTER: iconIndex = -21; break;
131         default:
132             return S_FALSE;
133         }
134 
135         if (iconIndex)
136         {
137             if ((psminfo->dwMask & SMIM_TYPE) != 0)
138                 psminfo->dwType = SMIT_STRING;
139             if ((psminfo->dwMask & SMIM_ICON) != 0)
140                 psminfo->iIcon = Shell_GetCachedImageIndex(L"shell32.dll", iconIndex, FALSE);
141             if ((psminfo->dwMask & SMIM_FLAGS) != 0)
142                 psminfo->dwFlags |= SMIF_ICON;
143 #ifdef TEST_TRACKPOPUPMENU_SUBMENUS
144             if ((psminfo->dwMask & SMIM_FLAGS) != 0)
145                 psminfo->dwFlags |= SMIF_TRACKPOPUP;
146 #endif
147         }
148         else
149         {
150             if ((psminfo->dwMask & SMIM_TYPE) != 0)
151                 psminfo->dwType = SMIT_SEPARATOR;
152         }
153         return S_OK;
154     }
155 
156     void InsertRecentItem(HMENU hMenu, UINT nID, INT csidl, BOOL bExpand) const
157     {
158         WCHAR szPath[MAX_PATH];
159         if (!SHGetSpecialFolderPathW(NULL, szPath, csidl, FALSE))
160         {
161             ERR("SHGetSpecialFolderPathW failed\n");
162             return;
163         }
164 
165         LPWSTR pszText = PathFindFileNameW(szPath);
166         if (bExpand)
167         {
168             MENUITEMINFOW mii = { sizeof(mii), MIIM_TYPE | MIIM_ID | MIIM_SUBMENU };
169             mii.fType = MFT_STRING;
170             mii.wID = nID;
171             mii.hSubMenu = ::CreatePopupMenu();
172             mii.dwTypeData = pszText;
173             mii.cch = lstrlenW(pszText);
174             InsertMenuItemW(hMenu, GetMenuItemCount(hMenu), TRUE, &mii);
175         }
176         else
177         {
178             AppendMenuW(hMenu, MF_STRING | MF_ENABLED, nID, pszText);
179         }
180     }
181 
182     HMENU CreateRecentMenu(BOOL bExpandMyDocuments, BOOL bExpandMyPictures) const
183     {
184         HMENU hMenu = ::CreateMenu();
185         InsertRecentItem(hMenu, IDM_MYDOCUMENTS, CSIDL_MYDOCUMENTS, bExpandMyDocuments);
186         InsertRecentItem(hMenu, IDM_MYPICTURES, CSIDL_MYPICTURES, bExpandMyPictures);
187         AppendMenuW(hMenu, MF_SEPARATOR, 0, NULL);
188         return hMenu;
189     }
190 
191     HRESULT OnGetSubMenu(LPSMDATA psmd, REFIID iid, void ** pv)
192     {
193         HRESULT hr;
194         int csidl = 0;
195         IShellMenu *pShellMenu;
196 
197         hr = CMenuBand_CreateInstance(IID_PPV_ARG(IShellMenu, &pShellMenu));
198         if (FAILED_UNEXPECTEDLY(hr))
199             return hr;
200 
201         hr = pShellMenu->Initialize(this, 0, ANCESTORDEFAULT, SMINIT_VERTICAL);
202         if (FAILED_UNEXPECTEDLY(hr))
203             return hr;
204 
205         switch (psmd->uId)
206         {
207         case IDM_PROGRAMS:  csidl = CSIDL_PROGRAMS; break;
208         case IDM_FAVORITES: csidl = CSIDL_FAVORITES; break;
209         case IDM_DOCUMENTS: csidl = CSIDL_RECENT; break;
210         case IDM_MYDOCUMENTS: csidl = CSIDL_MYDOCUMENTS; break;
211         case IDM_MYPICTURES: csidl = CSIDL_MYPICTURES; break;
212         }
213 
214         if (csidl)
215         {
216             IShellFolder *psfStartMenu;
217             DWORD dwFlags = SMSET_TOP;
218 
219             if (csidl == CSIDL_PROGRAMS && m_psfPrograms)
220             {
221                 psfStartMenu = m_psfPrograms;
222             }
223             else
224             {
225                 if (csidl == CSIDL_RECENT)
226                 {
227                     BOOL bExpandMyDocuments = FALSE; /* FIXME: Get value from registry */
228                     BOOL bExpandMyPictures = FALSE;  /* FIXME: Get value from registry */
229                     HMENU hMenu = CreateRecentMenu(bExpandMyDocuments, bExpandMyPictures);
230                     if (hMenu == NULL)
231                         ERR("CreateRecentMenu failed\n");
232 
233                     hr = pShellMenu->SetMenu(hMenu, NULL, SMSET_BOTTOM);
234                     if (FAILED_UNEXPECTEDLY(hr))
235                         return hr;
236 
237                     dwFlags = SMSET_BOTTOM;
238                 }
239 
240                 LPITEMIDLIST pidlStartMenu;
241                 IShellFolder *psfDestop;
242                 hr = SHGetFolderLocation(NULL, csidl, 0, 0, &pidlStartMenu);
243                 if (FAILED_UNEXPECTEDLY(hr))
244                     return hr;
245 
246                 hr = SHGetDesktopFolder(&psfDestop);
247                 if (FAILED_UNEXPECTEDLY(hr))
248                     return hr;
249 
250                 hr = psfDestop->BindToObject(pidlStartMenu, NULL, IID_PPV_ARG(IShellFolder, &psfStartMenu));
251                 if (FAILED_UNEXPECTEDLY(hr))
252                     return hr;
253             }
254 
255             hr = pShellMenu->SetShellFolder(psfStartMenu, NULL, NULL, dwFlags);
256             if (FAILED_UNEXPECTEDLY(hr))
257                 return hr;
258         }
259         else
260         {
261             MENUITEMINFO mii;
262             mii.cbSize = sizeof(mii);
263             mii.fMask = MIIM_SUBMENU;
264             if (GetMenuItemInfoW(psmd->hmenu, psmd->uId, FALSE, &mii))
265             {
266                 hr = pShellMenu->SetMenu(mii.hSubMenu, NULL, SMSET_BOTTOM);
267                 if (FAILED_UNEXPECTEDLY(hr))
268                     return hr;
269             }
270         }
271         return pShellMenu->QueryInterface(iid, pv);
272     }
273 
274     HRESULT OnGetContextMenu(LPSMDATA psmd, REFIID iid, void ** pv)
275     {
276         if (psmd->uId == IDM_PROGRAMS ||
277             psmd->uId == IDM_CONTROLPANEL ||
278             psmd->uId == IDM_NETWORKCONNECTIONS ||
279             psmd->uId == IDM_PRINTERSANDFAXES)
280         {
281             //UNIMPLEMENTED
282         }
283 
284         return S_FALSE;
285     }
286 
287     HRESULT OnGetObject(LPSMDATA psmd, REFIID iid, void ** pv)
288     {
289         if (IsEqualIID(iid, IID_IShellMenu))
290             return OnGetSubMenu(psmd, iid, pv);
291         else if (IsEqualIID(iid, IID_IContextMenu))
292             return OnGetContextMenu(psmd, iid, pv);
293 
294         return S_FALSE;
295     }
296 
297     HRESULT OnExec(LPSMDATA psmd)
298     {
299         WCHAR szPath[MAX_PATH];
300 
301         // HACK: Because our ShellExecute can't handle CLSID components in paths, we can't launch the paths using the "open" verb.
302         // FIXME: Change this back to using the path as the filename and the "open" verb, once ShellExecute can handle CLSID path components.
303 
304         if (psmd->uId == IDM_CONTROLPANEL)
305             ShellExecuteW(NULL, NULL, L"explorer.exe", L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}", NULL, SW_SHOWNORMAL);
306         else if (psmd->uId == IDM_NETWORKCONNECTIONS)
307             ShellExecuteW(NULL, NULL, L"explorer.exe", L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{7007ACC7-3202-11D1-AAD2-00805FC1270E}", NULL, SW_SHOWNORMAL);
308         else if (psmd->uId == IDM_PRINTERSANDFAXES)
309             ShellExecuteW(NULL, NULL, L"explorer.exe", L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{2227A280-3AEA-1069-A2DE-08002B30309D}", NULL, SW_SHOWNORMAL);
310         else if (psmd->uId == IDM_MYDOCUMENTS)
311         {
312             if (SHGetSpecialFolderPathW(NULL, szPath, CSIDL_PERSONAL, FALSE))
313                 ShellExecuteW(NULL, NULL, szPath, NULL, NULL, SW_SHOWNORMAL);
314             else
315                 ERR("SHGetSpecialFolderPathW failed\n");
316         }
317         else if (psmd->uId == IDM_MYPICTURES)
318         {
319             if (SHGetSpecialFolderPathW(NULL, szPath, CSIDL_MYPICTURES, FALSE))
320                 ShellExecuteW(NULL, NULL, szPath, NULL, NULL, SW_SHOWNORMAL);
321             else
322                 ERR("SHGetSpecialFolderPathW failed\n");
323         }
324         else
325             PostMessageW(m_hwndTray, WM_COMMAND, psmd->uId, 0);
326 
327         return S_OK;
328     }
329 
330 public:
331 
332     DECLARE_NOT_AGGREGATABLE(CShellMenuCallback)
333     DECLARE_PROTECT_FINAL_CONSTRUCT()
334     BEGIN_COM_MAP(CShellMenuCallback)
335         COM_INTERFACE_ENTRY_IID(IID_IShellMenuCallback, IShellMenuCallback)
336     END_COM_MAP()
337 
338     void Initialize(
339         IShellMenu* pShellMenu,
340         IBandSite* pBandSite,
341         IDeskBar* pDeskBar)
342     {
343         m_pShellMenu = pShellMenu;
344         m_pBandSite = pBandSite;
345         m_pDeskBar = pDeskBar;
346     }
347 
348     ~CShellMenuCallback()
349     {
350     }
351 
352     HRESULT _SetProgramsFolder(IShellFolder * psf, LPITEMIDLIST pidl)
353     {
354         m_psfPrograms = psf;
355         m_pidlPrograms = pidl;
356         return S_OK;
357     }
358 
359     HRESULT STDMETHODCALLTYPE CallbackSM(
360         LPSMDATA psmd,
361         UINT uMsg,
362         WPARAM wParam,
363         LPARAM lParam)
364     {
365         switch (uMsg)
366         {
367         case SMC_INITMENU:
368             return OnInitMenu();
369         case SMC_GETINFO:
370             return OnGetInfo(psmd, reinterpret_cast<SMINFO*>(lParam));
371         case SMC_GETOBJECT:
372             return OnGetObject(psmd, *reinterpret_cast<IID *>(wParam), reinterpret_cast<void **>(lParam));
373         case SMC_EXEC:
374             return OnExec(psmd);
375         case SMC_SFEXEC:
376             m_pTrayPriv->Execute(psmd->psf, psmd->pidlItem);
377             break;
378         case 0x10000000: // _FilterPIDL from CMenuSFToolbar
379             if (psmd->psf->CompareIDs(0, psmd->pidlItem, m_pidlPrograms) == 0)
380                 return S_OK;
381             return S_FALSE;
382         }
383 
384         return S_FALSE;
385     }
386 };
387 
388 HRESULT BindToDesktop(LPCITEMIDLIST pidl, IShellFolder ** ppsfResult)
389 {
390     HRESULT hr;
391     CComPtr<IShellFolder> psfDesktop;
392 
393     *ppsfResult = NULL;
394 
395     hr = SHGetDesktopFolder(&psfDesktop);
396     if (FAILED(hr))
397         return hr;
398 
399     hr = psfDesktop->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, ppsfResult));
400 
401     return hr;
402 }
403 
404 static HRESULT GetMergedFolder(int folder1, int folder2, IShellFolder ** ppsfStartMenu)
405 {
406     HRESULT hr;
407     LPITEMIDLIST pidlUserStartMenu;
408     LPITEMIDLIST pidlCommonStartMenu;
409     CComPtr<IShellFolder> psfUserStartMenu;
410     CComPtr<IShellFolder> psfCommonStartMenu;
411     CComPtr<IAugmentedShellFolder> pasf;
412 
413     *ppsfStartMenu = NULL;
414 
415     hr = SHGetSpecialFolderLocation(NULL, folder1, &pidlUserStartMenu);
416     if (FAILED(hr))
417     {
418         WARN("Failed to get the USER start menu folder. Trying to run with just the COMMON one.\n");
419 
420         hr = SHGetSpecialFolderLocation(NULL, folder2, &pidlCommonStartMenu);
421         if (FAILED_UNEXPECTEDLY(hr))
422             return hr;
423 
424         TRACE("COMMON start menu obtained.\n");
425         hr = BindToDesktop(pidlCommonStartMenu, ppsfStartMenu);
426         ILFree(pidlCommonStartMenu);
427         return hr;
428     }
429 #if MERGE_FOLDERS
430     hr = SHGetSpecialFolderLocation(NULL, folder2, &pidlCommonStartMenu);
431     if (FAILED_UNEXPECTEDLY(hr))
432 #else
433     else
434 #endif
435     {
436         WARN("Failed to get the COMMON start menu folder. Will use only the USER contents.\n");
437         hr = BindToDesktop(pidlUserStartMenu, ppsfStartMenu);
438         ILFree(pidlUserStartMenu);
439         return hr;
440     }
441 
442     TRACE("Both COMMON and USER statr menu folders obtained, merging them...\n");
443 
444     hr = BindToDesktop(pidlUserStartMenu, &psfUserStartMenu);
445     if (FAILED_UNEXPECTEDLY(hr))
446         return hr;
447 
448     hr = BindToDesktop(pidlCommonStartMenu, &psfCommonStartMenu);
449     if (FAILED_UNEXPECTEDLY(hr))
450         return hr;
451 
452     hr = CMergedFolder_CreateInstance(IID_PPV_ARG(IAugmentedShellFolder, &pasf));
453     if (FAILED_UNEXPECTEDLY(hr))
454     {
455         *ppsfStartMenu = psfUserStartMenu.Detach();
456         ILFree(pidlCommonStartMenu);
457         ILFree(pidlUserStartMenu);
458         return hr;
459     }
460 
461     hr = pasf->AddNameSpace(NULL, psfUserStartMenu, pidlUserStartMenu, 0xFF00);
462     if (FAILED_UNEXPECTEDLY(hr))
463         return hr;
464 
465     hr = pasf->AddNameSpace(NULL, psfCommonStartMenu, pidlCommonStartMenu, 0);
466     if (FAILED_UNEXPECTEDLY(hr))
467         return hr;
468 
469     hr = pasf->QueryInterface(IID_PPV_ARG(IShellFolder, ppsfStartMenu));
470     pasf.Release();
471 
472     ILFree(pidlCommonStartMenu);
473     ILFree(pidlUserStartMenu);
474 
475     return hr;
476 }
477 
478 static HRESULT GetStartMenuFolder(IShellFolder ** ppsfStartMenu)
479 {
480     return GetMergedFolder(CSIDL_STARTMENU, CSIDL_COMMON_STARTMENU, ppsfStartMenu);
481 }
482 
483 static HRESULT GetProgramsFolder(IShellFolder ** ppsfStartMenu)
484 {
485     return GetMergedFolder(CSIDL_PROGRAMS, CSIDL_COMMON_PROGRAMS, ppsfStartMenu);
486 }
487 
488 extern "C"
489 HRESULT WINAPI
490 RSHELL_CStartMenu_CreateInstance(REFIID riid, void **ppv)
491 {
492     CComPtr<IShellMenu> pShellMenu;
493     CComPtr<IBandSite> pBandSite;
494     CComPtr<IDeskBar> pDeskBar;
495 
496     HRESULT hr;
497     IShellFolder * psf;
498 
499     LPITEMIDLIST pidlProgramsAbsolute;
500     LPITEMIDLIST pidlPrograms;
501     CComPtr<IShellFolder> psfPrograms;
502 
503     hr = CMenuBand_CreateInstance(IID_PPV_ARG(IShellMenu, &pShellMenu));
504     if (FAILED_UNEXPECTEDLY(hr))
505         return hr;
506 
507     hr = CMenuSite_CreateInstance(IID_PPV_ARG(IBandSite, &pBandSite));
508     if (FAILED_UNEXPECTEDLY(hr))
509         return hr;
510 
511     hr = CMenuDeskBar_CreateInstance(IID_PPV_ARG(IDeskBar, &pDeskBar));
512     if (FAILED_UNEXPECTEDLY(hr))
513         return hr;
514 
515     CComObject<CShellMenuCallback> *pCallback;
516     hr = CComObject<CShellMenuCallback>::CreateInstance(&pCallback);
517     if (FAILED_UNEXPECTEDLY(hr))
518         return hr;
519 
520     pCallback->AddRef(); // CreateInstance returns object with 0 ref count */
521     pCallback->Initialize(pShellMenu, pBandSite, pDeskBar);
522 
523     hr = pShellMenu->Initialize(pCallback, (UINT) -1, 0, SMINIT_TOPLEVEL | SMINIT_VERTICAL);
524     if (FAILED_UNEXPECTEDLY(hr))
525         return hr;
526 
527     hr = GetStartMenuFolder(&psf);
528     if (FAILED_UNEXPECTEDLY(hr))
529         return hr;
530 
531     /* psf is a merged folder, so now we want to get the pidl of the programs item from the merged folder */
532     {
533         hr = SHGetSpecialFolderLocation(NULL, CSIDL_PROGRAMS, &pidlProgramsAbsolute);
534         if (FAILED_UNEXPECTEDLY(hr))
535         {
536             WARN("USER Programs folder not found.\n");
537             hr = SHGetSpecialFolderLocation(NULL, CSIDL_COMMON_PROGRAMS, &pidlProgramsAbsolute);
538             if (FAILED_UNEXPECTEDLY(hr))
539                 return hr;
540         }
541 
542         LPCITEMIDLIST pcidlPrograms;
543         CComPtr<IShellFolder> psfParent;
544         STRRET str;
545         TCHAR szDisplayName[MAX_PATH];
546 
547         hr = SHBindToParent(pidlProgramsAbsolute, IID_PPV_ARG(IShellFolder, &psfParent), &pcidlPrograms);
548         if (FAILED_UNEXPECTEDLY(hr))
549             return hr;
550 
551         hr = psfParent->GetDisplayNameOf(pcidlPrograms, SHGDN_FORPARSING | SHGDN_INFOLDER, &str);
552         if (FAILED_UNEXPECTEDLY(hr))
553             return hr;
554 
555         StrRetToBuf(&str, pcidlPrograms, szDisplayName, _countof(szDisplayName));
556         ILFree(pidlProgramsAbsolute);
557 
558         /* We got the display name from the fs folder and we parse it with the merged folder here */
559         hr = psf->ParseDisplayName(NULL, NULL, szDisplayName, NULL, &pidlPrograms, NULL);
560         if (FAILED_UNEXPECTEDLY(hr))
561             return hr;
562     }
563 
564     hr = GetProgramsFolder(&psfPrograms);
565     if (FAILED_UNEXPECTEDLY(hr))
566         return hr;
567 
568     hr = pCallback->_SetProgramsFolder(psfPrograms, pidlPrograms);
569     if (FAILED_UNEXPECTEDLY(hr))
570         return hr;
571 
572     hr = pShellMenu->SetShellFolder(psf, NULL, NULL, SMSET_TOP);
573     if (FAILED_UNEXPECTEDLY(hr))
574         return hr;
575 
576     hr = pDeskBar->SetClient(pBandSite);
577     if (FAILED_UNEXPECTEDLY(hr))
578         return hr;
579 
580     hr = pBandSite->AddBand(pShellMenu);
581     if (FAILED_UNEXPECTEDLY(hr))
582         return hr;
583 
584     return pDeskBar->QueryInterface(riid, ppv);
585 }
586