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)
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
);
18 WINE_DEFAULT_DEBUG_CHANNEL(dmenu
);
20 typedef struct _DynamicShellEntry_
26 struct _DynamicShellEntry_
*pNext
;
27 } DynamicShellEntry
, *PDynamicShellEntry
;
29 typedef struct _StaticShellEntry_
33 struct _StaticShellEntry_
*pNext
;
34 } StaticShellEntry
, *PStaticShellEntry
;
38 // verbs for InvokeCommandInfo
40 struct _StaticInvokeCommandMap_
44 } g_StaticInvokeCmdMap
[] =
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
}
56 class CDefaultContextMenu
:
57 public CComObjectRootEx
<CComMultiThreadModelNoCS
>,
59 public IObjectWithSite
62 CComPtr
<IUnknown
> m_site
;
63 CComPtr
<IShellFolder
> m_psf
;
64 CComPtr
<IContextMenuCB
> m_pmcb
;
65 LPFNDFMCALLBACK m_pfnmcb
;
67 PCUITEMID_CHILD_ARRAY m_apidl
;
68 CComPtr
<IDataObject
> m_pDataObj
;
71 PIDLIST_ABSOLUTE m_pidlFolder
;
72 DWORD m_bGroupPolicyActive
;
73 PDynamicShellEntry m_pDynamicEntries
; /* first dynamic shell extension entry */
74 UINT m_iIdSHEFirst
; /* first used id */
75 UINT m_iIdSHELast
; /* last used id */
76 PStaticShellEntry m_pStaticEntries
; /* first static shell extension entry */
77 UINT m_iIdSCMFirst
; /* first static used id */
78 UINT m_iIdSCMLast
; /* last static used id */
79 UINT m_iIdCBFirst
; /* first callback used id */
80 UINT m_iIdCBLast
; /* last callback used id */
82 HRESULT
_DoCallback(UINT uMsg
, WPARAM wParam
, LPVOID lParam
);
83 void AddStaticEntry(const HKEY hkeyClass
, const WCHAR
*szVerb
);
84 void AddStaticEntriesForKey(HKEY hKey
);
85 BOOL
IsShellExtensionAlreadyLoaded(const CLSID
*pclsid
);
86 HRESULT
LoadDynamicContextMenuHandler(HKEY hKey
, const CLSID
*pclsid
);
87 BOOL
EnumerateDynamicContextHandlerForKey(HKEY hRootKey
);
88 UINT
InsertMenuItemsOfDynamicContextMenuExtension(HMENU hMenu
, UINT IndexMenu
, UINT idCmdFirst
, UINT idCmdLast
);
89 UINT
AddStaticContextMenusToMenu(HMENU hMenu
, UINT IndexMenu
, UINT iIdCmdFirst
, UINT iIdCmdLast
);
90 HRESULT
DoPaste(LPCMINVOKECOMMANDINFO lpcmi
, BOOL bLink
);
91 HRESULT
DoOpenOrExplore(LPCMINVOKECOMMANDINFO lpcmi
);
92 HRESULT
DoCreateLink(LPCMINVOKECOMMANDINFO lpcmi
);
93 HRESULT
DoDelete(LPCMINVOKECOMMANDINFO lpcmi
);
94 HRESULT
DoCopyOrCut(LPCMINVOKECOMMANDINFO lpcmi
, BOOL bCopy
);
95 HRESULT
DoRename(LPCMINVOKECOMMANDINFO lpcmi
);
96 HRESULT
DoProperties(LPCMINVOKECOMMANDINFO lpcmi
);
97 HRESULT
DoCreateNewFolder(LPCMINVOKECOMMANDINFO lpici
);
98 HRESULT
DoDynamicShellExtensions(LPCMINVOKECOMMANDINFO lpcmi
);
99 HRESULT
DoStaticShellExtensions(LPCMINVOKECOMMANDINFO lpcmi
);
100 DWORD
BrowserFlagsFromVerb(LPCMINVOKECOMMANDINFO lpcmi
, PStaticShellEntry pEntry
);
101 HRESULT
TryToBrowse(LPCMINVOKECOMMANDINFO lpcmi
, LPCITEMIDLIST pidl
, DWORD wFlags
);
102 HRESULT
InvokePidl(LPCMINVOKECOMMANDINFO lpcmi
, LPCITEMIDLIST pidl
, PStaticShellEntry pEntry
);
103 PDynamicShellEntry
GetDynamicEntry(UINT idCmd
);
104 BOOL
MapVerbToCmdId(PVOID Verb
, PUINT idCmd
, BOOL IsUnicode
);
107 CDefaultContextMenu();
108 ~CDefaultContextMenu();
109 HRESULT WINAPI
Initialize(const DEFCONTEXTMENU
*pdcm
, LPFNDFMCALLBACK lpfn
);
112 virtual HRESULT WINAPI
QueryContextMenu(HMENU hMenu
, UINT indexMenu
, UINT idCmdFirst
, UINT idCmdLast
, UINT uFlags
);
113 virtual HRESULT WINAPI
InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi
);
114 virtual HRESULT WINAPI
GetCommandString(UINT_PTR idCommand
, UINT uFlags
, UINT
*lpReserved
, LPSTR lpszName
, UINT uMaxNameLen
);
117 virtual HRESULT WINAPI
HandleMenuMsg(UINT uMsg
, WPARAM wParam
, LPARAM lParam
);
120 virtual HRESULT WINAPI
HandleMenuMsg2(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, LRESULT
*plResult
);
123 virtual HRESULT STDMETHODCALLTYPE
SetSite(IUnknown
*pUnkSite
);
124 virtual HRESULT STDMETHODCALLTYPE
GetSite(REFIID riid
, void **ppvSite
);
126 BEGIN_COM_MAP(CDefaultContextMenu
)
127 COM_INTERFACE_ENTRY_IID(IID_IContextMenu
, IContextMenu
)
128 COM_INTERFACE_ENTRY_IID(IID_IContextMenu2
, IContextMenu2
)
129 COM_INTERFACE_ENTRY_IID(IID_IContextMenu3
, IContextMenu3
)
130 COM_INTERFACE_ENTRY_IID(IID_IObjectWithSite
, IObjectWithSite
)
134 CDefaultContextMenu::CDefaultContextMenu() :
144 m_bGroupPolicyActive(0),
145 m_pDynamicEntries(NULL
),
148 m_pStaticEntries(NULL
),
156 CDefaultContextMenu::~CDefaultContextMenu()
158 /* Free dynamic shell extension entries */
159 PDynamicShellEntry pDynamicEntry
= m_pDynamicEntries
, pNextDynamic
;
160 while (pDynamicEntry
)
162 pNextDynamic
= pDynamicEntry
->pNext
;
163 pDynamicEntry
->pCM
->Release();
164 HeapFree(GetProcessHeap(), 0, pDynamicEntry
);
165 pDynamicEntry
= pNextDynamic
;
168 /* Free static shell extension entries */
169 PStaticShellEntry pStaticEntry
= m_pStaticEntries
, pNextStatic
;
172 pNextStatic
= pStaticEntry
->pNext
;
173 HeapFree(GetProcessHeap(), 0, pStaticEntry
->szVerb
);
174 HeapFree(GetProcessHeap(), 0, pStaticEntry
);
175 pStaticEntry
= pNextStatic
;
178 for (UINT i
= 0; i
< m_cKeys
; i
++)
179 RegCloseKey(m_aKeys
[i
]);
180 HeapFree(GetProcessHeap(), 0, m_aKeys
);
183 CoTaskMemFree(m_pidlFolder
);
184 _ILFreeaPidl(const_cast<PITEMID_CHILD
*>(m_apidl
), m_cidl
);
187 HRESULT WINAPI
CDefaultContextMenu::Initialize(const DEFCONTEXTMENU
*pdcm
, LPFNDFMCALLBACK lpfn
)
189 TRACE("cidl %u\n", pdcm
->cidl
);
191 if (!pdcm
->pcmcb
&& !lpfn
)
193 ERR("CDefaultContextMenu needs a callback!\n");
198 m_apidl
= const_cast<PCUITEMID_CHILD_ARRAY
>(_ILCopyaPidl(pdcm
->apidl
, m_cidl
));
199 if (m_cidl
&& !m_apidl
)
200 return E_OUTOFMEMORY
;
202 m_pmcb
= pdcm
->pcmcb
;
205 m_cKeys
= pdcm
->cKeys
;
208 m_aKeys
= (HKEY
*)HeapAlloc(GetProcessHeap(), 0, sizeof(HKEY
) * pdcm
->cKeys
);
210 return E_OUTOFMEMORY
;
211 memcpy(m_aKeys
, pdcm
->aKeys
, sizeof(HKEY
) * pdcm
->cKeys
);
214 m_psf
->GetUIObjectOf(pdcm
->hwnd
, m_cidl
, m_apidl
, IID_NULL_PPV_ARG(IDataObject
, &m_pDataObj
));
216 if (pdcm
->pidlFolder
)
218 m_pidlFolder
= ILClone(pdcm
->pidlFolder
);
222 CComPtr
<IPersistFolder2
> pf
= NULL
;
223 if (SUCCEEDED(m_psf
->QueryInterface(IID_PPV_ARG(IPersistFolder2
, &pf
))))
225 if (FAILED(pf
->GetCurFolder(reinterpret_cast<LPITEMIDLIST
*>(&m_pidlFolder
))))
226 ERR("GetCurFolder failed\n");
228 TRACE("pidlFolder %p\n", m_pidlFolder
);
234 HRESULT
CDefaultContextMenu::_DoCallback(UINT uMsg
, WPARAM wParam
, LPVOID lParam
)
238 return m_pmcb
->CallBack(m_psf
, NULL
, m_pDataObj
, uMsg
, wParam
, (LPARAM
)lParam
);
242 return m_pfnmcb(m_psf
, NULL
, m_pDataObj
, uMsg
, wParam
, (LPARAM
)lParam
);
248 void CDefaultContextMenu::AddStaticEntry(const HKEY hkeyClass
, const WCHAR
*szVerb
)
250 PStaticShellEntry pEntry
= m_pStaticEntries
, pLastEntry
= NULL
;
253 if (!wcsicmp(pEntry
->szVerb
, szVerb
))
255 /* entry already exists */
259 pEntry
= pEntry
->pNext
;
262 TRACE("adding verb %s\n", debugstr_w(szVerb
));
264 pEntry
= (StaticShellEntry
*)HeapAlloc(GetProcessHeap(), 0, sizeof(StaticShellEntry
));
267 pEntry
->pNext
= NULL
;
268 pEntry
->szVerb
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, (wcslen(szVerb
) + 1) * sizeof(WCHAR
));
270 wcscpy(pEntry
->szVerb
, szVerb
);
271 pEntry
->hkClass
= hkeyClass
;
274 if (!wcsicmp(szVerb
, L
"open"))
276 /* open verb is always inserted in front */
277 pEntry
->pNext
= m_pStaticEntries
;
278 m_pStaticEntries
= pEntry
;
281 pLastEntry
->pNext
= pEntry
;
283 m_pStaticEntries
= pEntry
;
286 void CDefaultContextMenu::AddStaticEntriesForKey(HKEY hKey
)
289 DWORD cchName
, dwIndex
= 0;
292 LRESULT lres
= RegOpenKeyExW(hKey
, L
"shell", 0, KEY_READ
, &hShellKey
);
293 if (lres
!= STATUS_SUCCESS
)
298 cchName
= _countof(wszName
);
299 if (RegEnumKeyExW(hShellKey
, dwIndex
++, wszName
, &cchName
, NULL
, NULL
, NULL
, NULL
) != ERROR_SUCCESS
)
302 AddStaticEntry(hKey
, wszName
);
305 RegCloseKey(hShellKey
);
313 CComPtr
<IDataObject
> pDataObj
;
315 if (SUCCEEDED(OleGetClipboard(&pDataObj
)))
320 TRACE("pDataObj=%p\n", pDataObj
.p
);
322 /* Set the FORMATETC structure*/
323 InitFormatEtc(formatetc
, RegisterClipboardFormatW(CFSTR_SHELLIDLIST
), TYMED_HGLOBAL
);
324 if (SUCCEEDED(pDataObj
->GetData(&formatetc
, &medium
)))
327 ReleaseStgMedium(&medium
);
335 CDefaultContextMenu::IsShellExtensionAlreadyLoaded(const CLSID
*pclsid
)
337 PDynamicShellEntry pEntry
= m_pDynamicEntries
;
341 if (!memcmp(&pEntry
->ClassID
, pclsid
, sizeof(CLSID
)))
343 pEntry
= pEntry
->pNext
;
350 CDefaultContextMenu::LoadDynamicContextMenuHandler(HKEY hKey
, const CLSID
*pclsid
)
354 TRACE("LoadDynamicContextMenuHandler entered with This %p hKey %p pclsid %s\n", this, hKey
, wine_dbgstr_guid(pclsid
));
356 if (IsShellExtensionAlreadyLoaded(pclsid
))
359 CComPtr
<IContextMenu
> pcm
;
360 hr
= SHCoCreateInstance(NULL
, pclsid
, NULL
, IID_PPV_ARG(IContextMenu
, &pcm
));
361 if (FAILED_UNEXPECTEDLY(hr
))
364 CComPtr
<IShellExtInit
> pExtInit
;
365 hr
= pcm
->QueryInterface(IID_PPV_ARG(IShellExtInit
, &pExtInit
));
366 if (FAILED_UNEXPECTEDLY(hr
))
369 hr
= pExtInit
->Initialize(m_pidlFolder
, m_pDataObj
, hKey
);
372 WARN("IShellExtInit::Initialize failed.clsid %s hr 0x%x\n", wine_dbgstr_guid(pclsid
), hr
);
376 PDynamicShellEntry pEntry
= (DynamicShellEntry
*)HeapAlloc(GetProcessHeap(), 0, sizeof(DynamicShellEntry
));
378 return E_OUTOFMEMORY
;
380 pEntry
->iIdCmdFirst
= 0;
381 pEntry
->pNext
= NULL
;
383 pEntry
->pCM
= pcm
.Detach();
384 memcpy(&pEntry
->ClassID
, pclsid
, sizeof(CLSID
));
386 if (m_pDynamicEntries
)
388 PDynamicShellEntry pLastEntry
= m_pDynamicEntries
;
390 while (pLastEntry
->pNext
)
391 pLastEntry
= pLastEntry
->pNext
;
393 pLastEntry
->pNext
= pEntry
;
396 m_pDynamicEntries
= pEntry
;
402 CDefaultContextMenu::EnumerateDynamicContextHandlerForKey(HKEY hRootKey
)
404 WCHAR wszName
[MAX_PATH
], wszBuf
[MAX_PATH
], *pwszClsid
;
409 if (RegOpenKeyExW(hRootKey
, L
"shellex\\ContextMenuHandlers", 0, KEY_READ
, &hKey
) != ERROR_SUCCESS
)
411 TRACE("RegOpenKeyExW failed\n");
418 cchName
= _countof(wszName
);
419 if (RegEnumKeyExW(hKey
, dwIndex
++, wszName
, &cchName
, NULL
, NULL
, NULL
, NULL
) != ERROR_SUCCESS
)
422 /* Key name or key value is CLSID */
424 hr
= CLSIDFromString(wszName
, &clsid
);
429 DWORD cchBuf
= _countof(wszBuf
);
430 if (RegGetValueW(hKey
, wszName
, NULL
, RRF_RT_REG_SZ
, NULL
, wszBuf
, &cchBuf
) == ERROR_SUCCESS
)
431 hr
= CLSIDFromString(wszBuf
, &clsid
);
437 ERR("CLSIDFromString failed for clsid %S hr 0x%x\n", pwszClsid
, hr
);
441 if (m_bGroupPolicyActive
)
443 if (RegGetValueW(HKEY_LOCAL_MACHINE
,
444 L
"Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved",
449 NULL
) != ERROR_SUCCESS
)
451 ERR("Shell extension %s not approved!\n", pwszClsid
);
456 hr
= LoadDynamicContextMenuHandler(hKey
, &clsid
);
458 WARN("Failed to get context menu entires from shell extension! clsid: %S\n", pwszClsid
);
466 CDefaultContextMenu::InsertMenuItemsOfDynamicContextMenuExtension(HMENU hMenu
, UINT IndexMenu
, UINT idCmdFirst
, UINT idCmdLast
)
468 if (!m_pDynamicEntries
)
475 PDynamicShellEntry pEntry
= m_pDynamicEntries
;
476 m_iIdSHEFirst
= idCmdFirst
;
479 HRESULT hr
= pEntry
->pCM
->QueryContextMenu(hMenu
, IndexMenu
++, idCmdFirst
, idCmdLast
, CMF_NORMAL
);
482 pEntry
->iIdCmdFirst
= idCmdFirst
;
483 pEntry
->NumIds
= LOWORD(hr
);
484 IndexMenu
+= pEntry
->NumIds
;
485 idCmdFirst
+= pEntry
->NumIds
+ 0x10;
487 if(idCmdFirst
>= idCmdLast
)
489 /* There is no more room for items */
490 idCmdFirst
= idCmdLast
;
494 TRACE("pEntry %p hr %x contextmenu %p cmdfirst %x num ids %x\n", pEntry
, hr
, pEntry
->pCM
, pEntry
->iIdCmdFirst
, pEntry
->NumIds
);
495 pEntry
= pEntry
->pNext
;
498 m_iIdSHELast
= idCmdFirst
;
499 TRACE("SH_LoadContextMenuHandlers first %x last %x\n", m_iIdSHEFirst
, m_iIdSHELast
);
504 CDefaultContextMenu::AddStaticContextMenusToMenu(
515 mii
.cbSize
= sizeof(mii
);
516 mii
.fMask
= MIIM_ID
| MIIM_TYPE
| MIIM_STATE
| MIIM_DATA
;
517 mii
.fType
= MFT_STRING
;
518 mii
.wID
= iIdCmdFirst
;
519 mii
.dwTypeData
= NULL
;
520 m_iIdSCMFirst
= mii
.wID
;
522 PStaticShellEntry pEntry
= m_pStaticEntries
;
526 fState
= MFS_ENABLED
;
527 mii
.dwTypeData
= NULL
;
529 /* set first entry as default */
530 if (pEntry
== m_pStaticEntries
)
531 fState
|= MFS_DEFAULT
;
533 if (!wcsicmp(pEntry
->szVerb
, L
"open"))
535 /* override default when open verb is found */
536 fState
|= MFS_DEFAULT
;
537 idResource
= IDS_OPEN_VERB
;
539 else if (!wcsicmp(pEntry
->szVerb
, L
"explore"))
540 idResource
= IDS_EXPLORE_VERB
;
541 else if (!wcsicmp(pEntry
->szVerb
, L
"runas"))
542 idResource
= IDS_RUNAS_VERB
;
543 else if (!wcsicmp(pEntry
->szVerb
, L
"edit"))
544 idResource
= IDS_EDIT_VERB
;
545 else if (!wcsicmp(pEntry
->szVerb
, L
"find"))
546 idResource
= IDS_FIND_VERB
;
547 else if (!wcsicmp(pEntry
->szVerb
, L
"print"))
548 idResource
= IDS_PRINT_VERB
;
549 else if (!wcsicmp(pEntry
->szVerb
, L
"printto"))
551 pEntry
= pEntry
->pNext
;
557 /* By default use verb for menu item name */
558 mii
.dwTypeData
= pEntry
->szVerb
;
562 if (LoadStringW(shell32_hInstance
, idResource
, wszVerb
, _countof(wszVerb
)))
563 mii
.dwTypeData
= wszVerb
; /* use translated verb */
565 ERR("Failed to load string\n");
570 HRESULT hr
= StringCbPrintfW(wszKey
, sizeof(wszKey
), L
"shell\\%s", pEntry
->szVerb
);
575 DWORD cbVerb
= sizeof(wszVerb
);
576 LONG res
= RegOpenKeyW(pEntry
->hkClass
, wszKey
, &hkVerb
);
577 if (res
== ERROR_SUCCESS
)
579 res
= RegLoadMUIStringW(hkVerb
,
586 if (res
== ERROR_SUCCESS
)
588 /* use description for the menu entry */
589 mii
.dwTypeData
= wszVerb
;
597 mii
.cch
= wcslen(mii
.dwTypeData
);
599 InsertMenuItemW(hMenu
, IndexMenu
++, TRUE
, &mii
);
602 pEntry
= pEntry
->pNext
;
604 if (mii
.wID
>= iIdCmdLast
)
608 m_iIdSCMLast
= mii
.wID
- 1;
612 void WINAPI
_InsertMenuItemW(
624 ZeroMemory(&mii
, sizeof(mii
));
625 mii
.cbSize
= sizeof(mii
);
626 if (fType
== MFT_SEPARATOR
)
627 mii
.fMask
= MIIM_ID
| MIIM_TYPE
;
628 else if (fType
== MFT_STRING
)
630 mii
.fMask
= MIIM_ID
| MIIM_TYPE
| MIIM_STATE
;
631 if ((ULONG_PTR
)HIWORD((ULONG_PTR
)dwTypeData
) == 0)
633 if (LoadStringW(shell32_hInstance
, LOWORD((ULONG_PTR
)dwTypeData
), wszText
, _countof(wszText
)))
634 mii
.dwTypeData
= wszText
;
637 ERR("failed to load string %p\n", dwTypeData
);
642 mii
.dwTypeData
= (LPWSTR
)dwTypeData
;
648 InsertMenuItemW(hMenu
, indexMenu
, fByPosition
, &mii
);
653 CDefaultContextMenu::QueryContextMenu(
661 UINT idCmdNext
= idCmdFirst
;
663 /* Add a tiny hack to make all the shell happy until we understand how we should handle 0 ids */
667 TRACE("BuildShellItemContextMenu entered\n");
669 /* Load static verbs and shell extensions from registry */
670 for (UINT i
= 0; i
< m_cKeys
; i
++)
672 AddStaticEntriesForKey(m_aKeys
[i
]);
673 EnumerateDynamicContextHandlerForKey(m_aKeys
[i
]);
676 /* Add static context menu handlers */
677 IndexMenu
= AddStaticContextMenusToMenu(hMenu
, IndexMenu
, idCmdNext
, idCmdLast
);
678 if (m_iIdSCMLast
&& m_iIdSCMFirst
> m_iIdSCMLast
)
679 m_iIdSCMLast
= m_iIdSCMFirst
= 0;
680 else if (m_iIdSCMLast
)
681 idCmdNext
= m_iIdSCMLast
+ 1;
683 /* Add dynamic context menu handlers */
684 BOOL bAddSep
= FALSE
;
685 IndexMenu
= InsertMenuItemsOfDynamicContextMenuExtension(hMenu
, IndexMenu
, idCmdNext
, idCmdLast
);
686 if (m_iIdSHELast
&& m_iIdSHELast
!= m_iIdSHEFirst
)
687 idCmdNext
= m_iIdSHELast
+ 1;
689 /* Now let the callback add its own items */
690 QCMINFO qcminfo
= {hMenu
, IndexMenu
, idCmdNext
, idCmdLast
, NULL
};
691 if (SUCCEEDED(_DoCallback(DFM_MERGECONTEXTMENU
, uFlags
, &qcminfo
)))
693 m_iIdCBFirst
= idCmdNext
;
694 m_iIdCBLast
= qcminfo
.idCmdFirst
;
695 idCmdNext
= m_iIdCBLast
+ 1;
698 /* The rest of the items will be added in the end of the menu */
699 IndexMenu
= GetMenuItemCount(hMenu
);
701 if (uFlags
& CMF_VERBSONLY
)
702 return MAKE_HRESULT(SEVERITY_SUCCESS
, 0, idCmdNext
- idCmdFirst
);
704 /* If this is a background context menu we are done */
706 return MAKE_HRESULT(SEVERITY_SUCCESS
, 0, idCmdNext
- idCmdFirst
);
708 /* Get the attributes of the items */
709 SFGAOF rfg
= SFGAO_BROWSABLE
| SFGAO_CANCOPY
| SFGAO_CANLINK
| SFGAO_CANMOVE
| SFGAO_CANDELETE
| SFGAO_CANRENAME
| SFGAO_HASPROPSHEET
| SFGAO_FILESYSTEM
| SFGAO_FOLDER
;
710 hr
= m_psf
->GetAttributesOf(m_cidl
, m_apidl
, &rfg
);
711 if (FAILED_UNEXPECTEDLY(hr
))
712 return MAKE_HRESULT(SEVERITY_SUCCESS
, 0, idCmdNext
- idCmdFirst
);
714 /* Add the standard menu entries based on the attributes of the items */
715 BOOL bClipboardData
= (HasClipboardData() && (rfg
& SFGAO_FILESYSTEM
));
716 if (rfg
& (SFGAO_CANCOPY
| SFGAO_CANMOVE
) || bClipboardData
)
718 _InsertMenuItemW(hMenu
, IndexMenu
++, TRUE
, 0, MFT_SEPARATOR
, NULL
, 0);
719 if (rfg
& SFGAO_CANMOVE
)
720 _InsertMenuItemW(hMenu
, IndexMenu
++, TRUE
, FCIDM_SHVIEW_CUT
, MFT_STRING
, MAKEINTRESOURCEW(IDS_CUT
), MFS_ENABLED
);
721 if (rfg
& SFGAO_CANCOPY
)
722 _InsertMenuItemW(hMenu
, IndexMenu
++, TRUE
, FCIDM_SHVIEW_COPY
, MFT_STRING
, MAKEINTRESOURCEW(IDS_COPY
), MFS_ENABLED
);
724 _InsertMenuItemW(hMenu
, IndexMenu
++, TRUE
, FCIDM_SHVIEW_INSERT
, MFT_STRING
, MAKEINTRESOURCEW(IDS_PASTE
), MFS_ENABLED
);
729 if (rfg
& SFGAO_CANLINK
)
732 _InsertMenuItemW(hMenu
, IndexMenu
++, TRUE
, 0, MFT_SEPARATOR
, NULL
, 0);
733 _InsertMenuItemW(hMenu
, IndexMenu
++, TRUE
, FCIDM_SHVIEW_CREATELINK
, MFT_STRING
, MAKEINTRESOURCEW(IDS_CREATELINK
), MFS_ENABLED
);
736 if (rfg
& SFGAO_CANDELETE
)
741 _InsertMenuItemW(hMenu
, IndexMenu
++, TRUE
, 0, MFT_SEPARATOR
, NULL
, 0);
743 _InsertMenuItemW(hMenu
, IndexMenu
++, TRUE
, FCIDM_SHVIEW_DELETE
, MFT_STRING
, MAKEINTRESOURCEW(IDS_DELETE
), MFS_ENABLED
);
746 if (rfg
& SFGAO_CANRENAME
)
750 _InsertMenuItemW(hMenu
, IndexMenu
++, TRUE
, 0, MFT_SEPARATOR
, NULL
, 0);
752 _InsertMenuItemW(hMenu
, IndexMenu
++, TRUE
, FCIDM_SHVIEW_RENAME
, MFT_STRING
, MAKEINTRESOURCEW(IDS_RENAME
), MFS_ENABLED
);
756 if (rfg
& SFGAO_HASPROPSHEET
)
758 _InsertMenuItemW(hMenu
, IndexMenu
++, TRUE
, 0, MFT_SEPARATOR
, NULL
, 0);
759 _InsertMenuItemW(hMenu
, IndexMenu
++, TRUE
, FCIDM_SHVIEW_PROPERTIES
, MFT_STRING
, MAKEINTRESOURCEW(IDS_PROPERTIES
), MFS_ENABLED
);
765 HRESULT
CDefaultContextMenu::DoPaste(LPCMINVOKECOMMANDINFO lpcmi
, BOOL bLink
)
769 CComPtr
<IDataObject
> pda
;
770 hr
= OleGetClipboard(&pda
);
771 if (FAILED_UNEXPECTEDLY(hr
))
774 FORMATETC formatetc2
;
776 InitFormatEtc(formatetc2
, RegisterClipboardFormatW(CFSTR_PREFERREDDROPEFFECT
), TYMED_HGLOBAL
);
780 if (SUCCEEDED(pda
->GetData(&formatetc2
, &medium2
)))
782 DWORD
* pdwFlag
= (DWORD
*)GlobalLock(medium2
.hGlobal
);
785 if (*pdwFlag
== DROPEFFECT_COPY
)
791 ERR("No drop effect obtained");
793 GlobalUnlock(medium2
.hGlobal
);
798 dwKey
= MK_CONTROL
|MK_SHIFT
;
801 CComPtr
<IDropTarget
> pdrop
;
803 hr
= m_psf
->GetUIObjectOf(NULL
, 1, &m_apidl
[0], IID_NULL_PPV_ARG(IDropTarget
, &pdrop
));
805 hr
= m_psf
->CreateViewObject(NULL
, IID_PPV_ARG(IDropTarget
, &pdrop
));
807 if (FAILED_UNEXPECTEDLY(hr
))
810 SHSimulateDrop(pdrop
, pda
, dwKey
, NULL
, NULL
);
812 TRACE("CP result %x\n", hr
);
817 CDefaultContextMenu::DoOpenOrExplore(LPCMINVOKECOMMANDINFO lpcmi
)
823 HRESULT
CDefaultContextMenu::DoCreateLink(LPCMINVOKECOMMANDINFO lpcmi
)
825 if (!m_cidl
|| !m_pDataObj
)
828 CComPtr
<IDropTarget
> pDT
;
829 HRESULT hr
= m_psf
->CreateViewObject(NULL
, IID_PPV_ARG(IDropTarget
, &pDT
));
830 if (FAILED_UNEXPECTEDLY(hr
))
833 SHSimulateDrop(pDT
, m_pDataObj
, MK_CONTROL
|MK_SHIFT
, NULL
, NULL
);
838 HRESULT
CDefaultContextMenu::DoDelete(LPCMINVOKECOMMANDINFO lpcmi
)
840 if (!m_cidl
|| !m_pDataObj
)
843 DoDeleteAsync(m_pDataObj
, lpcmi
->fMask
);
847 HRESULT
CDefaultContextMenu::DoCopyOrCut(LPCMINVOKECOMMANDINFO lpcmi
, BOOL bCopy
)
849 if (!m_cidl
|| !m_pDataObj
)
856 InitFormatEtc(formatetc
, RegisterClipboardFormatW(CFSTR_PREFERREDDROPEFFECT
), TYMED_HGLOBAL
);
857 m_pDataObj
->GetData(&formatetc
, &medium
);
858 DWORD
* pdwFlag
= (DWORD
*)GlobalLock(medium
.hGlobal
);
860 *pdwFlag
= DROPEFFECT_MOVE
;
861 GlobalUnlock(medium
.hGlobal
);
862 m_pDataObj
->SetData(&formatetc
, &medium
, TRUE
);
865 HRESULT hr
= OleSetClipboard(m_pDataObj
);
866 if (FAILED_UNEXPECTEDLY(hr
))
872 HRESULT
CDefaultContextMenu::DoRename(LPCMINVOKECOMMANDINFO lpcmi
)
874 CComPtr
<IShellBrowser
> psb
;
877 if (!m_site
|| !m_cidl
)
880 /* Get a pointer to the shell browser */
881 hr
= IUnknown_QueryService(m_site
, SID_IShellBrowser
, IID_PPV_ARG(IShellBrowser
, &psb
));
882 if (FAILED_UNEXPECTEDLY(hr
))
885 CComPtr
<IShellView
> lpSV
;
886 hr
= psb
->QueryActiveShellView(&lpSV
);
887 if (FAILED_UNEXPECTEDLY(hr
))
890 SVSIF selFlags
= SVSI_DESELECTOTHERS
| SVSI_EDIT
| SVSI_ENSUREVISIBLE
| SVSI_FOCUSED
| SVSI_SELECT
;
891 hr
= lpSV
->SelectItem(m_apidl
[0], selFlags
);
892 if (FAILED_UNEXPECTEDLY(hr
))
899 CDefaultContextMenu::DoProperties(
900 LPCMINVOKECOMMANDINFO lpcmi
)
902 _DoCallback(DFM_INVOKECOMMAND
, DFM_CMD_PROPERTIES
, NULL
);
907 // This code is taken from CNewMenu and should be shared between the 2 classes
909 CDefaultContextMenu::DoCreateNewFolder(
910 LPCMINVOKECOMMANDINFO lpici
)
912 WCHAR wszPath
[MAX_PATH
];
913 WCHAR wszName
[MAX_PATH
];
914 WCHAR wszNewFolder
[25];
917 /* Get folder path */
918 hr
= SHGetPathFromIDListW(m_pidlFolder
, wszPath
);
919 if (FAILED_UNEXPECTEDLY(hr
))
922 if (!LoadStringW(shell32_hInstance
, IDS_NEWFOLDER
, wszNewFolder
, _countof(wszNewFolder
)))
925 /* Create the name of the new directory */
926 if (!PathYetAnotherMakeUniqueName(wszName
, wszPath
, NULL
, wszNewFolder
))
929 /* Create the new directory and show the appropriate dialog in case of error */
930 if (SHCreateDirectory(lpici
->hwnd
, wszName
) != ERROR_SUCCESS
)
933 /* Show and select the new item in the def view */
935 PITEMID_CHILD pidlNewItem
;
936 CComPtr
<IShellView
> psv
;
938 /* Notify the view object about the new item */
939 SHChangeNotify(SHCNE_MKDIR
, SHCNF_PATHW
, (LPCVOID
)wszName
, NULL
);
944 /* Get a pointer to the shell view */
945 hr
= IUnknown_QueryService(m_site
, SID_IFolderView
, IID_PPV_ARG(IShellView
, &psv
));
946 if (FAILED_UNEXPECTEDLY(hr
))
949 /* Attempt to get the pidl of the new item */
950 hr
= SHILCreateFromPathW(wszName
, &pidl
, NULL
);
951 if (FAILED_UNEXPECTEDLY(hr
))
954 pidlNewItem
= ILFindLastID(pidl
);
956 hr
= psv
->SelectItem(pidlNewItem
, SVSI_DESELECTOTHERS
| SVSI_EDIT
| SVSI_ENSUREVISIBLE
|
957 SVSI_FOCUSED
| SVSI_SELECT
);
958 if (FAILED_UNEXPECTEDLY(hr
))
966 PDynamicShellEntry
CDefaultContextMenu::GetDynamicEntry(UINT idCmd
)
968 PDynamicShellEntry pEntry
= m_pDynamicEntries
;
970 while(pEntry
&& idCmd
> pEntry
->iIdCmdFirst
+ pEntry
->NumIds
)
971 pEntry
= pEntry
->pNext
;
976 if (idCmd
< pEntry
->iIdCmdFirst
|| idCmd
> pEntry
->iIdCmdFirst
+ pEntry
->NumIds
)
982 // FIXME: 260 is correct, but should this be part of the SDK or just MAX_PATH?
986 CDefaultContextMenu::MapVerbToCmdId(PVOID Verb
, PUINT idCmd
, BOOL IsUnicode
)
988 WCHAR UnicodeStr
[MAX_VERB
];
990 /* Loop through all the static verbs looking for a match */
991 for (UINT i
= 0; i
< _countof(g_StaticInvokeCmdMap
); i
++)
993 /* We can match both ANSI and unicode strings */
996 /* The static verbs are ANSI, get a unicode version before doing the compare */
997 SHAnsiToUnicode(g_StaticInvokeCmdMap
[i
].szStringVerb
, UnicodeStr
, MAX_VERB
);
998 if (!wcscmp(UnicodeStr
, (LPWSTR
)Verb
))
1000 /* Return the Corresponding Id */
1001 *idCmd
= g_StaticInvokeCmdMap
[i
].IntVerb
;
1007 if (!strcmp(g_StaticInvokeCmdMap
[i
].szStringVerb
, (LPSTR
)Verb
))
1009 *idCmd
= g_StaticInvokeCmdMap
[i
].IntVerb
;
1019 CDefaultContextMenu::DoDynamicShellExtensions(
1020 LPCMINVOKECOMMANDINFO lpcmi
)
1022 TRACE("verb %p first %x last %x", lpcmi
->lpVerb
, m_iIdSHEFirst
, m_iIdSHELast
);
1024 UINT idCmd
= LOWORD(lpcmi
->lpVerb
);
1025 PDynamicShellEntry pEntry
= GetDynamicEntry(idCmd
);
1029 /* invoke the dynamic context menu */
1030 lpcmi
->lpVerb
= MAKEINTRESOURCEA(idCmd
- pEntry
->iIdCmdFirst
);
1031 return pEntry
->pCM
->InvokeCommand(lpcmi
);
1035 CDefaultContextMenu::BrowserFlagsFromVerb(LPCMINVOKECOMMANDINFO lpcmi
, PStaticShellEntry pEntry
)
1037 CComPtr
<IShellBrowser
> psb
;
1048 /* Get a pointer to the shell browser */
1049 hr
= IUnknown_QueryService(m_site
, SID_IShellBrowser
, IID_PPV_ARG(IShellBrowser
, &psb
));
1050 if (FAILED_UNEXPECTEDLY(hr
))
1053 /* See if we are in Explore or Browse mode. If the browser's tree is present, we are in Explore mode.*/
1054 if (SUCCEEDED(psb
->GetControlWindow(FCW_TREE
, &hwndTree
)) && hwndTree
)
1055 FlagsName
= L
"ExplorerFlags";
1057 FlagsName
= L
"BrowserFlags";
1059 /* Try to get the flag from the verb */
1060 hr
= StringCbPrintfW(wszKey
, sizeof(wszKey
), L
"shell\\%s", pEntry
->szVerb
);
1061 if (FAILED_UNEXPECTEDLY(hr
))
1064 cbVerb
= sizeof(wFlags
);
1065 if (RegGetValueW(pEntry
->hkClass
, wszKey
, FlagsName
, RRF_RT_REG_DWORD
, NULL
, &wFlags
, &cbVerb
) == ERROR_SUCCESS
)
1074 CDefaultContextMenu::TryToBrowse(
1075 LPCMINVOKECOMMANDINFO lpcmi
, LPCITEMIDLIST pidl
, DWORD wFlags
)
1077 CComPtr
<IShellBrowser
> psb
;
1083 /* Get a pointer to the shell browser */
1084 hr
= IUnknown_QueryService(m_site
, SID_IShellBrowser
, IID_PPV_ARG(IShellBrowser
, &psb
));
1085 if (FAILED_UNEXPECTEDLY(hr
))
1088 return psb
->BrowseObject(ILCombine(m_pidlFolder
, pidl
), wFlags
);
1092 CDefaultContextMenu::InvokePidl(LPCMINVOKECOMMANDINFO lpcmi
, LPCITEMIDLIST pidl
, PStaticShellEntry pEntry
)
1094 LPITEMIDLIST pidlFull
= ILCombine(m_pidlFolder
, pidl
);
1095 if (pidlFull
== NULL
)
1100 WCHAR wszPath
[MAX_PATH
];
1101 BOOL bHasPath
= SHGetPathFromIDListW(pidlFull
, wszPath
);
1103 WCHAR wszDir
[MAX_PATH
];
1106 wcscpy(wszDir
, wszPath
);
1107 PathRemoveFileSpec(wszDir
);
1111 SHGetPathFromIDListW(m_pidlFolder
, wszDir
);
1114 SHELLEXECUTEINFOW sei
;
1115 ZeroMemory(&sei
, sizeof(sei
));
1116 sei
.cbSize
= sizeof(sei
);
1117 sei
.hwnd
= lpcmi
->hwnd
;
1118 sei
.nShow
= SW_SHOWNORMAL
;
1119 sei
.lpVerb
= pEntry
->szVerb
;
1120 sei
.lpDirectory
= wszDir
;
1121 sei
.lpIDList
= pidlFull
;
1122 sei
.hkeyClass
= pEntry
->hkClass
;
1123 sei
.fMask
= SEE_MASK_CLASSKEY
| SEE_MASK_IDLIST
;
1126 sei
.lpFile
= wszPath
;
1129 ShellExecuteExW(&sei
);
1137 CDefaultContextMenu::DoStaticShellExtensions(
1138 LPCMINVOKECOMMANDINFO lpcmi
)
1140 PStaticShellEntry pEntry
= m_pStaticEntries
;
1141 INT iCmd
= LOWORD(lpcmi
->lpVerb
) - m_iIdSCMFirst
;
1145 while (pEntry
&& (iCmd
--) > 0)
1146 pEntry
= pEntry
->pNext
;
1151 /* Get the browse flags to see if we need to browse */
1152 DWORD wFlags
= BrowserFlagsFromVerb(lpcmi
, pEntry
);
1153 BOOL bBrowsed
= FALSE
;
1155 for (i
=0; i
< m_cidl
; i
++)
1157 /* Check if we need to browse */
1160 /* In xp if we have browsed, we don't open any more folders.
1161 * In win7 we browse to the first folder we find and
1162 * open new windows for each of the rest of the folders */
1166 hr
= TryToBrowse(lpcmi
, m_apidl
[i
], wFlags
);
1174 InvokePidl(lpcmi
, m_apidl
[i
], pEntry
);
1182 CDefaultContextMenu::InvokeCommand(
1183 LPCMINVOKECOMMANDINFO lpcmi
)
1185 CMINVOKECOMMANDINFO LocalInvokeInfo
;
1189 /* Take a local copy of the fixed members of the
1190 struct as we might need to modify the verb */
1191 LocalInvokeInfo
= *lpcmi
;
1193 /* Check if this is a string verb */
1194 if (HIWORD(LocalInvokeInfo
.lpVerb
))
1196 /* Get the ID which corresponds to this verb, and update our local copy */
1197 if (MapVerbToCmdId((LPVOID
)LocalInvokeInfo
.lpVerb
, &CmdId
, FALSE
))
1198 LocalInvokeInfo
.lpVerb
= MAKEINTRESOURCEA(CmdId
);
1201 /* Check if this is a Id */
1202 switch (LOWORD(LocalInvokeInfo
.lpVerb
))
1204 case FCIDM_SHVIEW_INSERT
:
1205 Result
= DoPaste(&LocalInvokeInfo
, FALSE
);
1207 case FCIDM_SHVIEW_INSERTLINK
:
1208 Result
= DoPaste(&LocalInvokeInfo
, TRUE
);
1210 case FCIDM_SHVIEW_OPEN
:
1211 case FCIDM_SHVIEW_EXPLORE
:
1212 Result
= DoOpenOrExplore(&LocalInvokeInfo
);
1214 case FCIDM_SHVIEW_COPY
:
1215 case FCIDM_SHVIEW_CUT
:
1216 Result
= DoCopyOrCut(&LocalInvokeInfo
, LOWORD(LocalInvokeInfo
.lpVerb
) == FCIDM_SHVIEW_COPY
);
1218 case FCIDM_SHVIEW_CREATELINK
:
1219 Result
= DoCreateLink(&LocalInvokeInfo
);
1221 case FCIDM_SHVIEW_DELETE
:
1222 Result
= DoDelete(&LocalInvokeInfo
);
1224 case FCIDM_SHVIEW_RENAME
:
1225 Result
= DoRename(&LocalInvokeInfo
);
1227 case FCIDM_SHVIEW_PROPERTIES
:
1228 Result
= DoProperties(&LocalInvokeInfo
);
1230 case FCIDM_SHVIEW_NEWFOLDER
:
1231 Result
= DoCreateNewFolder(&LocalInvokeInfo
);
1234 Result
= E_UNEXPECTED
;
1238 /* Check for ID's we didn't find a handler for */
1239 if (Result
== E_UNEXPECTED
)
1241 if (m_pDynamicEntries
)
1243 if (LOWORD(LocalInvokeInfo
.lpVerb
) >= m_iIdSHEFirst
&& LOWORD(LocalInvokeInfo
.lpVerb
) <= m_iIdSHELast
)
1244 Result
= DoDynamicShellExtensions(&LocalInvokeInfo
);
1247 if (m_pStaticEntries
)
1249 if (LOWORD(LocalInvokeInfo
.lpVerb
) >= m_iIdSCMFirst
&& LOWORD(LocalInvokeInfo
.lpVerb
) <= m_iIdSCMLast
)
1250 Result
= DoStaticShellExtensions(&LocalInvokeInfo
);
1253 if (m_iIdCBFirst
!= m_iIdCBLast
)
1255 if (LOWORD(LocalInvokeInfo
.lpVerb
) >= m_iIdCBFirst
&& LOWORD(LocalInvokeInfo
.lpVerb
) <= m_iIdCBLast
)
1256 Result
= _DoCallback(DFM_INVOKECOMMAND
, LOWORD(LocalInvokeInfo
.lpVerb
), NULL
);
1260 if (Result
== E_UNEXPECTED
)
1261 ERR("Unhandled Verb %xl\n", LOWORD(LocalInvokeInfo
.lpVerb
));
1268 CDefaultContextMenu::GetCommandString(
1275 /* We don't handle the help text yet */
1276 if (uFlags
== GCS_HELPTEXTA
||
1277 uFlags
== GCS_HELPTEXTW
)
1282 /* Loop looking for a matching Id */
1283 for (UINT i
= 0; i
< _countof(g_StaticInvokeCmdMap
); i
++)
1285 if (g_StaticInvokeCmdMap
[i
].IntVerb
== idCommand
)
1287 /* Validation just returns S_OK on a match */
1288 if (uFlags
== GCS_VALIDATEA
|| uFlags
== GCS_VALIDATEW
)
1291 /* Return a copy of the ANSI verb */
1292 if (uFlags
== GCS_VERBA
)
1293 return StringCchCopyA(lpszName
, uMaxNameLen
, g_StaticInvokeCmdMap
[i
].szStringVerb
);
1295 /* Convert the ANSI verb to unicode and return that */
1296 if (uFlags
== GCS_VERBW
)
1298 if (SHAnsiToUnicode(g_StaticInvokeCmdMap
[i
].szStringVerb
, (LPWSTR
)lpszName
, uMaxNameLen
))
1304 return E_INVALIDARG
;
1309 CDefaultContextMenu::HandleMenuMsg(
1314 /* FIXME: Should we implement this as well? */
1320 CDefaultContextMenu::HandleMenuMsg2(
1328 case WM_INITMENUPOPUP
:
1330 PDynamicShellEntry pEntry
= m_pDynamicEntries
;
1333 SHForwardContextMenuMsg(pEntry
->pCM
, uMsg
, wParam
, lParam
, plResult
, TRUE
);
1334 pEntry
= pEntry
->pNext
;
1340 DRAWITEMSTRUCT
* pDrawStruct
= reinterpret_cast<DRAWITEMSTRUCT
*>(lParam
);
1341 PDynamicShellEntry pEntry
= GetDynamicEntry(pDrawStruct
->itemID
);
1343 SHForwardContextMenuMsg(pEntry
->pCM
, uMsg
, wParam
, lParam
, plResult
, TRUE
);
1346 case WM_MEASUREITEM
:
1348 MEASUREITEMSTRUCT
* pMeasureStruct
= reinterpret_cast<MEASUREITEMSTRUCT
*>(lParam
);
1349 PDynamicShellEntry pEntry
= GetDynamicEntry(pMeasureStruct
->itemID
);
1351 SHForwardContextMenuMsg(pEntry
->pCM
, uMsg
, wParam
, lParam
, plResult
, TRUE
);
1358 ERR("Got unknown message:%d\n", uMsg
);
1365 CDefaultContextMenu::SetSite(IUnknown
*pUnkSite
)
1373 CDefaultContextMenu::GetSite(REFIID riid
, void **ppvSite
)
1378 return m_site
->QueryInterface(riid
, ppvSite
);
1383 CDefaultContextMenu_CreateInstance(const DEFCONTEXTMENU
*pdcm
, LPFNDFMCALLBACK lpfn
, REFIID riid
, void **ppv
)
1385 return ShellObjectCreatorInit
<CDefaultContextMenu
>(pdcm
, lpfn
, riid
, ppv
);
1388 /*************************************************************************
1389 * SHCreateDefaultContextMenu [SHELL32.325] Vista API
1395 SHCreateDefaultContextMenu(const DEFCONTEXTMENU
*pdcm
, REFIID riid
, void **ppv
)
1397 HRESULT hr
= CDefaultContextMenu_CreateInstance(pdcm
, NULL
, riid
, ppv
);
1398 if (FAILED_UNEXPECTEDLY(hr
))
1404 /*************************************************************************
1405 * CDefFolderMenu_Create2 [SHELL32.701]
1411 CDefFolderMenu_Create2(
1412 PCIDLIST_ABSOLUTE pidlFolder
,
1415 PCUITEMID_CHILD_ARRAY apidl
,
1417 LPFNDFMCALLBACK lpfn
,
1419 const HKEY
*ahkeyClsKeys
,
1420 IContextMenu
**ppcm
)
1425 dcm
.pidlFolder
= pidlFolder
;
1429 dcm
.punkAssociationInfo
= NULL
;
1431 dcm
.aKeys
= ahkeyClsKeys
;
1433 HRESULT hr
= CDefaultContextMenu_CreateInstance(&dcm
, lpfn
, IID_PPV_ARG(IContextMenu
, ppcm
));
1434 if (FAILED_UNEXPECTEDLY(hr
))