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