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->AppendMenuW(&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             return hr;
96 
97         return hr;
98     }
99 
100     HRESULT OnGetInfo(LPSMDATA psmd, SMINFO *psminfo)
101     {
102         int iconIndex = 0;
103 
104         switch (psmd->uId)
105         {
106             // Smaller "24x24" icons used for the start menu
107             // The bitmaps are still 32x32, but the image is centered
108         case IDM_FAVORITES: iconIndex = -IDI_SHELL_FAVOTITES; break;
109         case IDM_SEARCH: iconIndex = -IDI_SHELL_SEARCH1; break;
110         case IDM_HELPANDSUPPORT: iconIndex = -IDI_SHELL_HELP2; break;
111         case IDM_LOGOFF: iconIndex = -IDI_SHELL_LOGOFF1; break;
112         case IDM_PROGRAMS:  iconIndex = -IDI_SHELL_PROGRAMS_FOLDER1; break;
113         case IDM_DOCUMENTS: iconIndex = -IDI_SHELL_RECENT_DOCUMENTS1; break;
114         case IDM_RUN: iconIndex = -IDI_SHELL_RUN1; break;
115         case IDM_SHUTDOWN: iconIndex = -IDI_SHELL_SHUTDOWN1; break;
116         case IDM_SETTINGS: iconIndex = -IDI_SHELL_CONTROL_PANEL1; break;
117         case IDM_MYDOCUMENTS: iconIndex = -IDI_SHELL_MY_DOCUMENTS; break;
118         case IDM_MYPICTURES: iconIndex = -IDI_SHELL_MY_PICTURES; break;
119 
120         case IDM_CONTROLPANEL: iconIndex = -IDI_SHELL_CONTROL_PANEL; break;
121         case IDM_NETWORKCONNECTIONS: iconIndex = -IDI_SHELL_NETWORK_CONNECTIONS2; break;
122         case IDM_PRINTERSANDFAXES: iconIndex = -IDI_SHELL_PRINTER2; break;
123         case IDM_TASKBARANDSTARTMENU: iconIndex = -IDI_SHELL_TSKBAR_STARTMENU; break;
124         //case IDM_SECURITY: iconIndex = -21; break;
125         //case IDM_SYNCHRONIZE: iconIndex = -21; break;
126         //case IDM_DISCONNECT: iconIndex = -21; break;
127         //case IDM_UNDOCKCOMPUTER: iconIndex = -21; break;
128         default:
129             return S_FALSE;
130         }
131 
132         if (iconIndex)
133         {
134             if ((psminfo->dwMask & SMIM_TYPE) != 0)
135                 psminfo->dwType = SMIT_STRING;
136             if ((psminfo->dwMask & SMIM_ICON) != 0)
137                 psminfo->iIcon = Shell_GetCachedImageIndex(L"shell32.dll", iconIndex, FALSE);
138             if ((psminfo->dwMask & SMIM_FLAGS) != 0)
139                 psminfo->dwFlags |= SMIF_ICON;
140 #ifdef TEST_TRACKPOPUPMENU_SUBMENUS
141             if ((psminfo->dwMask & SMIM_FLAGS) != 0)
142                 psminfo->dwFlags |= SMIF_TRACKPOPUP;
143 #endif
144         }
145         else
146         {
147             if ((psminfo->dwMask & SMIM_TYPE) != 0)
148                 psminfo->dwType = SMIT_SEPARATOR;
149         }
150         return S_OK;
151     }
152 
153     void InsertRecentItem(HMENU hMenu, UINT nID, INT csidl, BOOL bExpand) const
154     {
155         WCHAR szPath[MAX_PATH];
156         if (!SHGetSpecialFolderPathW(NULL, szPath, csidl, FALSE))
157         {
158             ERR("SHGetSpecialFolderPathW failed\n");
159             return;
160         }
161 
162         LPWSTR pszText = PathFindFileNameW(szPath);
163         if (bExpand)
164         {
165             MENUITEMINFOW mii = { sizeof(mii), MIIM_TYPE | MIIM_ID | MIIM_SUBMENU };
166             mii.fType = MFT_STRING;
167             mii.wID = nID;
168             mii.hSubMenu = ::CreatePopupMenu();
169             mii.dwTypeData = pszText;
170             mii.cch = lstrlenW(pszText);
171             InsertMenuItemW(hMenu, GetMenuItemCount(hMenu), TRUE, &mii);
172         }
173         else
174         {
175             AppendMenuW(hMenu, MF_STRING | MF_ENABLED, nID, pszText);
176         }
177     }
178 
179     HMENU CreateRecentMenu(BOOL bExpandMyDocuments, BOOL bExpandMyPictures) const
180     {
181         HMENU hMenu = ::CreateMenu();
182         InsertRecentItem(hMenu, IDM_MYDOCUMENTS, CSIDL_MYDOCUMENTS, bExpandMyDocuments);
183         InsertRecentItem(hMenu, IDM_MYPICTURES, CSIDL_MYPICTURES, bExpandMyPictures);
184         AppendMenuW(hMenu, MF_SEPARATOR, 0, NULL);
185         return hMenu;
186     }
187 
188     HRESULT OnGetSubMenu(LPSMDATA psmd, REFIID iid, void ** pv)
189     {
190         HRESULT hr;
191         int csidl = 0;
192         IShellMenu *pShellMenu;
193 
194         hr = CMenuBand_CreateInstance(IID_PPV_ARG(IShellMenu, &pShellMenu));
195         if (FAILED_UNEXPECTEDLY(hr))
196             return hr;
197 
198         hr = pShellMenu->Initialize(this, 0, ANCESTORDEFAULT, SMINIT_VERTICAL);
199         if (FAILED_UNEXPECTEDLY(hr))
200             return hr;
201 
202         switch (psmd->uId)
203         {
204         case IDM_PROGRAMS:  csidl = CSIDL_PROGRAMS; break;
205         case IDM_FAVORITES: csidl = CSIDL_FAVORITES; break;
206         case IDM_DOCUMENTS: csidl = CSIDL_RECENT; break;
207         case IDM_MYDOCUMENTS: csidl = CSIDL_MYDOCUMENTS; break;
208         case IDM_MYPICTURES: csidl = CSIDL_MYPICTURES; break;
209         }
210 
211         if (csidl)
212         {
213             IShellFolder *psfStartMenu;
214             DWORD dwFlags = SMSET_TOP;
215 
216             if (csidl == CSIDL_PROGRAMS && m_psfPrograms)
217             {
218                 psfStartMenu = m_psfPrograms;
219             }
220             else
221             {
222                 if (csidl == CSIDL_RECENT)
223                 {
224                     BOOL bExpandMyDocuments = FALSE; /* FIXME: Get value from registry */
225                     BOOL bExpandMyPictures = FALSE;  /* FIXME: Get value from registry */
226                     HMENU hMenu = CreateRecentMenu(bExpandMyDocuments, bExpandMyPictures);
227                     if (hMenu == NULL)
228                         ERR("CreateRecentMenu failed\n");
229 
230                     hr = pShellMenu->SetMenu(hMenu, NULL, SMSET_BOTTOM);
231                     if (FAILED_UNEXPECTEDLY(hr))
232                         return hr;
233 
234                     dwFlags = SMSET_BOTTOM;
235                 }
236 
237                 LPITEMIDLIST pidlStartMenu;
238                 IShellFolder *psfDestop;
239                 hr = SHGetFolderLocation(NULL, csidl, 0, 0, &pidlStartMenu);
240                 if (FAILED_UNEXPECTEDLY(hr))
241                     return hr;
242 
243                 hr = SHGetDesktopFolder(&psfDestop);
244                 if (FAILED_UNEXPECTEDLY(hr))
245                     return hr;
246 
247                 hr = psfDestop->BindToObject(pidlStartMenu, NULL, IID_PPV_ARG(IShellFolder, &psfStartMenu));
248                 if (FAILED_UNEXPECTEDLY(hr))
249                     return hr;
250             }
251 
252             hr = pShellMenu->SetShellFolder(psfStartMenu, NULL, NULL, dwFlags);
253             if (FAILED_UNEXPECTEDLY(hr))
254                 return hr;
255         }
256         else
257         {
258             MENUITEMINFO mii;
259             mii.cbSize = sizeof(mii);
260             mii.fMask = MIIM_SUBMENU;
261             if (GetMenuItemInfoW(psmd->hmenu, psmd->uId, FALSE, &mii))
262             {
263                 hr = pShellMenu->SetMenu(mii.hSubMenu, NULL, SMSET_BOTTOM);
264                 if (FAILED_UNEXPECTEDLY(hr))
265                     return hr;
266             }
267         }
268         return pShellMenu->QueryInterface(iid, pv);
269     }
270 
271     HRESULT OnGetContextMenu(LPSMDATA psmd, REFIID iid, void ** pv)
272     {
273         if (psmd->uId == IDM_PROGRAMS ||
274             psmd->uId == IDM_CONTROLPANEL ||
275             psmd->uId == IDM_NETWORKCONNECTIONS ||
276             psmd->uId == IDM_PRINTERSANDFAXES)
277         {
278             //UNIMPLEMENTED
279         }
280 
281         return S_FALSE;
282     }
283 
284     HRESULT OnGetObject(LPSMDATA psmd, REFIID iid, void ** pv)
285     {
286         if (IsEqualIID(iid, IID_IShellMenu))
287             return OnGetSubMenu(psmd, iid, pv);
288         else if (IsEqualIID(iid, IID_IContextMenu))
289             return OnGetContextMenu(psmd, iid, pv);
290 
291         return S_FALSE;
292     }
293 
294     HRESULT OnExec(LPSMDATA psmd)
295     {
296         WCHAR szPath[MAX_PATH];
297 
298         // HACK: Because our ShellExecute can't handle CLSID components in paths, we can't launch the paths using the "open" verb.
299         // FIXME: Change this back to using the path as the filename and the "open" verb, once ShellExecute can handle CLSID path components.
300 
301         if (psmd->uId == IDM_CONTROLPANEL)
302             ShellExecuteW(NULL, NULL, L"explorer.exe", L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}", NULL, SW_SHOWNORMAL);
303         else if (psmd->uId == IDM_NETWORKCONNECTIONS)
304             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);
305         else if (psmd->uId == IDM_PRINTERSANDFAXES)
306             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);
307         else if (psmd->uId == IDM_MYDOCUMENTS)
308         {
309             if (SHGetSpecialFolderPathW(NULL, szPath, CSIDL_PERSONAL, FALSE))
310                 ShellExecuteW(NULL, NULL, szPath, NULL, NULL, SW_SHOWNORMAL);
311             else
312                 ERR("SHGetSpecialFolderPathW failed\n");
313         }
314         else if (psmd->uId == IDM_MYPICTURES)
315         {
316             if (SHGetSpecialFolderPathW(NULL, szPath, CSIDL_MYPICTURES, FALSE))
317                 ShellExecuteW(NULL, NULL, szPath, NULL, NULL, SW_SHOWNORMAL);
318             else
319                 ERR("SHGetSpecialFolderPathW failed\n");
320         }
321         else
322             PostMessageW(m_hwndTray, WM_COMMAND, psmd->uId, 0);
323 
324         return S_OK;
325     }
326 
327 public:
328 
329     DECLARE_NOT_AGGREGATABLE(CShellMenuCallback)
330     DECLARE_PROTECT_FINAL_CONSTRUCT()
331     BEGIN_COM_MAP(CShellMenuCallback)
332         COM_INTERFACE_ENTRY_IID(IID_IShellMenuCallback, IShellMenuCallback)
333     END_COM_MAP()
334 
335     void Initialize(
336         IShellMenu* pShellMenu,
337         IBandSite* pBandSite,
338         IDeskBar* pDeskBar)
339     {
340         m_pShellMenu = pShellMenu;
341         m_pBandSite = pBandSite;
342         m_pDeskBar = pDeskBar;
343     }
344 
345     ~CShellMenuCallback()
346     {
347     }
348 
349     HRESULT _SetProgramsFolder(IShellFolder * psf, LPITEMIDLIST pidl)
350     {
351         m_psfPrograms = psf;
352         m_pidlPrograms = pidl;
353         return S_OK;
354     }
355 
356     HRESULT STDMETHODCALLTYPE CallbackSM(
357         LPSMDATA psmd,
358         UINT uMsg,
359         WPARAM wParam,
360         LPARAM lParam)
361     {
362         switch (uMsg)
363         {
364         case SMC_INITMENU:
365             return OnInitMenu();
366         case SMC_GETINFO:
367             return OnGetInfo(psmd, reinterpret_cast<SMINFO*>(lParam));
368         case SMC_GETOBJECT:
369             return OnGetObject(psmd, *reinterpret_cast<IID *>(wParam), reinterpret_cast<void **>(lParam));
370         case SMC_EXEC:
371             return OnExec(psmd);
372         case SMC_SFEXEC:
373             m_pTrayPriv->Execute(psmd->psf, psmd->pidlItem);
374             break;
375         case 0x10000000: // _FilterPIDL from CMenuSFToolbar
376             if (psmd->psf->CompareIDs(0, psmd->pidlItem, m_pidlPrograms) == 0)
377                 return S_OK;
378             return S_FALSE;
379         }
380 
381         return S_FALSE;
382     }
383 };
384 
385 HRESULT BindToDesktop(LPCITEMIDLIST pidl, IShellFolder ** ppsfResult)
386 {
387     HRESULT hr;
388     CComPtr<IShellFolder> psfDesktop;
389 
390     *ppsfResult = NULL;
391 
392     hr = SHGetDesktopFolder(&psfDesktop);
393     if (FAILED(hr))
394         return hr;
395 
396     hr = psfDesktop->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, ppsfResult));
397 
398     return hr;
399 }
400 
401 static HRESULT GetMergedFolder(int folder1, int folder2, IShellFolder ** ppsfStartMenu)
402 {
403     HRESULT hr;
404     LPITEMIDLIST pidlUserStartMenu;
405     LPITEMIDLIST pidlCommonStartMenu;
406     CComPtr<IShellFolder> psfUserStartMenu;
407     CComPtr<IShellFolder> psfCommonStartMenu;
408     CComPtr<IAugmentedShellFolder> pasf;
409 
410     *ppsfStartMenu = NULL;
411 
412     hr = SHGetSpecialFolderLocation(NULL, folder1, &pidlUserStartMenu);
413     if (FAILED(hr))
414     {
415         WARN("Failed to get the USER start menu folder. Trying to run with just the COMMON one.\n");
416 
417         hr = SHGetSpecialFolderLocation(NULL, folder2, &pidlCommonStartMenu);
418         if (FAILED_UNEXPECTEDLY(hr))
419             return hr;
420 
421         TRACE("COMMON start menu obtained.\n");
422         hr = BindToDesktop(pidlCommonStartMenu, ppsfStartMenu);
423         ILFree(pidlCommonStartMenu);
424         return hr;
425     }
426 #if MERGE_FOLDERS
427     hr = SHGetSpecialFolderLocation(NULL, folder2, &pidlCommonStartMenu);
428     if (FAILED_UNEXPECTEDLY(hr))
429 #else
430     else
431 #endif
432     {
433         WARN("Failed to get the COMMON start menu folder. Will use only the USER contents.\n");
434         hr = BindToDesktop(pidlUserStartMenu, ppsfStartMenu);
435         ILFree(pidlUserStartMenu);
436         return hr;
437     }
438 
439     TRACE("Both COMMON and USER statr menu folders obtained, merging them...\n");
440 
441     hr = BindToDesktop(pidlUserStartMenu, &psfUserStartMenu);
442     if (FAILED_UNEXPECTEDLY(hr))
443         return hr;
444 
445     hr = BindToDesktop(pidlCommonStartMenu, &psfCommonStartMenu);
446     if (FAILED_UNEXPECTEDLY(hr))
447         return hr;
448 
449     hr = CMergedFolder_CreateInstance(IID_PPV_ARG(IAugmentedShellFolder, &pasf));
450     if (FAILED_UNEXPECTEDLY(hr))
451     {
452         *ppsfStartMenu = psfUserStartMenu.Detach();
453         ILFree(pidlCommonStartMenu);
454         ILFree(pidlUserStartMenu);
455         return hr;
456     }
457 
458     hr = pasf->AddNameSpace(NULL, psfUserStartMenu, pidlUserStartMenu, 0xFF00);
459     if (FAILED_UNEXPECTEDLY(hr))
460         return hr;
461 
462     hr = pasf->AddNameSpace(NULL, psfCommonStartMenu, pidlCommonStartMenu, 0);
463     if (FAILED_UNEXPECTEDLY(hr))
464         return hr;
465 
466     hr = pasf->QueryInterface(IID_PPV_ARG(IShellFolder, ppsfStartMenu));
467     pasf.Release();
468 
469     ILFree(pidlCommonStartMenu);
470     ILFree(pidlUserStartMenu);
471 
472     return hr;
473 }
474 
475 static HRESULT GetStartMenuFolder(IShellFolder ** ppsfStartMenu)
476 {
477     return GetMergedFolder(CSIDL_STARTMENU, CSIDL_COMMON_STARTMENU, ppsfStartMenu);
478 }
479 
480 static HRESULT GetProgramsFolder(IShellFolder ** ppsfStartMenu)
481 {
482     return GetMergedFolder(CSIDL_PROGRAMS, CSIDL_COMMON_PROGRAMS, ppsfStartMenu);
483 }
484 
485 extern "C"
486 HRESULT WINAPI
487 RSHELL_CStartMenu_CreateInstance(REFIID riid, void **ppv)
488 {
489     CComPtr<IShellMenu> pShellMenu;
490     CComPtr<IBandSite> pBandSite;
491     CComPtr<IDeskBar> pDeskBar;
492 
493     HRESULT hr;
494     IShellFolder * psf;
495 
496     LPITEMIDLIST pidlProgramsAbsolute;
497     LPITEMIDLIST pidlPrograms;
498     CComPtr<IShellFolder> psfPrograms;
499 
500     hr = CMenuBand_CreateInstance(IID_PPV_ARG(IShellMenu, &pShellMenu));
501     if (FAILED_UNEXPECTEDLY(hr))
502         return hr;
503 
504     hr = CMenuSite_CreateInstance(IID_PPV_ARG(IBandSite, &pBandSite));
505     if (FAILED_UNEXPECTEDLY(hr))
506         return hr;
507 
508     hr = CMenuDeskBar_CreateInstance(IID_PPV_ARG(IDeskBar, &pDeskBar));
509     if (FAILED_UNEXPECTEDLY(hr))
510         return hr;
511 
512     CComObject<CShellMenuCallback> *pCallback;
513     hr = CComObject<CShellMenuCallback>::CreateInstance(&pCallback);
514     if (FAILED_UNEXPECTEDLY(hr))
515         return hr;
516 
517     pCallback->AddRef(); // CreateInstance returns object with 0 ref count */
518     pCallback->Initialize(pShellMenu, pBandSite, pDeskBar);
519 
520     hr = pShellMenu->Initialize(pCallback, (UINT) -1, 0, SMINIT_TOPLEVEL | SMINIT_VERTICAL);
521     if (FAILED_UNEXPECTEDLY(hr))
522         return hr;
523 
524     hr = GetStartMenuFolder(&psf);
525     if (FAILED_UNEXPECTEDLY(hr))
526         return hr;
527 
528     /* psf is a merged folder, so now we want to get the pidl of the programs item from the merged folder */
529     {
530         hr = SHGetSpecialFolderLocation(NULL, CSIDL_PROGRAMS, &pidlProgramsAbsolute);
531         if (FAILED_UNEXPECTEDLY(hr))
532         {
533             WARN("USER Programs folder not found.\n");
534             hr = SHGetSpecialFolderLocation(NULL, CSIDL_COMMON_PROGRAMS, &pidlProgramsAbsolute);
535             if (FAILED_UNEXPECTEDLY(hr))
536                 return hr;
537         }
538 
539         LPCITEMIDLIST pcidlPrograms;
540         CComPtr<IShellFolder> psfParent;
541         STRRET str;
542         TCHAR szDisplayName[MAX_PATH];
543 
544         hr = SHBindToParent(pidlProgramsAbsolute, IID_PPV_ARG(IShellFolder, &psfParent), &pcidlPrograms);
545         if (FAILED_UNEXPECTEDLY(hr))
546             return hr;
547 
548         hr = psfParent->GetDisplayNameOf(pcidlPrograms, SHGDN_FORPARSING | SHGDN_INFOLDER, &str);
549         if (FAILED_UNEXPECTEDLY(hr))
550             return hr;
551 
552         StrRetToBuf(&str, pcidlPrograms, szDisplayName, _countof(szDisplayName));
553         ILFree(pidlProgramsAbsolute);
554 
555         /* We got the display name from the fs folder and we parse it with the merged folder here */
556         hr = psf->ParseDisplayName(NULL, NULL, szDisplayName, NULL, &pidlPrograms, NULL);
557         if (FAILED_UNEXPECTEDLY(hr))
558             return hr;
559     }
560 
561     hr = GetProgramsFolder(&psfPrograms);
562     if (FAILED_UNEXPECTEDLY(hr))
563         return hr;
564 
565     hr = pCallback->_SetProgramsFolder(psfPrograms, pidlPrograms);
566     if (FAILED_UNEXPECTEDLY(hr))
567         return hr;
568 
569     hr = pShellMenu->SetShellFolder(psf, NULL, NULL, SMSET_TOP);
570     if (FAILED_UNEXPECTEDLY(hr))
571         return hr;
572 
573     hr = pDeskBar->SetClient(pBandSite);
574     if (FAILED_UNEXPECTEDLY(hr))
575         return hr;
576 
577     hr = pBandSite->AddBand(pShellMenu);
578     if (FAILED_UNEXPECTEDLY(hr))
579         return hr;
580 
581     return pDeskBar->QueryInterface(riid, ppv);
582 }
583