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