[SHELL32]
[reactos.git] / reactos / dll / win32 / shell32 / CDefaultContextMenu.cpp
1 /*
2 * PROJECT: shell32
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: dll/win32/shell32/shv_item_new.c
5 * PURPOSE: provides default context menu implementation
6 * PROGRAMMERS: Johannes Anderwald (johannes.anderwald@reactos.org)
7 */
8
9 #include "precomp.h"
10
11 extern "C"
12 {
13 //fixme: this isn't in wine's shlwapi header, and the definition doesnt match the
14 // windows headers. When wine's header and lib are fixed this can be removed.
15 DWORD WINAPI SHAnsiToUnicode(LPCSTR lpSrcStr, LPWSTR lpDstStr, int iLen);
16 };
17
18 WINE_DEFAULT_DEBUG_CHANNEL(dmenu);
19
20 typedef struct _DynamicShellEntry_
21 {
22 UINT iIdCmdFirst;
23 UINT NumIds;
24 CLSID ClassID;
25 IContextMenu *pCM;
26 struct _DynamicShellEntry_ *pNext;
27 } DynamicShellEntry, *PDynamicShellEntry;
28
29 typedef struct _StaticShellEntry_
30 {
31 LPWSTR szVerb;
32 HKEY hkClass;
33 struct _StaticShellEntry_ *pNext;
34 } StaticShellEntry, *PStaticShellEntry;
35
36
37 //
38 // verbs for InvokeCommandInfo
39 //
40 struct _StaticInvokeCommandMap_
41 {
42 LPCSTR szStringVerb;
43 UINT IntVerb;
44 } g_StaticInvokeCmdMap[] =
45 {
46 { "RunAs", 0 }, // Unimplemented
47 { "Print", 0 }, // Unimplemented
48 { "Preview", 0 }, // Unimplemented
49 { "Open", FCIDM_SHVIEW_OPEN },
50 { CMDSTR_NEWFOLDERA, FCIDM_SHVIEW_NEWFOLDER },
51 { CMDSTR_VIEWLISTA, FCIDM_SHVIEW_LISTVIEW },
52 { CMDSTR_VIEWDETAILSA, FCIDM_SHVIEW_REPORTVIEW }
53 };
54
55
56 class CDefaultContextMenu :
57 public CComObjectRootEx<CComMultiThreadModelNoCS>,
58 public IContextMenu3,
59 public IObjectWithSite
60 {
61 private:
62 CComPtr<IUnknown> m_site;
63 CComPtr<IShellFolder> m_psf;
64 UINT m_cidl;
65 PCUITEMID_CHILD_ARRAY m_apidl;
66 CComPtr<IDataObject> m_pDataObj;
67 HKEY* m_aKeys;
68 UINT m_cKeys;
69 PIDLIST_ABSOLUTE m_pidlFolder;
70 DWORD m_bGroupPolicyActive;
71 PDynamicShellEntry m_pDynamicEntries; /* first dynamic shell extension entry */
72 UINT m_iIdSHEFirst; /* first used id */
73 UINT m_iIdSHELast; /* last used id */
74 PStaticShellEntry m_pStaticEntries; /* first static shell extension entry */
75 UINT m_iIdSCMFirst; /* first static used id */
76 UINT m_iIdSCMLast; /* last static used id */
77
78 void AddStaticEntry(const HKEY hkeyClass, const WCHAR *szVerb);
79 void AddStaticEntriesForKey(HKEY hKey);
80 BOOL IsShellExtensionAlreadyLoaded(const CLSID *pclsid);
81 HRESULT LoadDynamicContextMenuHandler(HKEY hKey, const CLSID *pclsid);
82 BOOL EnumerateDynamicContextHandlerForKey(HKEY hRootKey);
83 UINT InsertMenuItemsOfDynamicContextMenuExtension(HMENU hMenu, UINT IndexMenu, UINT idCmdFirst, UINT idCmdLast);
84 UINT BuildBackgroundContextMenu(HMENU hMenu, UINT iIdCmdFirst, UINT iIdCmdLast, UINT uFlags);
85 UINT AddStaticContextMenusToMenu(HMENU hMenu, UINT IndexMenu);
86 UINT BuildShellItemContextMenu(HMENU hMenu, UINT iIdCmdFirst, UINT iIdCmdLast, UINT uFlags);
87 HRESULT NotifyShellViewWindow(LPCMINVOKECOMMANDINFO lpcmi, BOOL bRefresh);
88 HRESULT DoPaste(LPCMINVOKECOMMANDINFO lpcmi, BOOL bLink);
89 HRESULT DoOpenOrExplore(LPCMINVOKECOMMANDINFO lpcmi);
90 HRESULT DoCreateLink(LPCMINVOKECOMMANDINFO lpcmi);
91 HRESULT DoRefresh(LPCMINVOKECOMMANDINFO lpcmi);
92 HRESULT DoDelete(LPCMINVOKECOMMANDINFO lpcmi);
93 HRESULT DoCopyOrCut(LPCMINVOKECOMMANDINFO lpcmi, BOOL bCopy);
94 HRESULT DoRename(LPCMINVOKECOMMANDINFO lpcmi);
95 HRESULT DoProperties(LPCMINVOKECOMMANDINFO lpcmi);
96 HRESULT DoFormat(LPCMINVOKECOMMANDINFO lpcmi);
97 HRESULT DoCreateNewFolder(LPCMINVOKECOMMANDINFO lpici);
98 HRESULT DoDynamicShellExtensions(LPCMINVOKECOMMANDINFO lpcmi);
99 HRESULT DoStaticShellExtensions(LPCMINVOKECOMMANDINFO lpcmi);
100 DWORD BrowserFlagsFromVerb(LPCMINVOKECOMMANDINFO lpcmi, PStaticShellEntry pEntry);
101 HRESULT TryToBrowse(LPCMINVOKECOMMANDINFO lpcmi, LPCITEMIDLIST pidl, DWORD wFlags);
102 HRESULT InvokePidl(LPCMINVOKECOMMANDINFO lpcmi, LPCITEMIDLIST pidl, PStaticShellEntry pEntry);
103 PDynamicShellEntry GetDynamicEntry(UINT idCmd);
104 BOOL MapVerbToCmdId(PVOID Verb, PUINT idCmd, BOOL IsUnicode);
105
106 public:
107 CDefaultContextMenu();
108 ~CDefaultContextMenu();
109 HRESULT WINAPI Initialize(const DEFCONTEXTMENU *pdcm);
110
111 // IContextMenu
112 virtual HRESULT WINAPI QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);
113 virtual HRESULT WINAPI InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi);
114 virtual HRESULT WINAPI GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen);
115
116 // IContextMenu2
117 virtual HRESULT WINAPI HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam);
118
119 // IContextMenu3
120 virtual HRESULT WINAPI HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plResult);
121
122 // IObjectWithSite
123 virtual HRESULT STDMETHODCALLTYPE SetSite(IUnknown *pUnkSite);
124 virtual HRESULT STDMETHODCALLTYPE GetSite(REFIID riid, void **ppvSite);
125
126 BEGIN_COM_MAP(CDefaultContextMenu)
127 COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu)
128 COM_INTERFACE_ENTRY_IID(IID_IContextMenu2, IContextMenu2)
129 COM_INTERFACE_ENTRY_IID(IID_IContextMenu3, IContextMenu3)
130 COM_INTERFACE_ENTRY_IID(IID_IObjectWithSite, IObjectWithSite)
131 END_COM_MAP()
132 };
133
134 CDefaultContextMenu::CDefaultContextMenu() :
135 m_psf(NULL),
136 m_cidl(0),
137 m_apidl(NULL),
138 m_pDataObj(NULL),
139 m_aKeys(NULL),
140 m_cKeys(NULL),
141 m_pidlFolder(NULL),
142 m_bGroupPolicyActive(0),
143 m_pDynamicEntries(NULL),
144 m_iIdSHEFirst(0),
145 m_iIdSHELast(0),
146 m_pStaticEntries(NULL),
147 m_iIdSCMFirst(0),
148 m_iIdSCMLast(0)
149 {
150 }
151
152 CDefaultContextMenu::~CDefaultContextMenu()
153 {
154 /* Free dynamic shell extension entries */
155 PDynamicShellEntry pDynamicEntry = m_pDynamicEntries, pNextDynamic;
156 while (pDynamicEntry)
157 {
158 pNextDynamic = pDynamicEntry->pNext;
159 pDynamicEntry->pCM->Release();
160 HeapFree(GetProcessHeap(), 0, pDynamicEntry);
161 pDynamicEntry = pNextDynamic;
162 }
163
164 /* Free static shell extension entries */
165 PStaticShellEntry pStaticEntry = m_pStaticEntries, pNextStatic;
166 while (pStaticEntry)
167 {
168 pNextStatic = pStaticEntry->pNext;
169 HeapFree(GetProcessHeap(), 0, pStaticEntry->szVerb);
170 HeapFree(GetProcessHeap(), 0, pStaticEntry);
171 pStaticEntry = pNextStatic;
172 }
173
174 for (UINT i = 0; i < m_cKeys; i++)
175 RegCloseKey(m_aKeys[i]);
176 HeapFree(GetProcessHeap(), 0, m_aKeys);
177
178 if (m_pidlFolder)
179 CoTaskMemFree(m_pidlFolder);
180 _ILFreeaPidl(const_cast<PITEMID_CHILD *>(m_apidl), m_cidl);
181 }
182
183 HRESULT WINAPI CDefaultContextMenu::Initialize(const DEFCONTEXTMENU *pdcm)
184 {
185 TRACE("cidl %u\n", pdcm->cidl);
186
187 m_cidl = pdcm->cidl;
188 m_apidl = const_cast<PCUITEMID_CHILD_ARRAY>(_ILCopyaPidl(pdcm->apidl, m_cidl));
189 if (m_cidl && !m_apidl)
190 return E_OUTOFMEMORY;
191 m_psf = pdcm->psf;
192
193 m_cKeys = pdcm->cKeys;
194 if (pdcm->cKeys)
195 {
196 m_aKeys = (HKEY*)HeapAlloc(GetProcessHeap(), 0, sizeof(HKEY) * pdcm->cKeys);
197 if (!m_aKeys)
198 return E_OUTOFMEMORY;
199 memcpy(m_aKeys, pdcm->aKeys, sizeof(HKEY) * pdcm->cKeys);
200 }
201
202 m_psf->GetUIObjectOf(pdcm->hwnd, m_cidl, m_apidl, IID_NULL_PPV_ARG(IDataObject, &m_pDataObj));
203
204 if (pdcm->pidlFolder)
205 {
206 m_pidlFolder = ILClone(pdcm->pidlFolder);
207 }
208 else
209 {
210 CComPtr<IPersistFolder2> pf = NULL;
211 if (SUCCEEDED(m_psf->QueryInterface(IID_PPV_ARG(IPersistFolder2, &pf))))
212 {
213 if (FAILED(pf->GetCurFolder(reinterpret_cast<LPITEMIDLIST*>(&m_pidlFolder))))
214 ERR("GetCurFolder failed\n");
215 }
216 TRACE("pidlFolder %p\n", m_pidlFolder);
217 }
218
219 return S_OK;
220 }
221
222 void CDefaultContextMenu::AddStaticEntry(const HKEY hkeyClass, const WCHAR *szVerb)
223 {
224 PStaticShellEntry pEntry = m_pStaticEntries, pLastEntry = NULL;
225 while(pEntry)
226 {
227 if (!wcsicmp(pEntry->szVerb, szVerb))
228 {
229 /* entry already exists */
230 return;
231 }
232 pLastEntry = pEntry;
233 pEntry = pEntry->pNext;
234 }
235
236 TRACE("adding verb %s\n", debugstr_w(szVerb));
237
238 pEntry = (StaticShellEntry *)HeapAlloc(GetProcessHeap(), 0, sizeof(StaticShellEntry));
239 if (pEntry)
240 {
241 pEntry->pNext = NULL;
242 pEntry->szVerb = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (wcslen(szVerb) + 1) * sizeof(WCHAR));
243 if (pEntry->szVerb)
244 wcscpy(pEntry->szVerb, szVerb);
245 pEntry->hkClass = hkeyClass;
246 }
247
248 if (!wcsicmp(szVerb, L"open"))
249 {
250 /* open verb is always inserted in front */
251 pEntry->pNext = m_pStaticEntries;
252 m_pStaticEntries = pEntry;
253 }
254 else if (pLastEntry)
255 pLastEntry->pNext = pEntry;
256 else
257 m_pStaticEntries = pEntry;
258 }
259
260 void CDefaultContextMenu::AddStaticEntriesForKey(HKEY hKey)
261 {
262 WCHAR wszName[40];
263 DWORD cchName, dwIndex = 0;
264 HKEY hShellKey;
265
266 LRESULT lres = RegOpenKeyExW(hKey, L"shell", 0, KEY_READ, &hShellKey);
267 if (lres != STATUS_SUCCESS)
268 return;
269
270 while(TRUE)
271 {
272 cchName = _countof(wszName);
273 if (RegEnumKeyExW(hShellKey, dwIndex++, wszName, &cchName, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
274 break;
275
276 AddStaticEntry(hKey, wszName);
277 }
278
279 RegCloseKey(hShellKey);
280 }
281
282 static
283 BOOL
284 HasClipboardData()
285 {
286 BOOL bRet = FALSE;
287 CComPtr<IDataObject> pDataObj;
288
289 if (SUCCEEDED(OleGetClipboard(&pDataObj)))
290 {
291 STGMEDIUM medium;
292 FORMATETC formatetc;
293
294 TRACE("pDataObj=%p\n", pDataObj.p);
295
296 /* Set the FORMATETC structure*/
297 InitFormatEtc(formatetc, RegisterClipboardFormatW(CFSTR_SHELLIDLIST), TYMED_HGLOBAL);
298 if (SUCCEEDED(pDataObj->GetData(&formatetc, &medium)))
299 {
300 bRet = TRUE;
301 ReleaseStgMedium(&medium);
302 }
303 }
304
305 return bRet;
306 }
307
308 static
309 VOID
310 DisablePasteOptions(HMENU hMenu)
311 {
312 MENUITEMINFOW mii;
313
314 mii.cbSize = sizeof(mii);
315 mii.fMask = MIIM_STATE;
316 mii.fState = MFS_DISABLED;
317
318 SetMenuItemInfoW(hMenu, FCIDM_SHVIEW_INSERT, FALSE, &mii);
319 SetMenuItemInfoW(hMenu, FCIDM_SHVIEW_INSERTLINK, FALSE, &mii);
320 }
321
322 BOOL
323 CDefaultContextMenu::IsShellExtensionAlreadyLoaded(const CLSID *pclsid)
324 {
325 PDynamicShellEntry pEntry = m_pDynamicEntries;
326
327 while (pEntry)
328 {
329 if (!memcmp(&pEntry->ClassID, pclsid, sizeof(CLSID)))
330 return TRUE;
331 pEntry = pEntry->pNext;
332 }
333
334 return FALSE;
335 }
336
337 HRESULT
338 CDefaultContextMenu::LoadDynamicContextMenuHandler(HKEY hKey, const CLSID *pclsid)
339 {
340 HRESULT hr;
341
342 TRACE("LoadDynamicContextMenuHandler entered with This %p hKey %p pclsid %s\n", this, hKey, wine_dbgstr_guid(pclsid));
343
344 if (IsShellExtensionAlreadyLoaded(pclsid))
345 return S_OK;
346
347 CComPtr<IContextMenu> pcm;
348 hr = SHCoCreateInstance(NULL, pclsid, NULL, IID_PPV_ARG(IContextMenu, &pcm));
349 if (FAILED_UNEXPECTEDLY(hr))
350 return hr;
351
352 CComPtr<IShellExtInit> pExtInit;
353 hr = pcm->QueryInterface(IID_PPV_ARG(IShellExtInit, &pExtInit));
354 if (FAILED_UNEXPECTEDLY(hr))
355 return hr;
356
357 hr = pExtInit->Initialize(m_pidlFolder, m_pDataObj, hKey);
358 if (FAILED_UNEXPECTEDLY(hr))
359 return hr;
360
361 PDynamicShellEntry pEntry = (DynamicShellEntry *)HeapAlloc(GetProcessHeap(), 0, sizeof(DynamicShellEntry));
362 if (!pEntry)
363 return E_OUTOFMEMORY;
364
365 pEntry->iIdCmdFirst = 0;
366 pEntry->pNext = NULL;
367 pEntry->NumIds = 0;
368 pEntry->pCM = pcm.Detach();
369 memcpy(&pEntry->ClassID, pclsid, sizeof(CLSID));
370
371 if (m_pDynamicEntries)
372 {
373 PDynamicShellEntry pLastEntry = m_pDynamicEntries;
374
375 while (pLastEntry->pNext)
376 pLastEntry = pLastEntry->pNext;
377
378 pLastEntry->pNext = pEntry;
379 }
380 else
381 m_pDynamicEntries = pEntry;
382
383 return S_OK;
384 }
385
386 BOOL
387 CDefaultContextMenu::EnumerateDynamicContextHandlerForKey(HKEY hRootKey)
388 {
389
390 WCHAR wszName[MAX_PATH], wszBuf[MAX_PATH], *pwszClsid;
391 DWORD cchName;
392 HRESULT hr;
393 HKEY hKey;
394
395 if (RegOpenKeyExW(hRootKey, L"shellex\\ContextMenuHandlers", 0, KEY_READ, &hKey) != ERROR_SUCCESS)
396 {
397 TRACE("RegOpenKeyExW failed\n");
398 return FALSE;
399 }
400
401 DWORD dwIndex = 0;
402 while (TRUE)
403 {
404 cchName = _countof(wszName);
405 if (RegEnumKeyExW(hKey, dwIndex++, wszName, &cchName, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
406 break;
407
408 /* Key name or key value is CLSID */
409 CLSID clsid;
410 hr = CLSIDFromString(wszName, &clsid);
411 if (hr == S_OK)
412 pwszClsid = wszName;
413 else
414 {
415 DWORD cchBuf = _countof(wszBuf);
416 if (RegGetValueW(hKey, wszName, NULL, RRF_RT_REG_SZ, NULL, wszBuf, &cchBuf) == ERROR_SUCCESS)
417 hr = CLSIDFromString(wszBuf, &clsid);
418 pwszClsid = wszBuf;
419 }
420 if (SUCCEEDED(hr))
421 {
422 if (m_bGroupPolicyActive)
423 {
424 if (RegGetValueW(HKEY_LOCAL_MACHINE,
425 L"Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved",
426 pwszClsid,
427 RRF_RT_REG_SZ,
428 NULL,
429 NULL,
430 NULL) == ERROR_SUCCESS)
431 {
432 LoadDynamicContextMenuHandler(hKey, &clsid);
433 }
434 }
435 else
436 LoadDynamicContextMenuHandler(hKey, &clsid);
437 }
438 }
439
440 RegCloseKey(hKey);
441 return TRUE;
442 }
443
444 UINT
445 CDefaultContextMenu::InsertMenuItemsOfDynamicContextMenuExtension(HMENU hMenu, UINT IndexMenu, UINT idCmdFirst, UINT idCmdLast)
446 {
447 if (!m_pDynamicEntries)
448 {
449 m_iIdSHEFirst = 0;
450 m_iIdSHELast = 0;
451 return IndexMenu;
452 }
453
454 PDynamicShellEntry pEntry = m_pDynamicEntries;
455 idCmdFirst = 0x5000;
456 idCmdLast = 0x6000;
457 m_iIdSHEFirst = idCmdFirst;
458 do
459 {
460 HRESULT hr = pEntry->pCM->QueryContextMenu(hMenu, IndexMenu++, idCmdFirst, idCmdLast, CMF_NORMAL);
461 if (SUCCEEDED(hr))
462 {
463 pEntry->iIdCmdFirst = idCmdFirst;
464 pEntry->NumIds = LOWORD(hr);
465 IndexMenu += pEntry->NumIds;
466 idCmdFirst += pEntry->NumIds + 0x10;
467 }
468 TRACE("pEntry %p hr %x contextmenu %p cmdfirst %x num ids %x\n", pEntry, hr, pEntry->pCM, pEntry->iIdCmdFirst, pEntry->NumIds);
469 pEntry = pEntry->pNext;
470 } while (pEntry);
471
472 m_iIdSHELast = idCmdFirst;
473 TRACE("SH_LoadContextMenuHandlers first %x last %x\n", m_iIdSHEFirst, m_iIdSHELast);
474 return IndexMenu;
475 }
476
477 UINT
478 CDefaultContextMenu::BuildBackgroundContextMenu(
479 HMENU hMenu,
480 UINT iIdCmdFirst,
481 UINT iIdCmdLast,
482 UINT uFlags)
483 {
484 UINT IndexMenu = 0;
485 HMENU hSubMenu;
486
487 TRACE("BuildBackgroundContextMenu entered\n");
488
489 SFGAOF rfg = SFGAO_FILESYSTEM | SFGAO_FOLDER;
490 HRESULT hr = m_psf->GetAttributesOf(0, NULL, &rfg);
491 if (FAILED(hr))
492 {
493 ERR("GetAttributesOf failed: %x\n", hr);
494 rfg = 0;
495 }
496
497 hSubMenu = LoadMenuW(shell32_hInstance, L"MENU_002");
498 if (hSubMenu)
499 {
500 /* view option is only available in browsing mode */
501 if (_ILIsDesktop(m_pidlFolder))
502 DeleteMenu(hSubMenu, FCIDM_SHVIEW_VIEW, MF_BYCOMMAND);
503
504 /* merge general background context menu in */
505 iIdCmdFirst = Shell_MergeMenus(hMenu, GetSubMenu(hSubMenu, 0), IndexMenu, 0, 0xFFFF, MM_DONTREMOVESEPS | MM_SUBMENUSHAVEIDS) + 1;
506 DestroyMenu(hSubMenu);
507 }
508
509 if (!HasClipboardData())
510 {
511 TRACE("disabling paste options\n");
512 DisablePasteOptions(hMenu);
513 }
514
515 /* Directory is progid of filesystem folders only */
516 if ((rfg & (SFGAO_FILESYSTEM|SFGAO_FOLDER)) == (SFGAO_FILESYSTEM|SFGAO_FOLDER))
517 {
518 /* Load context menu handlers */
519 TRACE("Add background handlers: %p\n", m_pidlFolder);
520 HKEY hKey;
521 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, L"Directory\\Background", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
522 {
523 EnumerateDynamicContextHandlerForKey(hKey);
524 RegCloseKey(hKey);
525 }
526
527 if (InsertMenuItemsOfDynamicContextMenuExtension(hMenu, GetMenuItemCount(hMenu) - 1, iIdCmdFirst, iIdCmdLast))
528 {
529 /* seperate dynamic context menu items */
530 _InsertMenuItemW(hMenu, GetMenuItemCount(hMenu) - 1, TRUE, -1, MFT_SEPARATOR, NULL, MFS_ENABLED);
531 }
532 }
533
534 return iIdCmdLast;
535 }
536
537 UINT
538 CDefaultContextMenu::AddStaticContextMenusToMenu(
539 HMENU hMenu,
540 UINT IndexMenu)
541 {
542 MENUITEMINFOW mii;
543 UINT idResource;
544 WCHAR wszVerb[40];
545 UINT fState;
546
547 mii.cbSize = sizeof(mii);
548 mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE | MIIM_DATA;
549 mii.fType = MFT_STRING;
550 mii.wID = 0x4000;
551 mii.dwTypeData = NULL;
552 m_iIdSCMFirst = mii.wID;
553
554 PStaticShellEntry pEntry = m_pStaticEntries;
555
556 while (pEntry)
557 {
558 fState = MFS_ENABLED;
559 mii.dwTypeData = NULL;
560
561 /* set first entry as default */
562 if (pEntry == m_pStaticEntries)
563 fState |= MFS_DEFAULT;
564
565 if (!wcsicmp(pEntry->szVerb, L"open"))
566 {
567 /* override default when open verb is found */
568 fState |= MFS_DEFAULT;
569 idResource = IDS_OPEN_VERB;
570 }
571 else if (!wcsicmp(pEntry->szVerb, L"explore"))
572 idResource = IDS_EXPLORE_VERB;
573 else if (!wcsicmp(pEntry->szVerb, L"runas"))
574 idResource = IDS_RUNAS_VERB;
575 else if (!wcsicmp(pEntry->szVerb, L"edit"))
576 idResource = IDS_EDIT_VERB;
577 else if (!wcsicmp(pEntry->szVerb, L"find"))
578 idResource = IDS_FIND_VERB;
579 else if (!wcsicmp(pEntry->szVerb, L"print"))
580 idResource = IDS_PRINT_VERB;
581 else if (!wcsicmp(pEntry->szVerb, L"printto"))
582 {
583 pEntry = pEntry->pNext;
584 continue;
585 }
586 else
587 idResource = 0;
588
589 /* By default use verb for menu item name */
590 mii.dwTypeData = pEntry->szVerb;
591
592 if (idResource > 0)
593 {
594 if (LoadStringW(shell32_hInstance, idResource, wszVerb, _countof(wszVerb)))
595 mii.dwTypeData = wszVerb; /* use translated verb */
596 else
597 ERR("Failed to load string\n");
598 }
599 else
600 {
601 WCHAR wszKey[256];
602 HRESULT hr = StringCbPrintfW(wszKey, sizeof(wszKey), L"shell\\%s", pEntry->szVerb);
603
604 if (SUCCEEDED(hr))
605 {
606 HKEY hkVerb;
607 DWORD cbVerb = sizeof(wszVerb);
608 LONG res = RegOpenKeyW(pEntry->hkClass, wszKey, &hkVerb);
609 if (res == ERROR_SUCCESS)
610 {
611 res = RegLoadMUIStringW(hkVerb,
612 NULL,
613 wszVerb,
614 cbVerb,
615 NULL,
616 0,
617 NULL);
618 if (res == ERROR_SUCCESS)
619 {
620 /* use description for the menu entry */
621 mii.dwTypeData = wszVerb;
622 }
623
624 RegCloseKey(hkVerb);
625 }
626 }
627 }
628
629 mii.cch = wcslen(mii.dwTypeData);
630 mii.fState = fState;
631 InsertMenuItemW(hMenu, IndexMenu++, TRUE, &mii);
632
633 mii.wID++;
634 pEntry = pEntry->pNext;
635 }
636
637 m_iIdSCMLast = mii.wID - 1;
638 return IndexMenu;
639 }
640
641 void WINAPI _InsertMenuItemW(
642 HMENU hMenu,
643 UINT indexMenu,
644 BOOL fByPosition,
645 UINT wID,
646 UINT fType,
647 LPCWSTR dwTypeData,
648 UINT fState)
649 {
650 MENUITEMINFOW mii;
651 WCHAR wszText[100];
652
653 ZeroMemory(&mii, sizeof(mii));
654 mii.cbSize = sizeof(mii);
655 if (fType == MFT_SEPARATOR)
656 mii.fMask = MIIM_ID | MIIM_TYPE;
657 else if (fType == MFT_STRING)
658 {
659 mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE;
660 if ((ULONG_PTR)HIWORD((ULONG_PTR)dwTypeData) == 0)
661 {
662 if (LoadStringW(shell32_hInstance, LOWORD((ULONG_PTR)dwTypeData), wszText, _countof(wszText)))
663 mii.dwTypeData = wszText;
664 else
665 {
666 ERR("failed to load string %p\n", dwTypeData);
667 return;
668 }
669 }
670 else
671 mii.dwTypeData = (LPWSTR)dwTypeData;
672 mii.fState = fState;
673 }
674
675 mii.wID = wID;
676 mii.fType = fType;
677 InsertMenuItemW(hMenu, indexMenu, fByPosition, &mii);
678 }
679
680 UINT
681 CDefaultContextMenu::BuildShellItemContextMenu(
682 HMENU hMenu,
683 UINT iIdCmdFirst,
684 UINT iIdCmdLast,
685 UINT uFlags)
686 {
687 HRESULT hr;
688
689 TRACE("BuildShellItemContextMenu entered\n");
690 ASSERT(m_cidl >= 1);
691
692 for (UINT i = 0; i < m_cKeys; i++)
693 {
694 AddStaticEntriesForKey(m_aKeys[i]);
695 EnumerateDynamicContextHandlerForKey(m_aKeys[i]);
696 }
697
698 /* add static actions */
699 SFGAOF rfg = SFGAO_BROWSABLE | SFGAO_CANCOPY | SFGAO_CANLINK | SFGAO_CANMOVE | SFGAO_CANDELETE | SFGAO_CANRENAME | SFGAO_HASPROPSHEET | SFGAO_FILESYSTEM | SFGAO_FOLDER;
700 hr = m_psf->GetAttributesOf(m_cidl, m_apidl, &rfg);
701 if (FAILED(hr))
702 {
703 ERR("GetAttributesOf failed: %x\n", hr);
704 rfg = 0;
705 }
706
707 /* add static context menu handlers */
708 UINT IndexMenu = AddStaticContextMenusToMenu(hMenu, 0);
709
710 /* now process dynamic context menu handlers */
711 BOOL bAddSep = FALSE;
712 IndexMenu = InsertMenuItemsOfDynamicContextMenuExtension(hMenu, IndexMenu, iIdCmdFirst, iIdCmdLast);
713 TRACE("IndexMenu %d\n", IndexMenu);
714
715 if (_ILIsDrive(m_apidl[0]))
716 {
717 char szDrive[8] = {0};
718 DWORD dwFlags;
719
720 _ILGetDrive(m_apidl[0], szDrive, sizeof(szDrive));
721 if (GetVolumeInformationA(szDrive, NULL, 0, NULL, NULL, &dwFlags, NULL, 0))
722 {
723 /* Disable format if read only */
724 if (!(dwFlags & FILE_READ_ONLY_VOLUME))
725 {
726 _InsertMenuItemW(hMenu, IndexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
727 _InsertMenuItemW(hMenu, IndexMenu++, TRUE, 0x7ABC, MFT_STRING, MAKEINTRESOURCEW(IDS_FORMATDRIVE), MFS_ENABLED);
728 bAddSep = TRUE;
729 }
730 }
731 }
732
733 BOOL bClipboardData = (HasClipboardData() && (rfg & SFGAO_FILESYSTEM));
734 if (rfg & (SFGAO_CANCOPY | SFGAO_CANMOVE) || bClipboardData)
735 {
736 _InsertMenuItemW(hMenu, IndexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
737 if (rfg & SFGAO_CANMOVE)
738 _InsertMenuItemW(hMenu, IndexMenu++, TRUE, FCIDM_SHVIEW_CUT, MFT_STRING, MAKEINTRESOURCEW(IDS_CUT), MFS_ENABLED);
739 if (rfg & SFGAO_CANCOPY)
740 _InsertMenuItemW(hMenu, IndexMenu++, TRUE, FCIDM_SHVIEW_COPY, MFT_STRING, MAKEINTRESOURCEW(IDS_COPY), MFS_ENABLED);
741 if (bClipboardData)
742 _InsertMenuItemW(hMenu, IndexMenu++, TRUE, FCIDM_SHVIEW_INSERT, MFT_STRING, MAKEINTRESOURCEW(IDS_PASTE), MFS_ENABLED);
743
744 bAddSep = TRUE;
745 }
746
747 if (rfg & SFGAO_CANLINK)
748 {
749 bAddSep = FALSE;
750 _InsertMenuItemW(hMenu, IndexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
751 _InsertMenuItemW(hMenu, IndexMenu++, TRUE, FCIDM_SHVIEW_CREATELINK, MFT_STRING, MAKEINTRESOURCEW(IDS_CREATELINK), MFS_ENABLED);
752 }
753
754 if (rfg & SFGAO_CANDELETE)
755 {
756 if (bAddSep)
757 {
758 bAddSep = FALSE;
759 _InsertMenuItemW(hMenu, IndexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
760 }
761 _InsertMenuItemW(hMenu, IndexMenu++, TRUE, FCIDM_SHVIEW_DELETE, MFT_STRING, MAKEINTRESOURCEW(IDS_DELETE), MFS_ENABLED);
762 }
763
764 if (rfg & SFGAO_CANRENAME)
765 {
766 if (bAddSep)
767 {
768 _InsertMenuItemW(hMenu, IndexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
769 }
770 _InsertMenuItemW(hMenu, IndexMenu++, TRUE, FCIDM_SHVIEW_RENAME, MFT_STRING, MAKEINTRESOURCEW(IDS_RENAME), MFS_ENABLED);
771 bAddSep = TRUE;
772 }
773
774 if (rfg & SFGAO_HASPROPSHEET)
775 {
776 _InsertMenuItemW(hMenu, IndexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
777 _InsertMenuItemW(hMenu, IndexMenu++, TRUE, FCIDM_SHVIEW_PROPERTIES, MFT_STRING, MAKEINTRESOURCEW(IDS_PROPERTIES), MFS_ENABLED);
778 }
779
780 return iIdCmdLast;
781 }
782
783 HRESULT
784 WINAPI
785 CDefaultContextMenu::QueryContextMenu(
786 HMENU hMenu,
787 UINT IndexMenu,
788 UINT idCmdFirst,
789 UINT idCmdLast,
790 UINT uFlags)
791 {
792 if (m_cidl)
793 idCmdFirst = BuildShellItemContextMenu(hMenu, idCmdFirst, idCmdLast, uFlags);
794 else
795 idCmdFirst = BuildBackgroundContextMenu(hMenu, idCmdFirst, idCmdLast, uFlags);
796
797 return S_OK;
798 }
799
800 HRESULT
801 CDefaultContextMenu::NotifyShellViewWindow(LPCMINVOKECOMMANDINFO lpcmi, BOOL bRefresh)
802 {
803 if (!m_site)
804 return E_FAIL;
805
806 /* Get a pointer to the shell browser */
807 CComPtr<IShellView> psv;
808 HRESULT hr = IUnknown_QueryService(m_site, SID_IFolderView, IID_PPV_ARG(IShellView, &psv));
809 if (FAILED_UNEXPECTEDLY(hr))
810 return hr;
811
812 HWND hwndSV = NULL;
813 if (SUCCEEDED(psv->GetWindow(&hwndSV)))
814 SendMessageW(hwndSV, WM_COMMAND, MAKEWPARAM(LOWORD(lpcmi->lpVerb), 0), 0);
815 return S_OK;
816 }
817
818
819 HRESULT CDefaultContextMenu::DoRefresh(LPCMINVOKECOMMANDINFO lpcmi)
820 {
821 if (!m_site)
822 return E_FAIL;
823
824 /* Get a pointer to the shell view */
825 CComPtr<IShellView> psv;
826 HRESULT hr = IUnknown_QueryService(m_site, SID_IFolderView, IID_PPV_ARG(IShellView, &psv));
827 if (FAILED_UNEXPECTEDLY(hr))
828 return hr;
829
830 hr = psv->Refresh();
831 if (FAILED_UNEXPECTEDLY(hr))
832 return hr;
833
834 return S_OK;
835 }
836
837 HRESULT CDefaultContextMenu::DoPaste(LPCMINVOKECOMMANDINFO lpcmi, BOOL bLink)
838 {
839 HRESULT hr;
840
841 CComPtr<IDataObject> pda;
842 hr = OleGetClipboard(&pda);
843 if (FAILED_UNEXPECTEDLY(hr))
844 return hr;
845
846 FORMATETC formatetc2;
847 STGMEDIUM medium2;
848 InitFormatEtc(formatetc2, RegisterClipboardFormatW(CFSTR_PREFERREDDROPEFFECT), TYMED_HGLOBAL);
849
850 DWORD dwKey= 0;
851
852 if (SUCCEEDED(pda->GetData(&formatetc2, &medium2)))
853 {
854 DWORD * pdwFlag = (DWORD*)GlobalLock(medium2.hGlobal);
855 if (pdwFlag)
856 {
857 if (*pdwFlag == DROPEFFECT_COPY)
858 dwKey = MK_CONTROL;
859 else
860 dwKey = MK_SHIFT;
861 }
862 else {
863 ERR("No drop effect obtained");
864 }
865 GlobalUnlock(medium2.hGlobal);
866 }
867
868 if (bLink)
869 {
870 dwKey = MK_CONTROL|MK_SHIFT;
871 }
872
873 CComPtr<IDropTarget> pdrop;
874 if (m_cidl)
875 hr = m_psf->GetUIObjectOf(NULL, 1, &m_apidl[0], IID_NULL_PPV_ARG(IDropTarget, &pdrop));
876 else
877 hr = m_psf->CreateViewObject(NULL, IID_PPV_ARG(IDropTarget, &pdrop));
878
879 if (FAILED_UNEXPECTEDLY(hr))
880 return hr;
881
882 SHSimulateDrop(pdrop, pda, dwKey, NULL, NULL);
883
884 TRACE("CP result %x\n", hr);
885 return S_OK;
886 }
887
888 HRESULT
889 CDefaultContextMenu::DoOpenOrExplore(LPCMINVOKECOMMANDINFO lpcmi)
890 {
891 UNIMPLEMENTED;
892 return E_FAIL;
893 }
894
895 HRESULT CDefaultContextMenu::DoCreateLink(LPCMINVOKECOMMANDINFO lpcmi)
896 {
897 if (!m_cidl || !m_pDataObj)
898 return E_FAIL;
899
900 CComPtr<IDropTarget> pDT;
901 HRESULT hr = m_psf->CreateViewObject(NULL, IID_PPV_ARG(IDropTarget, &pDT));
902 if (FAILED_UNEXPECTEDLY(hr))
903 return hr;
904
905 SHSimulateDrop(pDT, m_pDataObj, MK_CONTROL|MK_SHIFT, NULL, NULL);
906
907 return S_OK;
908 }
909
910 HRESULT CDefaultContextMenu::DoDelete(LPCMINVOKECOMMANDINFO lpcmi)
911 {
912 if (!m_cidl || !m_pDataObj)
913 return E_FAIL;
914
915 DoDeleteAsync(m_pDataObj, lpcmi->fMask);
916 return S_OK;
917 }
918
919 HRESULT CDefaultContextMenu::DoCopyOrCut(LPCMINVOKECOMMANDINFO lpcmi, BOOL bCopy)
920 {
921 if (!m_cidl || !m_pDataObj)
922 return E_FAIL;
923
924 if (!bCopy)
925 {
926 FORMATETC formatetc;
927 STGMEDIUM medium;
928 InitFormatEtc(formatetc, RegisterClipboardFormatW(CFSTR_PREFERREDDROPEFFECT), TYMED_HGLOBAL);
929 m_pDataObj->GetData(&formatetc, &medium);
930 DWORD * pdwFlag = (DWORD*)GlobalLock(medium.hGlobal);
931 if (pdwFlag)
932 *pdwFlag = DROPEFFECT_MOVE;
933 GlobalUnlock(medium.hGlobal);
934 m_pDataObj->SetData(&formatetc, &medium, TRUE);
935 }
936
937 HRESULT hr = OleSetClipboard(m_pDataObj);
938 if (FAILED_UNEXPECTEDLY(hr))
939 return hr;
940
941 return S_OK;
942 }
943
944 HRESULT CDefaultContextMenu::DoRename(LPCMINVOKECOMMANDINFO lpcmi)
945 {
946 CComPtr<IShellBrowser> psb;
947 HRESULT hr;
948
949 if (!m_site || !m_cidl)
950 return E_FAIL;
951
952 /* Get a pointer to the shell browser */
953 hr = IUnknown_QueryService(m_site, SID_IShellBrowser, IID_PPV_ARG(IShellBrowser, &psb));
954 if (FAILED_UNEXPECTEDLY(hr))
955 return hr;
956
957 CComPtr<IShellView> lpSV;
958 hr = psb->QueryActiveShellView(&lpSV);
959 if (FAILED_UNEXPECTEDLY(hr))
960 return hr;
961
962 SVSIF selFlags = SVSI_DESELECTOTHERS | SVSI_EDIT | SVSI_ENSUREVISIBLE | SVSI_FOCUSED | SVSI_SELECT;
963 hr = lpSV->SelectItem(m_apidl[0], selFlags);
964 if (FAILED_UNEXPECTEDLY(hr))
965 return hr;
966
967 return S_OK;
968 }
969
970 HRESULT
971 CDefaultContextMenu::DoProperties(
972 LPCMINVOKECOMMANDINFO lpcmi)
973 {
974 HRESULT hr = S_OK;
975 const ITEMIDLIST *pidlParent = m_pidlFolder, *pidlChild;
976
977 if (!pidlParent)
978 {
979 CComPtr<IPersistFolder2> pf;
980
981 /* pidlFolder is optional */
982 if (SUCCEEDED(m_psf->QueryInterface(IID_PPV_ARG(IPersistFolder2, &pf))))
983 {
984 pf->GetCurFolder((_ITEMIDLIST**)&pidlParent);
985 }
986 }
987
988 if (m_cidl > 0)
989 pidlChild = m_apidl[0];
990 else
991 {
992 /* Set pidlChild to last pidl of current folder */
993 if (pidlParent == m_pidlFolder)
994 pidlParent = (ITEMIDLIST*)ILClone(pidlParent);
995
996 pidlChild = (ITEMIDLIST*)ILClone(ILFindLastID(pidlParent));
997 ILRemoveLastID((ITEMIDLIST*)pidlParent);
998 }
999
1000 if (_ILIsMyComputer(pidlChild))
1001 {
1002 if (32 >= (UINT)ShellExecuteW(lpcmi->hwnd, L"open", L"rundll32.exe shell32.dll,Control_RunDLL sysdm.cpl", NULL, NULL, SW_SHOWNORMAL))
1003 hr = E_FAIL;
1004 }
1005 else if (_ILIsDesktop(pidlChild))
1006 {
1007 if (32 >= (UINT)ShellExecuteW(lpcmi->hwnd, L"open", L"rundll32.exe shell32.dll,Control_RunDLL desk.cpl", NULL, NULL, SW_SHOWNORMAL))
1008 hr = E_FAIL;
1009 }
1010 else if (_ILIsDrive(pidlChild))
1011 {
1012 WCHAR wszBuf[MAX_PATH];
1013 ILGetDisplayName(pidlChild, wszBuf);
1014 if (!SH_ShowDriveProperties(wszBuf, pidlParent, &pidlChild))
1015 hr = E_FAIL;
1016 }
1017 else if (_ILIsNetHood(pidlChild))
1018 {
1019 // FIXME path!
1020 if (32 >= (UINT)ShellExecuteW(NULL, L"open", L"explorer.exe",
1021 L"::{7007ACC7-3202-11D1-AAD2-00805FC1270E}",
1022 NULL, SW_SHOWDEFAULT))
1023 hr = E_FAIL;
1024 }
1025 else if (_ILIsBitBucket(pidlChild))
1026 {
1027 /* FIXME: detect the drive path of bitbucket if appropiate */
1028 if (!SH_ShowRecycleBinProperties(L'C'))
1029 hr = E_FAIL;
1030 }
1031 else
1032 {
1033 if (m_cidl > 1)
1034 WARN("SHMultiFileProperties is not yet implemented\n");
1035
1036 STRRET strFile;
1037 hr = m_psf->GetDisplayNameOf(pidlChild, SHGDN_FORPARSING, &strFile);
1038 if (SUCCEEDED(hr))
1039 {
1040 WCHAR wszBuf[MAX_PATH];
1041 hr = StrRetToBufW(&strFile, pidlChild, wszBuf, _countof(wszBuf));
1042 if (SUCCEEDED(hr))
1043 hr = SH_ShowPropertiesDialog(wszBuf, pidlParent, &pidlChild);
1044 else
1045 ERR("StrRetToBufW failed\n");
1046 }
1047 else
1048 ERR("IShellFolder_GetDisplayNameOf failed for apidl\n");
1049 }
1050
1051 /* Free allocated PIDLs */
1052 if (pidlParent != m_pidlFolder)
1053 ILFree((ITEMIDLIST*)pidlParent);
1054 if (m_cidl < 1 || pidlChild != m_apidl[0])
1055 ILFree((ITEMIDLIST*)pidlChild);
1056
1057 return hr;
1058 }
1059
1060 HRESULT
1061 CDefaultContextMenu::DoFormat(
1062 LPCMINVOKECOMMANDINFO lpcmi)
1063 {
1064 char szDrive[8] = {0};
1065
1066 if (!_ILGetDrive(m_apidl[0], szDrive, sizeof(szDrive)))
1067 {
1068 ERR("pidl is not a drive\n");
1069 return E_FAIL;
1070 }
1071
1072 SHFormatDrive(lpcmi->hwnd, szDrive[0] - 'A', SHFMT_ID_DEFAULT, 0);
1073 return S_OK;
1074 }
1075
1076 // This code is taken from CNewMenu and should be shared between the 2 classes
1077 HRESULT
1078 CDefaultContextMenu::DoCreateNewFolder(
1079 LPCMINVOKECOMMANDINFO lpici)
1080 {
1081 WCHAR wszPath[MAX_PATH];
1082 WCHAR wszName[MAX_PATH];
1083 WCHAR wszNewFolder[25];
1084 HRESULT hr;
1085
1086 /* Get folder path */
1087 hr = SHGetPathFromIDListW(m_pidlFolder, wszPath);
1088 if (FAILED_UNEXPECTEDLY(hr))
1089 return hr;
1090
1091 if (!LoadStringW(shell32_hInstance, IDS_NEWFOLDER, wszNewFolder, _countof(wszNewFolder)))
1092 return E_FAIL;
1093
1094 /* Create the name of the new directory */
1095 if (!PathYetAnotherMakeUniqueName(wszName, wszPath, NULL, wszNewFolder))
1096 return E_FAIL;
1097
1098 /* Create the new directory and show the appropriate dialog in case of error */
1099 if (SHCreateDirectory(lpici->hwnd, wszName) != ERROR_SUCCESS)
1100 return E_FAIL;
1101
1102 /* Show and select the new item in the def view */
1103 LPITEMIDLIST pidl;
1104 PITEMID_CHILD pidlNewItem;
1105 CComPtr<IShellView> psv;
1106
1107 /* Notify the view object about the new item */
1108 SHChangeNotify(SHCNE_MKDIR, SHCNF_PATHW, (LPCVOID)wszName, NULL);
1109
1110 if (!m_site)
1111 return S_OK;
1112
1113 /* Get a pointer to the shell view */
1114 hr = IUnknown_QueryService(m_site, SID_IFolderView, IID_PPV_ARG(IShellView, &psv));
1115 if (FAILED_UNEXPECTEDLY(hr))
1116 return S_OK;
1117
1118 /* Attempt to get the pidl of the new item */
1119 hr = SHILCreateFromPathW(wszName, &pidl, NULL);
1120 if (FAILED_UNEXPECTEDLY(hr))
1121 return hr;
1122
1123 pidlNewItem = ILFindLastID(pidl);
1124
1125 hr = psv->SelectItem(pidlNewItem, SVSI_DESELECTOTHERS | SVSI_EDIT | SVSI_ENSUREVISIBLE |
1126 SVSI_FOCUSED | SVSI_SELECT);
1127 if (FAILED_UNEXPECTEDLY(hr))
1128 return hr;
1129
1130 SHFree(pidl);
1131
1132 return S_OK;
1133 }
1134
1135 PDynamicShellEntry CDefaultContextMenu::GetDynamicEntry(UINT idCmd)
1136 {
1137 PDynamicShellEntry pEntry = m_pDynamicEntries;
1138
1139 while(pEntry && idCmd > pEntry->iIdCmdFirst + pEntry->NumIds)
1140 pEntry = pEntry->pNext;
1141
1142 if (!pEntry)
1143 return NULL;
1144
1145 if (idCmd < pEntry->iIdCmdFirst || idCmd > pEntry->iIdCmdFirst + pEntry->NumIds)
1146 return NULL;
1147
1148 return pEntry;
1149 }
1150
1151 //FIXME: 260 is correct, but should this be part of the SDK or just MAX_PATH?
1152 #define MAX_VERB 260
1153
1154 BOOL
1155 CDefaultContextMenu::MapVerbToCmdId(PVOID Verb, PUINT idCmd, BOOL IsUnicode)
1156 {
1157 WCHAR UnicodeStr[MAX_VERB];
1158
1159 /* Loop through all the static verbs looking for a match */
1160 for (UINT i = 0; i < _countof(g_StaticInvokeCmdMap); i++)
1161 {
1162 /* We can match both ANSI and unicode strings */
1163 if (IsUnicode)
1164 {
1165 /* The static verbs are ANSI, get a unicode version before doing the compare */
1166 SHAnsiToUnicode(g_StaticInvokeCmdMap[i].szStringVerb, UnicodeStr, MAX_VERB);
1167 if (!wcscmp(UnicodeStr, (LPWSTR)Verb))
1168 {
1169 /* Return the Corresponding Id */
1170 *idCmd = g_StaticInvokeCmdMap[i].IntVerb;
1171 return TRUE;
1172 }
1173 }
1174 else
1175 {
1176 if (!strcmp(g_StaticInvokeCmdMap[i].szStringVerb, (LPSTR)Verb))
1177 {
1178 *idCmd = g_StaticInvokeCmdMap[i].IntVerb;
1179 return TRUE;
1180 }
1181 }
1182 }
1183
1184 return FALSE;
1185 }
1186
1187 HRESULT
1188 CDefaultContextMenu::DoDynamicShellExtensions(
1189 LPCMINVOKECOMMANDINFO lpcmi)
1190 {
1191 TRACE("verb %p first %x last %x", lpcmi->lpVerb, m_iIdSHEFirst, m_iIdSHELast);
1192
1193 UINT idCmd = LOWORD(lpcmi->lpVerb);
1194 PDynamicShellEntry pEntry = GetDynamicEntry(idCmd);
1195 if (!pEntry)
1196 return E_FAIL;
1197
1198 /* invoke the dynamic context menu */
1199 lpcmi->lpVerb = MAKEINTRESOURCEA(idCmd - pEntry->iIdCmdFirst);
1200 return pEntry->pCM->InvokeCommand(lpcmi);
1201 }
1202
1203 DWORD
1204 CDefaultContextMenu::BrowserFlagsFromVerb(LPCMINVOKECOMMANDINFO lpcmi, PStaticShellEntry pEntry)
1205 {
1206 CComPtr<IShellBrowser> psb;
1207 HWND hwndTree;
1208 LPCWSTR FlagsName;
1209 WCHAR wszKey[256];
1210 HRESULT hr;
1211 DWORD wFlags;
1212 DWORD cbVerb;
1213
1214 if (!m_site)
1215 return 0;
1216
1217 /* Get a pointer to the shell browser */
1218 hr = IUnknown_QueryService(m_site, SID_IShellBrowser, IID_PPV_ARG(IShellBrowser, &psb));
1219 if (FAILED_UNEXPECTEDLY(hr))
1220 return 0;
1221
1222 /* See if we are in Explore or Browse mode. If the browser's tree is present, we are in Explore mode.*/
1223 if (SUCCEEDED(psb->GetControlWindow(FCW_TREE, &hwndTree)) && hwndTree)
1224 FlagsName = L"ExplorerFlags";
1225 else
1226 FlagsName = L"BrowserFlags";
1227
1228 /* Try to get the flag from the verb */
1229 hr = StringCbPrintfW(wszKey, sizeof(wszKey), L"shell\\%s", pEntry->szVerb);
1230 if (FAILED_UNEXPECTEDLY(hr))
1231 return 0;
1232
1233 cbVerb = sizeof(wFlags);
1234 if (RegGetValueW(pEntry->hkClass, wszKey, FlagsName, RRF_RT_REG_DWORD, NULL, &wFlags, &cbVerb) == ERROR_SUCCESS)
1235 {
1236 return wFlags;
1237 }
1238
1239 return 0;
1240 }
1241
1242 HRESULT
1243 CDefaultContextMenu::TryToBrowse(
1244 LPCMINVOKECOMMANDINFO lpcmi, LPCITEMIDLIST pidl, DWORD wFlags)
1245 {
1246 CComPtr<IShellBrowser> psb;
1247 HRESULT hr;
1248
1249 if (!m_site)
1250 return E_FAIL;
1251
1252 /* Get a pointer to the shell browser */
1253 hr = IUnknown_QueryService(m_site, SID_IShellBrowser, IID_PPV_ARG(IShellBrowser, &psb));
1254 if (FAILED_UNEXPECTEDLY(hr))
1255 return 0;
1256
1257 return psb->BrowseObject(ILCombine(m_pidlFolder, pidl), wFlags);
1258 }
1259
1260 HRESULT
1261 CDefaultContextMenu::InvokePidl(LPCMINVOKECOMMANDINFO lpcmi, LPCITEMIDLIST pidl, PStaticShellEntry pEntry)
1262 {
1263 LPITEMIDLIST pidlFull = ILCombine(m_pidlFolder, pidl);
1264 if (pidlFull == NULL)
1265 {
1266 return E_FAIL;
1267 }
1268
1269 WCHAR wszPath[MAX_PATH];
1270 BOOL bHasPath = SHGetPathFromIDListW(pidlFull, wszPath);
1271
1272 WCHAR wszDir[MAX_PATH];
1273 if (bHasPath)
1274 {
1275 wcscpy(wszDir, wszPath);
1276 PathRemoveFileSpec(wszDir);
1277 }
1278 else
1279 {
1280 SHGetPathFromIDListW(m_pidlFolder, wszDir);
1281 }
1282
1283 SHELLEXECUTEINFOW sei;
1284 ZeroMemory(&sei, sizeof(sei));
1285 sei.cbSize = sizeof(sei);
1286 sei.hwnd = lpcmi->hwnd;
1287 sei.nShow = SW_SHOWNORMAL;
1288 sei.lpVerb = pEntry->szVerb;
1289 sei.lpDirectory = wszDir;
1290 sei.lpIDList = pidlFull;
1291 sei.hkeyClass = pEntry->hkClass;
1292 sei.fMask = SEE_MASK_CLASSKEY | SEE_MASK_IDLIST;
1293 if (bHasPath)
1294 {
1295 sei.lpFile = wszPath;
1296 }
1297
1298 ShellExecuteExW(&sei);
1299
1300 ILFree(pidlFull);
1301
1302 return S_OK;
1303 }
1304
1305 HRESULT
1306 CDefaultContextMenu::DoStaticShellExtensions(
1307 LPCMINVOKECOMMANDINFO lpcmi)
1308 {
1309 PStaticShellEntry pEntry = m_pStaticEntries;
1310 INT iCmd = LOWORD(lpcmi->lpVerb) - m_iIdSCMFirst;
1311 HRESULT hr;
1312 UINT i;
1313
1314 while (pEntry && (iCmd--) > 0)
1315 pEntry = pEntry->pNext;
1316
1317 if (iCmd > 0)
1318 return E_FAIL;
1319
1320 /* Get the browse flags to see if we need to browse */
1321 DWORD wFlags = BrowserFlagsFromVerb(lpcmi, pEntry);
1322 BOOL bBrowsed = FALSE;
1323
1324 for (i=0; i < m_cidl; i++)
1325 {
1326 /* Check if we need to browse */
1327 if (wFlags > 0)
1328 {
1329 /* In xp if we have browsed, we don't open any more folders .
1330 * In win7 we browse to the first folder we find and
1331 * open new windows fo for each of the rest of the folders */
1332 if (bBrowsed)
1333 continue;
1334
1335 hr = TryToBrowse(lpcmi, m_apidl[i], wFlags);
1336 if (SUCCEEDED(hr))
1337 {
1338 bBrowsed = TRUE;
1339 continue;
1340 }
1341 }
1342
1343 InvokePidl(lpcmi, m_apidl[i], pEntry);
1344 }
1345
1346 return S_OK;
1347 }
1348
1349 HRESULT
1350 WINAPI
1351 CDefaultContextMenu::InvokeCommand(
1352 LPCMINVOKECOMMANDINFO lpcmi)
1353 {
1354 CMINVOKECOMMANDINFO LocalInvokeInfo;
1355 HRESULT Result;
1356 UINT CmdId;
1357
1358 /* Take a local copy of the fixed members of the
1359 struct as we might need to modify the verb */
1360 LocalInvokeInfo = *lpcmi;
1361
1362 /* Check if this is a string verb */
1363 if (HIWORD(LocalInvokeInfo.lpVerb))
1364 {
1365 /* Get the ID which corresponds to this verb, and update our local copy */
1366 if (MapVerbToCmdId((LPVOID)LocalInvokeInfo.lpVerb, &CmdId, FALSE))
1367 LocalInvokeInfo.lpVerb = MAKEINTRESOURCEA(CmdId);
1368 }
1369
1370 /* Check if this is a Id */
1371 switch (LOWORD(LocalInvokeInfo.lpVerb))
1372 {
1373 case FCIDM_SHVIEW_BIGICON:
1374 case FCIDM_SHVIEW_SMALLICON:
1375 case FCIDM_SHVIEW_LISTVIEW:
1376 case FCIDM_SHVIEW_REPORTVIEW:
1377 case 0x30: /* FIX IDS in resource files */
1378 case 0x31:
1379 case 0x32:
1380 case 0x33:
1381 case FCIDM_SHVIEW_AUTOARRANGE:
1382 case FCIDM_SHVIEW_SNAPTOGRID:
1383 Result = NotifyShellViewWindow(&LocalInvokeInfo, FALSE);
1384 break;
1385 case FCIDM_SHVIEW_REFRESH:
1386 Result = DoRefresh(&LocalInvokeInfo);
1387 break;
1388 case FCIDM_SHVIEW_INSERT:
1389 Result = DoPaste(&LocalInvokeInfo, FALSE);
1390 break;
1391 case FCIDM_SHVIEW_INSERTLINK:
1392 Result = DoPaste(&LocalInvokeInfo, TRUE);
1393 break;
1394 case FCIDM_SHVIEW_OPEN:
1395 case FCIDM_SHVIEW_EXPLORE:
1396 Result = DoOpenOrExplore(&LocalInvokeInfo);
1397 break;
1398 case FCIDM_SHVIEW_COPY:
1399 case FCIDM_SHVIEW_CUT:
1400 Result = DoCopyOrCut(&LocalInvokeInfo, LOWORD(LocalInvokeInfo.lpVerb) == FCIDM_SHVIEW_COPY);
1401 break;
1402 case FCIDM_SHVIEW_CREATELINK:
1403 Result = DoCreateLink(&LocalInvokeInfo);
1404 break;
1405 case FCIDM_SHVIEW_DELETE:
1406 Result = DoDelete(&LocalInvokeInfo);
1407 break;
1408 case FCIDM_SHVIEW_RENAME:
1409 Result = DoRename(&LocalInvokeInfo);
1410 break;
1411 case FCIDM_SHVIEW_PROPERTIES:
1412 Result = DoProperties(&LocalInvokeInfo);
1413 break;
1414 case 0x7ABC:
1415 Result = DoFormat(&LocalInvokeInfo);
1416 break;
1417 case FCIDM_SHVIEW_NEWFOLDER:
1418 Result = DoCreateNewFolder(&LocalInvokeInfo);
1419 break;
1420 default:
1421 Result = E_UNEXPECTED;
1422 break;
1423 }
1424
1425 /* Check for ID's we didn't find a handler for */
1426 if (Result == E_UNEXPECTED)
1427 {
1428 if (m_iIdSHEFirst && m_iIdSHELast)
1429 {
1430 if (LOWORD(LocalInvokeInfo.lpVerb) >= m_iIdSHEFirst && LOWORD(LocalInvokeInfo.lpVerb) <= m_iIdSHELast)
1431 Result = DoDynamicShellExtensions(&LocalInvokeInfo);
1432 }
1433
1434 if (m_iIdSCMFirst && m_iIdSCMLast)
1435 {
1436 if (LOWORD(LocalInvokeInfo.lpVerb) >= m_iIdSCMFirst && LOWORD(LocalInvokeInfo.lpVerb) <= m_iIdSCMLast)
1437 Result = DoStaticShellExtensions(&LocalInvokeInfo);
1438 }
1439 }
1440
1441 if (Result == E_UNEXPECTED)
1442 FIXME("Unhandled Verb %xl\n", LOWORD(LocalInvokeInfo.lpVerb));
1443
1444 return Result;
1445 }
1446
1447 HRESULT
1448 WINAPI
1449 CDefaultContextMenu::GetCommandString(
1450 UINT_PTR idCommand,
1451 UINT uFlags,
1452 UINT* lpReserved,
1453 LPSTR lpszName,
1454 UINT uMaxNameLen)
1455 {
1456 /* We don't handle the help text yet */
1457 if (uFlags == GCS_HELPTEXTA ||
1458 uFlags == GCS_HELPTEXTW)
1459 {
1460 return E_NOTIMPL;
1461 }
1462
1463 /* Loop looking for a matching Id */
1464 for (UINT i = 0; i < _countof(g_StaticInvokeCmdMap); i++)
1465 {
1466 if (g_StaticInvokeCmdMap[i].IntVerb == idCommand)
1467 {
1468 /* Validation just returns S_OK on a match */
1469 if (uFlags == GCS_VALIDATEA || uFlags == GCS_VALIDATEW)
1470 return S_OK;
1471
1472 /* Return a copy of the ANSI verb */
1473 if (uFlags == GCS_VERBA)
1474 return StringCchCopyA(lpszName, uMaxNameLen, g_StaticInvokeCmdMap[i].szStringVerb);
1475
1476 /* Convert the ANSI verb to unicode and return that */
1477 if (uFlags == GCS_VERBW)
1478 {
1479 if (SHAnsiToUnicode(g_StaticInvokeCmdMap[i].szStringVerb, (LPWSTR)lpszName, uMaxNameLen))
1480 return S_OK;
1481 }
1482 }
1483 }
1484
1485 return E_INVALIDARG;
1486 }
1487
1488 HRESULT
1489 WINAPI
1490 CDefaultContextMenu::HandleMenuMsg(
1491 UINT uMsg,
1492 WPARAM wParam,
1493 LPARAM lParam)
1494 {
1495 /* FIXME: Should we implement this as well? */
1496 return S_OK;
1497 }
1498
1499 HRESULT
1500 WINAPI
1501 CDefaultContextMenu::HandleMenuMsg2(
1502 UINT uMsg,
1503 WPARAM wParam,
1504 LPARAM lParam,
1505 LRESULT *plResult)
1506 {
1507 switch (uMsg)
1508 {
1509 case WM_INITMENUPOPUP:
1510 {
1511 PDynamicShellEntry pEntry = m_pDynamicEntries;
1512 while (pEntry)
1513 {
1514 SHForwardContextMenuMsg(pEntry->pCM, uMsg, wParam, lParam, plResult, TRUE);
1515 pEntry = pEntry->pNext;
1516 }
1517 break;
1518 }
1519 case WM_DRAWITEM:
1520 {
1521 DRAWITEMSTRUCT* pDrawStruct = reinterpret_cast<DRAWITEMSTRUCT*>(lParam);
1522 PDynamicShellEntry pEntry = GetDynamicEntry(pDrawStruct->itemID);
1523 if (pEntry)
1524 SHForwardContextMenuMsg(pEntry->pCM, uMsg, wParam, lParam, plResult, TRUE);
1525 break;
1526 }
1527 case WM_MEASUREITEM:
1528 {
1529 MEASUREITEMSTRUCT* pMeasureStruct = reinterpret_cast<MEASUREITEMSTRUCT*>(lParam);
1530 PDynamicShellEntry pEntry = GetDynamicEntry(pMeasureStruct->itemID);
1531 if (pEntry)
1532 SHForwardContextMenuMsg(pEntry->pCM, uMsg, wParam, lParam, plResult, TRUE);
1533 break;
1534 }
1535 case WM_MENUCHAR :
1536 /* FIXME */
1537 break;
1538 default:
1539 ERR("Got unknown message:%d\n", uMsg);
1540 }
1541 return S_OK;
1542 }
1543
1544 HRESULT
1545 WINAPI
1546 CDefaultContextMenu::SetSite(IUnknown *pUnkSite)
1547 {
1548 m_site = pUnkSite;
1549 return S_OK;
1550 }
1551
1552 HRESULT
1553 WINAPI
1554 CDefaultContextMenu::GetSite(REFIID riid, void **ppvSite)
1555 {
1556 if (!m_site)
1557 return E_FAIL;
1558
1559 return m_site->QueryInterface(riid, ppvSite);
1560 }
1561
1562 static
1563 HRESULT
1564 CDefaultContextMenu_CreateInstance(const DEFCONTEXTMENU *pdcm, REFIID riid, void **ppv)
1565 {
1566 return ShellObjectCreatorInit<CDefaultContextMenu>(pdcm, riid, ppv);
1567 }
1568
1569 /*************************************************************************
1570 * SHCreateDefaultContextMenu [SHELL32.325] Vista API
1571 *
1572 */
1573
1574 static void AddClassKey(const WCHAR * szClass, HKEY* buffer, UINT* cKeys)
1575 {
1576 LSTATUS result;
1577 HKEY hkey;
1578 result = RegOpenKeyExW(HKEY_CLASSES_ROOT, szClass, 0, KEY_READ | KEY_QUERY_VALUE, &hkey);
1579 if (result != ERROR_SUCCESS)
1580 return;
1581
1582 buffer[*cKeys] = hkey;
1583 *cKeys +=1;
1584 }
1585
1586 void HackFillKeys(DEFCONTEXTMENU *pdcm, HKEY* buffer)
1587 {
1588 PCUITEMID_CHILD pidl = pdcm->apidl[0];
1589 pdcm->cKeys = 0;
1590 pdcm->aKeys = buffer;
1591
1592 if (_ILIsValue(pidl))
1593 {
1594 FileStructW* pFileData = _ILGetFileStructW(pidl);
1595 LPWSTR extension = PathFindExtension(pFileData->wszName);
1596
1597 if (extension)
1598 {
1599 AddClassKey(extension, buffer, &pdcm->cKeys);
1600
1601 WCHAR wszClass[40], wszClass2[40];
1602 DWORD dwSize = sizeof(wszClass);
1603 if (RegGetValueW(HKEY_CLASSES_ROOT, extension, NULL, RRF_RT_REG_SZ, NULL, wszClass, &dwSize) == ERROR_SUCCESS)
1604 {
1605 swprintf(wszClass2, L"%s//%s", extension, wszClass);
1606
1607 AddClassKey(wszClass, buffer, &pdcm->cKeys);
1608 AddClassKey(wszClass2, buffer, &pdcm->cKeys);
1609 }
1610
1611 swprintf(wszClass2, L"SystemFileAssociations//%s", extension);
1612 AddClassKey(wszClass2, buffer, &pdcm->cKeys);
1613
1614 if (RegGetValueW(HKEY_CLASSES_ROOT, extension, L"PerceivedType ", RRF_RT_REG_SZ, NULL, wszClass, &dwSize) == ERROR_SUCCESS)
1615 {
1616 swprintf(wszClass2, L"SystemFileAssociations//%s", wszClass);
1617 AddClassKey(wszClass2, buffer, &pdcm->cKeys);
1618 }
1619 }
1620
1621 AddClassKey(L"AllFilesystemObjects", buffer, &pdcm->cKeys);
1622 AddClassKey(L"*", buffer, &pdcm->cKeys);
1623 }
1624 else if (_ILIsSpecialFolder(pidl))
1625 {
1626 GUID *pGuid = _ILGetGUIDPointer(pidl);
1627 if (pGuid)
1628 {
1629 LPOLESTR pwszCLSID;
1630 WCHAR key[60];
1631
1632 wcscpy(key, L"CLSID\\");
1633 HRESULT hr = StringFromCLSID(*pGuid, &pwszCLSID);
1634 if (hr == S_OK)
1635 {
1636 wcscpy(&key[6], pwszCLSID);
1637 AddClassKey(key, buffer, &pdcm->cKeys);
1638 }
1639 }
1640 AddClassKey(L"Folder", buffer, &pdcm->cKeys);
1641 }
1642 else if (_ILIsFolder(pidl))
1643 {
1644 AddClassKey(L"AllFilesystemObjects", buffer, &pdcm->cKeys);
1645 AddClassKey(L"Directory", buffer, &pdcm->cKeys);
1646 AddClassKey(L"Folder", buffer, &pdcm->cKeys);
1647 }
1648 else if (_ILIsDrive(pidl))
1649 {
1650 AddClassKey(L"Drive", buffer, &pdcm->cKeys);
1651 AddClassKey(L"Folder", buffer, &pdcm->cKeys);
1652 }
1653 }
1654
1655 HRESULT
1656 WINAPI
1657 SHCreateDefaultContextMenu(const DEFCONTEXTMENU *pdcm, REFIID riid, void **ppv)
1658 {
1659 /* HACK: move to the shell folders implementation */
1660 HKEY hkeyHack[16];
1661 if (!pdcm->aKeys && pdcm->cidl)
1662 HackFillKeys((DEFCONTEXTMENU *)pdcm, hkeyHack);
1663
1664 HRESULT hr = CDefaultContextMenu_CreateInstance(pdcm, riid, ppv);
1665 if (FAILED_UNEXPECTEDLY(hr))
1666 return hr;
1667
1668 return S_OK;
1669 }
1670
1671 /*************************************************************************
1672 * CDefFolderMenu_Create2 [SHELL32.701]
1673 *
1674 */
1675
1676 HRESULT
1677 WINAPI
1678 CDefFolderMenu_Create2(
1679 PCIDLIST_ABSOLUTE pidlFolder,
1680 HWND hwnd,
1681 UINT cidl,
1682 PCUITEMID_CHILD_ARRAY apidl,
1683 IShellFolder *psf,
1684 LPFNDFMCALLBACK lpfn,
1685 UINT nKeys,
1686 const HKEY *ahkeyClsKeys,
1687 IContextMenu **ppcm)
1688 {
1689 DEFCONTEXTMENU pdcm;
1690 pdcm.hwnd = hwnd;
1691 pdcm.pcmcb = NULL;
1692 pdcm.pidlFolder = pidlFolder;
1693 pdcm.psf = psf;
1694 pdcm.cidl = cidl;
1695 pdcm.apidl = apidl;
1696 pdcm.punkAssociationInfo = NULL;
1697 pdcm.cKeys = nKeys;
1698 pdcm.aKeys = ahkeyClsKeys;
1699
1700 HRESULT hr = SHCreateDefaultContextMenu(&pdcm, IID_PPV_ARG(IContextMenu, ppcm));
1701 if (FAILED_UNEXPECTEDLY(hr))
1702 return hr;
1703
1704 return S_OK;
1705 }