Sync with trunk r63831.
[reactos.git] / 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 DoRefresh(LPCMINVOKECOMMANDINFO lpcmi);
65 HRESULT DoDelete(LPCMINVOKECOMMANDINFO lpcmi);
66 HRESULT DoCopyOrCut(LPCMINVOKECOMMANDINFO lpcmi, BOOL bCopy);
67 HRESULT DoRename(LPCMINVOKECOMMANDINFO lpcmi);
68 HRESULT DoProperties(LPCMINVOKECOMMANDINFO lpcmi);
69 HRESULT DoFormat(LPCMINVOKECOMMANDINFO lpcmi);
70 HRESULT DoDynamicShellExtensions(LPCMINVOKECOMMANDINFO lpcmi);
71 HRESULT DoStaticShellExtensions(LPCMINVOKECOMMANDINFO lpcmi);
72 DWORD BrowserFlagsFromVerb(LPCMINVOKECOMMANDINFO lpcmi, PStaticShellEntry pEntry);
73 HRESULT TryToBrowse(LPCMINVOKECOMMANDINFO lpcmi, LPCITEMIDLIST pidl, DWORD wFlags);
74 HRESULT InvokePidl(LPCMINVOKECOMMANDINFO lpcmi, LPCITEMIDLIST pidl, PStaticShellEntry pEntry);
75
76 public:
77 CDefaultContextMenu();
78 ~CDefaultContextMenu();
79 HRESULT WINAPI Initialize(const DEFCONTEXTMENU *pdcm);
80
81 // IContextMenu
82 virtual HRESULT WINAPI QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);
83 virtual HRESULT WINAPI InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi);
84 virtual HRESULT WINAPI GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen);
85
86 // IContextMenu2
87 virtual HRESULT WINAPI HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam);
88
89 BEGIN_COM_MAP(CDefaultContextMenu)
90 COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu)
91 COM_INTERFACE_ENTRY_IID(IID_IContextMenu2, IContextMenu2)
92 END_COM_MAP()
93 };
94
95 CDefaultContextMenu::CDefaultContextMenu()
96 {
97 memset(&m_Dcm, 0, sizeof(m_Dcm));
98 m_pDataObj = NULL;
99 m_pidlFolder = NULL;
100 m_bGroupPolicyActive = 0;
101 m_pDynamicEntries = NULL;
102 m_iIdSHEFirst = 0;
103 m_iIdSHELast = 0;
104 m_pStaticEntries = NULL;
105 m_iIdSCMFirst = 0;
106 m_iIdSCMLast = 0;
107 }
108
109 CDefaultContextMenu::~CDefaultContextMenu()
110 {
111 /* Free dynamic shell extension entries */
112 PDynamicShellEntry pDynamicEntry = m_pDynamicEntries, pNextDynamic;
113 while (pDynamicEntry)
114 {
115 pNextDynamic = pDynamicEntry->pNext;
116 pDynamicEntry->pCM->Release();
117 HeapFree(GetProcessHeap(), 0, pDynamicEntry);
118 pDynamicEntry = pNextDynamic;
119 }
120
121 /* Free static shell extension entries */
122 PStaticShellEntry pStaticEntry = m_pStaticEntries, pNextStatic;
123 while (pStaticEntry)
124 {
125 pNextStatic = pStaticEntry->pNext;
126 HeapFree(GetProcessHeap(), 0, pStaticEntry->szClass);
127 HeapFree(GetProcessHeap(), 0, pStaticEntry->szVerb);
128 HeapFree(GetProcessHeap(), 0, pStaticEntry);
129 pStaticEntry = pNextStatic;
130 }
131
132 if (m_pidlFolder)
133 ILFree((_ITEMIDLIST*)m_pidlFolder);
134 if (m_pDataObj)
135 m_pDataObj->Release();
136 }
137
138 HRESULT WINAPI CDefaultContextMenu::Initialize(const DEFCONTEXTMENU *pdcm)
139 {
140 IDataObject *pDataObj;
141
142 TRACE("cidl %u\n", pdcm->cidl);
143 if (SUCCEEDED(SHCreateDataObject(pdcm->pidlFolder, pdcm->cidl, pdcm->apidl, NULL, IID_PPV_ARG(IDataObject, &pDataObj))))
144 m_pDataObj = pDataObj;
145
146 if (!pdcm->cidl)
147 {
148 /* Init pidlFolder only if it is background context menu. See IShellExtInit::Initialize */
149 if (pdcm->pidlFolder)
150 m_pidlFolder = ILClone(pdcm->pidlFolder);
151 else
152 {
153 IPersistFolder2 *pf = NULL;
154 if (SUCCEEDED(pdcm->psf->QueryInterface(IID_PPV_ARG(IPersistFolder2, &pf))))
155 {
156 if (FAILED(pf->GetCurFolder((_ITEMIDLIST**)&m_pidlFolder)))
157 ERR("GetCurFolder failed\n");
158 pf->Release();
159 }
160 }
161 TRACE("pidlFolder %p\n", m_pidlFolder);
162 }
163
164 CopyMemory(&m_Dcm, pdcm, sizeof(DEFCONTEXTMENU));
165 return S_OK;
166 }
167
168 void
169 CDefaultContextMenu::AddStaticEntry(const WCHAR *szVerb, const WCHAR *szClass)
170 {
171 PStaticShellEntry pEntry = m_pStaticEntries, pLastEntry = NULL;
172 while(pEntry)
173 {
174 if (!wcsicmp(pEntry->szVerb, szVerb))
175 {
176 /* entry already exists */
177 return;
178 }
179 pLastEntry = pEntry;
180 pEntry = pEntry->pNext;
181 }
182
183 TRACE("adding verb %s szClass %s\n", debugstr_w(szVerb), debugstr_w(szClass));
184
185 pEntry = (StaticShellEntry *)HeapAlloc(GetProcessHeap(), 0, sizeof(StaticShellEntry));
186 if (pEntry)
187 {
188 pEntry->pNext = NULL;
189 pEntry->szVerb = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (wcslen(szVerb) + 1) * sizeof(WCHAR));
190 if (pEntry->szVerb)
191 wcscpy(pEntry->szVerb, szVerb);
192 pEntry->szClass = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (wcslen(szClass) + 1) * sizeof(WCHAR));
193 if (pEntry->szClass)
194 wcscpy(pEntry->szClass, szClass);
195 }
196
197 if (!wcsicmp(szVerb, L"open"))
198 {
199 /* open verb is always inserted in front */
200 pEntry->pNext = m_pStaticEntries;
201 m_pStaticEntries = pEntry;
202 }
203 else if (pLastEntry)
204 pLastEntry->pNext = pEntry;
205 else
206 m_pStaticEntries = pEntry;
207 }
208
209 void
210 CDefaultContextMenu::AddStaticEntryForKey(HKEY hKey, const WCHAR *pwszClass)
211 {
212 WCHAR wszName[40];
213 DWORD cchName, dwIndex = 0;
214
215 TRACE("AddStaticEntryForKey %x %ls\n", hKey, pwszClass);
216
217 while(TRUE)
218 {
219 cchName = _countof(wszName);
220 if (RegEnumKeyExW(hKey, dwIndex++, wszName, &cchName, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
221 break;
222
223 AddStaticEntry(wszName, pwszClass);
224 }
225 }
226
227 void
228 CDefaultContextMenu::AddStaticEntryForFileClass(const WCHAR * szExt)
229 {
230 WCHAR szBuffer[100];
231 HKEY hKey;
232 LONG result;
233 DWORD dwBuffer;
234 UINT Length;
235 static WCHAR szShell[] = L"\\shell";
236 static WCHAR szShellAssoc[] = L"SystemFileAssociations\\";
237
238 TRACE("AddStaticEntryForFileClass entered with %s\n", debugstr_w(szExt));
239
240 Length = wcslen(szExt);
241 if (Length + (sizeof(szShell) / sizeof(WCHAR)) + 1 < sizeof(szBuffer) / sizeof(WCHAR))
242 {
243 wcscpy(szBuffer, szExt);
244 wcscpy(&szBuffer[Length], szShell);
245 result = RegOpenKeyExW(HKEY_CLASSES_ROOT, szBuffer, 0, KEY_READ | KEY_QUERY_VALUE, &hKey);
246 if (result == ERROR_SUCCESS)
247 {
248 szBuffer[Length] = 0;
249 AddStaticEntryForKey(hKey, szExt);
250 RegCloseKey(hKey);
251 }
252 }
253
254 dwBuffer = sizeof(szBuffer);
255 result = RegGetValueW(HKEY_CLASSES_ROOT, szExt, NULL, RRF_RT_REG_SZ, NULL, (LPBYTE)szBuffer, &dwBuffer);
256 if (result == ERROR_SUCCESS)
257 {
258 Length = wcslen(szBuffer);
259 if (Length + (sizeof(szShell) / sizeof(WCHAR)) + 1 < sizeof(szBuffer) / sizeof(WCHAR))
260 {
261 wcscpy(&szBuffer[Length], szShell);
262 TRACE("szBuffer %s\n", debugstr_w(szBuffer));
263
264 result = RegOpenKeyExW(HKEY_CLASSES_ROOT, szBuffer, 0, KEY_READ | KEY_QUERY_VALUE, &hKey);
265 if (result == ERROR_SUCCESS)
266 {
267 szBuffer[Length] = 0;
268 AddStaticEntryForKey(hKey, szBuffer);
269 RegCloseKey(hKey);
270 }
271 }
272 }
273
274 wcscpy(szBuffer, szShellAssoc);
275 dwBuffer = sizeof(szBuffer) - sizeof(szShellAssoc) - sizeof(WCHAR);
276 result = RegGetValueW(HKEY_CLASSES_ROOT, szExt, L"PerceivedType", RRF_RT_REG_SZ, NULL, (LPBYTE)&szBuffer[_countof(szShellAssoc) - 1], &dwBuffer);
277 if (result == ERROR_SUCCESS)
278 {
279 Length = wcslen(&szBuffer[_countof(szShellAssoc)]) + _countof(szShellAssoc);
280 wcscat(szBuffer, L"\\shell");
281 TRACE("szBuffer %s\n", debugstr_w(szBuffer));
282
283 result = RegOpenKeyExW(HKEY_CLASSES_ROOT, szBuffer, 0, KEY_READ | KEY_QUERY_VALUE, &hKey);
284 if (result == ERROR_SUCCESS)
285 {
286 szBuffer[Length] = 0;
287 AddStaticEntryForKey(hKey, szBuffer);
288 RegCloseKey(hKey);
289 }
290 }
291 }
292
293 static
294 BOOL
295 HasClipboardData()
296 {
297 BOOL bRet = FALSE;
298 IDataObject *pDataObj;
299
300 if(SUCCEEDED(OleGetClipboard(&pDataObj)))
301 {
302 STGMEDIUM medium;
303 FORMATETC formatetc;
304
305 TRACE("pDataObj=%p\n", pDataObj);
306
307 /* Set the FORMATETC structure*/
308 InitFormatEtc(formatetc, RegisterClipboardFormatW(CFSTR_SHELLIDLIST), TYMED_HGLOBAL);
309 if(SUCCEEDED(pDataObj->GetData(&formatetc, &medium)))
310 {
311 bRet = TRUE;
312 ReleaseStgMedium(&medium);
313 }
314
315 pDataObj->Release();
316 }
317
318 return bRet;
319 }
320
321 static
322 VOID
323 DisablePasteOptions(HMENU hMenu)
324 {
325 MENUITEMINFOW mii;
326
327 mii.cbSize = sizeof(mii);
328 mii.fMask = MIIM_STATE;
329 mii.fState = MFS_DISABLED;
330
331 TRACE("result %d\n", SetMenuItemInfoW(hMenu, FCIDM_SHVIEW_INSERT, FALSE, &mii));
332 TRACE("result %d\n", SetMenuItemInfoW(hMenu, FCIDM_SHVIEW_INSERTLINK, FALSE, &mii));
333 }
334
335 BOOL
336 CDefaultContextMenu::IsShellExtensionAlreadyLoaded(const CLSID *pclsid)
337 {
338 PDynamicShellEntry pEntry = m_pDynamicEntries;
339
340 while (pEntry)
341 {
342 if (!memcmp(&pEntry->ClassID, pclsid, sizeof(CLSID)))
343 return TRUE;
344 pEntry = pEntry->pNext;
345 }
346
347 return FALSE;
348 }
349
350 HRESULT
351 CDefaultContextMenu::LoadDynamicContextMenuHandler(HKEY hKey, const CLSID *pclsid)
352 {
353 HRESULT hr;
354
355 TRACE("LoadDynamicContextMenuHandler entered with This %p hKey %p pclsid %s\n", this, hKey, wine_dbgstr_guid(pclsid));
356
357 if (IsShellExtensionAlreadyLoaded(pclsid))
358 return S_OK;
359
360 IContextMenu *pcm;
361 hr = SHCoCreateInstance(NULL, pclsid, NULL, IID_PPV_ARG(IContextMenu, &pcm));
362 if (hr != S_OK)
363 {
364 ERR("SHCoCreateInstance failed %x\n", GetLastError());
365 return hr;
366 }
367
368 IShellExtInit *pExtInit;
369 hr = pcm->QueryInterface(IID_PPV_ARG(IShellExtInit, &pExtInit));
370 if (hr != S_OK)
371 {
372 ERR("Failed to query for interface IID_IShellExtInit hr %x pclsid %s\n", hr, wine_dbgstr_guid(pclsid));
373 pcm->Release();
374 return hr;
375 }
376
377 hr = pExtInit->Initialize(m_pidlFolder, m_pDataObj, hKey);
378 pExtInit->Release();
379 if (hr != S_OK)
380 {
381 TRACE("Failed to initialize shell extension error %x pclsid %s\n", hr, wine_dbgstr_guid(pclsid));
382 pcm->Release();
383 return hr;
384 }
385
386 PDynamicShellEntry pEntry = (DynamicShellEntry *)HeapAlloc(GetProcessHeap(), 0, sizeof(DynamicShellEntry));
387 if (!pEntry)
388 {
389 pcm->Release();
390 return E_OUTOFMEMORY;
391 }
392
393 pEntry->iIdCmdFirst = 0;
394 pEntry->pNext = NULL;
395 pEntry->NumIds = 0;
396 pEntry->pCM = pcm;
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_Dcm.cidl >= 1);
718
719 STRRET strFile;
720 hr = m_Dcm.psf->GetDisplayNameOf(m_Dcm.apidl[0], SHGDN_FORPARSING, &strFile);
721 if (hr == S_OK)
722 {
723 WCHAR wszPath[MAX_PATH];
724 hr = StrRetToBufW(&strFile, m_Dcm.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_Dcm.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_Dcm.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_Dcm.psf->GetAttributesOf(m_Dcm.cidl, m_Dcm.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_Dcm.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_Dcm.apidl[0]) || _ILIsValue(m_Dcm.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_Dcm.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_Dcm.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 LPSHELLVIEW lpSV = NULL;
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
944 lpSV->Release();
945 return S_OK;
946 }
947
948 HRESULT
949 CDefaultContextMenu::DoRefresh(
950 LPCMINVOKECOMMANDINFO lpcmi)
951 {
952 CComPtr<IPersistFolder2> ppf2 = NULL;
953 LPITEMIDLIST pidl;
954 HRESULT hr = m_Dcm.psf->QueryInterface(IID_PPV_ARG(IPersistFolder2, &ppf2));
955 if (SUCCEEDED(hr))
956 {
957 hr = ppf2->GetCurFolder(&pidl);
958 if (SUCCEEDED(hr))
959 {
960 SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_IDLIST, pidl, NULL);
961 ILFree(pidl);
962 }
963 ppf2->Release();
964 }
965 return hr;
966 }
967
968 HRESULT
969 CDefaultContextMenu::DoPaste(
970 LPCMINVOKECOMMANDINFO lpcmi, BOOL bLink)
971 {
972 HRESULT hr;
973
974 CComPtr<IDataObject> pda;
975 hr = OleGetClipboard(&pda);
976 if (FAILED(hr))
977 return hr;
978
979 CComPtr<IShellFolder> psfDesktop;
980 CComPtr<IShellFolder> psfTarget = NULL;
981
982 hr = SHGetDesktopFolder(&psfDesktop);
983 if (FAILED(hr))
984 return hr;
985
986 /* Find target folder */
987 if (m_Dcm.cidl)
988 {
989 hr = m_Dcm.psf->BindToObject(m_Dcm.apidl[0], NULL, IID_PPV_ARG(IShellFolder, &psfTarget));
990 }
991 else
992 {
993 CComPtr<IPersistFolder2> ppf2 = NULL;
994 LPITEMIDLIST pidl;
995
996 /* cidl is zero due to explorer view */
997 hr = m_Dcm.psf->QueryInterface(IID_PPV_ARG(IPersistFolder2, &ppf2));
998 if (SUCCEEDED(hr))
999 {
1000 hr = ppf2->GetCurFolder(&pidl);
1001 if (SUCCEEDED(hr))
1002 {
1003 if (_ILIsDesktop(pidl))
1004 {
1005 /* use desktop shellfolder */
1006 psfTarget = psfDesktop;
1007 }
1008 else
1009 {
1010 /* retrieve target desktop folder */
1011 hr = psfDesktop->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfTarget));
1012 }
1013 TRACE("psfTarget %x %p, Desktop %u\n", hr, psfTarget.p, _ILIsDesktop(pidl));
1014 ILFree(pidl);
1015 }
1016 }
1017 }
1018
1019 if (FAILED(hr))
1020 {
1021 ERR("no IShellFolder\n");
1022 return hr;
1023 }
1024
1025 FORMATETC formatetc2;
1026 STGMEDIUM medium2;
1027 InitFormatEtc(formatetc2, RegisterClipboardFormatW(CFSTR_PREFERREDDROPEFFECT), TYMED_HGLOBAL);
1028
1029 DWORD dwKey= 0;
1030
1031 if (SUCCEEDED(pda->GetData(&formatetc2, &medium2)))
1032 {
1033 DWORD * pdwFlag = (DWORD*)GlobalLock(medium2.hGlobal);
1034 if (pdwFlag)
1035 {
1036 if (*pdwFlag == DROPEFFECT_COPY)
1037 dwKey = MK_CONTROL;
1038 else
1039 dwKey = MK_SHIFT;
1040 }
1041 else {
1042 ERR("No drop effect obtained");
1043 }
1044 GlobalUnlock(medium2.hGlobal);
1045 }
1046
1047 if (bLink)
1048 {
1049 dwKey = MK_CONTROL|MK_SHIFT;
1050 }
1051
1052 IDropTarget *pdrop;
1053 hr = psfTarget->QueryInterface(IID_PPV_ARG(IDropTarget, &pdrop));
1054 if (FAILED(hr))
1055 {
1056 ERR("Error getting IDropTarget interface\n");
1057 return hr;
1058 }
1059
1060 SHSimulateDrop(pdrop, pda, dwKey, NULL, NULL);
1061
1062 TRACE("CP result %x\n", hr);
1063 return S_OK;
1064 }
1065
1066 HRESULT
1067 CDefaultContextMenu::DoOpenOrExplore(
1068 LPCMINVOKECOMMANDINFO lpcmi)
1069 {
1070 UNIMPLEMENTED;
1071 return E_FAIL;
1072 }
1073
1074 HRESULT
1075 CDefaultContextMenu::DoCreateLink(
1076 LPCMINVOKECOMMANDINFO lpcmi)
1077 {
1078 LPDATAOBJECT pDataObj;
1079 IDropTarget *pDT;
1080 HRESULT hr;
1081 CComPtr<IPersistFolder2> ppf2 = NULL;
1082 LPITEMIDLIST pidl;
1083 CComPtr<IShellFolder> psfDesktop;
1084 CComPtr<IShellFolder> psfTarget = NULL;
1085
1086 hr = SHGetDesktopFolder(&psfDesktop);
1087 if (FAILED(hr))
1088 return hr;
1089
1090 if (SUCCEEDED(hr = SHCreateDataObject(m_Dcm.pidlFolder, m_Dcm.cidl, m_Dcm.apidl, NULL, IID_PPV_ARG(IDataObject, &pDataObj))))
1091 {
1092 hr = m_Dcm.psf->QueryInterface(IID_PPV_ARG(IPersistFolder2, &ppf2));
1093 if (SUCCEEDED(hr))
1094 {
1095 hr = ppf2->GetCurFolder(&pidl);
1096 if (SUCCEEDED(hr))
1097 {
1098 if (_ILIsDesktop(pidl))
1099 {
1100 /* use desktop shellfolder */
1101 psfTarget = psfDesktop;
1102 }
1103 else
1104 {
1105 /* retrieve target desktop folder */
1106 hr = psfDesktop->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfTarget));
1107 }
1108 TRACE("psfTarget %x %p, Desktop %u\n", hr, psfTarget.p, _ILIsDesktop(pidl));
1109 ILFree(pidl);
1110 }
1111 }
1112
1113 }
1114
1115 if (FAILED(hr))
1116 {
1117 ERR("no IShellFolder\n");
1118 return hr;
1119 }
1120
1121 psfTarget->QueryInterface(IID_PPV_ARG(IDropTarget, &pDT));
1122 if (FAILED(hr))
1123 {
1124 ERR("no IDropTarget Interface\n");
1125 return hr;
1126 }
1127 SHSimulateDrop(pDT, pDataObj, MK_CONTROL|MK_SHIFT, NULL, NULL);
1128
1129 return S_OK;
1130 }
1131
1132 HRESULT CDefaultContextMenu::DoDelete(LPCMINVOKECOMMANDINFO lpcmi) {
1133 TRACE("(%p) Deleting\n", this);
1134
1135 LPDATAOBJECT pDataObj;
1136
1137 if (SUCCEEDED(SHCreateDataObject(m_Dcm.pidlFolder, m_Dcm.cidl, m_Dcm.apidl, NULL, IID_PPV_ARG(IDataObject, &pDataObj))))
1138 {
1139 pDataObj->AddRef();
1140 SHCreateThread(DoDeleteThreadProc, pDataObj, NULL, NULL);
1141 pDataObj->Release();
1142 }
1143 else
1144 return E_FAIL;
1145 return S_OK;
1146
1147 }
1148
1149 HRESULT
1150 CDefaultContextMenu::DoCopyOrCut(
1151 LPCMINVOKECOMMANDINFO lpcmi,
1152 BOOL bCopy)
1153 {
1154 LPDATAOBJECT pDataObj;
1155 HRESULT hr;
1156
1157 if (SUCCEEDED(SHCreateDataObject(m_Dcm.pidlFolder, m_Dcm.cidl, m_Dcm.apidl, NULL, IID_PPV_ARG(IDataObject, &pDataObj))))
1158 {
1159 if (!bCopy)
1160 {
1161 FORMATETC formatetc;
1162 STGMEDIUM medium;
1163 InitFormatEtc(formatetc, RegisterClipboardFormatW(CFSTR_PREFERREDDROPEFFECT), TYMED_HGLOBAL);
1164 pDataObj->GetData(&formatetc, &medium);
1165 DWORD * pdwFlag = (DWORD*)GlobalLock(medium.hGlobal);
1166 if (pdwFlag)
1167 *pdwFlag = DROPEFFECT_MOVE;
1168 GlobalUnlock(medium.hGlobal);
1169 pDataObj->SetData(&formatetc, &medium, TRUE);
1170 }
1171
1172 hr = OleSetClipboard(pDataObj);
1173 pDataObj->Release();
1174 return hr;
1175 }
1176
1177 /* Note: CWM_GETISHELLBROWSER returns not referenced object */
1178 LPSHELLBROWSER lpSB = (LPSHELLBROWSER)SendMessageA(lpcmi->hwnd, CWM_GETISHELLBROWSER, 0, 0);
1179 if (!lpSB)
1180 {
1181 ERR("failed to get shellbrowser\n");
1182 return E_FAIL;
1183 }
1184
1185 LPSHELLVIEW lpSV;
1186 hr = lpSB->QueryActiveShellView(&lpSV);
1187 if (FAILED(hr))
1188 {
1189 ERR("failed to query the active shellview\n");
1190 return hr;
1191 }
1192
1193 hr = lpSV->GetItemObject(SVGIO_SELECTION, IID_IDataObject, (LPVOID*)&pDataObj);
1194 if (SUCCEEDED(hr))
1195 {
1196 hr = OleSetClipboard(pDataObj);
1197 if (FAILED(hr))
1198 ERR("OleSetClipboard failed");
1199 pDataObj->Release();
1200 } else
1201 ERR("failed to get item object\n");
1202
1203 lpSV->Release();
1204 return hr;
1205 }
1206
1207 HRESULT
1208 CDefaultContextMenu::DoRename(
1209 LPCMINVOKECOMMANDINFO lpcmi)
1210 {
1211 /* get the active IShellView. Note: CWM_GETISHELLBROWSER returns not referenced object */
1212 LPSHELLBROWSER lpSB = (LPSHELLBROWSER)SendMessageA(lpcmi->hwnd, CWM_GETISHELLBROWSER, 0, 0);
1213 if (!lpSB)
1214 {
1215 ERR("CWM_GETISHELLBROWSER failed\n");
1216 return E_FAIL;
1217 }
1218
1219 /* is the treeview focused */
1220 HWND hwnd;
1221 if (SUCCEEDED(lpSB->GetControlWindow(FCW_TREE, &hwnd)))
1222 {
1223 HTREEITEM hItem = TreeView_GetSelection(hwnd);
1224 if (hItem)
1225 (void)TreeView_EditLabel(hwnd, hItem);
1226 }
1227
1228 LPSHELLVIEW lpSV;
1229 HRESULT hr = lpSB->QueryActiveShellView(&lpSV);
1230 if (FAILED(hr))
1231 {
1232 ERR("CWM_GETISHELLBROWSER failed\n");
1233 return hr;
1234 }
1235
1236 lpSV->SelectItem(m_Dcm.apidl[0],
1237 SVSI_DESELECTOTHERS | SVSI_EDIT | SVSI_ENSUREVISIBLE | SVSI_FOCUSED | SVSI_SELECT);
1238 lpSV->Release();
1239 return S_OK;
1240 }
1241
1242 HRESULT
1243 CDefaultContextMenu::DoProperties(
1244 LPCMINVOKECOMMANDINFO lpcmi)
1245 {
1246 HRESULT hr = S_OK;
1247 const ITEMIDLIST *pidlParent = m_Dcm.pidlFolder, *pidlChild;
1248
1249 if (!pidlParent)
1250 {
1251 IPersistFolder2 *pf;
1252
1253 /* pidlFolder is optional */
1254 if (SUCCEEDED(m_Dcm.psf->QueryInterface(IID_PPV_ARG(IPersistFolder2, &pf))))
1255 {
1256 pf->GetCurFolder((_ITEMIDLIST**)&pidlParent);
1257 pf->Release();
1258 }
1259 }
1260
1261 if (m_Dcm.cidl > 0)
1262 pidlChild = m_Dcm.apidl[0];
1263 else
1264 {
1265 /* Set pidlChild to last pidl of current folder */
1266 if (pidlParent == m_Dcm.pidlFolder)
1267 pidlParent = (ITEMIDLIST*)ILClone(pidlParent);
1268
1269 pidlChild = (ITEMIDLIST*)ILClone(ILFindLastID(pidlParent));
1270 ILRemoveLastID((ITEMIDLIST*)pidlParent);
1271 }
1272
1273 if (_ILIsMyComputer(pidlChild))
1274 {
1275 if (32 >= (UINT)ShellExecuteW(lpcmi->hwnd, L"open", L"rundll32.exe shell32.dll,Control_RunDLL sysdm.cpl", NULL, NULL, SW_SHOWNORMAL))
1276 hr = E_FAIL;
1277 }
1278 else if (_ILIsDesktop(pidlChild))
1279 {
1280 if (32 >= (UINT)ShellExecuteW(lpcmi->hwnd, L"open", L"rundll32.exe shell32.dll,Control_RunDLL desk.cpl", NULL, NULL, SW_SHOWNORMAL))
1281 hr = E_FAIL;
1282 }
1283 else if (_ILIsDrive(pidlChild))
1284 {
1285 WCHAR wszBuf[MAX_PATH];
1286 ILGetDisplayName(pidlChild, wszBuf);
1287 if (!SH_ShowDriveProperties(wszBuf, pidlParent, &pidlChild))
1288 hr = E_FAIL;
1289 }
1290 else if (_ILIsNetHood(pidlChild))
1291 {
1292 // FIXME path!
1293 if (32 >= (UINT)ShellExecuteW(NULL, L"open", L"explorer.exe",
1294 L"::{7007ACC7-3202-11D1-AAD2-00805FC1270E}",
1295 NULL, SW_SHOWDEFAULT))
1296 hr = E_FAIL;
1297 }
1298 else if (_ILIsBitBucket(pidlChild))
1299 {
1300 /* FIXME: detect the drive path of bitbucket if appropiate */
1301 if(!SH_ShowRecycleBinProperties(L'C'))
1302 hr = E_FAIL;
1303 }
1304 else
1305 {
1306 if (m_Dcm.cidl > 1)
1307 WARN("SHMultiFileProperties is not yet implemented\n");
1308
1309 STRRET strFile;
1310 hr = m_Dcm.psf->GetDisplayNameOf(pidlChild, SHGDN_FORPARSING, &strFile);
1311 if (SUCCEEDED(hr))
1312 {
1313 WCHAR wszBuf[MAX_PATH];
1314 hr = StrRetToBufW(&strFile, pidlChild, wszBuf, _countof(wszBuf));
1315 if (SUCCEEDED(hr))
1316 hr = SH_ShowPropertiesDialog(wszBuf, pidlParent, &pidlChild);
1317 else
1318 ERR("StrRetToBufW failed\n");
1319 }
1320 else
1321 ERR("IShellFolder_GetDisplayNameOf failed for apidl\n");
1322 }
1323
1324 /* Free allocated PIDLs */
1325 if (pidlParent != m_Dcm.pidlFolder)
1326 ILFree((ITEMIDLIST*)pidlParent);
1327 if (m_Dcm.cidl < 1 || pidlChild != m_Dcm.apidl[0])
1328 ILFree((ITEMIDLIST*)pidlChild);
1329
1330 return hr;
1331 }
1332
1333 HRESULT
1334 CDefaultContextMenu::DoFormat(
1335 LPCMINVOKECOMMANDINFO lpcmi)
1336 {
1337 char szDrive[8] = {0};
1338
1339 if (!_ILGetDrive(m_Dcm.apidl[0], szDrive, sizeof(szDrive)))
1340 {
1341 ERR("pidl is not a drive\n");
1342 return E_FAIL;
1343 }
1344
1345 SHFormatDrive(lpcmi->hwnd, szDrive[0] - 'A', SHFMT_ID_DEFAULT, 0);
1346 return S_OK;
1347 }
1348
1349 HRESULT
1350 CDefaultContextMenu::DoDynamicShellExtensions(
1351 LPCMINVOKECOMMANDINFO lpcmi)
1352 {
1353 UINT idCmd = LOWORD(lpcmi->lpVerb);
1354 PDynamicShellEntry pEntry = m_pDynamicEntries;
1355
1356 TRACE("verb %p first %x last %x", lpcmi->lpVerb, m_iIdSHEFirst, m_iIdSHELast);
1357
1358 while(pEntry && idCmd > pEntry->iIdCmdFirst + pEntry->NumIds)
1359 pEntry = pEntry->pNext;
1360
1361 if (!pEntry)
1362 return E_FAIL;
1363
1364 if (idCmd >= pEntry->iIdCmdFirst && idCmd <= pEntry->iIdCmdFirst + pEntry->NumIds)
1365 {
1366 /* invoke the dynamic context menu */
1367 lpcmi->lpVerb = MAKEINTRESOURCEA(idCmd - pEntry->iIdCmdFirst);
1368 return pEntry->pCM->InvokeCommand(lpcmi);
1369 }
1370
1371 return E_FAIL;
1372 }
1373
1374 DWORD
1375 CDefaultContextMenu::BrowserFlagsFromVerb(LPCMINVOKECOMMANDINFO lpcmi, PStaticShellEntry pEntry)
1376 {
1377 LPSHELLBROWSER lpSB;
1378 HWND hwndTree;
1379 LPCWSTR FlagsName;
1380 WCHAR wszKey[256];
1381 HRESULT hr;
1382 DWORD wFlags;
1383 DWORD cbVerb;
1384
1385 /* Get a pointer to the shell browser */
1386 lpSB = (LPSHELLBROWSER)SendMessageA(lpcmi->hwnd, CWM_GETISHELLBROWSER, 0, 0);
1387 if (lpSB == NULL)
1388 return 0;
1389
1390 /* See if we are in Explore or Browse mode. If the browser's tree is present, we are in Explore mode.*/
1391 if (SUCCEEDED(lpSB->GetControlWindow(FCW_TREE, &hwndTree)) && hwndTree)
1392 FlagsName = L"ExplorerFlags";
1393 else
1394 FlagsName = L"BrowserFlags";
1395
1396 /* Try to get the flag from the verb */
1397 hr = StringCbPrintfW(wszKey, sizeof(wszKey), L"%s\\shell\\%s", pEntry->szClass, pEntry->szVerb);
1398 if (!SUCCEEDED(hr))
1399 return 0;
1400
1401 cbVerb = sizeof(wFlags);
1402 if (RegGetValueW(HKEY_CLASSES_ROOT, wszKey, FlagsName, RRF_RT_REG_DWORD, NULL, &wFlags, &cbVerb) == ERROR_SUCCESS)
1403 {
1404 return wFlags;
1405 }
1406
1407 return 0;
1408 }
1409
1410 HRESULT
1411 CDefaultContextMenu::TryToBrowse(
1412 LPCMINVOKECOMMANDINFO lpcmi, LPCITEMIDLIST pidl, DWORD wFlags)
1413 {
1414 LPSHELLBROWSER lpSB = (LPSHELLBROWSER)SendMessageW(lpcmi->hwnd, CWM_GETISHELLBROWSER, 0, 0);
1415 HRESULT hr;
1416
1417 if (lpSB == NULL)
1418 return E_FAIL;
1419
1420 hr = lpSB->BrowseObject(ILCombine(m_Dcm.pidlFolder, pidl), wFlags);
1421
1422 return hr;
1423 }
1424
1425 HRESULT
1426 CDefaultContextMenu::InvokePidl(LPCMINVOKECOMMANDINFO lpcmi, LPCITEMIDLIST pidl, PStaticShellEntry pEntry)
1427 {
1428 LPITEMIDLIST pidlFull = ILCombine(m_Dcm.pidlFolder, pidl);
1429 if (pidlFull == NULL)
1430 {
1431 return E_FAIL;
1432 }
1433
1434 WCHAR wszPath[MAX_PATH];
1435 BOOL bHasPath = SHGetPathFromIDListW(pidlFull, wszPath);
1436
1437 WCHAR wszDir[MAX_PATH];
1438 if(bHasPath)
1439 {
1440 wcscpy(wszDir, wszPath);
1441 PathRemoveFileSpec(wszDir);
1442 }
1443 else
1444 {
1445 SHGetPathFromIDListW(m_Dcm.pidlFolder, wszDir);
1446 }
1447
1448 HKEY hkeyClass;
1449 RegOpenKeyExW(HKEY_CLASSES_ROOT, pEntry->szClass, 0, KEY_READ, &hkeyClass);
1450
1451 SHELLEXECUTEINFOW sei;
1452 ZeroMemory(&sei, sizeof(sei));
1453 sei.cbSize = sizeof(sei);
1454 sei.hwnd = lpcmi->hwnd;
1455 sei.nShow = SW_SHOWNORMAL;
1456 sei.lpVerb = pEntry->szVerb;
1457 sei.lpDirectory = wszDir;
1458 sei.lpIDList = pidlFull;
1459 sei.hkeyClass = hkeyClass;
1460 sei.fMask = SEE_MASK_CLASSKEY | SEE_MASK_IDLIST;
1461 if (bHasPath)
1462 {
1463 sei.lpFile = wszPath;
1464 }
1465
1466 ShellExecuteExW(&sei);
1467
1468 RegCloseKey(hkeyClass);
1469
1470 ILFree(pidlFull);
1471
1472 return S_OK;
1473 }
1474
1475 HRESULT
1476 CDefaultContextMenu::DoStaticShellExtensions(
1477 LPCMINVOKECOMMANDINFO lpcmi)
1478 {
1479 PStaticShellEntry pEntry = m_pStaticEntries;
1480 INT iCmd = LOWORD(lpcmi->lpVerb) - m_iIdSCMFirst;
1481 HRESULT hr;
1482 UINT i;
1483
1484 while (pEntry && (iCmd--) > 0)
1485 pEntry = pEntry->pNext;
1486
1487 if (iCmd > 0)
1488 return E_FAIL;
1489
1490 /* Get the browse flags to see if we need to browse */
1491 DWORD wFlags = BrowserFlagsFromVerb(lpcmi, pEntry);
1492 BOOL bBrowsed = FALSE;
1493
1494 for (i=0; i < m_Dcm.cidl; i++)
1495 {
1496 /* Check if we need to browse */
1497 if (wFlags > 0)
1498 {
1499 /* In xp if we have browsed, we don't open any more folders .
1500 * In win7 we browse to the first folder we find and
1501 * open new windows fo for each of the rest of the folders */
1502 if (bBrowsed)
1503 continue;
1504
1505 hr = TryToBrowse(lpcmi, m_Dcm.apidl[i], wFlags);
1506 if (SUCCEEDED(hr))
1507 {
1508 bBrowsed = TRUE;
1509 continue;
1510 }
1511 }
1512
1513 InvokePidl(lpcmi, m_Dcm.apidl[i], pEntry);
1514 }
1515
1516 return S_OK;
1517 }
1518
1519 HRESULT
1520 WINAPI
1521 CDefaultContextMenu::InvokeCommand(
1522 LPCMINVOKECOMMANDINFO lpcmi)
1523 {
1524 switch(LOWORD(lpcmi->lpVerb))
1525 {
1526 case FCIDM_SHVIEW_BIGICON:
1527 case FCIDM_SHVIEW_SMALLICON:
1528 case FCIDM_SHVIEW_LISTVIEW:
1529 case FCIDM_SHVIEW_REPORTVIEW:
1530 case 0x30: /* FIX IDS in resource files */
1531 case 0x31:
1532 case 0x32:
1533 case 0x33:
1534 case FCIDM_SHVIEW_AUTOARRANGE:
1535 case FCIDM_SHVIEW_SNAPTOGRID:
1536 return NotifyShellViewWindow(lpcmi, FALSE);
1537 case FCIDM_SHVIEW_REFRESH:
1538 return DoRefresh(lpcmi);
1539 case FCIDM_SHVIEW_INSERT:
1540 return DoPaste(lpcmi, FALSE);
1541 case FCIDM_SHVIEW_INSERTLINK:
1542 return DoPaste(lpcmi, TRUE);
1543 case FCIDM_SHVIEW_OPEN:
1544 case FCIDM_SHVIEW_EXPLORE:
1545 return DoOpenOrExplore(lpcmi);
1546 case FCIDM_SHVIEW_COPY:
1547 case FCIDM_SHVIEW_CUT:
1548 return DoCopyOrCut(lpcmi, LOWORD(lpcmi->lpVerb) == FCIDM_SHVIEW_COPY);
1549 case FCIDM_SHVIEW_CREATELINK:
1550 return DoCreateLink(lpcmi);
1551 case FCIDM_SHVIEW_DELETE:
1552 return DoDelete(lpcmi);
1553 case FCIDM_SHVIEW_RENAME:
1554 return DoRename(lpcmi);
1555 case FCIDM_SHVIEW_PROPERTIES:
1556 return DoProperties(lpcmi);
1557 case 0x7ABC:
1558 return DoFormat(lpcmi);
1559 }
1560
1561 if (m_iIdSHEFirst && m_iIdSHELast)
1562 {
1563 if (LOWORD(lpcmi->lpVerb) >= m_iIdSHEFirst && LOWORD(lpcmi->lpVerb) <= m_iIdSHELast)
1564 return DoDynamicShellExtensions(lpcmi);
1565 }
1566
1567 if (m_iIdSCMFirst && m_iIdSCMLast)
1568 {
1569 if (LOWORD(lpcmi->lpVerb) >= m_iIdSCMFirst && LOWORD(lpcmi->lpVerb) <= m_iIdSCMLast)
1570 return DoStaticShellExtensions(lpcmi);
1571 }
1572
1573 FIXME("Unhandled Verb %xl\n", LOWORD(lpcmi->lpVerb));
1574 return E_UNEXPECTED;
1575 }
1576
1577 HRESULT
1578 WINAPI
1579 CDefaultContextMenu::GetCommandString(
1580 UINT_PTR idCommand,
1581 UINT uFlags,
1582 UINT* lpReserved,
1583 LPSTR lpszName,
1584 UINT uMaxNameLen)
1585 {
1586 return S_OK;
1587 }
1588
1589 HRESULT
1590 WINAPI
1591 CDefaultContextMenu::HandleMenuMsg(
1592 UINT uMsg,
1593 WPARAM wParam,
1594 LPARAM lParam)
1595 {
1596 return S_OK;
1597 }
1598
1599 static
1600 HRESULT
1601 IDefaultContextMenu_Constructor(
1602 const DEFCONTEXTMENU *pdcm,
1603 REFIID riid,
1604 void **ppv)
1605 {
1606 if (ppv == NULL)
1607 return E_POINTER;
1608 *ppv = NULL;
1609
1610 CComObject<CDefaultContextMenu> *pCM;
1611 HRESULT hr = CComObject<CDefaultContextMenu>::CreateInstance(&pCM);
1612 if (FAILED(hr))
1613 return hr;
1614 pCM->AddRef(); // CreateInstance returns object with 0 ref count */
1615
1616 CComPtr<IUnknown> pResult;
1617 hr = pCM->QueryInterface(riid, (void **)&pResult);
1618 if (FAILED(hr))
1619 {
1620 pCM->Release();
1621 return hr;
1622 }
1623
1624 hr = pCM->Initialize(pdcm);
1625 if (FAILED(hr))
1626 {
1627 pCM->Release();
1628 return hr;
1629 }
1630
1631 *ppv = pResult.Detach();
1632 pCM->Release();
1633 TRACE("This(%p) cidl %u\n", *ppv, pdcm->cidl);
1634 return S_OK;
1635 }
1636
1637 /*************************************************************************
1638 * SHCreateDefaultContextMenu [SHELL32.325] Vista API
1639 *
1640 */
1641
1642 HRESULT
1643 WINAPI
1644 SHCreateDefaultContextMenu(
1645 const DEFCONTEXTMENU *pdcm,
1646 REFIID riid,
1647 void **ppv)
1648 {
1649 *ppv = NULL;
1650 HRESULT hr = IDefaultContextMenu_Constructor(pdcm, riid, ppv);
1651 if (FAILED(hr))
1652 ERR("IDefaultContextMenu_Constructor failed: %x\n", hr);
1653 TRACE("pcm %p hr %x\n", pdcm, hr);
1654 return hr;
1655 }
1656
1657 /*************************************************************************
1658 * CDefFolderMenu_Create2 [SHELL32.701]
1659 *
1660 */
1661
1662 HRESULT
1663 WINAPI
1664 CDefFolderMenu_Create2(
1665 LPCITEMIDLIST pidlFolder,
1666 HWND hwnd,
1667 UINT cidl,
1668 LPCITEMIDLIST *apidl,
1669 IShellFolder *psf,
1670 LPFNDFMCALLBACK lpfn,
1671 UINT nKeys,
1672 const HKEY *ahkeyClsKeys,
1673 IContextMenu **ppcm)
1674 {
1675 DEFCONTEXTMENU pdcm;
1676 pdcm.hwnd = hwnd;
1677 pdcm.pcmcb = NULL;
1678 pdcm.pidlFolder = pidlFolder;
1679 pdcm.psf = psf;
1680 pdcm.cidl = cidl;
1681 pdcm.apidl = apidl;
1682 pdcm.punkAssociationInfo = NULL;
1683 pdcm.cKeys = nKeys;
1684 pdcm.aKeys = ahkeyClsKeys;
1685
1686 HRESULT hr = SHCreateDefaultContextMenu(&pdcm, IID_PPV_ARG(IContextMenu, ppcm));
1687 return hr;
1688 }
1689