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