4 * Copyright 2014 Giannis Adamopoulos
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.
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.
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
22 #include "CMergedFolder.h"
24 WINE_DEFAULT_DEBUG_CHANNEL(CStartMenu
);
26 // TODO: declare these GUIDs and interfaces in the right place (whatever that may be)
27 IID IID_IAugmentedShellFolder
= { 0x91EA3F8C, 0xC99B, 0x11D0, { 0x98, 0x15, 0x00, 0xC0, 0x4F, 0xD9, 0x19, 0x72 } };
28 IID IID_IAugmentedShellFolder2
= { 0x8DB3B3F4, 0x6CFE, 0x11D1, { 0x8A, 0xE9, 0x00, 0xC0, 0x4F, 0xD9, 0x18, 0xD0 } };
29 IID IID_IAugmentedShellFolder3
= { 0x4F755EA8, 0x247D, 0x479B, { 0x91, 0x81, 0x22, 0x7D, 0x09, 0xC2, 0xE0, 0x01 } };
30 CLSID CLSID_MergedFolder
= { 0x26FDC864, 0xBE88, 0x46E7, { 0x92, 0x35, 0x03, 0x2D, 0x8E, 0xA5, 0x16, 0x2E } };
32 //#define TEST_TRACKPOPUPMENU_SUBMENUS
35 /* NOTE: The following constants *MUST NOT* be changed because
36 they're hardcoded and need to be the exact values
37 in order to get the start menu to work! */
39 #define IDM_LOGOFF 402
40 #define IDM_UNDOCKCOMPUTER 410
41 #define IDM_TASKBARANDSTARTMENU 413
42 #define IDM_LASTSTARTMENU_SEPARATOR 450
43 #define IDM_DOCUMENTS 501
44 #define IDM_HELPANDSUPPORT 503
45 #define IDM_PROGRAMS 504
46 #define IDM_CONTROLPANEL 505
47 #define IDM_SHUTDOWN 506
48 #define IDM_FAVORITES 507
49 #define IDM_SETTINGS 508
50 #define IDM_PRINTERSANDFAXES 510
51 #define IDM_SEARCH 520
52 #define IDM_SYNCHRONIZE 553
53 #define IDM_NETWORKCONNECTIONS 557
54 #define IDM_DISCONNECT 5000
55 #define IDM_SECURITY 5001
59 * 1. append the start menu contents from all users
60 * 2. implement the context menu for start menu entries (programs, control panel, network connetions, printers)
61 * 3. filter out programs folder from the shell folder part of the start menu
62 * 4. showing the programs start menu is SLOW compared to windows. this needs some investigation
65 class CShellMenuCallback
:
66 public CComObjectRootEx
<CComMultiThreadModelNoCS
>,
67 public IShellMenuCallback
72 CComPtr
<IShellMenu
> m_pShellMenu
;
73 CComPtr
<IBandSite
> m_pBandSite
;
74 CComPtr
<IDeskBar
> m_pDeskBar
;
75 CComPtr
<ITrayPriv
> m_pTrayPriv
;
76 CComPtr
<IShellFolder
> m_psfPrograms
;
78 LPITEMIDLIST m_pidlPrograms
;
88 hr
= IUnknown_GetSite(m_pDeskBar
, IID_PPV_ARG(ITrayPriv
, &m_pTrayPriv
));
89 if (FAILED_UNEXPECTEDLY(hr
))
92 hr
= IUnknown_GetWindow(m_pTrayPriv
, &m_hwndTray
);
93 if (FAILED_UNEXPECTEDLY(hr
))
96 hr
= m_pTrayPriv
->AppendMenuW(&hmenu
);
97 if (FAILED_UNEXPECTEDLY(hr
))
100 hr
= m_pShellMenu
->SetMenu(hmenu
, NULL
, SMSET_BOTTOM
);
101 if (FAILED_UNEXPECTEDLY(hr
))
107 HRESULT
OnGetInfo(LPSMDATA psmd
, SMINFO
*psminfo
)
113 // Smaller "24x24" icons used for the start menu
114 // The bitmaps are still 32x32, but the image is centered
115 case IDM_FAVORITES
: iconIndex
= -322; break;
116 case IDM_SEARCH
: iconIndex
= -323; break;
117 case IDM_HELPANDSUPPORT
: iconIndex
= -324; break;
118 case IDM_LOGOFF
: iconIndex
= -325; break;
119 case IDM_PROGRAMS
: iconIndex
= -326; break;
120 case IDM_DOCUMENTS
: iconIndex
= -327; break;
121 case IDM_RUN
: iconIndex
= -328; break;
122 case IDM_SHUTDOWN
: iconIndex
= -329; break;
123 case IDM_SETTINGS
: iconIndex
= -330; break;
125 case IDM_CONTROLPANEL
: iconIndex
= -22; break;
126 case IDM_NETWORKCONNECTIONS
: iconIndex
= -257; break;
127 case IDM_PRINTERSANDFAXES
: iconIndex
= -138; break;
128 case IDM_TASKBARANDSTARTMENU
: iconIndex
= -40; break;
129 //case IDM_SECURITY: iconIndex = -21; break;
130 //case IDM_SYNCHRONIZE: iconIndex = -21; break;
131 //case IDM_DISCONNECT: iconIndex = -21; break;
132 //case IDM_UNDOCKCOMPUTER: iconIndex = -21; break;
139 if ((psminfo
->dwMask
& SMIM_TYPE
) != 0)
140 psminfo
->dwType
= SMIT_STRING
;
141 if ((psminfo
->dwMask
& SMIM_ICON
) != 0)
142 psminfo
->iIcon
= Shell_GetCachedImageIndex(L
"shell32.dll", iconIndex
, FALSE
);
143 if ((psminfo
->dwMask
& SMIM_FLAGS
) != 0)
144 psminfo
->dwFlags
|= SMIF_ICON
;
145 #ifdef TEST_TRACKPOPUPMENU_SUBMENUS
146 if ((psminfo
->dwMask
& SMIM_FLAGS
) != 0)
147 psminfo
->dwFlags
|= SMIF_TRACKPOPUP
;
152 if ((psminfo
->dwMask
& SMIM_TYPE
) != 0)
153 psminfo
->dwType
= SMIT_SEPARATOR
;
158 HRESULT
OnGetSubMenu(LPSMDATA psmd
, REFIID iid
, void ** pv
)
162 IShellMenu
*pShellMenu
;
164 #if USE_SYSTEM_MENUBAND
165 hr
= CoCreateInstance(CLSID_MenuBand
,
167 CLSCTX_INPROC_SERVER
,
168 IID_PPV_ARG(IShellMenu
, &pShellMenu
));
170 hr
= CMenuBand_Constructor(IID_PPV_ARG(IShellMenu
, &pShellMenu
));
172 if (FAILED_UNEXPECTEDLY(hr
))
175 hr
= pShellMenu
->Initialize(this, 0, ANCESTORDEFAULT
, SMINIT_VERTICAL
);
179 case IDM_PROGRAMS
: csidl
= CSIDL_PROGRAMS
; break;
180 case IDM_FAVORITES
: csidl
= CSIDL_FAVORITES
; break;
181 case IDM_DOCUMENTS
: csidl
= CSIDL_RECENT
; break;
186 IShellFolder
*psfStartMenu
;
188 if (csidl
== CSIDL_PROGRAMS
&& m_psfPrograms
)
190 psfStartMenu
= m_psfPrograms
;
194 LPITEMIDLIST pidlStartMenu
;
195 IShellFolder
*psfDestop
;
196 hr
= SHGetFolderLocation(NULL
, csidl
, 0, 0, &pidlStartMenu
);
197 hr
= SHGetDesktopFolder(&psfDestop
);
198 hr
= psfDestop
->BindToObject(pidlStartMenu
, NULL
, IID_PPV_ARG(IShellFolder
, &psfStartMenu
));
201 hr
= pShellMenu
->SetShellFolder(psfStartMenu
, NULL
, NULL
, 0);
206 mii
.cbSize
= sizeof(mii
);
207 mii
.fMask
= MIIM_SUBMENU
;
208 if (GetMenuItemInfoW(psmd
->hmenu
, psmd
->uId
, FALSE
, &mii
))
210 hr
= pShellMenu
->SetMenu(mii
.hSubMenu
, NULL
, SMSET_BOTTOM
);
213 return pShellMenu
->QueryInterface(iid
, pv
);
216 HRESULT
OnGetContextMenu(LPSMDATA psmd
, REFIID iid
, void ** pv
)
218 if (psmd
->uId
== IDM_PROGRAMS
||
219 psmd
->uId
== IDM_CONTROLPANEL
||
220 psmd
->uId
== IDM_NETWORKCONNECTIONS
||
221 psmd
->uId
== IDM_PRINTERSANDFAXES
)
229 HRESULT
OnGetObject(LPSMDATA psmd
, REFIID iid
, void ** pv
)
231 if (IsEqualIID(iid
, IID_IShellMenu
))
232 return OnGetSubMenu(psmd
, iid
, pv
);
233 else if (IsEqualIID(iid
, IID_IContextMenu
))
234 return OnGetContextMenu(psmd
, iid
, pv
);
239 HRESULT
OnExec(LPSMDATA psmd
)
241 // HACK: Instead of running explorer.exe with the path, we should be using ShellExecute to "open" the path directly!
242 // Remove once ShellExecute can handle CLSID path components.
244 if (psmd
->uId
== IDM_CONTROLPANEL
)
245 ShellExecuteW(NULL
, NULL
, L
"explorer.exe", L
"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}", NULL
, SW_SHOWNORMAL
);
246 else if (psmd
->uId
== IDM_NETWORKCONNECTIONS
)
247 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
);
248 else if (psmd
->uId
== IDM_PRINTERSANDFAXES
)
249 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
);
251 PostMessageW(m_hwndTray
, WM_COMMAND
, psmd
->uId
, 0);
258 DECLARE_NOT_AGGREGATABLE(CShellMenuCallback
)
259 DECLARE_PROTECT_FINAL_CONSTRUCT()
260 BEGIN_COM_MAP(CShellMenuCallback
)
261 COM_INTERFACE_ENTRY_IID(IID_IShellMenuCallback
, IShellMenuCallback
)
265 IShellMenu
* pShellMenu
,
266 IBandSite
* pBandSite
,
269 m_pShellMenu
= pShellMenu
;
270 m_pBandSite
= pBandSite
;
271 m_pDeskBar
= pDeskBar
;
274 ~CShellMenuCallback()
278 HRESULT
_SetProgramsFolder(IShellFolder
* psf
, LPITEMIDLIST pidl
)
281 m_pidlPrograms
= pidl
;
285 HRESULT STDMETHODCALLTYPE
CallbackSM(
296 return OnGetInfo(psmd
, reinterpret_cast<SMINFO
*>(lParam
));
298 return OnGetObject(psmd
, *reinterpret_cast<IID
*>(wParam
), reinterpret_cast<void **>(lParam
));
302 m_pTrayPriv
->Execute(psmd
->psf
, psmd
->pidlItem
);
304 case 0x10000000: // _FilterPIDL from CMenuSFToolbar
305 if (psmd
->psf
->CompareIDs(0, psmd
->pidlItem
, m_pidlPrograms
) == 0)
314 HRESULT
BindToDesktop(LPCITEMIDLIST pidl
, IShellFolder
** ppsfResult
)
317 CComPtr
<IShellFolder
> psfDesktop
;
321 hr
= SHGetDesktopFolder(&psfDesktop
);
325 hr
= psfDesktop
->BindToObject(pidl
, NULL
, IID_PPV_ARG(IShellFolder
, ppsfResult
));
330 static HRESULT
GetMergedFolder(int folder1
, int folder2
, IShellFolder
** ppsfStartMenu
)
333 LPITEMIDLIST pidlUserStartMenu
;
334 LPITEMIDLIST pidlCommonStartMenu
;
335 CComPtr
<IShellFolder
> psfUserStartMenu
;
336 CComPtr
<IShellFolder
> psfCommonStartMenu
;
337 CComPtr
<IAugmentedShellFolder
> pasf
;
339 *ppsfStartMenu
= NULL
;
341 hr
= SHGetSpecialFolderLocation(NULL
, folder1
, &pidlUserStartMenu
);
344 WARN("Failed to get the USER start menu folder. Trying to run with just the COMMON one.\n");
346 hr
= SHGetSpecialFolderLocation(NULL
, folder2
, &pidlCommonStartMenu
);
347 if (FAILED_UNEXPECTEDLY(hr
))
350 TRACE("COMMON start menu obtained.\n");
351 hr
= BindToDesktop(pidlCommonStartMenu
, ppsfStartMenu
);
352 ILFree(pidlCommonStartMenu
);
356 hr
= SHGetSpecialFolderLocation(NULL
, folder2
, &pidlCommonStartMenu
);
357 if (FAILED_UNEXPECTEDLY(hr
))
362 WARN("Failed to get the COMMON start menu folder. Will use only the USER contents.\n");
363 hr
= BindToDesktop(pidlUserStartMenu
, ppsfStartMenu
);
364 ILFree(pidlUserStartMenu
);
368 TRACE("Both COMMON and USER statr menu folders obtained, merging them...\n");
370 hr
= BindToDesktop(pidlUserStartMenu
, &psfUserStartMenu
);
371 if (FAILED_UNEXPECTEDLY(hr
))
374 hr
= BindToDesktop(pidlCommonStartMenu
, &psfCommonStartMenu
);
375 if (FAILED_UNEXPECTEDLY(hr
))
378 #if !USE_SYSTEM_MERGED_FOLDERS
379 hr
= CMergedFolder_Constructor(IID_PPV_ARG(IAugmentedShellFolder
, &pasf
));
381 hr
= CoCreateInstance(CLSID_MergedFolder
, NULL
, CLSCTX_INPROC_SERVER
, IID_PPV_ARG(IAugmentedShellFolder
, &pasf
));
383 if (FAILED_UNEXPECTEDLY(hr
))
385 *ppsfStartMenu
= psfUserStartMenu
.Detach();
386 ILFree(pidlCommonStartMenu
);
387 ILFree(pidlUserStartMenu
);
391 hr
= pasf
->AddNameSpace(NULL
, psfUserStartMenu
, pidlUserStartMenu
, 0xFF00);
392 if (FAILED_UNEXPECTEDLY(hr
))
395 hr
= pasf
->AddNameSpace(NULL
, psfCommonStartMenu
, pidlCommonStartMenu
, 0);
396 if (FAILED_UNEXPECTEDLY(hr
))
399 hr
= pasf
->QueryInterface(IID_PPV_ARG(IShellFolder
, ppsfStartMenu
));
402 ILFree(pidlCommonStartMenu
);
403 ILFree(pidlUserStartMenu
);
408 static HRESULT
GetStartMenuFolder(IShellFolder
** ppsfStartMenu
)
410 return GetMergedFolder(CSIDL_STARTMENU
, CSIDL_COMMON_STARTMENU
, ppsfStartMenu
);
413 static HRESULT
GetProgramsFolder(IShellFolder
** ppsfStartMenu
)
415 return GetMergedFolder(CSIDL_PROGRAMS
, CSIDL_COMMON_PROGRAMS
, ppsfStartMenu
);
420 CStartMenu_Constructor(REFIID riid
, void **ppv
)
422 CComPtr
<IShellMenu
> pShellMenu
;
423 CComPtr
<IBandSite
> pBandSite
;
424 CComPtr
<IDeskBar
> pDeskBar
;
429 LPITEMIDLIST pidlProgramsAbsolute
;
430 LPITEMIDLIST pidlPrograms
;
431 CComPtr
<IShellFolder
> psfPrograms
;
433 #if USE_SYSTEM_MENUBAND
434 hr
= CoCreateInstance(CLSID_MenuBand
,
436 CLSCTX_INPROC_SERVER
,
437 IID_PPV_ARG(IShellMenu
, &pShellMenu
));
439 hr
= CMenuBand_Constructor(IID_PPV_ARG(IShellMenu
, &pShellMenu
));
441 if (FAILED_UNEXPECTEDLY(hr
))
444 #if USE_SYSTEM_MENUSITE
445 hr
= CoCreateInstance(CLSID_MenuBandSite
,
447 CLSCTX_INPROC_SERVER
,
448 IID_PPV_ARG(IBandSite
, &pBandSite
));
450 hr
= CMenuSite_Constructor(IID_PPV_ARG(IBandSite
, &pBandSite
));
452 if (FAILED_UNEXPECTEDLY(hr
))
455 #if USE_SYSTEM_MENUDESKBAR
456 hr
= CoCreateInstance(CLSID_MenuDeskBar
,
458 CLSCTX_INPROC_SERVER
,
459 IID_PPV_ARG(IDeskBar
, &pDeskBar
));
461 hr
= CMenuDeskBar_Constructor(IID_PPV_ARG(IDeskBar
, &pDeskBar
));
463 if (FAILED_UNEXPECTEDLY(hr
))
466 CComObject
<CShellMenuCallback
> *pCallback
;
467 hr
= CComObject
<CShellMenuCallback
>::CreateInstance(&pCallback
);
468 if (FAILED_UNEXPECTEDLY(hr
))
470 pCallback
->AddRef(); // CreateInstance returns object with 0 ref count */
471 pCallback
->Initialize(pShellMenu
, pBandSite
, pDeskBar
);
473 pShellMenu
->Initialize(pCallback
, (UINT
) -1, 0, SMINIT_TOPLEVEL
| SMINIT_VERTICAL
);
474 if (FAILED_UNEXPECTEDLY(hr
))
477 hr
= GetStartMenuFolder(&psf
);
478 if (FAILED_UNEXPECTEDLY(hr
))
482 hr
= SHGetSpecialFolderLocation(NULL
, CSIDL_PROGRAMS
, &pidlProgramsAbsolute
);
485 WARN("USER Programs folder not found.");
486 hr
= SHGetSpecialFolderLocation(NULL
, CSIDL_COMMON_PROGRAMS
, &pidlProgramsAbsolute
);
487 if (FAILED_UNEXPECTEDLY(hr
))
491 LPCITEMIDLIST pcidlPrograms
;
492 CComPtr
<IShellFolder
> psfParent
;
494 TCHAR szDisplayName
[MAX_PATH
];
496 hr
= SHBindToParent(pidlProgramsAbsolute
, IID_PPV_ARG(IShellFolder
, &psfParent
), &pcidlPrograms
);
500 hr
= psfParent
->GetDisplayNameOf(pcidlPrograms
, SHGDN_NORMAL
, &str
);
504 StrRetToBuf(&str
, pcidlPrograms
, szDisplayName
, _countof(szDisplayName
));
505 ILFree((LPITEMIDLIST
)pcidlPrograms
);
506 ILFree(pidlProgramsAbsolute
);
508 hr
= psf
->ParseDisplayName(NULL
, NULL
, szDisplayName
, NULL
, &pidlPrograms
, NULL
);
513 hr
= GetProgramsFolder(&psfPrograms
);
514 if (FAILED_UNEXPECTEDLY(hr
))
517 hr
= pCallback
->_SetProgramsFolder(psfPrograms
, pidlPrograms
);
518 if (FAILED_UNEXPECTEDLY(hr
))
521 hr
= pShellMenu
->SetShellFolder(psf
, NULL
, NULL
, 0);
522 if (FAILED_UNEXPECTEDLY(hr
))
525 hr
= pDeskBar
->SetClient(pBandSite
);
526 if (FAILED_UNEXPECTEDLY(hr
))
529 hr
= pBandSite
->AddBand(pShellMenu
);
530 if (FAILED_UNEXPECTEDLY(hr
))
533 return pDeskBar
->QueryInterface(riid
, ppv
);