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 
OnInitMenu()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 
OnGetInfo(LPSMDATA psmd,SMINFO * psminfo)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 
AddOrSetMenuItem(HMENU hMenu,UINT nID,INT csidl,BOOL bExpand,BOOL bAdd=TRUE,BOOL bSetText=TRUE) const156     void AddOrSetMenuItem(HMENU hMenu, UINT nID, INT csidl, BOOL bExpand,
157                           BOOL bAdd = TRUE, BOOL bSetText = TRUE) const
158     {
159         MENUITEMINFOW mii = { sizeof(mii), MIIM_ID | MIIM_SUBMENU };
160         mii.wID = nID;
161 
162         SHFILEINFOW fileInfo = { 0 };
163         if (bAdd || bSetText)
164         {
165             LPITEMIDLIST pidl;
166             if (SHGetSpecialFolderLocation(NULL, csidl, &pidl) != S_OK)
167             {
168                 ERR("SHGetSpecialFolderLocation failed\n");
169                 return;
170             }
171 
172             SHGetFileInfoW((LPWSTR)pidl, 0, &fileInfo, sizeof(fileInfo),
173                            SHGFI_PIDL | SHGFI_DISPLAYNAME);
174             CoTaskMemFree(pidl);
175 
176             mii.fMask |= MIIM_TYPE;
177             mii.fType = MFT_STRING;
178             mii.dwTypeData = fileInfo.szDisplayName;
179         }
180 
181         if (bExpand)
182             mii.hSubMenu = ::CreatePopupMenu();
183 
184         if (bAdd)
185             InsertMenuItemW(hMenu, GetMenuItemCount(hMenu), TRUE, &mii);
186         else
187             SetMenuItemInfoW(hMenu, nID, FALSE, &mii);
188     }
189 
GetAdvancedValue(LPCWSTR pszName,BOOL bDefault=FALSE) const190     BOOL GetAdvancedValue(LPCWSTR pszName, BOOL bDefault = FALSE) const
191     {
192         return SHRegGetBoolUSValueW(
193             L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced",
194             pszName, FALSE, bDefault);
195     }
196 
CreateRecentMenu() const197     HMENU CreateRecentMenu() const
198     {
199         HMENU hMenu = ::CreateMenu();
200         BOOL bAdded = FALSE;
201 
202         // My Documents
203         if (!SHRestricted(REST_NOSMMYDOCS) &&
204             GetAdvancedValue(L"Start_ShowMyDocs", TRUE))
205         {
206             BOOL bExpand = GetAdvancedValue(L"CascadeMyDocuments", FALSE);
207             AddOrSetMenuItem(hMenu, IDM_MYDOCUMENTS, CSIDL_MYDOCUMENTS, bExpand);
208             bAdded = TRUE;
209         }
210 
211         // My Pictures
212         if (!SHRestricted(REST_NOSMMYPICS) &&
213             GetAdvancedValue(L"Start_ShowMyPics", TRUE))
214         {
215             BOOL bExpand = GetAdvancedValue(L"CascadeMyPictures", FALSE);
216             AddOrSetMenuItem(hMenu, IDM_MYPICTURES, CSIDL_MYPICTURES, bExpand);
217             bAdded = TRUE;
218         }
219 
220         if (bAdded)
221             AppendMenuW(hMenu, MF_SEPARATOR, 0, NULL);
222 
223         return hMenu;
224     }
225 
UpdateSettingsMenu(HMENU hMenu)226     void UpdateSettingsMenu(HMENU hMenu)
227     {
228         BOOL bExpand;
229 
230         bExpand = GetAdvancedValue(L"CascadeControlPanel");
231         AddOrSetMenuItem(hMenu, IDM_CONTROLPANEL, CSIDL_CONTROLS, bExpand, FALSE, FALSE);
232 
233         bExpand = GetAdvancedValue(L"CascadeNetworkConnections");
234         AddOrSetMenuItem(hMenu, IDM_NETWORKCONNECTIONS, CSIDL_NETWORK, bExpand, FALSE, FALSE);
235 
236         bExpand = GetAdvancedValue(L"CascadePrinters");
237         AddOrSetMenuItem(hMenu, IDM_PRINTERSANDFAXES, CSIDL_PRINTERS, bExpand, FALSE, FALSE);
238     }
239 
AddStartMenuItems(IShellMenu * pShellMenu,INT csidl,DWORD dwFlags,IShellFolder * psf=NULL)240     HRESULT AddStartMenuItems(IShellMenu *pShellMenu, INT csidl, DWORD dwFlags, IShellFolder *psf = NULL)
241     {
242         CComHeapPtr<ITEMIDLIST> pidlFolder;
243         CComPtr<IShellFolder> psfDesktop;
244         CComPtr<IShellFolder> pShellFolder;
245         HRESULT hr;
246 
247         hr = SHGetFolderLocation(NULL, csidl, 0, 0, &pidlFolder);
248         if (FAILED_UNEXPECTEDLY(hr))
249             return hr;
250 
251         if (psf)
252         {
253             pShellFolder = psf;
254         }
255         else
256         {
257             hr = SHGetDesktopFolder(&psfDesktop);
258             if (FAILED_UNEXPECTEDLY(hr))
259                 return hr;
260 
261             hr = psfDesktop->BindToObject(pidlFolder, NULL, IID_PPV_ARG(IShellFolder, &pShellFolder));
262             if (FAILED_UNEXPECTEDLY(hr))
263                 return hr;
264         }
265 
266         hr = pShellMenu->SetShellFolder(pShellFolder, pidlFolder, NULL, dwFlags);
267         if (FAILED_UNEXPECTEDLY(hr))
268             return hr;
269 
270         return hr;
271     }
272 
OnGetSubMenu(LPSMDATA psmd,REFIID iid,void ** pv)273     HRESULT OnGetSubMenu(LPSMDATA psmd, REFIID iid, void ** pv)
274     {
275         HRESULT hr;
276         CComPtr<IShellMenu> pShellMenu;
277 
278         hr = CMenuBand_CreateInstance(IID_PPV_ARG(IShellMenu, &pShellMenu));
279         if (FAILED_UNEXPECTEDLY(hr))
280             return hr;
281 
282         hr = pShellMenu->Initialize(this, 0, ANCESTORDEFAULT, SMINIT_VERTICAL);
283         if (FAILED_UNEXPECTEDLY(hr))
284             return hr;
285 
286         hr = E_FAIL;
287         switch (psmd->uId)
288         {
289             case IDM_PROGRAMS:
290             {
291                 hr = AddStartMenuItems(pShellMenu, CSIDL_PROGRAMS, SMSET_TOP, m_psfPrograms);
292                 break;
293             }
294             case IDM_FAVORITES:
295             case IDM_MYDOCUMENTS:
296             case IDM_MYPICTURES:
297             case IDM_CONTROLPANEL:
298             case IDM_NETWORKCONNECTIONS:
299             case IDM_PRINTERSANDFAXES:
300             {
301                 hr = AddStartMenuItems(pShellMenu, CSIDLFromID(psmd->uId), SMSET_TOP);
302                 break;
303             }
304             case IDM_DOCUMENTS:
305             {
306                 HMENU hMenu = CreateRecentMenu();
307                 if (hMenu == NULL)
308                     ERR("CreateRecentMenu failed\n");
309 
310                 hr = pShellMenu->SetMenu(hMenu, NULL, SMSET_BOTTOM);
311                 if (FAILED_UNEXPECTEDLY(hr))
312                     return hr;
313 
314                 hr = AddStartMenuItems(pShellMenu, CSIDL_RECENT, SMSET_BOTTOM);
315                 break;
316             }
317             case IDM_SETTINGS:
318             {
319                 MENUITEMINFOW mii = { sizeof(mii), MIIM_SUBMENU };
320                 if (GetMenuItemInfoW(psmd->hmenu, psmd->uId, FALSE, &mii))
321                 {
322                     UpdateSettingsMenu(mii.hSubMenu);
323 
324                     hr = pShellMenu->SetMenu(mii.hSubMenu, NULL, SMSET_BOTTOM);
325                     if (FAILED_UNEXPECTEDLY(hr))
326                         return hr;
327                 }
328                 break;
329             }
330             default:
331             {
332                 MENUITEMINFOW mii = { sizeof(mii), MIIM_SUBMENU };
333                 if (GetMenuItemInfoW(psmd->hmenu, psmd->uId, FALSE, &mii))
334                 {
335                     hr = pShellMenu->SetMenu(mii.hSubMenu, NULL, SMSET_BOTTOM);
336                     if (FAILED_UNEXPECTEDLY(hr))
337                         return hr;
338                 }
339             }
340         }
341 
342         if (FAILED(hr))
343             return hr;
344 
345         hr = pShellMenu->QueryInterface(iid, pv);
346         pShellMenu.Detach();
347         return hr;
348     }
349 
CSIDLFromID(UINT uId) const350     INT CSIDLFromID(UINT uId) const
351     {
352         switch (uId)
353         {
354             case IDM_PROGRAMS: return CSIDL_PROGRAMS;
355             case IDM_FAVORITES: return CSIDL_FAVORITES;
356             case IDM_DOCUMENTS: return CSIDL_RECENT;
357             case IDM_MYDOCUMENTS: return CSIDL_MYDOCUMENTS;
358             case IDM_MYPICTURES: return CSIDL_MYPICTURES;
359             case IDM_CONTROLPANEL: return CSIDL_CONTROLS;
360             case IDM_NETWORKCONNECTIONS: return CSIDL_NETWORK;
361             case IDM_PRINTERSANDFAXES: return CSIDL_PRINTERS;
362             default: return 0;
363         }
364     }
365 
OnGetContextMenu(LPSMDATA psmd,REFIID iid,void ** pv)366     HRESULT OnGetContextMenu(LPSMDATA psmd, REFIID iid, void ** pv)
367     {
368         INT csidl = CSIDLFromID(psmd->uId);
369         if (!csidl)
370             return S_FALSE;
371 
372         TRACE("csidl: 0x%X\n", csidl);
373 
374         if (csidl == CSIDL_CONTROLS || csidl == CSIDL_NETWORK || csidl == CSIDL_PRINTERS)
375             FIXME("This CSIDL %d wrongly opens My Computer. CORE-19477\n", csidl);
376 
377         CComHeapPtr<ITEMIDLIST> pidl;
378         SHGetSpecialFolderLocation(NULL, csidl, &pidl);
379 
380         CComPtr<IShellFolder> pSF;
381         LPCITEMIDLIST pidlChild = NULL;
382         HRESULT hr = SHBindToParent(pidl, IID_IShellFolder, (void**)&pSF, &pidlChild);
383         if (FAILED(hr))
384             return hr;
385 
386         return pSF->GetUIObjectOf(NULL, 1, &pidlChild, IID_IContextMenu, NULL, pv);
387     }
388 
OnGetObject(LPSMDATA psmd,REFIID iid,void ** pv)389     HRESULT OnGetObject(LPSMDATA psmd, REFIID iid, void ** pv)
390     {
391         if (IsEqualIID(iid, IID_IShellMenu))
392             return OnGetSubMenu(psmd, iid, pv);
393         else if (IsEqualIID(iid, IID_IContextMenu))
394             return OnGetContextMenu(psmd, iid, pv);
395 
396         return S_FALSE;
397     }
398 
OnExec(LPSMDATA psmd)399     HRESULT OnExec(LPSMDATA psmd)
400     {
401         WCHAR szPath[MAX_PATH];
402 
403         // HACK: Because our ShellExecute can't handle CLSID components in paths, we can't launch the paths using the "open" verb.
404         // FIXME: Change this back to using the path as the filename and the "open" verb, once ShellExecute can handle CLSID path components.
405 
406         if (psmd->uId == IDM_CONTROLPANEL)
407             ShellExecuteW(NULL, NULL, L"explorer.exe", L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}", NULL, SW_SHOWNORMAL);
408         else if (psmd->uId == IDM_NETWORKCONNECTIONS)
409             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);
410         else if (psmd->uId == IDM_PRINTERSANDFAXES)
411             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);
412         else if (psmd->uId == IDM_MYDOCUMENTS)
413         {
414             if (SHGetSpecialFolderPathW(NULL, szPath, CSIDL_PERSONAL, FALSE))
415                 ShellExecuteW(NULL, NULL, szPath, NULL, NULL, SW_SHOWNORMAL);
416             else
417                 ERR("SHGetSpecialFolderPathW failed\n");
418         }
419         else if (psmd->uId == IDM_MYPICTURES)
420         {
421             if (SHGetSpecialFolderPathW(NULL, szPath, CSIDL_MYPICTURES, FALSE))
422                 ShellExecuteW(NULL, NULL, szPath, NULL, NULL, SW_SHOWNORMAL);
423             else
424                 ERR("SHGetSpecialFolderPathW failed\n");
425         }
426         else
427             PostMessageW(m_hwndTray, WM_COMMAND, psmd->uId, 0);
428 
429         return S_OK;
430     }
431 
432 public:
433 
434     DECLARE_NOT_AGGREGATABLE(CShellMenuCallback)
DECLARE_PROTECT_FINAL_CONSTRUCT()435     DECLARE_PROTECT_FINAL_CONSTRUCT()
436     BEGIN_COM_MAP(CShellMenuCallback)
437         COM_INTERFACE_ENTRY_IID(IID_IShellMenuCallback, IShellMenuCallback)
438     END_COM_MAP()
439 
440     void Initialize(
441         IShellMenu* pShellMenu,
442         IBandSite* pBandSite,
443         IDeskBar* pDeskBar)
444     {
445         m_pShellMenu = pShellMenu;
446         m_pBandSite = pBandSite;
447         m_pDeskBar = pDeskBar;
448     }
449 
~CShellMenuCallback()450     ~CShellMenuCallback()
451     {
452     }
453 
_SetProgramsFolder(IShellFolder * psf,LPITEMIDLIST pidl)454     HRESULT _SetProgramsFolder(IShellFolder * psf, LPITEMIDLIST pidl)
455     {
456         m_psfPrograms = psf;
457         m_pidlPrograms = pidl;
458         return S_OK;
459     }
460 
CallbackSM(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)461     HRESULT STDMETHODCALLTYPE CallbackSM(
462         LPSMDATA psmd,
463         UINT uMsg,
464         WPARAM wParam,
465         LPARAM lParam)
466     {
467         switch (uMsg)
468         {
469         case SMC_INITMENU:
470             return OnInitMenu();
471         case SMC_GETINFO:
472             return OnGetInfo(psmd, reinterpret_cast<SMINFO*>(lParam));
473         case SMC_GETOBJECT:
474             return OnGetObject(psmd, *reinterpret_cast<IID *>(wParam), reinterpret_cast<void **>(lParam));
475         case SMC_EXEC:
476             return OnExec(psmd);
477         case SMC_SFEXEC:
478             m_pTrayPriv->Execute(psmd->psf, psmd->pidlItem);
479             break;
480         case 0x10000000: // _FilterPIDL from CMenuSFToolbar
481             if (psmd->psf->CompareIDs(0, psmd->pidlItem, m_pidlPrograms) == 0)
482                 return S_OK;
483             return S_FALSE;
484         }
485 
486         return S_FALSE;
487     }
488 };
489 
BindToDesktop(LPCITEMIDLIST pidl,IShellFolder ** ppsfResult)490 HRESULT BindToDesktop(LPCITEMIDLIST pidl, IShellFolder ** ppsfResult)
491 {
492     HRESULT hr;
493     CComPtr<IShellFolder> psfDesktop;
494 
495     *ppsfResult = NULL;
496 
497     hr = SHGetDesktopFolder(&psfDesktop);
498     if (FAILED(hr))
499         return hr;
500 
501     hr = psfDesktop->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, ppsfResult));
502 
503     return hr;
504 }
505 
GetMergedFolder(int folder1,int folder2,IShellFolder ** ppsfStartMenu)506 static HRESULT GetMergedFolder(int folder1, int folder2, IShellFolder ** ppsfStartMenu)
507 {
508     HRESULT hr;
509     LPITEMIDLIST pidlUserStartMenu;
510     LPITEMIDLIST pidlCommonStartMenu;
511     CComPtr<IShellFolder> psfUserStartMenu;
512     CComPtr<IShellFolder> psfCommonStartMenu;
513     CComPtr<IAugmentedShellFolder> pasf;
514 
515     *ppsfStartMenu = NULL;
516 
517     hr = SHGetSpecialFolderLocation(NULL, folder1, &pidlUserStartMenu);
518     if (FAILED(hr))
519     {
520         WARN("Failed to get the USER start menu folder. Trying to run with just the COMMON one.\n");
521 
522         hr = SHGetSpecialFolderLocation(NULL, folder2, &pidlCommonStartMenu);
523         if (FAILED_UNEXPECTEDLY(hr))
524             return hr;
525 
526         TRACE("COMMON start menu obtained.\n");
527         hr = BindToDesktop(pidlCommonStartMenu, ppsfStartMenu);
528         ILFree(pidlCommonStartMenu);
529         return hr;
530     }
531 #if MERGE_FOLDERS
532     hr = SHGetSpecialFolderLocation(NULL, folder2, &pidlCommonStartMenu);
533     if (FAILED_UNEXPECTEDLY(hr))
534 #else
535     else
536 #endif
537     {
538         WARN("Failed to get the COMMON start menu folder. Will use only the USER contents.\n");
539         hr = BindToDesktop(pidlUserStartMenu, ppsfStartMenu);
540         ILFree(pidlUserStartMenu);
541         return hr;
542     }
543 
544     TRACE("Both COMMON and USER statr menu folders obtained, merging them...\n");
545 
546     hr = BindToDesktop(pidlUserStartMenu, &psfUserStartMenu);
547     if (FAILED_UNEXPECTEDLY(hr))
548         return hr;
549 
550     hr = BindToDesktop(pidlCommonStartMenu, &psfCommonStartMenu);
551     if (FAILED_UNEXPECTEDLY(hr))
552         return hr;
553 
554     hr = CMergedFolder_CreateInstance(IID_PPV_ARG(IAugmentedShellFolder, &pasf));
555     if (FAILED_UNEXPECTEDLY(hr))
556     {
557         *ppsfStartMenu = psfUserStartMenu.Detach();
558         ILFree(pidlCommonStartMenu);
559         ILFree(pidlUserStartMenu);
560         return hr;
561     }
562 
563     hr = pasf->AddNameSpace(NULL, psfUserStartMenu, pidlUserStartMenu, 0xFF00);
564     if (FAILED_UNEXPECTEDLY(hr))
565         return hr;
566 
567     hr = pasf->AddNameSpace(NULL, psfCommonStartMenu, pidlCommonStartMenu, 0);
568     if (FAILED_UNEXPECTEDLY(hr))
569         return hr;
570 
571     hr = pasf->QueryInterface(IID_PPV_ARG(IShellFolder, ppsfStartMenu));
572     pasf.Release();
573 
574     ILFree(pidlCommonStartMenu);
575     ILFree(pidlUserStartMenu);
576 
577     return hr;
578 }
579 
GetStartMenuFolder(IShellFolder ** ppsfStartMenu)580 static HRESULT GetStartMenuFolder(IShellFolder ** ppsfStartMenu)
581 {
582     return GetMergedFolder(CSIDL_STARTMENU, CSIDL_COMMON_STARTMENU, ppsfStartMenu);
583 }
584 
GetProgramsFolder(IShellFolder ** ppsfStartMenu)585 static HRESULT GetProgramsFolder(IShellFolder ** ppsfStartMenu)
586 {
587     return GetMergedFolder(CSIDL_PROGRAMS, CSIDL_COMMON_PROGRAMS, ppsfStartMenu);
588 }
589 
590 extern "C"
591 HRESULT WINAPI
RSHELL_CStartMenu_CreateInstance(REFIID riid,void ** ppv)592 RSHELL_CStartMenu_CreateInstance(REFIID riid, void **ppv)
593 {
594     CComPtr<IShellMenu> pShellMenu;
595     CComPtr<IBandSite> pBandSite;
596     CComPtr<IDeskBar> pDeskBar;
597 
598     HRESULT hr;
599     IShellFolder * psf;
600 
601     LPITEMIDLIST pidlProgramsAbsolute;
602     LPITEMIDLIST pidlPrograms;
603     CComPtr<IShellFolder> psfPrograms;
604 
605     hr = CMenuBand_CreateInstance(IID_PPV_ARG(IShellMenu, &pShellMenu));
606     if (FAILED_UNEXPECTEDLY(hr))
607         return hr;
608 
609     hr = CMenuSite_CreateInstance(IID_PPV_ARG(IBandSite, &pBandSite));
610     if (FAILED_UNEXPECTEDLY(hr))
611         return hr;
612 
613     hr = CMenuDeskBar_CreateInstance(IID_PPV_ARG(IDeskBar, &pDeskBar));
614     if (FAILED_UNEXPECTEDLY(hr))
615         return hr;
616 
617     CComObject<CShellMenuCallback> *pCallback;
618     hr = CComObject<CShellMenuCallback>::CreateInstance(&pCallback);
619     if (FAILED_UNEXPECTEDLY(hr))
620         return hr;
621 
622     pCallback->AddRef(); // CreateInstance returns object with 0 ref count */
623     pCallback->Initialize(pShellMenu, pBandSite, pDeskBar);
624 
625     hr = pShellMenu->Initialize(pCallback, (UINT) -1, 0, SMINIT_TOPLEVEL | SMINIT_VERTICAL);
626     if (FAILED_UNEXPECTEDLY(hr))
627         return hr;
628 
629     hr = GetStartMenuFolder(&psf);
630     if (FAILED_UNEXPECTEDLY(hr))
631         return hr;
632 
633     /* psf is a merged folder, so now we want to get the pidl of the programs item from the merged folder */
634     {
635         hr = SHGetSpecialFolderLocation(NULL, CSIDL_PROGRAMS, &pidlProgramsAbsolute);
636         if (FAILED_UNEXPECTEDLY(hr))
637         {
638             WARN("USER Programs folder not found\n");
639             hr = SHGetSpecialFolderLocation(NULL, CSIDL_COMMON_PROGRAMS, &pidlProgramsAbsolute);
640             if (FAILED_UNEXPECTEDLY(hr))
641                 return hr;
642         }
643 
644         LPCITEMIDLIST pcidlPrograms;
645         CComPtr<IShellFolder> psfParent;
646         STRRET str;
647         TCHAR szDisplayName[MAX_PATH];
648 
649         hr = SHBindToParent(pidlProgramsAbsolute, IID_PPV_ARG(IShellFolder, &psfParent), &pcidlPrograms);
650         if (FAILED_UNEXPECTEDLY(hr))
651             return hr;
652 
653         hr = psfParent->GetDisplayNameOf(pcidlPrograms, SHGDN_FORPARSING | SHGDN_INFOLDER, &str);
654         if (FAILED_UNEXPECTEDLY(hr))
655             return hr;
656 
657         StrRetToBuf(&str, pcidlPrograms, szDisplayName, _countof(szDisplayName));
658         ILFree(pidlProgramsAbsolute);
659 
660         /* We got the display name from the fs folder and we parse it with the merged folder here */
661         hr = psf->ParseDisplayName(NULL, NULL, szDisplayName, NULL, &pidlPrograms, NULL);
662         if (FAILED_UNEXPECTEDLY(hr))
663             return hr;
664     }
665 
666     hr = GetProgramsFolder(&psfPrograms);
667     if (FAILED_UNEXPECTEDLY(hr))
668         return hr;
669 
670     hr = pCallback->_SetProgramsFolder(psfPrograms, pidlPrograms);
671     if (FAILED_UNEXPECTEDLY(hr))
672         return hr;
673 
674     hr = pShellMenu->SetShellFolder(psf, NULL, NULL, SMSET_TOP);
675     if (FAILED_UNEXPECTEDLY(hr))
676         return hr;
677 
678     hr = pDeskBar->SetClient(pBandSite);
679     if (FAILED_UNEXPECTEDLY(hr))
680         return hr;
681 
682     hr = pBandSite->AddBand(pShellMenu);
683     if (FAILED_UNEXPECTEDLY(hr))
684         return hr;
685 
686     return pDeskBar->QueryInterface(riid, ppv);
687 }
688