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