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