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