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
;
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 */
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
);
106 CDefaultContextMenu();
107 ~CDefaultContextMenu();
108 HRESULT WINAPI
Initialize(const DEFCONTEXTMENU
*pdcm
);
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
);
116 virtual HRESULT WINAPI
HandleMenuMsg(UINT uMsg
, WPARAM wParam
, LPARAM lParam
);
119 virtual HRESULT WINAPI
HandleMenuMsg2(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, LRESULT
*plResult
);
122 virtual HRESULT STDMETHODCALLTYPE
SetSite(IUnknown
*pUnkSite
);
123 virtual HRESULT STDMETHODCALLTYPE
GetSite(REFIID riid
, void **ppvSite
);
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
)
133 CDefaultContextMenu::CDefaultContextMenu() :
139 m_bGroupPolicyActive(0),
140 m_pDynamicEntries(NULL
),
143 m_pStaticEntries(NULL
),
149 CDefaultContextMenu::~CDefaultContextMenu()
151 /* Free dynamic shell extension entries */
152 PDynamicShellEntry pDynamicEntry
= m_pDynamicEntries
, pNextDynamic
;
153 while (pDynamicEntry
)
155 pNextDynamic
= pDynamicEntry
->pNext
;
156 pDynamicEntry
->pCM
->Release();
157 HeapFree(GetProcessHeap(), 0, pDynamicEntry
);
158 pDynamicEntry
= pNextDynamic
;
161 /* Free static shell extension entries */
162 PStaticShellEntry pStaticEntry
= m_pStaticEntries
, pNextStatic
;
165 pNextStatic
= pStaticEntry
->pNext
;
166 HeapFree(GetProcessHeap(), 0, pStaticEntry
->szClass
);
167 HeapFree(GetProcessHeap(), 0, pStaticEntry
->szVerb
);
168 HeapFree(GetProcessHeap(), 0, pStaticEntry
);
169 pStaticEntry
= pNextStatic
;
173 CoTaskMemFree(m_pidlFolder
);
174 _ILFreeaPidl(const_cast<PITEMID_CHILD
*>(m_apidl
), m_cidl
);
177 HRESULT WINAPI
CDefaultContextMenu::Initialize(const DEFCONTEXTMENU
*pdcm
)
179 CComPtr
<IDataObject
> pDataObj
;
181 TRACE("cidl %u\n", 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
;
189 if (SUCCEEDED(SHCreateDataObject(pdcm
->pidlFolder
, pdcm
->cidl
, pdcm
->apidl
, NULL
, IID_PPV_ARG(IDataObject
, &pDataObj
))))
190 m_pDataObj
= pDataObj
;
192 if (pdcm
->pidlFolder
)
194 m_pidlFolder
= ILClone(pdcm
->pidlFolder
);
198 CComPtr
<IPersistFolder2
> pf
= NULL
;
199 if (SUCCEEDED(m_psf
->QueryInterface(IID_PPV_ARG(IPersistFolder2
, &pf
))))
201 if (FAILED(pf
->GetCurFolder(reinterpret_cast<LPITEMIDLIST
*>(&m_pidlFolder
))))
202 ERR("GetCurFolder failed\n");
204 TRACE("pidlFolder %p\n", m_pidlFolder
);
211 CDefaultContextMenu::AddStaticEntry(const WCHAR
*szVerb
, const WCHAR
*szClass
)
213 PStaticShellEntry pEntry
= m_pStaticEntries
, pLastEntry
= NULL
;
216 if (!wcsicmp(pEntry
->szVerb
, szVerb
))
218 /* entry already exists */
222 pEntry
= pEntry
->pNext
;
225 TRACE("adding verb %s szClass %s\n", debugstr_w(szVerb
), debugstr_w(szClass
));
227 pEntry
= (StaticShellEntry
*)HeapAlloc(GetProcessHeap(), 0, sizeof(StaticShellEntry
));
230 pEntry
->pNext
= NULL
;
231 pEntry
->szVerb
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, (wcslen(szVerb
) + 1) * sizeof(WCHAR
));
233 wcscpy(pEntry
->szVerb
, szVerb
);
234 pEntry
->szClass
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, (wcslen(szClass
) + 1) * sizeof(WCHAR
));
236 wcscpy(pEntry
->szClass
, szClass
);
239 if (!wcsicmp(szVerb
, L
"open"))
241 /* open verb is always inserted in front */
242 pEntry
->pNext
= m_pStaticEntries
;
243 m_pStaticEntries
= pEntry
;
246 pLastEntry
->pNext
= pEntry
;
248 m_pStaticEntries
= pEntry
;
252 CDefaultContextMenu::AddStaticEntryForKey(HKEY hKey
, const WCHAR
*pwszClass
)
255 DWORD cchName
, dwIndex
= 0;
257 TRACE("AddStaticEntryForKey %x %ls\n", hKey
, pwszClass
);
261 cchName
= _countof(wszName
);
262 if (RegEnumKeyExW(hKey
, dwIndex
++, wszName
, &cchName
, NULL
, NULL
, NULL
, NULL
) != ERROR_SUCCESS
)
265 AddStaticEntry(wszName
, pwszClass
);
270 CDefaultContextMenu::AddStaticEntryForFileClass(const WCHAR
* szExt
)
277 static WCHAR szShell
[] = L
"\\shell";
278 static WCHAR szShellAssoc
[] = L
"SystemFileAssociations\\";
280 TRACE("AddStaticEntryForFileClass entered with %s\n", debugstr_w(szExt
));
282 Length
= wcslen(szExt
);
283 if (Length
+ (sizeof(szShell
) / sizeof(WCHAR
)) + 1 < sizeof(szBuffer
) / sizeof(WCHAR
))
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
)
290 szBuffer
[Length
] = 0;
291 AddStaticEntryForKey(hKey
, szExt
);
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
)
300 Length
= wcslen(szBuffer
);
301 if (Length
+ (sizeof(szShell
) / sizeof(WCHAR
)) + 1 < sizeof(szBuffer
) / sizeof(WCHAR
))
303 wcscpy(&szBuffer
[Length
], szShell
);
304 TRACE("szBuffer %s\n", debugstr_w(szBuffer
));
306 result
= RegOpenKeyExW(HKEY_CLASSES_ROOT
, szBuffer
, 0, KEY_READ
| KEY_QUERY_VALUE
, &hKey
);
307 if (result
== ERROR_SUCCESS
)
309 szBuffer
[Length
] = 0;
310 AddStaticEntryForKey(hKey
, szBuffer
);
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
)
321 Length
= wcslen(&szBuffer
[_countof(szShellAssoc
)]) + _countof(szShellAssoc
);
322 wcscat(szBuffer
, L
"\\shell");
323 TRACE("szBuffer %s\n", debugstr_w(szBuffer
));
325 result
= RegOpenKeyExW(HKEY_CLASSES_ROOT
, szBuffer
, 0, KEY_READ
| KEY_QUERY_VALUE
, &hKey
);
326 if (result
== ERROR_SUCCESS
)
328 szBuffer
[Length
] = 0;
329 AddStaticEntryForKey(hKey
, szBuffer
);
340 CComPtr
<IDataObject
> pDataObj
;
342 if (SUCCEEDED(OleGetClipboard(&pDataObj
)))
347 TRACE("pDataObj=%p\n", pDataObj
.p
);
349 /* Set the FORMATETC structure*/
350 InitFormatEtc(formatetc
, RegisterClipboardFormatW(CFSTR_SHELLIDLIST
), TYMED_HGLOBAL
);
351 if (SUCCEEDED(pDataObj
->GetData(&formatetc
, &medium
)))
354 ReleaseStgMedium(&medium
);
363 DisablePasteOptions(HMENU hMenu
)
367 mii
.cbSize
= sizeof(mii
);
368 mii
.fMask
= MIIM_STATE
;
369 mii
.fState
= MFS_DISABLED
;
371 SetMenuItemInfoW(hMenu
, FCIDM_SHVIEW_INSERT
, FALSE
, &mii
);
372 SetMenuItemInfoW(hMenu
, FCIDM_SHVIEW_INSERTLINK
, FALSE
, &mii
);
376 CDefaultContextMenu::IsShellExtensionAlreadyLoaded(const CLSID
*pclsid
)
378 PDynamicShellEntry pEntry
= m_pDynamicEntries
;
382 if (!memcmp(&pEntry
->ClassID
, pclsid
, sizeof(CLSID
)))
384 pEntry
= pEntry
->pNext
;
391 CDefaultContextMenu::LoadDynamicContextMenuHandler(HKEY hKey
, const CLSID
*pclsid
)
395 TRACE("LoadDynamicContextMenuHandler entered with This %p hKey %p pclsid %s\n", this, hKey
, wine_dbgstr_guid(pclsid
));
397 if (IsShellExtensionAlreadyLoaded(pclsid
))
400 CComPtr
<IContextMenu
> pcm
;
401 hr
= SHCoCreateInstance(NULL
, pclsid
, NULL
, IID_PPV_ARG(IContextMenu
, &pcm
));
404 ERR("SHCoCreateInstance failed %x\n", GetLastError());
408 CComPtr
<IShellExtInit
> pExtInit
;
409 hr
= pcm
->QueryInterface(IID_PPV_ARG(IShellExtInit
, &pExtInit
));
412 ERR("Failed to query for interface IID_IShellExtInit hr %x pclsid %s\n", hr
, wine_dbgstr_guid(pclsid
));
416 hr
= pExtInit
->Initialize(m_pidlFolder
, m_pDataObj
, hKey
);
419 TRACE("Failed to initialize shell extension error %x pclsid %s\n", hr
, wine_dbgstr_guid(pclsid
));
423 PDynamicShellEntry pEntry
= (DynamicShellEntry
*)HeapAlloc(GetProcessHeap(), 0, sizeof(DynamicShellEntry
));
426 return E_OUTOFMEMORY
;
429 pEntry
->iIdCmdFirst
= 0;
430 pEntry
->pNext
= NULL
;
432 pEntry
->pCM
= pcm
.Detach();
433 memcpy(&pEntry
->ClassID
, pclsid
, sizeof(CLSID
));
435 if (m_pDynamicEntries
)
437 PDynamicShellEntry pLastEntry
= m_pDynamicEntries
;
439 while (pLastEntry
->pNext
)
440 pLastEntry
= pLastEntry
->pNext
;
442 pLastEntry
->pNext
= pEntry
;
445 m_pDynamicEntries
= pEntry
;
451 CDefaultContextMenu::EnumerateDynamicContextHandlerForKey(HKEY hRootKey
)
454 WCHAR wszName
[MAX_PATH
], wszBuf
[MAX_PATH
], *pwszClsid
;
459 if (RegOpenKeyExW(hRootKey
, L
"shellex\\ContextMenuHandlers", 0, KEY_READ
, &hKey
) != ERROR_SUCCESS
)
461 TRACE("RegOpenKeyExW failed\n");
468 cchName
= _countof(wszName
);
469 if (RegEnumKeyExW(hKey
, dwIndex
++, wszName
, &cchName
, NULL
, NULL
, NULL
, NULL
) != ERROR_SUCCESS
)
472 /* Key name or key value is CLSID */
474 hr
= CLSIDFromString(wszName
, &clsid
);
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
);
486 if (m_bGroupPolicyActive
)
488 if (RegGetValueW(HKEY_LOCAL_MACHINE
,
489 L
"Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved",
494 NULL
) == ERROR_SUCCESS
)
496 LoadDynamicContextMenuHandler(hKey
, &clsid
);
500 LoadDynamicContextMenuHandler(hKey
, &clsid
);
509 CDefaultContextMenu::InsertMenuItemsOfDynamicContextMenuExtension(HMENU hMenu
, UINT IndexMenu
, UINT idCmdFirst
, UINT idCmdLast
)
511 if (!m_pDynamicEntries
)
518 PDynamicShellEntry pEntry
= m_pDynamicEntries
;
521 m_iIdSHEFirst
= idCmdFirst
;
524 HRESULT hr
= pEntry
->pCM
->QueryContextMenu(hMenu
, IndexMenu
++, idCmdFirst
, idCmdLast
, CMF_NORMAL
);
527 pEntry
->iIdCmdFirst
= idCmdFirst
;
528 pEntry
->NumIds
= LOWORD(hr
);
529 IndexMenu
+= pEntry
->NumIds
;
530 idCmdFirst
+= pEntry
->NumIds
+ 0x10;
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
;
536 m_iIdSHELast
= idCmdFirst
;
537 TRACE("SH_LoadContextMenuHandlers first %x last %x\n", m_iIdSHEFirst
, m_iIdSHELast
);
542 CDefaultContextMenu::BuildBackgroundContextMenu(
551 TRACE("BuildBackgroundContextMenu entered\n");
553 SFGAOF rfg
= SFGAO_FILESYSTEM
| SFGAO_FOLDER
;
554 HRESULT hr
= m_psf
->GetAttributesOf(0, NULL
, &rfg
);
557 ERR("GetAttributesOf failed: %x\n", hr
);
561 if (!_ILIsDesktop(m_pidlFolder
))
563 WCHAR wszBuf
[MAX_PATH
];
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
)))
569 TRACE("wszBuf %s\n", debugstr_w(wszBuf
));
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
);
586 hSubMenu
= LoadMenuW(shell32_hInstance
, L
"MENU_002");
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
);
594 if (!HasClipboardData())
596 TRACE("disabling paste options\n");
597 DisablePasteOptions(hMenu
);
600 /* Directory is progid of filesystem folders only */
601 if ((rfg
& (SFGAO_FILESYSTEM
|SFGAO_FOLDER
)) == (SFGAO_FILESYSTEM
|SFGAO_FOLDER
))
603 /* Load context menu handlers */
604 TRACE("Add background handlers: %p\n", m_pidlFolder
);
606 if (RegOpenKeyExW(HKEY_CLASSES_ROOT
, L
"Directory\\Background", 0, KEY_READ
, &hKey
) == ERROR_SUCCESS
)
608 EnumerateDynamicContextHandlerForKey(hKey
);
612 if (InsertMenuItemsOfDynamicContextMenuExtension(hMenu
, GetMenuItemCount(hMenu
) - 1, iIdCmdFirst
, iIdCmdLast
))
614 /* seperate dynamic context menu items */
615 _InsertMenuItemW(hMenu
, GetMenuItemCount(hMenu
) - 1, TRUE
, -1, MFT_SEPARATOR
, NULL
, MFS_ENABLED
);
623 CDefaultContextMenu::AddStaticContextMenusToMenu(
632 mii
.cbSize
= sizeof(mii
);
633 mii
.fMask
= MIIM_ID
| MIIM_TYPE
| MIIM_STATE
| MIIM_DATA
;
634 mii
.fType
= MFT_STRING
;
636 mii
.dwTypeData
= NULL
;
637 m_iIdSCMFirst
= mii
.wID
;
639 PStaticShellEntry pEntry
= m_pStaticEntries
;
643 fState
= MFS_ENABLED
;
644 mii
.dwTypeData
= NULL
;
646 /* set first entry as default */
647 if (pEntry
== m_pStaticEntries
)
648 fState
|= MFS_DEFAULT
;
650 if (!wcsicmp(pEntry
->szVerb
, L
"open"))
652 /* override default when open verb is found */
653 fState
|= MFS_DEFAULT
;
654 idResource
= IDS_OPEN_VERB
;
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"))
668 pEntry
= pEntry
->pNext
;
674 /* By default use verb for menu item name */
675 mii
.dwTypeData
= pEntry
->szVerb
;
679 if (LoadStringW(shell32_hInstance
, idResource
, wszVerb
, _countof(wszVerb
)))
680 mii
.dwTypeData
= wszVerb
; /* use translated verb */
682 ERR("Failed to load string\n");
687 HRESULT hr
= StringCbPrintfW(wszKey
, sizeof(wszKey
), L
"%s\\shell\\%s", pEntry
->szClass
, pEntry
->szVerb
);
692 DWORD cbVerb
= sizeof(wszVerb
);
693 LONG res
= RegOpenKeyW(HKEY_CLASSES_ROOT
, wszKey
, &hkVerb
);
694 if (res
== ERROR_SUCCESS
)
696 res
= RegLoadMUIStringW(hkVerb
,
703 if (res
== ERROR_SUCCESS
)
705 /* use description for the menu entry */
706 mii
.dwTypeData
= wszVerb
;
714 mii
.cch
= wcslen(mii
.dwTypeData
);
716 InsertMenuItemW(hMenu
, IndexMenu
++, TRUE
, &mii
);
719 pEntry
= pEntry
->pNext
;
722 m_iIdSCMLast
= mii
.wID
- 1;
726 void WINAPI
_InsertMenuItemW(
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
)
744 mii
.fMask
= MIIM_ID
| MIIM_TYPE
| MIIM_STATE
;
745 if ((ULONG_PTR
)HIWORD((ULONG_PTR
)dwTypeData
) == 0)
747 if (LoadStringW(shell32_hInstance
, LOWORD((ULONG_PTR
)dwTypeData
), wszText
, _countof(wszText
)))
748 mii
.dwTypeData
= wszText
;
751 ERR("failed to load string %p\n", dwTypeData
);
756 mii
.dwTypeData
= (LPWSTR
)dwTypeData
;
762 InsertMenuItemW(hMenu
, indexMenu
, fByPosition
, &mii
);
766 CDefaultContextMenu::BuildShellItemContextMenu(
775 TRACE("BuildShellItemContextMenu entered\n");
779 hr
= m_psf
->GetDisplayNameOf(m_apidl
[0], SHGDN_FORPARSING
, &strFile
);
782 WCHAR wszPath
[MAX_PATH
];
783 hr
= StrRetToBufW(&strFile
, m_apidl
[0], wszPath
, _countof(wszPath
));
786 LPCWSTR pwszExt
= PathFindExtensionW(wszPath
);
789 /* enumerate dynamic/static for a given file class */
790 if (RegOpenKeyExW(HKEY_CLASSES_ROOT
, pwszExt
, 0, KEY_READ
, &hKey
) == ERROR_SUCCESS
)
792 /* add static verbs */
793 AddStaticEntryForFileClass(pwszExt
);
795 /* load dynamic extensions from file extension key */
796 EnumerateDynamicContextHandlerForKey(hKey
);
801 DWORD dwSize
= sizeof(wszTemp
);
802 if (RegGetValueW(HKEY_CLASSES_ROOT
, pwszExt
, NULL
, RRF_RT_REG_SZ
, NULL
, wszTemp
, &dwSize
) == ERROR_SUCCESS
)
804 if (RegOpenKeyExW(HKEY_CLASSES_ROOT
, wszTemp
, 0, KEY_READ
, &hKey
) == ERROR_SUCCESS
)
806 /* add static verbs from progid key */
807 AddStaticEntryForFileClass(wszTemp
);
809 /* load dynamic extensions from progid key */
810 EnumerateDynamicContextHandlerForKey(hKey
);
818 ERR("GetDisplayNameOf failed: %x\n", hr
);
820 GUID
*pGuid
= _ILGetGUIDPointer(m_apidl
[0]);
826 wcscpy(buffer
, L
"CLSID\\");
827 hr
= StringFromCLSID(*pGuid
, &pwszCLSID
);
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
)
834 EnumerateDynamicContextHandlerForKey(hKey
);
835 AddStaticEntryForFileClass(buffer
);
838 CoTaskMemFree(pwszCLSID
);
842 if (_ILIsDrive(m_apidl
[0]))
844 AddStaticEntryForFileClass(L
"Drive");
845 if (RegOpenKeyExW(HKEY_CLASSES_ROOT
, L
"Drive", 0, KEY_READ
, &hKey
) == ERROR_SUCCESS
)
847 EnumerateDynamicContextHandlerForKey(hKey
);
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
);
858 ERR("GetAttributesOf failed: %x\n", hr
);
862 if (rfg
& SFGAO_FOLDER
)
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
)
868 EnumerateDynamicContextHandlerForKey(hKey
);
872 /* Directory is only loaded for real filesystem directories */
873 if (rfg
& SFGAO_FILESYSTEM
)
875 AddStaticEntryForFileClass(L
"Directory");
876 if (RegOpenKeyExW(HKEY_CLASSES_ROOT
, L
"Directory", 0, KEY_READ
, &hKey
) == ERROR_SUCCESS
)
878 EnumerateDynamicContextHandlerForKey(hKey
);
884 /* AllFilesystemObjects class is loaded only for files and directories */
885 if (rfg
& SFGAO_FILESYSTEM
)
887 if (RegOpenKeyExW(HKEY_CLASSES_ROOT
, L
"AllFilesystemObjects", 0, KEY_READ
, &hKey
) == ERROR_SUCCESS
)
889 /* sendto service is registered here */
890 EnumerateDynamicContextHandlerForKey(hKey
);
894 if (!(rfg
& SFGAO_FOLDER
))
896 if (RegOpenKeyExW(HKEY_CLASSES_ROOT
, L
"*", 0, KEY_READ
, &hKey
) == ERROR_SUCCESS
)
898 EnumerateDynamicContextHandlerForKey(hKey
);
904 /* add static context menu handlers */
905 UINT IndexMenu
= AddStaticContextMenusToMenu(hMenu
, 0);
907 /* now process dynamic context menu handlers */
908 BOOL bAddSep
= FALSE
;
909 IndexMenu
= InsertMenuItemsOfDynamicContextMenuExtension(hMenu
, IndexMenu
, iIdCmdFirst
, iIdCmdLast
);
910 TRACE("IndexMenu %d\n", IndexMenu
);
912 if (_ILIsDrive(m_apidl
[0]))
914 char szDrive
[8] = {0};
917 _ILGetDrive(m_apidl
[0], szDrive
, sizeof(szDrive
));
918 if (GetVolumeInformationA(szDrive
, NULL
, 0, NULL
, NULL
, &dwFlags
, NULL
, 0))
920 /* Disable format if read only */
921 if (!(dwFlags
& FILE_READ_ONLY_VOLUME
))
923 _InsertMenuItemW(hMenu
, IndexMenu
++, TRUE
, 0, MFT_SEPARATOR
, NULL
, 0);
924 _InsertMenuItemW(hMenu
, IndexMenu
++, TRUE
, 0x7ABC, MFT_STRING
, MAKEINTRESOURCEW(IDS_FORMATDRIVE
), MFS_ENABLED
);
930 BOOL bClipboardData
= (HasClipboardData() && (rfg
& SFGAO_FILESYSTEM
));
931 if (rfg
& (SFGAO_CANCOPY
| SFGAO_CANMOVE
) || bClipboardData
)
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
);
939 _InsertMenuItemW(hMenu
, IndexMenu
++, TRUE
, FCIDM_SHVIEW_INSERT
, MFT_STRING
, MAKEINTRESOURCEW(IDS_PASTE
), MFS_ENABLED
);
944 if (rfg
& SFGAO_CANLINK
)
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
);
951 if (rfg
& SFGAO_CANDELETE
)
956 _InsertMenuItemW(hMenu
, IndexMenu
++, TRUE
, 0, MFT_SEPARATOR
, NULL
, 0);
958 _InsertMenuItemW(hMenu
, IndexMenu
++, TRUE
, FCIDM_SHVIEW_DELETE
, MFT_STRING
, MAKEINTRESOURCEW(IDS_DELETE
), MFS_ENABLED
);
961 if (rfg
& SFGAO_CANRENAME
)
965 _InsertMenuItemW(hMenu
, IndexMenu
++, TRUE
, 0, MFT_SEPARATOR
, NULL
, 0);
967 _InsertMenuItemW(hMenu
, IndexMenu
++, TRUE
, FCIDM_SHVIEW_RENAME
, MFT_STRING
, MAKEINTRESOURCEW(IDS_RENAME
), MFS_ENABLED
);
971 if (rfg
& SFGAO_HASPROPSHEET
)
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
);
982 CDefaultContextMenu::QueryContextMenu(
990 idCmdFirst
= BuildShellItemContextMenu(hMenu
, idCmdFirst
, idCmdLast
, uFlags
);
992 idCmdFirst
= BuildBackgroundContextMenu(hMenu
, idCmdFirst
, idCmdLast
, uFlags
);
998 CDefaultContextMenu::NotifyShellViewWindow(LPCMINVOKECOMMANDINFO lpcmi
, BOOL bRefresh
)
1000 CComPtr
<IShellView
> psv
;
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
))
1013 if (SUCCEEDED(psv
->GetWindow(&hwndSV
)))
1014 SendMessageW(hwndSV
, WM_COMMAND
, MAKEWPARAM(LOWORD(lpcmi
->lpVerb
), 0), 0);
1019 CDefaultContextMenu::DoRefresh(
1020 LPCMINVOKECOMMANDINFO lpcmi
)
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
))
1031 return psv
->Refresh();
1035 CDefaultContextMenu::DoPaste(
1036 LPCMINVOKECOMMANDINFO lpcmi
, BOOL bLink
)
1040 CComPtr
<IDataObject
> pda
;
1041 hr
= OleGetClipboard(&pda
);
1045 CComPtr
<IShellFolder
> psfDesktop
;
1046 CComPtr
<IShellFolder
> psfTarget
= NULL
;
1048 hr
= SHGetDesktopFolder(&psfDesktop
);
1052 /* Find target folder */
1055 hr
= m_psf
->BindToObject(m_apidl
[0], NULL
, IID_PPV_ARG(IShellFolder
, &psfTarget
));
1059 CComPtr
<IPersistFolder2
> ppf2
= NULL
;
1062 /* cidl is zero due to explorer view */
1063 hr
= m_psf
->QueryInterface(IID_PPV_ARG(IPersistFolder2
, &ppf2
));
1066 hr
= ppf2
->GetCurFolder(&pidl
);
1069 if (_ILIsDesktop(pidl
))
1071 /* use desktop shellfolder */
1072 psfTarget
= psfDesktop
;
1076 /* retrieve target desktop folder */
1077 hr
= psfDesktop
->BindToObject(pidl
, NULL
, IID_PPV_ARG(IShellFolder
, &psfTarget
));
1079 TRACE("psfTarget %x %p, Desktop %u\n", hr
, psfTarget
.p
, _ILIsDesktop(pidl
));
1087 ERR("no IShellFolder\n");
1091 FORMATETC formatetc2
;
1093 InitFormatEtc(formatetc2
, RegisterClipboardFormatW(CFSTR_PREFERREDDROPEFFECT
), TYMED_HGLOBAL
);
1097 if (SUCCEEDED(pda
->GetData(&formatetc2
, &medium2
)))
1099 DWORD
* pdwFlag
= (DWORD
*)GlobalLock(medium2
.hGlobal
);
1102 if (*pdwFlag
== DROPEFFECT_COPY
)
1108 ERR("No drop effect obtained");
1110 GlobalUnlock(medium2
.hGlobal
);
1115 dwKey
= MK_CONTROL
|MK_SHIFT
;
1118 CComPtr
<IDropTarget
> pdrop
;
1119 hr
= psfTarget
->CreateViewObject(NULL
, IID_PPV_ARG(IDropTarget
, &pdrop
));
1122 ERR("Error getting IDropTarget interface\n");
1126 SHSimulateDrop(pdrop
, pda
, dwKey
, NULL
, NULL
);
1128 TRACE("CP result %x\n", hr
);
1133 CDefaultContextMenu::DoOpenOrExplore(
1134 LPCMINVOKECOMMANDINFO lpcmi
)
1141 CDefaultContextMenu::DoCreateLink(
1142 LPCMINVOKECOMMANDINFO lpcmi
)
1144 CComPtr
<IDataObject
> pDataObj
;
1145 CComPtr
<IDropTarget
> pDT
;
1147 CComPtr
<IPersistFolder2
> ppf2
= NULL
;
1149 CComPtr
<IShellFolder
> psfDesktop
;
1150 CComPtr
<IShellFolder
> psfTarget
= NULL
;
1152 hr
= SHGetDesktopFolder(&psfDesktop
);
1156 if (SUCCEEDED(hr
= SHCreateDataObject(m_pidlFolder
, m_cidl
, m_apidl
, NULL
, IID_PPV_ARG(IDataObject
, &pDataObj
))))
1158 hr
= m_psf
->QueryInterface(IID_PPV_ARG(IPersistFolder2
, &ppf2
));
1161 hr
= ppf2
->GetCurFolder(&pidl
);
1164 if (_ILIsDesktop(pidl
))
1166 /* use desktop shellfolder */
1167 psfTarget
= psfDesktop
;
1171 /* retrieve target desktop folder */
1172 hr
= psfDesktop
->BindToObject(pidl
, NULL
, IID_PPV_ARG(IShellFolder
, &psfTarget
));
1174 TRACE("psfTarget %x %p, Desktop %u\n", hr
, psfTarget
.p
, _ILIsDesktop(pidl
));
1183 ERR("no IShellFolder\n");
1187 hr
= psfTarget
->CreateViewObject(NULL
, IID_PPV_ARG(IDropTarget
, &pDT
));
1190 ERR("no IDropTarget Interface\n");
1193 SHSimulateDrop(pDT
, pDataObj
, MK_CONTROL
|MK_SHIFT
, NULL
, NULL
);
1198 HRESULT
CDefaultContextMenu::DoDelete(LPCMINVOKECOMMANDINFO lpcmi
)
1200 DoDeleteAsync(m_pDataObj
, lpcmi
->fMask
);
1205 CDefaultContextMenu::DoCopyOrCut(
1206 LPCMINVOKECOMMANDINFO lpcmi
,
1209 CComPtr
<IDataObject
> pDataObj
;
1212 hr
= SHCreateDataObject(m_pidlFolder
, m_cidl
, m_apidl
, NULL
, IID_PPV_ARG(IDataObject
, &pDataObj
));
1213 if (FAILED_UNEXPECTEDLY(hr
))
1218 FORMATETC formatetc
;
1220 InitFormatEtc(formatetc
, RegisterClipboardFormatW(CFSTR_PREFERREDDROPEFFECT
), TYMED_HGLOBAL
);
1221 pDataObj
->GetData(&formatetc
, &medium
);
1222 DWORD
* pdwFlag
= (DWORD
*)GlobalLock(medium
.hGlobal
);
1224 *pdwFlag
= DROPEFFECT_MOVE
;
1225 GlobalUnlock(medium
.hGlobal
);
1226 pDataObj
->SetData(&formatetc
, &medium
, TRUE
);
1229 return OleSetClipboard(pDataObj
);
1233 CDefaultContextMenu::DoRename(
1234 LPCMINVOKECOMMANDINFO lpcmi
)
1236 CComPtr
<IShellBrowser
> psb
;
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
))
1247 /* is the treeview focused */
1249 if (SUCCEEDED(psb
->GetControlWindow(FCW_TREE
, &hwnd
)))
1251 HTREEITEM hItem
= TreeView_GetSelection(hwnd
);
1253 (void)TreeView_EditLabel(hwnd
, hItem
);
1256 CComPtr
<IShellView
> lpSV
;
1257 hr
= psb
->QueryActiveShellView(&lpSV
);
1258 if (FAILED_UNEXPECTEDLY(hr
))
1261 SVSIF selFlags
= SVSI_DESELECTOTHERS
| SVSI_EDIT
| SVSI_ENSUREVISIBLE
| SVSI_FOCUSED
| SVSI_SELECT
;
1262 lpSV
->SelectItem(m_apidl
[0], selFlags
);
1267 CDefaultContextMenu::DoProperties(
1268 LPCMINVOKECOMMANDINFO lpcmi
)
1271 const ITEMIDLIST
*pidlParent
= m_pidlFolder
, *pidlChild
;
1275 CComPtr
<IPersistFolder2
> pf
;
1277 /* pidlFolder is optional */
1278 if (SUCCEEDED(m_psf
->QueryInterface(IID_PPV_ARG(IPersistFolder2
, &pf
))))
1280 pf
->GetCurFolder((_ITEMIDLIST
**)&pidlParent
);
1285 pidlChild
= m_apidl
[0];
1288 /* Set pidlChild to last pidl of current folder */
1289 if (pidlParent
== m_pidlFolder
)
1290 pidlParent
= (ITEMIDLIST
*)ILClone(pidlParent
);
1292 pidlChild
= (ITEMIDLIST
*)ILClone(ILFindLastID(pidlParent
));
1293 ILRemoveLastID((ITEMIDLIST
*)pidlParent
);
1296 if (_ILIsMyComputer(pidlChild
))
1298 if (32 >= (UINT
)ShellExecuteW(lpcmi
->hwnd
, L
"open", L
"rundll32.exe shell32.dll,Control_RunDLL sysdm.cpl", NULL
, NULL
, SW_SHOWNORMAL
))
1301 else if (_ILIsDesktop(pidlChild
))
1303 if (32 >= (UINT
)ShellExecuteW(lpcmi
->hwnd
, L
"open", L
"rundll32.exe shell32.dll,Control_RunDLL desk.cpl", NULL
, NULL
, SW_SHOWNORMAL
))
1306 else if (_ILIsDrive(pidlChild
))
1308 WCHAR wszBuf
[MAX_PATH
];
1309 ILGetDisplayName(pidlChild
, wszBuf
);
1310 if (!SH_ShowDriveProperties(wszBuf
, pidlParent
, &pidlChild
))
1313 else if (_ILIsNetHood(pidlChild
))
1316 if (32 >= (UINT
)ShellExecuteW(NULL
, L
"open", L
"explorer.exe",
1317 L
"::{7007ACC7-3202-11D1-AAD2-00805FC1270E}",
1318 NULL
, SW_SHOWDEFAULT
))
1321 else if (_ILIsBitBucket(pidlChild
))
1323 /* FIXME: detect the drive path of bitbucket if appropiate */
1324 if (!SH_ShowRecycleBinProperties(L
'C'))
1330 WARN("SHMultiFileProperties is not yet implemented\n");
1333 hr
= m_psf
->GetDisplayNameOf(pidlChild
, SHGDN_FORPARSING
, &strFile
);
1336 WCHAR wszBuf
[MAX_PATH
];
1337 hr
= StrRetToBufW(&strFile
, pidlChild
, wszBuf
, _countof(wszBuf
));
1339 hr
= SH_ShowPropertiesDialog(wszBuf
, pidlParent
, &pidlChild
);
1341 ERR("StrRetToBufW failed\n");
1344 ERR("IShellFolder_GetDisplayNameOf failed for apidl\n");
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
);
1357 CDefaultContextMenu::DoFormat(
1358 LPCMINVOKECOMMANDINFO lpcmi
)
1360 char szDrive
[8] = {0};
1362 if (!_ILGetDrive(m_apidl
[0], szDrive
, sizeof(szDrive
)))
1364 ERR("pidl is not a drive\n");
1368 SHFormatDrive(lpcmi
->hwnd
, szDrive
[0] - 'A', SHFMT_ID_DEFAULT
, 0);
1372 // This code is taken from CNewMenu and should be shared between the 2 classes
1374 CDefaultContextMenu::DoCreateNewFolder(
1375 LPCMINVOKECOMMANDINFO lpici
)
1377 WCHAR wszPath
[MAX_PATH
];
1378 WCHAR wszName
[MAX_PATH
];
1379 WCHAR wszNewFolder
[25];
1382 /* Get folder path */
1383 hr
= SHGetPathFromIDListW(m_pidlFolder
, wszPath
);
1384 if (FAILED_UNEXPECTEDLY(hr
))
1387 if (!LoadStringW(shell32_hInstance
, IDS_NEWFOLDER
, wszNewFolder
, _countof(wszNewFolder
)))
1390 /* Create the name of the new directory */
1391 if (!PathYetAnotherMakeUniqueName(wszName
, wszPath
, NULL
, wszNewFolder
))
1394 /* Create the new directory and show the appropriate dialog in case of error */
1395 if (SHCreateDirectory(lpici
->hwnd
, wszName
) != ERROR_SUCCESS
)
1398 /* Show and select the new item in the def view */
1400 PITEMID_CHILD pidlNewItem
;
1401 CComPtr
<IShellView
> psv
;
1403 /* Notify the view object about the new item */
1404 SHChangeNotify(SHCNE_MKDIR
, SHCNF_PATHW
, (LPCVOID
)wszName
, NULL
);
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
))
1414 /* Attempt to get the pidl of the new item */
1415 hr
= SHILCreateFromPathW(wszName
, &pidl
, NULL
);
1416 if (FAILED_UNEXPECTEDLY(hr
))
1419 pidlNewItem
= ILFindLastID(pidl
);
1421 hr
= psv
->SelectItem(pidlNewItem
, SVSI_DESELECTOTHERS
| SVSI_EDIT
| SVSI_ENSUREVISIBLE
|
1422 SVSI_FOCUSED
| SVSI_SELECT
);
1429 PDynamicShellEntry
CDefaultContextMenu::GetDynamicEntry(UINT idCmd
)
1431 PDynamicShellEntry pEntry
= m_pDynamicEntries
;
1433 while(pEntry
&& idCmd
> pEntry
->iIdCmdFirst
+ pEntry
->NumIds
)
1434 pEntry
= pEntry
->pNext
;
1439 if (idCmd
< pEntry
->iIdCmdFirst
|| idCmd
> pEntry
->iIdCmdFirst
+ pEntry
->NumIds
)
1445 //FIXME: 260 is correct, but should this be part of the SDK or just MAX_PATH?
1446 #define MAX_VERB 260
1449 CDefaultContextMenu::MapVerbToCmdId(PVOID Verb
, PUINT idCmd
, BOOL IsUnicode
)
1451 WCHAR UnicodeStr
[MAX_VERB
];
1453 /* Loop through all the static verbs looking for a match */
1454 for (UINT i
= 0; i
< _countof(g_StaticInvokeCmdMap
); i
++)
1456 /* We can match both ANSI and unicode strings */
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
))
1463 /* Return the Corresponding Id */
1464 *idCmd
= g_StaticInvokeCmdMap
[i
].IntVerb
;
1470 if (!strcmp(g_StaticInvokeCmdMap
[i
].szStringVerb
, (LPSTR
)Verb
))
1472 *idCmd
= g_StaticInvokeCmdMap
[i
].IntVerb
;
1482 CDefaultContextMenu::DoDynamicShellExtensions(
1483 LPCMINVOKECOMMANDINFO lpcmi
)
1485 TRACE("verb %p first %x last %x", lpcmi
->lpVerb
, m_iIdSHEFirst
, m_iIdSHELast
);
1487 UINT idCmd
= LOWORD(lpcmi
->lpVerb
);
1488 PDynamicShellEntry pEntry
= GetDynamicEntry(idCmd
);
1492 /* invoke the dynamic context menu */
1493 lpcmi
->lpVerb
= MAKEINTRESOURCEA(idCmd
- pEntry
->iIdCmdFirst
);
1494 return pEntry
->pCM
->InvokeCommand(lpcmi
);
1498 CDefaultContextMenu::BrowserFlagsFromVerb(LPCMINVOKECOMMANDINFO lpcmi
, PStaticShellEntry pEntry
)
1500 CComPtr
<IShellBrowser
> psb
;
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
))
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";
1520 FlagsName
= L
"BrowserFlags";
1522 /* Try to get the flag from the verb */
1523 hr
= StringCbPrintfW(wszKey
, sizeof(wszKey
), L
"%s\\shell\\%s", pEntry
->szClass
, pEntry
->szVerb
);
1527 cbVerb
= sizeof(wFlags
);
1528 if (RegGetValueW(HKEY_CLASSES_ROOT
, wszKey
, FlagsName
, RRF_RT_REG_DWORD
, NULL
, &wFlags
, &cbVerb
) == ERROR_SUCCESS
)
1537 CDefaultContextMenu::TryToBrowse(
1538 LPCMINVOKECOMMANDINFO lpcmi
, LPCITEMIDLIST pidl
, DWORD wFlags
)
1540 CComPtr
<IShellBrowser
> psb
;
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
))
1551 return psb
->BrowseObject(ILCombine(m_pidlFolder
, pidl
), wFlags
);
1555 CDefaultContextMenu::InvokePidl(LPCMINVOKECOMMANDINFO lpcmi
, LPCITEMIDLIST pidl
, PStaticShellEntry pEntry
)
1557 LPITEMIDLIST pidlFull
= ILCombine(m_pidlFolder
, pidl
);
1558 if (pidlFull
== NULL
)
1563 WCHAR wszPath
[MAX_PATH
];
1564 BOOL bHasPath
= SHGetPathFromIDListW(pidlFull
, wszPath
);
1566 WCHAR wszDir
[MAX_PATH
];
1569 wcscpy(wszDir
, wszPath
);
1570 PathRemoveFileSpec(wszDir
);
1574 SHGetPathFromIDListW(m_pidlFolder
, wszDir
);
1578 RegOpenKeyExW(HKEY_CLASSES_ROOT
, pEntry
->szClass
, 0, KEY_READ
, &hkeyClass
);
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
;
1592 sei
.lpFile
= wszPath
;
1595 ShellExecuteExW(&sei
);
1597 RegCloseKey(hkeyClass
);
1605 CDefaultContextMenu::DoStaticShellExtensions(
1606 LPCMINVOKECOMMANDINFO lpcmi
)
1608 PStaticShellEntry pEntry
= m_pStaticEntries
;
1609 INT iCmd
= LOWORD(lpcmi
->lpVerb
) - m_iIdSCMFirst
;
1613 while (pEntry
&& (iCmd
--) > 0)
1614 pEntry
= pEntry
->pNext
;
1619 /* Get the browse flags to see if we need to browse */
1620 DWORD wFlags
= BrowserFlagsFromVerb(lpcmi
, pEntry
);
1621 BOOL bBrowsed
= FALSE
;
1623 for (i
=0; i
< m_cidl
; i
++)
1625 /* Check if we need to browse */
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 */
1634 hr
= TryToBrowse(lpcmi
, m_apidl
[i
], wFlags
);
1642 InvokePidl(lpcmi
, m_apidl
[i
], pEntry
);
1650 CDefaultContextMenu::InvokeCommand(
1651 LPCMINVOKECOMMANDINFO lpcmi
)
1653 CMINVOKECOMMANDINFO LocalInvokeInfo
;
1657 /* Take a local copy of the fixed members of the
1658 struct as we might need to modify the verb */
1659 LocalInvokeInfo
= *lpcmi
;
1661 /* Check if this is a string verb */
1662 if (HIWORD(LocalInvokeInfo
.lpVerb
))
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
);
1669 /* Check if this is a Id */
1670 switch (LOWORD(LocalInvokeInfo
.lpVerb
))
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 */
1680 case FCIDM_SHVIEW_AUTOARRANGE
:
1681 case FCIDM_SHVIEW_SNAPTOGRID
:
1682 Result
= NotifyShellViewWindow(&LocalInvokeInfo
, FALSE
);
1684 case FCIDM_SHVIEW_REFRESH
:
1685 Result
= DoRefresh(&LocalInvokeInfo
);
1687 case FCIDM_SHVIEW_INSERT
:
1688 Result
= DoPaste(&LocalInvokeInfo
, FALSE
);
1690 case FCIDM_SHVIEW_INSERTLINK
:
1691 Result
= DoPaste(&LocalInvokeInfo
, TRUE
);
1693 case FCIDM_SHVIEW_OPEN
:
1694 case FCIDM_SHVIEW_EXPLORE
:
1695 Result
= DoOpenOrExplore(&LocalInvokeInfo
);
1697 case FCIDM_SHVIEW_COPY
:
1698 case FCIDM_SHVIEW_CUT
:
1699 Result
= DoCopyOrCut(&LocalInvokeInfo
, LOWORD(LocalInvokeInfo
.lpVerb
) == FCIDM_SHVIEW_COPY
);
1701 case FCIDM_SHVIEW_CREATELINK
:
1702 Result
= DoCreateLink(&LocalInvokeInfo
);
1704 case FCIDM_SHVIEW_DELETE
:
1705 Result
= DoDelete(&LocalInvokeInfo
);
1707 case FCIDM_SHVIEW_RENAME
:
1708 Result
= DoRename(&LocalInvokeInfo
);
1710 case FCIDM_SHVIEW_PROPERTIES
:
1711 Result
= DoProperties(&LocalInvokeInfo
);
1714 Result
= DoFormat(&LocalInvokeInfo
);
1716 case FCIDM_SHVIEW_NEWFOLDER
:
1717 Result
= DoCreateNewFolder(&LocalInvokeInfo
);
1720 Result
= E_UNEXPECTED
;
1724 /* Check for ID's we didn't find a handler for */
1725 if (Result
== E_UNEXPECTED
)
1727 if (m_iIdSHEFirst
&& m_iIdSHELast
)
1729 if (LOWORD(LocalInvokeInfo
.lpVerb
) >= m_iIdSHEFirst
&& LOWORD(LocalInvokeInfo
.lpVerb
) <= m_iIdSHELast
)
1730 Result
= DoDynamicShellExtensions(&LocalInvokeInfo
);
1733 if (m_iIdSCMFirst
&& m_iIdSCMLast
)
1735 if (LOWORD(LocalInvokeInfo
.lpVerb
) >= m_iIdSCMFirst
&& LOWORD(LocalInvokeInfo
.lpVerb
) <= m_iIdSCMLast
)
1736 Result
= DoStaticShellExtensions(&LocalInvokeInfo
);
1740 if (Result
== E_UNEXPECTED
)
1741 FIXME("Unhandled Verb %xl\n", LOWORD(LocalInvokeInfo
.lpVerb
));
1748 CDefaultContextMenu::GetCommandString(
1755 /* We don't handle the help text yet */
1756 if (uFlags
== GCS_HELPTEXTA
||
1757 uFlags
== GCS_HELPTEXTW
)
1762 /* Loop looking for a matching Id */
1763 for (UINT i
= 0; i
< _countof(g_StaticInvokeCmdMap
); i
++)
1765 if (g_StaticInvokeCmdMap
[i
].IntVerb
== idCommand
)
1767 /* Validation just returns S_OK on a match */
1768 if (uFlags
== GCS_VALIDATEA
|| uFlags
== GCS_VALIDATEW
)
1771 /* Return a copy of the ANSI verb */
1772 if (uFlags
== GCS_VERBA
)
1773 return StringCchCopyA(lpszName
, uMaxNameLen
, g_StaticInvokeCmdMap
[i
].szStringVerb
);
1775 /* Convert the ANSI verb to unicode and return that */
1776 if (uFlags
== GCS_VERBW
)
1778 if (SHAnsiToUnicode(g_StaticInvokeCmdMap
[i
].szStringVerb
, (LPWSTR
)lpszName
, uMaxNameLen
))
1784 return E_INVALIDARG
;
1789 CDefaultContextMenu::HandleMenuMsg(
1794 /* FIXME: Should we implement this as well? */
1800 CDefaultContextMenu::HandleMenuMsg2(
1808 case WM_INITMENUPOPUP
:
1810 PDynamicShellEntry pEntry
= m_pDynamicEntries
;
1813 SHForwardContextMenuMsg(pEntry
->pCM
, uMsg
, wParam
, lParam
, plResult
, TRUE
);
1814 pEntry
= pEntry
->pNext
;
1820 DRAWITEMSTRUCT
* pDrawStruct
= reinterpret_cast<DRAWITEMSTRUCT
*>(lParam
);
1821 PDynamicShellEntry pEntry
= GetDynamicEntry(pDrawStruct
->itemID
);
1823 SHForwardContextMenuMsg(pEntry
->pCM
, uMsg
, wParam
, lParam
, plResult
, TRUE
);
1826 case WM_MEASUREITEM
:
1828 MEASUREITEMSTRUCT
* pMeasureStruct
= reinterpret_cast<MEASUREITEMSTRUCT
*>(lParam
);
1829 PDynamicShellEntry pEntry
= GetDynamicEntry(pMeasureStruct
->itemID
);
1831 SHForwardContextMenuMsg(pEntry
->pCM
, uMsg
, wParam
, lParam
, plResult
, TRUE
);
1838 ERR("Got unknown message:%d\n", uMsg
);
1845 CDefaultContextMenu::SetSite(IUnknown
*pUnkSite
)
1853 CDefaultContextMenu::GetSite(REFIID riid
, void **ppvSite
)
1858 return m_site
->QueryInterface(riid
, ppvSite
);
1863 CDefaultContextMenu_CreateInstance(const DEFCONTEXTMENU
*pdcm
, REFIID riid
, void **ppv
)
1865 return ShellObjectCreatorInit
<CDefaultContextMenu
>(pdcm
, riid
, ppv
);
1868 /*************************************************************************
1869 * SHCreateDefaultContextMenu [SHELL32.325] Vista API
1875 SHCreateDefaultContextMenu(const DEFCONTEXTMENU
*pdcm
, REFIID riid
, void **ppv
)
1877 return CDefaultContextMenu_CreateInstance(pdcm
, riid
, ppv
);
1880 /*************************************************************************
1881 * CDefFolderMenu_Create2 [SHELL32.701]
1887 CDefFolderMenu_Create2(
1888 PCIDLIST_ABSOLUTE pidlFolder
,
1891 PCUITEMID_CHILD_ARRAY apidl
,
1893 LPFNDFMCALLBACK lpfn
,
1895 const HKEY
*ahkeyClsKeys
,
1896 IContextMenu
**ppcm
)
1898 DEFCONTEXTMENU pdcm
;
1901 pdcm
.pidlFolder
= pidlFolder
;
1905 pdcm
.punkAssociationInfo
= NULL
;
1907 pdcm
.aKeys
= ahkeyClsKeys
;
1909 HRESULT hr
= SHCreateDefaultContextMenu(&pdcm
, IID_PPV_ARG(IContextMenu
, ppcm
));