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