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