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)
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.
19 //fixme: this isn't in wine's shlwapi header, and the definition doesnt match the
20 // windows headers. When wine's header and lib are fixed this can be removed.
21 DWORD WINAPI
SHAnsiToUnicode(LPCSTR lpSrcStr
, LPWSTR lpDstStr
, int iLen
);
24 WINE_DEFAULT_DEBUG_CHANNEL(dmenu
);
26 typedef struct _DynamicShellEntry_
32 struct _DynamicShellEntry_
*pNext
;
33 } DynamicShellEntry
, *PDynamicShellEntry
;
35 typedef struct _StaticShellEntry_
39 struct _StaticShellEntry_
*pNext
;
40 } StaticShellEntry
, *PStaticShellEntry
;
44 // verbs for InvokeCommandInfo
46 struct _StaticInvokeCommandMap_
50 } g_StaticInvokeCmdMap
[] =
52 { "RunAs", 0 }, // Unimplemented
53 { "Print", 0 }, // Unimplemented
54 { "Preview", 0 }, // Unimplemented
55 { "Open", FCIDM_SHVIEW_OPEN
},
56 { CMDSTR_NEWFOLDERA
, FCIDM_SHVIEW_NEWFOLDER
},
57 { CMDSTR_VIEWLISTA
, FCIDM_SHVIEW_LISTVIEW
},
58 { CMDSTR_VIEWDETAILSA
, FCIDM_SHVIEW_REPORTVIEW
}
62 class CDefaultContextMenu
:
63 public CComObjectRootEx
<CComMultiThreadModelNoCS
>,
67 CComPtr
<IShellFolder
> m_psf
;
69 PCUITEMID_CHILD_ARRAY m_apidl
;
70 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 */
80 void AddStaticEntry(LPCWSTR pwszVerb
, LPCWSTR pwszClass
);
81 void AddStaticEntryForKey(HKEY hKey
, LPCWSTR pwszClass
);
82 void AddStaticEntryForFileClass(LPCWSTR pwszExt
);
83 BOOL
IsShellExtensionAlreadyLoaded(const CLSID
*pclsid
);
84 HRESULT
LoadDynamicContextMenuHandler(HKEY hKey
, const CLSID
*pclsid
);
85 BOOL
EnumerateDynamicContextHandlerForKey(HKEY hRootKey
);
86 UINT
InsertMenuItemsOfDynamicContextMenuExtension(HMENU hMenu
, UINT IndexMenu
, UINT idCmdFirst
, UINT idCmdLast
);
87 UINT
BuildBackgroundContextMenu(HMENU hMenu
, UINT iIdCmdFirst
, UINT iIdCmdLast
, UINT uFlags
);
88 UINT
AddStaticContextMenusToMenu(HMENU hMenu
, UINT IndexMenu
);
89 UINT
BuildShellItemContextMenu(HMENU hMenu
, UINT iIdCmdFirst
, UINT iIdCmdLast
, UINT uFlags
);
90 HRESULT
DoPaste(LPCMINVOKECOMMANDINFO lpcmi
, BOOL bLink
);
91 HRESULT
DoOpenOrExplore(LPCMINVOKECOMMANDINFO lpcmi
);
92 HRESULT
DoCreateLink(LPCMINVOKECOMMANDINFO lpcmi
);
93 HRESULT
DoRefresh(LPCMINVOKECOMMANDINFO lpcmi
);
94 HRESULT
DoDelete(LPCMINVOKECOMMANDINFO lpcmi
);
95 HRESULT
DoCopyOrCut(LPCMINVOKECOMMANDINFO lpcmi
, BOOL bCopy
);
96 HRESULT
DoRename(LPCMINVOKECOMMANDINFO lpcmi
);
97 HRESULT
DoProperties(LPCMINVOKECOMMANDINFO lpcmi
);
98 HRESULT
DoFormat(LPCMINVOKECOMMANDINFO lpcmi
);
99 HRESULT
DoCreateNewFolder(LPCMINVOKECOMMANDINFO lpici
);
100 HRESULT
DoDynamicShellExtensions(LPCMINVOKECOMMANDINFO lpcmi
);
101 HRESULT
DoStaticShellExtensions(LPCMINVOKECOMMANDINFO lpcmi
);
102 DWORD
BrowserFlagsFromVerb(LPCMINVOKECOMMANDINFO lpcmi
, PStaticShellEntry pEntry
);
103 HRESULT
TryToBrowse(LPCMINVOKECOMMANDINFO lpcmi
, LPCITEMIDLIST pidl
, DWORD wFlags
);
104 HRESULT
InvokePidl(LPCMINVOKECOMMANDINFO lpcmi
, LPCITEMIDLIST pidl
, PStaticShellEntry pEntry
);
105 PDynamicShellEntry
GetDynamicEntry(UINT idCmd
);
106 BOOL
MapVerbToCmdId(PVOID Verb
, PUINT idCmd
, BOOL IsUnicode
);
109 CDefaultContextMenu();
110 ~CDefaultContextMenu();
111 HRESULT WINAPI
Initialize(const DEFCONTEXTMENU
*pdcm
);
114 virtual HRESULT WINAPI
QueryContextMenu(HMENU hMenu
, UINT indexMenu
, UINT idCmdFirst
, UINT idCmdLast
, UINT uFlags
);
115 virtual HRESULT WINAPI
InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi
);
116 virtual HRESULT WINAPI
GetCommandString(UINT_PTR idCommand
, UINT uFlags
, UINT
*lpReserved
, LPSTR lpszName
, UINT uMaxNameLen
);
119 virtual HRESULT WINAPI
HandleMenuMsg(UINT uMsg
, WPARAM wParam
, LPARAM lParam
);
122 virtual HRESULT WINAPI
HandleMenuMsg2(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, LRESULT
*plResult
);
124 BEGIN_COM_MAP(CDefaultContextMenu
)
125 COM_INTERFACE_ENTRY_IID(IID_IContextMenu
, IContextMenu
)
126 COM_INTERFACE_ENTRY_IID(IID_IContextMenu2
, IContextMenu2
)
127 COM_INTERFACE_ENTRY_IID(IID_IContextMenu3
, IContextMenu3
)
131 CDefaultContextMenu::CDefaultContextMenu() :
137 m_bGroupPolicyActive(0),
138 m_pDynamicEntries(NULL
),
141 m_pStaticEntries(NULL
),
147 CDefaultContextMenu::~CDefaultContextMenu()
149 /* Free dynamic shell extension entries */
150 PDynamicShellEntry pDynamicEntry
= m_pDynamicEntries
, pNextDynamic
;
151 while (pDynamicEntry
)
153 pNextDynamic
= pDynamicEntry
->pNext
;
154 pDynamicEntry
->pCM
->Release();
155 HeapFree(GetProcessHeap(), 0, pDynamicEntry
);
156 pDynamicEntry
= pNextDynamic
;
159 /* Free static shell extension entries */
160 PStaticShellEntry pStaticEntry
= m_pStaticEntries
, pNextStatic
;
163 pNextStatic
= pStaticEntry
->pNext
;
164 HeapFree(GetProcessHeap(), 0, pStaticEntry
->szClass
);
165 HeapFree(GetProcessHeap(), 0, pStaticEntry
->szVerb
);
166 HeapFree(GetProcessHeap(), 0, pStaticEntry
);
167 pStaticEntry
= pNextStatic
;
171 CoTaskMemFree(m_pidlFolder
);
172 _ILFreeaPidl(const_cast<PITEMID_CHILD
*>(m_apidl
), m_cidl
);
175 HRESULT WINAPI
CDefaultContextMenu::Initialize(const DEFCONTEXTMENU
*pdcm
)
177 CComPtr
<IDataObject
> pDataObj
;
179 TRACE("cidl %u\n", pdcm
->cidl
);
182 m_apidl
= const_cast<PCUITEMID_CHILD_ARRAY
>(_ILCopyaPidl(pdcm
->apidl
, m_cidl
));
183 if (m_cidl
&& !m_apidl
)
184 return E_OUTOFMEMORY
;
187 if (SUCCEEDED(SHCreateDataObject(pdcm
->pidlFolder
, pdcm
->cidl
, pdcm
->apidl
, NULL
, IID_PPV_ARG(IDataObject
, &pDataObj
))))
188 m_pDataObj
= pDataObj
;
190 if (pdcm
->pidlFolder
)
192 m_pidlFolder
= ILClone(pdcm
->pidlFolder
);
196 CComPtr
<IPersistFolder2
> pf
= NULL
;
197 if (SUCCEEDED(m_psf
->QueryInterface(IID_PPV_ARG(IPersistFolder2
, &pf
))))
199 if (FAILED(pf
->GetCurFolder(reinterpret_cast<LPITEMIDLIST
*>(&m_pidlFolder
))))
200 ERR("GetCurFolder failed\n");
202 TRACE("pidlFolder %p\n", m_pidlFolder
);
209 CDefaultContextMenu::AddStaticEntry(const WCHAR
*szVerb
, const WCHAR
*szClass
)
211 PStaticShellEntry pEntry
= m_pStaticEntries
, pLastEntry
= NULL
;
214 if (!wcsicmp(pEntry
->szVerb
, szVerb
))
216 /* entry already exists */
220 pEntry
= pEntry
->pNext
;
223 TRACE("adding verb %s szClass %s\n", debugstr_w(szVerb
), debugstr_w(szClass
));
225 pEntry
= (StaticShellEntry
*)HeapAlloc(GetProcessHeap(), 0, sizeof(StaticShellEntry
));
228 pEntry
->pNext
= NULL
;
229 pEntry
->szVerb
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, (wcslen(szVerb
) + 1) * sizeof(WCHAR
));
231 wcscpy(pEntry
->szVerb
, szVerb
);
232 pEntry
->szClass
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, (wcslen(szClass
) + 1) * sizeof(WCHAR
));
234 wcscpy(pEntry
->szClass
, szClass
);
237 if (!wcsicmp(szVerb
, L
"open"))
239 /* open verb is always inserted in front */
240 pEntry
->pNext
= m_pStaticEntries
;
241 m_pStaticEntries
= pEntry
;
244 pLastEntry
->pNext
= pEntry
;
246 m_pStaticEntries
= pEntry
;
250 CDefaultContextMenu::AddStaticEntryForKey(HKEY hKey
, const WCHAR
*pwszClass
)
253 DWORD cchName
, dwIndex
= 0;
255 TRACE("AddStaticEntryForKey %x %ls\n", hKey
, pwszClass
);
259 cchName
= _countof(wszName
);
260 if (RegEnumKeyExW(hKey
, dwIndex
++, wszName
, &cchName
, NULL
, NULL
, NULL
, NULL
) != ERROR_SUCCESS
)
263 AddStaticEntry(wszName
, pwszClass
);
268 CDefaultContextMenu::AddStaticEntryForFileClass(const WCHAR
* szExt
)
275 static WCHAR szShell
[] = L
"\\shell";
276 static WCHAR szShellAssoc
[] = L
"SystemFileAssociations\\";
278 TRACE("AddStaticEntryForFileClass entered with %s\n", debugstr_w(szExt
));
280 Length
= wcslen(szExt
);
281 if (Length
+ (sizeof(szShell
) / sizeof(WCHAR
)) + 1 < sizeof(szBuffer
) / sizeof(WCHAR
))
283 wcscpy(szBuffer
, szExt
);
284 wcscpy(&szBuffer
[Length
], szShell
);
285 result
= RegOpenKeyExW(HKEY_CLASSES_ROOT
, szBuffer
, 0, KEY_READ
| KEY_QUERY_VALUE
, &hKey
);
286 if (result
== ERROR_SUCCESS
)
288 szBuffer
[Length
] = 0;
289 AddStaticEntryForKey(hKey
, szExt
);
294 dwBuffer
= sizeof(szBuffer
);
295 result
= RegGetValueW(HKEY_CLASSES_ROOT
, szExt
, NULL
, RRF_RT_REG_SZ
, NULL
, (LPBYTE
)szBuffer
, &dwBuffer
);
296 if (result
== ERROR_SUCCESS
)
298 Length
= wcslen(szBuffer
);
299 if (Length
+ (sizeof(szShell
) / sizeof(WCHAR
)) + 1 < sizeof(szBuffer
) / sizeof(WCHAR
))
301 wcscpy(&szBuffer
[Length
], szShell
);
302 TRACE("szBuffer %s\n", debugstr_w(szBuffer
));
304 result
= RegOpenKeyExW(HKEY_CLASSES_ROOT
, szBuffer
, 0, KEY_READ
| KEY_QUERY_VALUE
, &hKey
);
305 if (result
== ERROR_SUCCESS
)
307 szBuffer
[Length
] = 0;
308 AddStaticEntryForKey(hKey
, szBuffer
);
314 wcscpy(szBuffer
, szShellAssoc
);
315 dwBuffer
= sizeof(szBuffer
) - sizeof(szShellAssoc
) - sizeof(WCHAR
);
316 result
= RegGetValueW(HKEY_CLASSES_ROOT
, szExt
, L
"PerceivedType", RRF_RT_REG_SZ
, NULL
, (LPBYTE
)&szBuffer
[_countof(szShellAssoc
) - 1], &dwBuffer
);
317 if (result
== ERROR_SUCCESS
)
319 Length
= wcslen(&szBuffer
[_countof(szShellAssoc
)]) + _countof(szShellAssoc
);
320 wcscat(szBuffer
, L
"\\shell");
321 TRACE("szBuffer %s\n", debugstr_w(szBuffer
));
323 result
= RegOpenKeyExW(HKEY_CLASSES_ROOT
, szBuffer
, 0, KEY_READ
| KEY_QUERY_VALUE
, &hKey
);
324 if (result
== ERROR_SUCCESS
)
326 szBuffer
[Length
] = 0;
327 AddStaticEntryForKey(hKey
, szBuffer
);
338 CComPtr
<IDataObject
> pDataObj
;
340 if(SUCCEEDED(OleGetClipboard(&pDataObj
)))
345 TRACE("pDataObj=%p\n", pDataObj
.p
);
347 /* Set the FORMATETC structure*/
348 InitFormatEtc(formatetc
, RegisterClipboardFormatW(CFSTR_SHELLIDLIST
), TYMED_HGLOBAL
);
349 if(SUCCEEDED(pDataObj
->GetData(&formatetc
, &medium
)))
352 ReleaseStgMedium(&medium
);
361 DisablePasteOptions(HMENU hMenu
)
365 mii
.cbSize
= sizeof(mii
);
366 mii
.fMask
= MIIM_STATE
;
367 mii
.fState
= MFS_DISABLED
;
369 SetMenuItemInfoW(hMenu
, FCIDM_SHVIEW_INSERT
, FALSE
, &mii
);
370 SetMenuItemInfoW(hMenu
, FCIDM_SHVIEW_INSERTLINK
, FALSE
, &mii
);
374 CDefaultContextMenu::IsShellExtensionAlreadyLoaded(const CLSID
*pclsid
)
376 PDynamicShellEntry pEntry
= m_pDynamicEntries
;
380 if (!memcmp(&pEntry
->ClassID
, pclsid
, sizeof(CLSID
)))
382 pEntry
= pEntry
->pNext
;
389 CDefaultContextMenu::LoadDynamicContextMenuHandler(HKEY hKey
, const CLSID
*pclsid
)
393 TRACE("LoadDynamicContextMenuHandler entered with This %p hKey %p pclsid %s\n", this, hKey
, wine_dbgstr_guid(pclsid
));
395 if (IsShellExtensionAlreadyLoaded(pclsid
))
398 CComPtr
<IContextMenu
> pcm
;
399 hr
= SHCoCreateInstance(NULL
, pclsid
, NULL
, IID_PPV_ARG(IContextMenu
, &pcm
));
402 ERR("SHCoCreateInstance failed %x\n", GetLastError());
406 CComPtr
<IShellExtInit
> pExtInit
;
407 hr
= pcm
->QueryInterface(IID_PPV_ARG(IShellExtInit
, &pExtInit
));
410 ERR("Failed to query for interface IID_IShellExtInit hr %x pclsid %s\n", hr
, wine_dbgstr_guid(pclsid
));
414 hr
= pExtInit
->Initialize(m_pidlFolder
, m_pDataObj
, hKey
);
417 TRACE("Failed to initialize shell extension error %x pclsid %s\n", hr
, wine_dbgstr_guid(pclsid
));
421 PDynamicShellEntry pEntry
= (DynamicShellEntry
*)HeapAlloc(GetProcessHeap(), 0, sizeof(DynamicShellEntry
));
424 return E_OUTOFMEMORY
;
427 pEntry
->iIdCmdFirst
= 0;
428 pEntry
->pNext
= NULL
;
430 pEntry
->pCM
= pcm
.Detach();
431 memcpy(&pEntry
->ClassID
, pclsid
, sizeof(CLSID
));
433 if (m_pDynamicEntries
)
435 PDynamicShellEntry pLastEntry
= m_pDynamicEntries
;
437 while (pLastEntry
->pNext
)
438 pLastEntry
= pLastEntry
->pNext
;
440 pLastEntry
->pNext
= pEntry
;
443 m_pDynamicEntries
= pEntry
;
449 CDefaultContextMenu::EnumerateDynamicContextHandlerForKey(HKEY hRootKey
)
452 WCHAR wszName
[MAX_PATH
], wszBuf
[MAX_PATH
], *pwszClsid
;
457 if (RegOpenKeyExW(hRootKey
, L
"shellex\\ContextMenuHandlers", 0, KEY_READ
, &hKey
) != ERROR_SUCCESS
)
459 TRACE("RegOpenKeyExW failed\n");
466 cchName
= _countof(wszName
);
467 if (RegEnumKeyExW(hKey
, dwIndex
++, wszName
, &cchName
, NULL
, NULL
, NULL
, NULL
) != ERROR_SUCCESS
)
470 /* Key name or key value is CLSID */
472 hr
= CLSIDFromString(wszName
, &clsid
);
477 DWORD cchBuf
= _countof(wszBuf
);
478 if (RegGetValueW(hKey
, wszName
, NULL
, RRF_RT_REG_SZ
, NULL
, wszBuf
, &cchBuf
) == ERROR_SUCCESS
)
479 hr
= CLSIDFromString(wszBuf
, &clsid
);
484 if (m_bGroupPolicyActive
)
486 if (RegGetValueW(HKEY_LOCAL_MACHINE
,
487 L
"Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved",
492 NULL
) == ERROR_SUCCESS
)
494 LoadDynamicContextMenuHandler(hKey
, &clsid
);
498 LoadDynamicContextMenuHandler(hKey
, &clsid
);
507 CDefaultContextMenu::InsertMenuItemsOfDynamicContextMenuExtension(HMENU hMenu
, UINT IndexMenu
, UINT idCmdFirst
, UINT idCmdLast
)
509 if (!m_pDynamicEntries
)
516 PDynamicShellEntry pEntry
= m_pDynamicEntries
;
519 m_iIdSHEFirst
= idCmdFirst
;
522 HRESULT hr
= pEntry
->pCM
->QueryContextMenu(hMenu
, IndexMenu
++, idCmdFirst
, idCmdLast
, CMF_NORMAL
);
525 pEntry
->iIdCmdFirst
= idCmdFirst
;
526 pEntry
->NumIds
= LOWORD(hr
);
527 IndexMenu
+= pEntry
->NumIds
;
528 idCmdFirst
+= pEntry
->NumIds
+ 0x10;
530 TRACE("pEntry %p hr %x contextmenu %p cmdfirst %x num ids %x\n", pEntry
, hr
, pEntry
->pCM
, pEntry
->iIdCmdFirst
, pEntry
->NumIds
);
531 pEntry
= pEntry
->pNext
;
534 m_iIdSHELast
= idCmdFirst
;
535 TRACE("SH_LoadContextMenuHandlers first %x last %x\n", m_iIdSHEFirst
, m_iIdSHELast
);
540 CDefaultContextMenu::BuildBackgroundContextMenu(
549 TRACE("BuildBackgroundContextMenu entered\n");
551 SFGAOF rfg
= SFGAO_FILESYSTEM
| SFGAO_FOLDER
;
552 HRESULT hr
= m_psf
->GetAttributesOf(0, NULL
, &rfg
);
555 ERR("GetAttributesOf failed: %x\n", hr
);
559 if (!_ILIsDesktop(m_pidlFolder
))
561 WCHAR wszBuf
[MAX_PATH
];
563 /* view option is only available in browsing mode */
564 hSubMenu
= LoadMenuW(shell32_hInstance
, L
"MENU_001");
565 if (hSubMenu
&& LoadStringW(shell32_hInstance
, FCIDM_SHVIEW_VIEW
, wszBuf
, _countof(wszBuf
)))
567 TRACE("wszBuf %s\n", debugstr_w(wszBuf
));
570 ZeroMemory(&mii
, sizeof(mii
));
571 mii
.cbSize
= sizeof(mii
);
572 mii
.fMask
= MIIM_TYPE
| MIIM_STATE
| MIIM_SUBMENU
| MIIM_ID
;
573 mii
.fType
= MFT_STRING
;
574 mii
.wID
= iIdCmdFirst
++;
575 mii
.dwTypeData
= wszBuf
;
576 mii
.cch
= wcslen(mii
.dwTypeData
);
577 mii
.fState
= MFS_ENABLED
;
578 mii
.hSubMenu
= hSubMenu
;
579 InsertMenuItemW(hMenu
, IndexMenu
++, TRUE
, &mii
);
580 DestroyMenu(hSubMenu
);
584 hSubMenu
= LoadMenuW(shell32_hInstance
, L
"MENU_002");
587 /* merge general background context menu in */
588 iIdCmdFirst
= Shell_MergeMenus(hMenu
, GetSubMenu(hSubMenu
, 0), IndexMenu
, 0, 0xFFFF, MM_DONTREMOVESEPS
| MM_SUBMENUSHAVEIDS
) + 1;
589 DestroyMenu(hSubMenu
);
592 if (!HasClipboardData())
594 TRACE("disabling paste options\n");
595 DisablePasteOptions(hMenu
);
598 /* Directory is progid of filesystem folders only */
599 if ((rfg
& (SFGAO_FILESYSTEM
|SFGAO_FOLDER
)) == (SFGAO_FILESYSTEM
|SFGAO_FOLDER
))
601 /* Load context menu handlers */
602 TRACE("Add background handlers: %p\n", m_pidlFolder
);
604 if (RegOpenKeyExW(HKEY_CLASSES_ROOT
, L
"Directory\\Background", 0, KEY_READ
, &hKey
) == ERROR_SUCCESS
)
606 EnumerateDynamicContextHandlerForKey(hKey
);
610 if (InsertMenuItemsOfDynamicContextMenuExtension(hMenu
, GetMenuItemCount(hMenu
) - 1, iIdCmdFirst
, iIdCmdLast
))
612 /* seperate dynamic context menu items */
613 _InsertMenuItemW(hMenu
, GetMenuItemCount(hMenu
) - 1, TRUE
, -1, MFT_SEPARATOR
, NULL
, MFS_ENABLED
);
621 CDefaultContextMenu::AddStaticContextMenusToMenu(
630 mii
.cbSize
= sizeof(mii
);
631 mii
.fMask
= MIIM_ID
| MIIM_TYPE
| MIIM_STATE
| MIIM_DATA
;
632 mii
.fType
= MFT_STRING
;
634 mii
.dwTypeData
= NULL
;
635 m_iIdSCMFirst
= mii
.wID
;
637 PStaticShellEntry pEntry
= m_pStaticEntries
;
641 fState
= MFS_ENABLED
;
642 mii
.dwTypeData
= NULL
;
644 /* set first entry as default */
645 if (pEntry
== m_pStaticEntries
)
646 fState
|= MFS_DEFAULT
;
648 if (!wcsicmp(pEntry
->szVerb
, L
"open"))
650 /* override default when open verb is found */
651 fState
|= MFS_DEFAULT
;
652 idResource
= IDS_OPEN_VERB
;
654 else if (!wcsicmp(pEntry
->szVerb
, L
"explore"))
655 idResource
= IDS_EXPLORE_VERB
;
656 else if (!wcsicmp(pEntry
->szVerb
, L
"runas"))
657 idResource
= IDS_RUNAS_VERB
;
658 else if (!wcsicmp(pEntry
->szVerb
, L
"edit"))
659 idResource
= IDS_EDIT_VERB
;
660 else if (!wcsicmp(pEntry
->szVerb
, L
"find"))
661 idResource
= IDS_FIND_VERB
;
662 else if (!wcsicmp(pEntry
->szVerb
, L
"print"))
663 idResource
= IDS_PRINT_VERB
;
664 else if (!wcsicmp(pEntry
->szVerb
, L
"printto"))
666 pEntry
= pEntry
->pNext
;
672 /* By default use verb for menu item name */
673 mii
.dwTypeData
= pEntry
->szVerb
;
677 if (LoadStringW(shell32_hInstance
, idResource
, wszVerb
, _countof(wszVerb
)))
678 mii
.dwTypeData
= wszVerb
; /* use translated verb */
680 ERR("Failed to load string\n");
685 HRESULT hr
= StringCbPrintfW(wszKey
, sizeof(wszKey
), L
"%s\\shell\\%s", pEntry
->szClass
, pEntry
->szVerb
);
690 DWORD cbVerb
= sizeof(wszVerb
);
691 LONG res
= RegOpenKeyW(HKEY_CLASSES_ROOT
, wszKey
, &hkVerb
);
692 if (res
== ERROR_SUCCESS
)
694 res
= RegLoadMUIStringW(hkVerb
,
701 if (res
== ERROR_SUCCESS
)
703 /* use description for the menu entry */
704 mii
.dwTypeData
= wszVerb
;
712 mii
.cch
= wcslen(mii
.dwTypeData
);
714 InsertMenuItemW(hMenu
, IndexMenu
++, TRUE
, &mii
);
717 pEntry
= pEntry
->pNext
;
720 m_iIdSCMLast
= mii
.wID
- 1;
724 void WINAPI
_InsertMenuItemW(
736 ZeroMemory(&mii
, sizeof(mii
));
737 mii
.cbSize
= sizeof(mii
);
738 if (fType
== MFT_SEPARATOR
)
739 mii
.fMask
= MIIM_ID
| MIIM_TYPE
;
740 else if (fType
== MFT_STRING
)
742 mii
.fMask
= MIIM_ID
| MIIM_TYPE
| MIIM_STATE
;
743 if ((ULONG_PTR
)HIWORD((ULONG_PTR
)dwTypeData
) == 0)
745 if (LoadStringW(shell32_hInstance
, LOWORD((ULONG_PTR
)dwTypeData
), wszText
, _countof(wszText
)))
746 mii
.dwTypeData
= wszText
;
749 ERR("failed to load string %p\n", dwTypeData
);
754 mii
.dwTypeData
= (LPWSTR
)dwTypeData
;
760 InsertMenuItemW(hMenu
, indexMenu
, fByPosition
, &mii
);
764 CDefaultContextMenu::BuildShellItemContextMenu(
773 TRACE("BuildShellItemContextMenu entered\n");
777 hr
= m_psf
->GetDisplayNameOf(m_apidl
[0], SHGDN_FORPARSING
, &strFile
);
780 WCHAR wszPath
[MAX_PATH
];
781 hr
= StrRetToBufW(&strFile
, m_apidl
[0], wszPath
, _countof(wszPath
));
784 LPCWSTR pwszExt
= PathFindExtensionW(wszPath
);
787 /* enumerate dynamic/static for a given file class */
788 if (RegOpenKeyExW(HKEY_CLASSES_ROOT
, pwszExt
, 0, KEY_READ
, &hKey
) == ERROR_SUCCESS
)
790 /* add static verbs */
791 AddStaticEntryForFileClass(pwszExt
);
793 /* load dynamic extensions from file extension key */
794 EnumerateDynamicContextHandlerForKey(hKey
);
799 DWORD dwSize
= sizeof(wszTemp
);
800 if (RegGetValueW(HKEY_CLASSES_ROOT
, pwszExt
, NULL
, RRF_RT_REG_SZ
, NULL
, wszTemp
, &dwSize
) == ERROR_SUCCESS
)
802 if (RegOpenKeyExW(HKEY_CLASSES_ROOT
, wszTemp
, 0, KEY_READ
, &hKey
) == ERROR_SUCCESS
)
804 /* add static verbs from progid key */
805 AddStaticEntryForFileClass(wszTemp
);
807 /* load dynamic extensions from progid key */
808 EnumerateDynamicContextHandlerForKey(hKey
);
816 ERR("GetDisplayNameOf failed: %x\n", hr
);
818 GUID
*pGuid
= _ILGetGUIDPointer(m_apidl
[0]);
824 wcscpy(buffer
, L
"CLSID\\");
825 hr
= StringFromCLSID(*pGuid
, &pwszCLSID
);
828 wcscpy(&buffer
[6], pwszCLSID
);
829 TRACE("buffer %s\n", debugstr_w(buffer
));
830 if (RegOpenKeyExW(HKEY_CLASSES_ROOT
, buffer
, 0, KEY_READ
, &hKey
) == ERROR_SUCCESS
)
832 EnumerateDynamicContextHandlerForKey(hKey
);
833 AddStaticEntryForFileClass(buffer
);
836 CoTaskMemFree(pwszCLSID
);
840 if (_ILIsDrive(m_apidl
[0]))
842 AddStaticEntryForFileClass(L
"Drive");
843 if (RegOpenKeyExW(HKEY_CLASSES_ROOT
, L
"Drive", 0, KEY_READ
, &hKey
) == ERROR_SUCCESS
)
845 EnumerateDynamicContextHandlerForKey(hKey
);
851 /* add static actions */
852 SFGAOF rfg
= SFGAO_BROWSABLE
| SFGAO_CANCOPY
| SFGAO_CANLINK
| SFGAO_CANMOVE
| SFGAO_CANDELETE
| SFGAO_CANRENAME
| SFGAO_HASPROPSHEET
| SFGAO_FILESYSTEM
| SFGAO_FOLDER
;
853 hr
= m_psf
->GetAttributesOf(m_cidl
, m_apidl
, &rfg
);
856 ERR("GetAttributesOf failed: %x\n", hr
);
860 if (rfg
& SFGAO_FOLDER
)
862 /* add the default verbs open / explore */
863 AddStaticEntryForFileClass(L
"Folder");
864 if (RegOpenKeyExW(HKEY_CLASSES_ROOT
, L
"Folder", 0, KEY_READ
, &hKey
) == ERROR_SUCCESS
)
866 EnumerateDynamicContextHandlerForKey(hKey
);
870 /* Directory is only loaded for real filesystem directories */
871 if (rfg
& SFGAO_FILESYSTEM
)
873 AddStaticEntryForFileClass(L
"Directory");
874 if (RegOpenKeyExW(HKEY_CLASSES_ROOT
, L
"Directory", 0, KEY_READ
, &hKey
) == ERROR_SUCCESS
)
876 EnumerateDynamicContextHandlerForKey(hKey
);
882 /* AllFilesystemObjects class is loaded only for files and directories */
883 if (rfg
& SFGAO_FILESYSTEM
)
885 if (RegOpenKeyExW(HKEY_CLASSES_ROOT
, L
"AllFilesystemObjects", 0, KEY_READ
, &hKey
) == ERROR_SUCCESS
)
887 /* sendto service is registered here */
888 EnumerateDynamicContextHandlerForKey(hKey
);
892 if (!(rfg
& SFGAO_FOLDER
))
894 if (RegOpenKeyExW(HKEY_CLASSES_ROOT
, L
"*", 0, KEY_READ
, &hKey
) == ERROR_SUCCESS
)
896 EnumerateDynamicContextHandlerForKey(hKey
);
902 /* add static context menu handlers */
903 UINT IndexMenu
= AddStaticContextMenusToMenu(hMenu
, 0);
905 /* now process dynamic context menu handlers */
906 BOOL bAddSep
= FALSE
;
907 IndexMenu
= InsertMenuItemsOfDynamicContextMenuExtension(hMenu
, IndexMenu
, iIdCmdFirst
, iIdCmdLast
);
908 TRACE("IndexMenu %d\n", IndexMenu
);
910 if (_ILIsDrive(m_apidl
[0]))
912 char szDrive
[8] = {0};
915 _ILGetDrive(m_apidl
[0], szDrive
, sizeof(szDrive
));
916 if (GetVolumeInformationA(szDrive
, NULL
, 0, NULL
, NULL
, &dwFlags
, NULL
, 0))
918 /* Disable format if read only */
919 if (!(dwFlags
& FILE_READ_ONLY_VOLUME
))
921 _InsertMenuItemW(hMenu
, IndexMenu
++, TRUE
, 0, MFT_SEPARATOR
, NULL
, 0);
922 _InsertMenuItemW(hMenu
, IndexMenu
++, TRUE
, 0x7ABC, MFT_STRING
, MAKEINTRESOURCEW(IDS_FORMATDRIVE
), MFS_ENABLED
);
928 BOOL bClipboardData
= (HasClipboardData() && (rfg
& SFGAO_FILESYSTEM
));
929 if (rfg
& (SFGAO_CANCOPY
| SFGAO_CANMOVE
) || bClipboardData
)
931 _InsertMenuItemW(hMenu
, IndexMenu
++, TRUE
, 0, MFT_SEPARATOR
, NULL
, 0);
932 if (rfg
& SFGAO_CANMOVE
)
933 _InsertMenuItemW(hMenu
, IndexMenu
++, TRUE
, FCIDM_SHVIEW_CUT
, MFT_STRING
, MAKEINTRESOURCEW(IDS_CUT
), MFS_ENABLED
);
934 if (rfg
& SFGAO_CANCOPY
)
935 _InsertMenuItemW(hMenu
, IndexMenu
++, TRUE
, FCIDM_SHVIEW_COPY
, MFT_STRING
, MAKEINTRESOURCEW(IDS_COPY
), MFS_ENABLED
);
937 _InsertMenuItemW(hMenu
, IndexMenu
++, TRUE
, FCIDM_SHVIEW_INSERT
, MFT_STRING
, MAKEINTRESOURCEW(IDS_PASTE
), MFS_ENABLED
);
942 if (rfg
& SFGAO_CANLINK
)
945 _InsertMenuItemW(hMenu
, IndexMenu
++, TRUE
, 0, MFT_SEPARATOR
, NULL
, 0);
946 _InsertMenuItemW(hMenu
, IndexMenu
++, TRUE
, FCIDM_SHVIEW_CREATELINK
, MFT_STRING
, MAKEINTRESOURCEW(IDS_CREATELINK
), MFS_ENABLED
);
949 if (rfg
& SFGAO_CANDELETE
)
954 _InsertMenuItemW(hMenu
, IndexMenu
++, TRUE
, 0, MFT_SEPARATOR
, NULL
, 0);
956 _InsertMenuItemW(hMenu
, IndexMenu
++, TRUE
, FCIDM_SHVIEW_DELETE
, MFT_STRING
, MAKEINTRESOURCEW(IDS_DELETE
), MFS_ENABLED
);
959 if (rfg
& SFGAO_CANRENAME
)
963 _InsertMenuItemW(hMenu
, IndexMenu
++, TRUE
, 0, MFT_SEPARATOR
, NULL
, 0);
965 _InsertMenuItemW(hMenu
, IndexMenu
++, TRUE
, FCIDM_SHVIEW_RENAME
, MFT_STRING
, MAKEINTRESOURCEW(IDS_RENAME
), MFS_ENABLED
);
969 if (rfg
& SFGAO_HASPROPSHEET
)
971 _InsertMenuItemW(hMenu
, IndexMenu
++, TRUE
, 0, MFT_SEPARATOR
, NULL
, 0);
972 _InsertMenuItemW(hMenu
, IndexMenu
++, TRUE
, FCIDM_SHVIEW_PROPERTIES
, MFT_STRING
, MAKEINTRESOURCEW(IDS_PROPERTIES
), MFS_ENABLED
);
980 CDefaultContextMenu::QueryContextMenu(
988 idCmdFirst
= BuildShellItemContextMenu(hMenu
, idCmdFirst
, idCmdLast
, uFlags
);
990 idCmdFirst
= BuildBackgroundContextMenu(hMenu
, idCmdFirst
, idCmdLast
, uFlags
);
997 NotifyShellViewWindow(LPCMINVOKECOMMANDINFO lpcmi
, BOOL bRefresh
)
999 /* Note: CWM_GETISHELLBROWSER returns not referenced object */
1000 LPSHELLBROWSER lpSB
= (LPSHELLBROWSER
)SendMessageA(lpcmi
->hwnd
, CWM_GETISHELLBROWSER
, 0, 0);
1004 CComPtr
<IShellView
> lpSV
;
1005 if (FAILED(lpSB
->QueryActiveShellView(&lpSV
)))
1009 if (SUCCEEDED(lpSV
->GetWindow(&hwndSV
)))
1010 SendMessageW(hwndSV
, WM_COMMAND
, MAKEWPARAM(LOWORD(lpcmi
->lpVerb
), 0), 0);
1015 CDefaultContextMenu::DoRefresh(
1016 LPCMINVOKECOMMANDINFO lpcmi
)
1018 CComPtr
<IPersistFolder2
> ppf2
= NULL
;
1020 HRESULT hr
= m_psf
->QueryInterface(IID_PPV_ARG(IPersistFolder2
, &ppf2
));
1023 hr
= ppf2
->GetCurFolder(&pidl
);
1026 SHChangeNotify(SHCNE_UPDATEDIR
, SHCNF_IDLIST
, pidl
, NULL
);
1034 CDefaultContextMenu::DoPaste(
1035 LPCMINVOKECOMMANDINFO lpcmi
, BOOL bLink
)
1039 CComPtr
<IDataObject
> pda
;
1040 hr
= OleGetClipboard(&pda
);
1044 CComPtr
<IShellFolder
> psfDesktop
;
1045 CComPtr
<IShellFolder
> psfTarget
= NULL
;
1047 hr
= SHGetDesktopFolder(&psfDesktop
);
1051 /* Find target folder */
1054 hr
= m_psf
->BindToObject(m_apidl
[0], NULL
, IID_PPV_ARG(IShellFolder
, &psfTarget
));
1058 CComPtr
<IPersistFolder2
> ppf2
= NULL
;
1061 /* cidl is zero due to explorer view */
1062 hr
= m_psf
->QueryInterface(IID_PPV_ARG(IPersistFolder2
, &ppf2
));
1065 hr
= ppf2
->GetCurFolder(&pidl
);
1068 if (_ILIsDesktop(pidl
))
1070 /* use desktop shellfolder */
1071 psfTarget
= psfDesktop
;
1075 /* retrieve target desktop folder */
1076 hr
= psfDesktop
->BindToObject(pidl
, NULL
, IID_PPV_ARG(IShellFolder
, &psfTarget
));
1078 TRACE("psfTarget %x %p, Desktop %u\n", hr
, psfTarget
.p
, _ILIsDesktop(pidl
));
1086 ERR("no IShellFolder\n");
1090 FORMATETC formatetc2
;
1092 InitFormatEtc(formatetc2
, RegisterClipboardFormatW(CFSTR_PREFERREDDROPEFFECT
), TYMED_HGLOBAL
);
1096 if (SUCCEEDED(pda
->GetData(&formatetc2
, &medium2
)))
1098 DWORD
* pdwFlag
= (DWORD
*)GlobalLock(medium2
.hGlobal
);
1101 if (*pdwFlag
== DROPEFFECT_COPY
)
1107 ERR("No drop effect obtained");
1109 GlobalUnlock(medium2
.hGlobal
);
1114 dwKey
= MK_CONTROL
|MK_SHIFT
;
1117 CComPtr
<IDropTarget
> pdrop
;
1118 hr
= psfTarget
->CreateViewObject(NULL
, IID_PPV_ARG(IDropTarget
, &pdrop
));
1121 ERR("Error getting IDropTarget interface\n");
1125 SHSimulateDrop(pdrop
, pda
, dwKey
, NULL
, NULL
);
1127 TRACE("CP result %x\n", hr
);
1132 CDefaultContextMenu::DoOpenOrExplore(
1133 LPCMINVOKECOMMANDINFO lpcmi
)
1140 CDefaultContextMenu::DoCreateLink(
1141 LPCMINVOKECOMMANDINFO lpcmi
)
1143 CComPtr
<IDataObject
> pDataObj
;
1144 CComPtr
<IDropTarget
> pDT
;
1146 CComPtr
<IPersistFolder2
> ppf2
= NULL
;
1148 CComPtr
<IShellFolder
> psfDesktop
;
1149 CComPtr
<IShellFolder
> psfTarget
= NULL
;
1151 hr
= SHGetDesktopFolder(&psfDesktop
);
1155 if (SUCCEEDED(hr
= SHCreateDataObject(m_pidlFolder
, m_cidl
, m_apidl
, NULL
, IID_PPV_ARG(IDataObject
, &pDataObj
))))
1157 hr
= m_psf
->QueryInterface(IID_PPV_ARG(IPersistFolder2
, &ppf2
));
1160 hr
= ppf2
->GetCurFolder(&pidl
);
1163 if (_ILIsDesktop(pidl
))
1165 /* use desktop shellfolder */
1166 psfTarget
= psfDesktop
;
1170 /* retrieve target desktop folder */
1171 hr
= psfDesktop
->BindToObject(pidl
, NULL
, IID_PPV_ARG(IShellFolder
, &psfTarget
));
1173 TRACE("psfTarget %x %p, Desktop %u\n", hr
, psfTarget
.p
, _ILIsDesktop(pidl
));
1182 ERR("no IShellFolder\n");
1186 hr
= psfTarget
->CreateViewObject(NULL
, IID_PPV_ARG(IDropTarget
, &pDT
));
1189 ERR("no IDropTarget Interface\n");
1192 SHSimulateDrop(pDT
, pDataObj
, MK_CONTROL
|MK_SHIFT
, NULL
, NULL
);
1197 HRESULT
CDefaultContextMenu::DoDelete(LPCMINVOKECOMMANDINFO lpcmi
)
1199 DoDeleteAsync(m_pDataObj
, lpcmi
->fMask
);
1204 CDefaultContextMenu::DoCopyOrCut(
1205 LPCMINVOKECOMMANDINFO lpcmi
,
1208 CComPtr
<IDataObject
> pDataObj
;
1211 if (SUCCEEDED(SHCreateDataObject(m_pidlFolder
, m_cidl
, m_apidl
, NULL
, IID_PPV_ARG(IDataObject
, &pDataObj
))))
1215 FORMATETC formatetc
;
1217 InitFormatEtc(formatetc
, RegisterClipboardFormatW(CFSTR_PREFERREDDROPEFFECT
), TYMED_HGLOBAL
);
1218 pDataObj
->GetData(&formatetc
, &medium
);
1219 DWORD
* pdwFlag
= (DWORD
*)GlobalLock(medium
.hGlobal
);
1221 *pdwFlag
= DROPEFFECT_MOVE
;
1222 GlobalUnlock(medium
.hGlobal
);
1223 pDataObj
->SetData(&formatetc
, &medium
, TRUE
);
1226 hr
= OleSetClipboard(pDataObj
);
1230 /* Note: CWM_GETISHELLBROWSER returns not referenced object */
1231 LPSHELLBROWSER lpSB
= (LPSHELLBROWSER
)SendMessageA(lpcmi
->hwnd
, CWM_GETISHELLBROWSER
, 0, 0);
1234 ERR("failed to get shellbrowser\n");
1238 CComPtr
<IShellView
> lpSV
;
1239 hr
= lpSB
->QueryActiveShellView(&lpSV
);
1242 ERR("failed to query the active shellview\n");
1246 hr
= lpSV
->GetItemObject(SVGIO_SELECTION
, IID_PPV_ARG(IDataObject
, &pDataObj
));
1249 hr
= OleSetClipboard(pDataObj
);
1251 ERR("OleSetClipboard failed");
1252 pDataObj
->Release();
1254 ERR("failed to get item object\n");
1260 CDefaultContextMenu::DoRename(
1261 LPCMINVOKECOMMANDINFO lpcmi
)
1263 /* get the active IShellView. Note: CWM_GETISHELLBROWSER returns not referenced object */
1264 LPSHELLBROWSER lpSB
= (LPSHELLBROWSER
)SendMessageA(lpcmi
->hwnd
, CWM_GETISHELLBROWSER
, 0, 0);
1267 ERR("CWM_GETISHELLBROWSER failed\n");
1271 /* is the treeview focused */
1273 if (SUCCEEDED(lpSB
->GetControlWindow(FCW_TREE
, &hwnd
)))
1275 HTREEITEM hItem
= TreeView_GetSelection(hwnd
);
1277 (void)TreeView_EditLabel(hwnd
, hItem
);
1280 CComPtr
<IShellView
> lpSV
;
1281 HRESULT hr
= lpSB
->QueryActiveShellView(&lpSV
);
1284 ERR("CWM_GETISHELLBROWSER failed\n");
1288 SVSIF selFlags
= SVSI_DESELECTOTHERS
| SVSI_EDIT
| SVSI_ENSUREVISIBLE
| SVSI_FOCUSED
| SVSI_SELECT
;
1289 lpSV
->SelectItem(m_apidl
[0], selFlags
);
1294 CDefaultContextMenu::DoProperties(
1295 LPCMINVOKECOMMANDINFO lpcmi
)
1298 const ITEMIDLIST
*pidlParent
= m_pidlFolder
, *pidlChild
;
1302 CComPtr
<IPersistFolder2
> pf
;
1304 /* pidlFolder is optional */
1305 if (SUCCEEDED(m_psf
->QueryInterface(IID_PPV_ARG(IPersistFolder2
, &pf
))))
1307 pf
->GetCurFolder((_ITEMIDLIST
**)&pidlParent
);
1312 pidlChild
= m_apidl
[0];
1315 /* Set pidlChild to last pidl of current folder */
1316 if (pidlParent
== m_pidlFolder
)
1317 pidlParent
= (ITEMIDLIST
*)ILClone(pidlParent
);
1319 pidlChild
= (ITEMIDLIST
*)ILClone(ILFindLastID(pidlParent
));
1320 ILRemoveLastID((ITEMIDLIST
*)pidlParent
);
1323 if (_ILIsMyComputer(pidlChild
))
1325 if (32 >= (UINT
)ShellExecuteW(lpcmi
->hwnd
, L
"open", L
"rundll32.exe shell32.dll,Control_RunDLL sysdm.cpl", NULL
, NULL
, SW_SHOWNORMAL
))
1328 else if (_ILIsDesktop(pidlChild
))
1330 if (32 >= (UINT
)ShellExecuteW(lpcmi
->hwnd
, L
"open", L
"rundll32.exe shell32.dll,Control_RunDLL desk.cpl", NULL
, NULL
, SW_SHOWNORMAL
))
1333 else if (_ILIsDrive(pidlChild
))
1335 WCHAR wszBuf
[MAX_PATH
];
1336 ILGetDisplayName(pidlChild
, wszBuf
);
1337 if (!SH_ShowDriveProperties(wszBuf
, pidlParent
, &pidlChild
))
1340 else if (_ILIsNetHood(pidlChild
))
1343 if (32 >= (UINT
)ShellExecuteW(NULL
, L
"open", L
"explorer.exe",
1344 L
"::{7007ACC7-3202-11D1-AAD2-00805FC1270E}",
1345 NULL
, SW_SHOWDEFAULT
))
1348 else if (_ILIsBitBucket(pidlChild
))
1350 /* FIXME: detect the drive path of bitbucket if appropiate */
1351 if(!SH_ShowRecycleBinProperties(L
'C'))
1357 WARN("SHMultiFileProperties is not yet implemented\n");
1360 hr
= m_psf
->GetDisplayNameOf(pidlChild
, SHGDN_FORPARSING
, &strFile
);
1363 WCHAR wszBuf
[MAX_PATH
];
1364 hr
= StrRetToBufW(&strFile
, pidlChild
, wszBuf
, _countof(wszBuf
));
1366 hr
= SH_ShowPropertiesDialog(wszBuf
, pidlParent
, &pidlChild
);
1368 ERR("StrRetToBufW failed\n");
1371 ERR("IShellFolder_GetDisplayNameOf failed for apidl\n");
1374 /* Free allocated PIDLs */
1375 if (pidlParent
!= m_pidlFolder
)
1376 ILFree((ITEMIDLIST
*)pidlParent
);
1377 if (m_cidl
< 1 || pidlChild
!= m_apidl
[0])
1378 ILFree((ITEMIDLIST
*)pidlChild
);
1384 CDefaultContextMenu::DoFormat(
1385 LPCMINVOKECOMMANDINFO lpcmi
)
1387 char szDrive
[8] = {0};
1389 if (!_ILGetDrive(m_apidl
[0], szDrive
, sizeof(szDrive
)))
1391 ERR("pidl is not a drive\n");
1395 SHFormatDrive(lpcmi
->hwnd
, szDrive
[0] - 'A', SHFMT_ID_DEFAULT
, 0);
1399 // This code is taken from CNewMenu and should be shared between the 2 classes
1401 CDefaultContextMenu::DoCreateNewFolder(
1402 LPCMINVOKECOMMANDINFO lpici
)
1404 WCHAR wszPath
[MAX_PATH
];
1405 WCHAR wszName
[MAX_PATH
];
1406 WCHAR wszNewFolder
[25];
1409 /* Get folder path */
1410 hr
= SHGetPathFromIDListW(m_pidlFolder
, wszPath
);
1411 if (FAILED_UNEXPECTEDLY(hr
))
1414 if (!LoadStringW(shell32_hInstance
, IDS_NEWFOLDER
, wszNewFolder
, _countof(wszNewFolder
)))
1417 /* Create the name of the new directory */
1418 if (!PathYetAnotherMakeUniqueName(wszName
, wszPath
, NULL
, wszNewFolder
))
1421 /* Create the new directory and show the appropriate dialog in case of error */
1422 if (SHCreateDirectory(lpici
->hwnd
, wszName
) != ERROR_SUCCESS
)
1425 /* Show and select the new item in the def view */
1426 CComPtr
<IShellBrowser
> lpSB
;
1427 CComPtr
<IShellView
> lpSV
;
1429 PITEMID_CHILD pidlNewItem
;
1431 /* Notify the view object about the new item */
1432 SHChangeNotify(SHCNE_MKDIR
, SHCNF_PATHW
, (LPCVOID
)wszName
, NULL
);
1434 /* FIXME: I think that this can be implemented using callbacks to the shell folder */
1436 /* Note: CWM_GETISHELLBROWSER returns shell browser without adding reference */
1437 lpSB
= (LPSHELLBROWSER
)SendMessageA(lpici
->hwnd
, CWM_GETISHELLBROWSER
, 0, 0);
1441 hr
= lpSB
->QueryActiveShellView(&lpSV
);
1445 /* Attempt to get the pidl of the new item */
1446 hr
= SHILCreateFromPathW(wszName
, &pidl
, NULL
);
1447 if (FAILED_UNEXPECTEDLY(hr
))
1450 pidlNewItem
= ILFindLastID(pidl
);
1452 hr
= lpSV
->SelectItem(pidlNewItem
, SVSI_DESELECTOTHERS
| SVSI_EDIT
| SVSI_ENSUREVISIBLE
|
1453 SVSI_FOCUSED
| SVSI_SELECT
);
1460 PDynamicShellEntry
CDefaultContextMenu::GetDynamicEntry(UINT idCmd
)
1462 PDynamicShellEntry pEntry
= m_pDynamicEntries
;
1464 while(pEntry
&& idCmd
> pEntry
->iIdCmdFirst
+ pEntry
->NumIds
)
1465 pEntry
= pEntry
->pNext
;
1470 if (idCmd
< pEntry
->iIdCmdFirst
|| idCmd
> pEntry
->iIdCmdFirst
+ pEntry
->NumIds
)
1476 //FIXME: 260 is correct, but should this be part of the SDK or just MAX_PATH?
1477 #define MAX_VERB 260
1480 CDefaultContextMenu::MapVerbToCmdId(PVOID Verb
, PUINT idCmd
, BOOL IsUnicode
)
1482 WCHAR UnicodeStr
[MAX_VERB
];
1484 /* Loop through all the static verbs looking for a match */
1485 for (UINT i
= 0; i
< _countof(g_StaticInvokeCmdMap
); i
++)
1487 /* We can match both ANSI and unicode strings */
1490 /* The static verbs are ANSI, get a unicode version before doing the compare */
1491 SHAnsiToUnicode(g_StaticInvokeCmdMap
[i
].szStringVerb
, UnicodeStr
, MAX_VERB
);
1492 if (!wcscmp(UnicodeStr
, (LPWSTR
)Verb
))
1494 /* Return the Corresponding Id */
1495 *idCmd
= g_StaticInvokeCmdMap
[i
].IntVerb
;
1501 if (!strcmp(g_StaticInvokeCmdMap
[i
].szStringVerb
, (LPSTR
)Verb
))
1503 *idCmd
= g_StaticInvokeCmdMap
[i
].IntVerb
;
1513 CDefaultContextMenu::DoDynamicShellExtensions(
1514 LPCMINVOKECOMMANDINFO lpcmi
)
1516 TRACE("verb %p first %x last %x", lpcmi
->lpVerb
, m_iIdSHEFirst
, m_iIdSHELast
);
1518 UINT idCmd
= LOWORD(lpcmi
->lpVerb
);
1519 PDynamicShellEntry pEntry
= GetDynamicEntry(idCmd
);
1523 /* invoke the dynamic context menu */
1524 lpcmi
->lpVerb
= MAKEINTRESOURCEA(idCmd
- pEntry
->iIdCmdFirst
);
1525 return pEntry
->pCM
->InvokeCommand(lpcmi
);
1529 CDefaultContextMenu::BrowserFlagsFromVerb(LPCMINVOKECOMMANDINFO lpcmi
, PStaticShellEntry pEntry
)
1531 LPSHELLBROWSER lpSB
;
1539 /* Get a pointer to the shell browser */
1540 lpSB
= (LPSHELLBROWSER
)SendMessageA(lpcmi
->hwnd
, CWM_GETISHELLBROWSER
, 0, 0);
1544 /* See if we are in Explore or Browse mode. If the browser's tree is present, we are in Explore mode.*/
1545 if (SUCCEEDED(lpSB
->GetControlWindow(FCW_TREE
, &hwndTree
)) && hwndTree
)
1546 FlagsName
= L
"ExplorerFlags";
1548 FlagsName
= L
"BrowserFlags";
1550 /* Try to get the flag from the verb */
1551 hr
= StringCbPrintfW(wszKey
, sizeof(wszKey
), L
"%s\\shell\\%s", pEntry
->szClass
, pEntry
->szVerb
);
1555 cbVerb
= sizeof(wFlags
);
1556 if (RegGetValueW(HKEY_CLASSES_ROOT
, wszKey
, FlagsName
, RRF_RT_REG_DWORD
, NULL
, &wFlags
, &cbVerb
) == ERROR_SUCCESS
)
1565 CDefaultContextMenu::TryToBrowse(
1566 LPCMINVOKECOMMANDINFO lpcmi
, LPCITEMIDLIST pidl
, DWORD wFlags
)
1568 LPSHELLBROWSER lpSB
= (LPSHELLBROWSER
)SendMessageW(lpcmi
->hwnd
, CWM_GETISHELLBROWSER
, 0, 0);
1574 hr
= lpSB
->BrowseObject(ILCombine(m_pidlFolder
, pidl
), wFlags
);
1580 CDefaultContextMenu::InvokePidl(LPCMINVOKECOMMANDINFO lpcmi
, LPCITEMIDLIST pidl
, PStaticShellEntry pEntry
)
1582 LPITEMIDLIST pidlFull
= ILCombine(m_pidlFolder
, pidl
);
1583 if (pidlFull
== NULL
)
1588 WCHAR wszPath
[MAX_PATH
];
1589 BOOL bHasPath
= SHGetPathFromIDListW(pidlFull
, wszPath
);
1591 WCHAR wszDir
[MAX_PATH
];
1594 wcscpy(wszDir
, wszPath
);
1595 PathRemoveFileSpec(wszDir
);
1599 SHGetPathFromIDListW(m_pidlFolder
, wszDir
);
1603 RegOpenKeyExW(HKEY_CLASSES_ROOT
, pEntry
->szClass
, 0, KEY_READ
, &hkeyClass
);
1605 SHELLEXECUTEINFOW sei
;
1606 ZeroMemory(&sei
, sizeof(sei
));
1607 sei
.cbSize
= sizeof(sei
);
1608 sei
.hwnd
= lpcmi
->hwnd
;
1609 sei
.nShow
= SW_SHOWNORMAL
;
1610 sei
.lpVerb
= pEntry
->szVerb
;
1611 sei
.lpDirectory
= wszDir
;
1612 sei
.lpIDList
= pidlFull
;
1613 sei
.hkeyClass
= hkeyClass
;
1614 sei
.fMask
= SEE_MASK_CLASSKEY
| SEE_MASK_IDLIST
;
1617 sei
.lpFile
= wszPath
;
1620 ShellExecuteExW(&sei
);
1622 RegCloseKey(hkeyClass
);
1630 CDefaultContextMenu::DoStaticShellExtensions(
1631 LPCMINVOKECOMMANDINFO lpcmi
)
1633 PStaticShellEntry pEntry
= m_pStaticEntries
;
1634 INT iCmd
= LOWORD(lpcmi
->lpVerb
) - m_iIdSCMFirst
;
1638 while (pEntry
&& (iCmd
--) > 0)
1639 pEntry
= pEntry
->pNext
;
1644 /* Get the browse flags to see if we need to browse */
1645 DWORD wFlags
= BrowserFlagsFromVerb(lpcmi
, pEntry
);
1646 BOOL bBrowsed
= FALSE
;
1648 for (i
=0; i
< m_cidl
; i
++)
1650 /* Check if we need to browse */
1653 /* In xp if we have browsed, we don't open any more folders .
1654 * In win7 we browse to the first folder we find and
1655 * open new windows fo for each of the rest of the folders */
1659 hr
= TryToBrowse(lpcmi
, m_apidl
[i
], wFlags
);
1667 InvokePidl(lpcmi
, m_apidl
[i
], pEntry
);
1675 CDefaultContextMenu::InvokeCommand(
1676 LPCMINVOKECOMMANDINFO lpcmi
)
1678 CMINVOKECOMMANDINFO LocalInvokeInfo
;
1682 /* Take a local copy of the fixed members of the
1683 struct as we might need to modify the verb */
1684 LocalInvokeInfo
= *lpcmi
;
1686 /* Check if this is a string verb */
1687 if (HIWORD(LocalInvokeInfo
.lpVerb
))
1689 /* Get the ID which corresponds to this verb, and update our local copy */
1690 if (MapVerbToCmdId((LPVOID
)LocalInvokeInfo
.lpVerb
, &CmdId
, FALSE
))
1691 LocalInvokeInfo
.lpVerb
= MAKEINTRESOURCEA(CmdId
);
1694 /* Check if this is a Id */
1695 switch (LOWORD(LocalInvokeInfo
.lpVerb
))
1697 case FCIDM_SHVIEW_BIGICON
:
1698 case FCIDM_SHVIEW_SMALLICON
:
1699 case FCIDM_SHVIEW_LISTVIEW
:
1700 case FCIDM_SHVIEW_REPORTVIEW
:
1701 case 0x30: /* FIX IDS in resource files */
1705 case FCIDM_SHVIEW_AUTOARRANGE
:
1706 case FCIDM_SHVIEW_SNAPTOGRID
:
1707 Result
= NotifyShellViewWindow(&LocalInvokeInfo
, FALSE
);
1709 case FCIDM_SHVIEW_REFRESH
:
1710 Result
= DoRefresh(&LocalInvokeInfo
);
1712 case FCIDM_SHVIEW_INSERT
:
1713 Result
= DoPaste(&LocalInvokeInfo
, FALSE
);
1715 case FCIDM_SHVIEW_INSERTLINK
:
1716 Result
= DoPaste(&LocalInvokeInfo
, TRUE
);
1718 case FCIDM_SHVIEW_OPEN
:
1719 case FCIDM_SHVIEW_EXPLORE
:
1720 Result
= DoOpenOrExplore(&LocalInvokeInfo
);
1722 case FCIDM_SHVIEW_COPY
:
1723 case FCIDM_SHVIEW_CUT
:
1724 Result
= DoCopyOrCut(&LocalInvokeInfo
, LOWORD(LocalInvokeInfo
.lpVerb
) == FCIDM_SHVIEW_COPY
);
1726 case FCIDM_SHVIEW_CREATELINK
:
1727 Result
= DoCreateLink(&LocalInvokeInfo
);
1729 case FCIDM_SHVIEW_DELETE
:
1730 Result
= DoDelete(&LocalInvokeInfo
);
1732 case FCIDM_SHVIEW_RENAME
:
1733 Result
= DoRename(&LocalInvokeInfo
);
1735 case FCIDM_SHVIEW_PROPERTIES
:
1736 Result
= DoProperties(&LocalInvokeInfo
);
1739 Result
= DoFormat(&LocalInvokeInfo
);
1741 case FCIDM_SHVIEW_NEWFOLDER
:
1742 Result
= DoCreateNewFolder(&LocalInvokeInfo
);
1745 Result
= E_UNEXPECTED
;
1749 /* Check for ID's we didn't find a handler for */
1750 if (Result
== E_UNEXPECTED
)
1752 if (m_iIdSHEFirst
&& m_iIdSHELast
)
1754 if (LOWORD(LocalInvokeInfo
.lpVerb
) >= m_iIdSHEFirst
&& LOWORD(LocalInvokeInfo
.lpVerb
) <= m_iIdSHELast
)
1755 Result
= DoDynamicShellExtensions(&LocalInvokeInfo
);
1758 if (m_iIdSCMFirst
&& m_iIdSCMLast
)
1760 if (LOWORD(LocalInvokeInfo
.lpVerb
) >= m_iIdSCMFirst
&& LOWORD(LocalInvokeInfo
.lpVerb
) <= m_iIdSCMLast
)
1761 Result
= DoStaticShellExtensions(&LocalInvokeInfo
);
1765 if (Result
== E_UNEXPECTED
)
1766 FIXME("Unhandled Verb %xl\n", LOWORD(LocalInvokeInfo
.lpVerb
));
1773 CDefaultContextMenu::GetCommandString(
1780 /* We don't handle the help text yet */
1781 if (uFlags
== GCS_HELPTEXTA
||
1782 uFlags
== GCS_HELPTEXTW
)
1787 /* Loop looking for a matching Id */
1788 for (UINT i
= 0; i
< _countof(g_StaticInvokeCmdMap
); i
++)
1790 if (g_StaticInvokeCmdMap
[i
].IntVerb
== idCommand
)
1792 /* Validation just returns S_OK on a match */
1793 if (uFlags
== GCS_VALIDATEA
|| uFlags
== GCS_VALIDATEW
)
1796 /* Return a copy of the ANSI verb */
1797 if (uFlags
== GCS_VERBA
)
1798 return StringCchCopyA(lpszName
, uMaxNameLen
, g_StaticInvokeCmdMap
[i
].szStringVerb
);
1800 /* Convert the ANSI verb to unicode and return that */
1801 if (uFlags
== GCS_VERBW
)
1803 if (SHAnsiToUnicode(g_StaticInvokeCmdMap
[i
].szStringVerb
, (LPWSTR
)lpszName
, uMaxNameLen
))
1809 return E_INVALIDARG
;
1814 CDefaultContextMenu::HandleMenuMsg(
1819 /* FIXME: Should we implement this as well? */
1825 CDefaultContextMenu::HandleMenuMsg2(
1833 case WM_INITMENUPOPUP
:
1835 PDynamicShellEntry pEntry
= m_pDynamicEntries
;
1838 SHForwardContextMenuMsg(pEntry
->pCM
, uMsg
, wParam
, lParam
, plResult
, TRUE
);
1839 pEntry
= pEntry
->pNext
;
1845 DRAWITEMSTRUCT
* pDrawStruct
= reinterpret_cast<DRAWITEMSTRUCT
*>(lParam
);
1846 PDynamicShellEntry pEntry
= GetDynamicEntry(pDrawStruct
->itemID
);
1848 SHForwardContextMenuMsg(pEntry
->pCM
, uMsg
, wParam
, lParam
, plResult
, TRUE
);
1851 case WM_MEASUREITEM
:
1853 MEASUREITEMSTRUCT
* pMeasureStruct
= reinterpret_cast<MEASUREITEMSTRUCT
*>(lParam
);
1854 PDynamicShellEntry pEntry
= GetDynamicEntry(pMeasureStruct
->itemID
);
1856 SHForwardContextMenuMsg(pEntry
->pCM
, uMsg
, wParam
, lParam
, plResult
, TRUE
);
1863 ERR("Got unknown message:%d\n", uMsg
);
1870 IDefaultContextMenu_Constructor(
1871 const DEFCONTEXTMENU
*pdcm
,
1879 CComObject
<CDefaultContextMenu
> *pCM
;
1880 HRESULT hr
= CComObject
<CDefaultContextMenu
>::CreateInstance(&pCM
);
1883 pCM
->AddRef(); // CreateInstance returns object with 0 ref count */
1885 CComPtr
<IUnknown
> pResult
;
1886 hr
= pCM
->QueryInterface(riid
, (void **)&pResult
);
1893 hr
= pCM
->Initialize(pdcm
);
1900 *ppv
= pResult
.Detach();
1902 TRACE("This(%p) cidl %u\n", *ppv
, pdcm
->cidl
);
1906 /*************************************************************************
1907 * SHCreateDefaultContextMenu [SHELL32.325] Vista API
1913 SHCreateDefaultContextMenu(
1914 const DEFCONTEXTMENU
*pdcm
,
1919 HRESULT hr
= IDefaultContextMenu_Constructor(pdcm
, riid
, ppv
);
1921 ERR("IDefaultContextMenu_Constructor failed: %x\n", hr
);
1922 TRACE("pcm %p hr %x\n", pdcm
, hr
);
1926 /*************************************************************************
1927 * CDefFolderMenu_Create2 [SHELL32.701]
1933 CDefFolderMenu_Create2(
1934 PCIDLIST_ABSOLUTE pidlFolder
,
1937 PCUITEMID_CHILD_ARRAY apidl
,
1939 LPFNDFMCALLBACK lpfn
,
1941 const HKEY
*ahkeyClsKeys
,
1942 IContextMenu
**ppcm
)
1944 DEFCONTEXTMENU pdcm
;
1947 pdcm
.pidlFolder
= pidlFolder
;
1951 pdcm
.punkAssociationInfo
= NULL
;
1953 pdcm
.aKeys
= ahkeyClsKeys
;
1955 HRESULT hr
= SHCreateDefaultContextMenu(&pdcm
, IID_PPV_ARG(IContextMenu
, ppcm
));