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