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