4 * Copyright 2016 Giannis Adamopoulos
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 WINE_DEFAULT_DEBUG_CHANNEL (shell
);
25 HRESULT CALLBACK
RegFolderContextMenuCallback(IShellFolder
*psf
,
32 if (uMsg
!= DFM_INVOKECOMMAND
|| wParam
!= DFM_CMD_PROPERTIES
)
35 PIDLIST_ABSOLUTE pidlFolder
;
36 PUITEMID_CHILD
*apidl
;
38 HRESULT hr
= SH_GetApidlFromDataObject(pdtobj
, &pidlFolder
, &apidl
, &cidl
);
39 if (FAILED_UNEXPECTEDLY(hr
))
42 if (_ILIsMyComputer(apidl
[0]))
44 if (32 >= (UINT
)ShellExecuteW(hwnd
, L
"open", L
"rundll32.exe shell32.dll,Control_RunDLL sysdm.cpl", NULL
, NULL
, SW_SHOWNORMAL
))
47 else if (_ILIsDesktop(apidl
[0]))
49 if (32 >= (UINT
)ShellExecuteW(hwnd
, L
"open", L
"rundll32.exe shell32.dll,Control_RunDLL desk.cpl", NULL
, NULL
, SW_SHOWNORMAL
))
52 else if (_ILIsNetHood(apidl
[0]))
55 if (32 >= (UINT
)ShellExecuteW(NULL
, L
"open", L
"explorer.exe",
56 L
"::{7007ACC7-3202-11D1-AAD2-00805FC1270E}",
57 NULL
, SW_SHOWDEFAULT
))
60 else if (_ILIsBitBucket(apidl
[0]))
62 /* FIXME: detect the drive path of bitbucket if appropiate */
63 if (!SH_ShowRecycleBinProperties(L
'C'))
68 _ILFreeaPidl(apidl
, cidl
);
73 HRESULT
CGuidItemContextMenu_CreateInstance(PCIDLIST_ABSOLUTE pidlFolder
,
76 PCUITEMID_CHILD_ARRAY apidl
,
83 GUID
*pGuid
= _ILGetGUIDPointer(apidl
[0]);
89 wcscpy(key
, L
"CLSID\\");
90 HRESULT hr
= StringFromCLSID(*pGuid
, &pwszCLSID
);
93 wcscpy(&key
[6], pwszCLSID
);
94 AddClassKeyToArray(key
, hKeys
, &cKeys
);
97 AddClassKeyToArray(L
"Folder", hKeys
, &cKeys
);
99 return CDefFolderMenu_Create2(pidlFolder
, hwnd
, cidl
, apidl
, psf
, RegFolderContextMenuCallback
, cKeys
, hKeys
, ppcm
);
102 HRESULT
CGuidItemExtractIcon_CreateInstance(LPCITEMIDLIST pidl
, REFIID iid
, LPVOID
* ppvOut
)
104 CComPtr
<IDefaultExtractIconInit
> initIcon
;
108 WCHAR wTemp
[MAX_PATH
];
110 hr
= SHCreateDefaultExtractIcon(IID_PPV_ARG(IDefaultExtractIconInit
,&initIcon
));
114 if (_ILIsDesktop(pidl
))
116 initIcon
->SetNormalIcon(swShell32Name
, -IDI_SHELL_DESKTOP
);
117 return initIcon
->QueryInterface(iid
, ppvOut
);
120 riid
= _ILGetGUIDPointer(pidl
);
124 /* my computer and other shell extensions */
125 static const WCHAR fmt
[] = { 'C', 'L', 'S', 'I', 'D', '\\',
126 '{', '%', '0', '8', 'l', 'x', '-', '%', '0', '4', 'x', '-', '%', '0', '4', 'x', '-',
127 '%', '0', '2', 'x', '%', '0', '2', 'x', '-', '%', '0', '2', 'x', '%', '0', '2', 'x',
128 '%', '0', '2', 'x', '%', '0', '2', 'x', '%', '0', '2', 'x', '%', '0', '2', 'x', '}', 0
133 riid
->Data1
, riid
->Data2
, riid
->Data3
,
134 riid
->Data4
[0], riid
->Data4
[1], riid
->Data4
[2], riid
->Data4
[3],
135 riid
->Data4
[4], riid
->Data4
[5], riid
->Data4
[6], riid
->Data4
[7]);
137 const WCHAR
* iconname
= NULL
;
138 if (_ILIsBitBucket(pidl
))
140 static const WCHAR szFull
[] = {'F','u','l','l',0};
141 static const WCHAR szEmpty
[] = {'E','m','p','t','y',0};
142 CComPtr
<IEnumIDList
> EnumIDList
;
145 CComPtr
<IShellFolder2
> psfRecycleBin
;
146 CComPtr
<IShellFolder
> psfDesktop
;
147 hr
= SHGetDesktopFolder(&psfDesktop
);
150 hr
= psfDesktop
->BindToObject(pidl
, NULL
, IID_PPV_ARG(IShellFolder2
, &psfRecycleBin
));
152 hr
= psfRecycleBin
->EnumObjects(NULL
, SHCONTF_FOLDERS
| SHCONTF_NONFOLDERS
, &EnumIDList
);
155 LPITEMIDLIST pidl
= NULL
;
156 if (SUCCEEDED(hr
) && (hr
= EnumIDList
->Next(1, &pidl
, &itemcount
)) == S_OK
)
165 if (HCR_GetIconW(xriid
, wTemp
, iconname
, MAX_PATH
, &icon_idx
))
167 initIcon
->SetNormalIcon(wTemp
, icon_idx
);
171 if (IsEqualGUID(*riid
, CLSID_MyComputer
))
172 initIcon
->SetNormalIcon(swShell32Name
, -IDI_SHELL_MY_COMPUTER
);
173 else if (IsEqualGUID(*riid
, CLSID_MyDocuments
))
174 initIcon
->SetNormalIcon(swShell32Name
, -IDI_SHELL_MY_DOCUMENTS
);
175 else if (IsEqualGUID(*riid
, CLSID_NetworkPlaces
))
176 initIcon
->SetNormalIcon(swShell32Name
, -IDI_SHELL_MY_NETWORK_PLACES
);
178 initIcon
->SetNormalIcon(swShell32Name
, -IDI_SHELL_FOLDER
);
181 return initIcon
->QueryInterface(iid
, ppvOut
);
185 public CComObjectRootEx
<CComMultiThreadModelNoCS
>,
190 CAtlStringW m_rootPath
;
191 CComHeapPtr
<ITEMIDLIST
> m_pidlRoot
;
193 HRESULT
GetGuidItemAttributes (LPCITEMIDLIST pidl
, LPDWORD pdwAttributes
);
197 HRESULT WINAPI
Initialize(const GUID
*pGuid
, LPCITEMIDLIST pidlRoot
, LPCWSTR lpszPath
);
200 virtual HRESULT WINAPI
ParseDisplayName(HWND hwndOwner
, LPBC pbc
, LPOLESTR lpszDisplayName
, ULONG
*pchEaten
, PIDLIST_RELATIVE
*ppidl
, ULONG
*pdwAttributes
);
201 virtual HRESULT WINAPI
EnumObjects(HWND hwndOwner
, DWORD dwFlags
, LPENUMIDLIST
*ppEnumIDList
);
202 virtual HRESULT WINAPI
BindToObject(PCUIDLIST_RELATIVE pidl
, LPBC pbcReserved
, REFIID riid
, LPVOID
*ppvOut
);
203 virtual HRESULT WINAPI
BindToStorage(PCUIDLIST_RELATIVE pidl
, LPBC pbcReserved
, REFIID riid
, LPVOID
*ppvOut
);
204 virtual HRESULT WINAPI
CompareIDs(LPARAM lParam
, PCUIDLIST_RELATIVE pidl1
, PCUIDLIST_RELATIVE pidl2
);
205 virtual HRESULT WINAPI
CreateViewObject(HWND hwndOwner
, REFIID riid
, LPVOID
*ppvOut
);
206 virtual HRESULT WINAPI
GetAttributesOf(UINT cidl
, PCUITEMID_CHILD_ARRAY apidl
, DWORD
*rgfInOut
);
207 virtual HRESULT WINAPI
GetUIObjectOf(HWND hwndOwner
, UINT cidl
, PCUITEMID_CHILD_ARRAY apidl
, REFIID riid
, UINT
* prgfInOut
, LPVOID
* ppvOut
);
208 virtual HRESULT WINAPI
GetDisplayNameOf(PCUITEMID_CHILD pidl
, DWORD dwFlags
, LPSTRRET strRet
);
209 virtual HRESULT WINAPI
SetNameOf(HWND hwndOwner
, PCUITEMID_CHILD pidl
, LPCOLESTR lpName
, DWORD dwFlags
, PITEMID_CHILD
*pPidlOut
);
212 virtual HRESULT WINAPI
GetDefaultSearchGUID(GUID
*pguid
);
213 virtual HRESULT WINAPI
EnumSearches(IEnumExtraSearch
**ppenum
);
214 virtual HRESULT WINAPI
GetDefaultColumn(DWORD dwRes
, ULONG
*pSort
, ULONG
*pDisplay
);
215 virtual HRESULT WINAPI
GetDefaultColumnState(UINT iColumn
, DWORD
*pcsFlags
);
216 virtual HRESULT WINAPI
GetDetailsEx(PCUITEMID_CHILD pidl
, const SHCOLUMNID
*pscid
, VARIANT
*pv
);
217 virtual HRESULT WINAPI
GetDetailsOf(PCUITEMID_CHILD pidl
, UINT iColumn
, SHELLDETAILS
*psd
);
218 virtual HRESULT WINAPI
MapColumnToSCID(UINT column
, SHCOLUMNID
*pscid
);
220 DECLARE_NOT_AGGREGATABLE(CRegFolder
)
222 DECLARE_PROTECT_FINAL_CONSTRUCT()
224 BEGIN_COM_MAP(CRegFolder
)
225 COM_INTERFACE_ENTRY_IID(IID_IShellFolder2
, IShellFolder2
)
226 COM_INTERFACE_ENTRY_IID(IID_IShellFolder
, IShellFolder
)
230 CRegFolder::CRegFolder()
234 CRegFolder::~CRegFolder()
238 HRESULT WINAPI
CRegFolder::Initialize(const GUID
*pGuid
, LPCITEMIDLIST pidlRoot
, LPCWSTR lpszPath
)
240 memcpy(&m_guid
, pGuid
, sizeof(m_guid
));
242 m_rootPath
= lpszPath
;
244 return E_OUTOFMEMORY
;
246 m_pidlRoot
.Attach(ILClone(pidlRoot
));
248 return E_OUTOFMEMORY
;
253 HRESULT
CRegFolder::GetGuidItemAttributes (LPCITEMIDLIST pidl
, LPDWORD pdwAttributes
)
255 DWORD dwAttributes
= *pdwAttributes
;
257 /* First try to get them from the registry */
258 if (!HCR_GetFolderAttributes(pidl
, pdwAttributes
))
260 /* We couldn't get anything */
264 /* Items have more attributes when on desktop */
265 if (_ILIsDesktop(m_pidlRoot
))
267 *pdwAttributes
|= (dwAttributes
& (SFGAO_CANLINK
|SFGAO_CANDELETE
|SFGAO_CANRENAME
|SFGAO_HASPROPSHEET
));
270 /* In any case, links can be created */
271 if (*pdwAttributes
== NULL
)
273 *pdwAttributes
|= (dwAttributes
& SFGAO_CANLINK
);
278 HRESULT WINAPI
CRegFolder::ParseDisplayName(HWND hwndOwner
, LPBC pbc
, LPOLESTR lpszDisplayName
,
279 ULONG
*pchEaten
, PIDLIST_RELATIVE
*ppidl
, ULONG
*pdwAttributes
)
283 if (!lpszDisplayName
|| !ppidl
)
291 UINT cch
= wcslen(lpszDisplayName
);
292 if (cch
< 39 || lpszDisplayName
[0] != L
':' || lpszDisplayName
[1] != L
':')
295 pidl
= _ILCreateGuidFromStrW(lpszDisplayName
+ 2);
302 if (pdwAttributes
&& *pdwAttributes
)
304 GetGuidItemAttributes(*ppidl
, pdwAttributes
);
309 HRESULT hr
= SHELL32_ParseNextElement(this, hwndOwner
, pbc
, &pidl
, lpszDisplayName
+ 41, pchEaten
, pdwAttributes
);
320 HRESULT WINAPI
CRegFolder::EnumObjects(HWND hwndOwner
, DWORD dwFlags
, LPENUMIDLIST
*ppEnumIDList
)
325 HRESULT WINAPI
CRegFolder::BindToObject(PCUIDLIST_RELATIVE pidl
, LPBC pbcReserved
, REFIID riid
, LPVOID
*ppvOut
)
327 CComPtr
<IPersistFolder
> pFolder
;
330 if (!ppvOut
|| !pidl
|| !pidl
->mkid
.cb
)
335 GUID
*pGUID
= _ILGetGUIDPointer(pidl
);
338 ERR("CRegFolder::BindToObject called for non guid item!\n");
342 hr
= SHELL32_BindToSF(m_pidlRoot
, NULL
, pidl
, pGUID
, riid
, ppvOut
);
343 if (FAILED_UNEXPECTEDLY(hr
))
349 HRESULT WINAPI
CRegFolder::BindToStorage(PCUIDLIST_RELATIVE pidl
, LPBC pbcReserved
, REFIID riid
, LPVOID
*ppvOut
)
354 HRESULT WINAPI
CRegFolder::CompareIDs(LPARAM lParam
, PCUIDLIST_RELATIVE pidl1
, PCUIDLIST_RELATIVE pidl2
)
356 if (!pidl1
|| !pidl2
|| pidl1
->mkid
.cb
== 0 || pidl2
->mkid
.cb
== 0)
358 ERR("Got an empty pidl!\n");
362 GUID
const *clsid1
= _ILGetGUIDPointer (pidl1
);
363 GUID
const *clsid2
= _ILGetGUIDPointer (pidl2
);
365 if (!clsid1
&& !clsid2
)
367 ERR("Got no guid pidl!\n");
370 else if (clsid1
&& clsid2
)
372 if (memcmp(clsid1
, clsid2
, sizeof(GUID
)) == 0)
373 return SHELL32_CompareChildren(this, lParam
, pidl1
, pidl2
);
375 return SHELL32_CompareDetails(this, lParam
, pidl1
, pidl2
);
378 /* Guid folders come first compared to everything else */
379 return MAKE_COMPARE_HRESULT(clsid1
? -1 : 1);
382 HRESULT WINAPI
CRegFolder::CreateViewObject(HWND hwndOwner
, REFIID riid
, LPVOID
*ppvOut
)
387 HRESULT WINAPI
CRegFolder::GetAttributesOf(UINT cidl
, PCUITEMID_CHILD_ARRAY apidl
, DWORD
*rgfInOut
)
389 if (!rgfInOut
|| !cidl
|| !apidl
)
395 while(cidl
> 0 && *apidl
)
397 if (_ILIsSpecialFolder(*apidl
))
398 GetGuidItemAttributes(*apidl
, rgfInOut
);
400 ERR("Got an unkown pidl here!\n");
405 /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */
406 *rgfInOut
&= ~SFGAO_VALIDATE
;
411 HRESULT WINAPI
CRegFolder::GetUIObjectOf(HWND hwndOwner
, UINT cidl
, PCUITEMID_CHILD_ARRAY apidl
,
412 REFIID riid
, UINT
* prgfInOut
, LPVOID
* ppvOut
)
415 HRESULT hr
= E_INVALIDARG
;
422 if ((IsEqualIID (riid
, IID_IExtractIconA
) || IsEqualIID (riid
, IID_IExtractIconW
)) && (cidl
== 1))
424 hr
= CGuidItemExtractIcon_CreateInstance(apidl
[0], riid
, &pObj
);
426 else if (IsEqualIID (riid
, IID_IContextMenu
) && (cidl
>= 1))
428 if (!_ILGetGUIDPointer (apidl
[0]))
430 ERR("Got non guid item!\n");
434 hr
= CGuidItemContextMenu_CreateInstance(m_pidlRoot
, hwndOwner
, cidl
, apidl
, static_cast<IShellFolder
*>(this), (IContextMenu
**)&pObj
);
436 else if (IsEqualIID (riid
, IID_IDataObject
) && (cidl
>= 1))
438 hr
= IDataObject_Constructor (hwndOwner
, m_pidlRoot
, apidl
, cidl
, (IDataObject
**)&pObj
);
450 HRESULT WINAPI
CRegFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl
, DWORD dwFlags
, LPSTRRET strRet
)
452 if (!strRet
|| (!_ILIsSpecialFolder(pidl
) && pidl
!= NULL
))
455 if (!pidl
|| !pidl
->mkid
.cb
)
457 if ((GET_SHGDN_RELATION(dwFlags
) == SHGDN_NORMAL
) && (GET_SHGDN_FOR(dwFlags
) & SHGDN_FORPARSING
))
459 LPWSTR pszPath
= (LPWSTR
)CoTaskMemAlloc((MAX_PATH
+ 1) * sizeof(WCHAR
));
461 return E_OUTOFMEMORY
;
463 /* parsing name like ::{...} */
466 SHELL32_GUIDToStringW(m_guid
, &pszPath
[2]);
468 strRet
->uType
= STRRET_WSTR
;
469 strRet
->pOleStr
= pszPath
;
476 WCHAR wstrName
[MAX_PATH
+1];
477 bRet
= HCR_GetClassNameW(m_guid
, wstrName
, MAX_PATH
);
481 return SHSetStrRet(strRet
, wstrName
);
487 GUID
const *clsid
= _ILGetGUIDPointer (pidl
);
489 /* First of all check if we need to query the name from the child item */
490 if (GET_SHGDN_FOR (dwFlags
) == SHGDN_FORPARSING
&&
491 GET_SHGDN_RELATION (dwFlags
) == SHGDN_NORMAL
)
493 int bWantsForParsing
= FALSE
;
496 * We can only get a filesystem path from a shellfolder if the
497 * value WantsFORPARSING in CLSID\\{...}\\shellfolder exists.
499 * Exception: The MyComputer folder doesn't have this key,
500 * but any other filesystem backed folder it needs it.
502 if (IsEqualIID (*clsid
, CLSID_MyComputer
))
504 bWantsForParsing
= TRUE
;
509 if (HCR_RegOpenClassIDKey(*clsid
, &hkeyClass
))
511 LONG res
= SHGetValueW(hkeyClass
, L
"Shellfolder", L
"WantsForParsing", NULL
, NULL
, NULL
);
512 bWantsForParsing
= (res
== ERROR_SUCCESS
);
513 RegCloseKey(hkeyClass
);
517 if (bWantsForParsing
)
520 * we need the filesystem path to the destination folder.
521 * Only the folder itself can know it
523 return SHELL32_GetDisplayNameOfChild (this, pidl
, dwFlags
, strRet
);
527 /* Allocate the buffer for the result */
528 LPWSTR pszPath
= (LPWSTR
)CoTaskMemAlloc((MAX_PATH
+ 1) * sizeof(WCHAR
));
530 return E_OUTOFMEMORY
;
534 if (GET_SHGDN_FOR (dwFlags
) == SHGDN_FORPARSING
)
536 wcscpy(pszPath
, m_rootPath
);
537 PWCHAR pItemName
= &pszPath
[wcslen(pszPath
)];
539 /* parsing name like ::{...} */
542 SHELL32_GUIDToStringW (*clsid
, &pItemName
[2]);
546 /* user friendly name */
547 if (!HCR_GetClassNameW (*clsid
, pszPath
, MAX_PATH
))
553 strRet
->uType
= STRRET_WSTR
;
554 strRet
->pOleStr
= pszPath
;
558 CoTaskMemFree(pszPath
);
564 HRESULT WINAPI
CRegFolder::SetNameOf(HWND hwndOwner
, PCUITEMID_CHILD pidl
, /* simple pidl */
565 LPCOLESTR lpName
, DWORD dwFlags
, PITEMID_CHILD
*pPidlOut
)
567 GUID
const *clsid
= _ILGetGUIDPointer (pidl
);
574 ERR("Pidl is not reg item!\n");
578 hr
= StringFromCLSID(*clsid
, &pStr
);
579 if (FAILED_UNEXPECTEDLY(hr
))
582 swprintf(szName
, L
"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\CLSID\\%s", pStr
);
584 DWORD cbData
= (wcslen(lpName
) + 1) * sizeof(WCHAR
);
585 LONG res
= SHSetValueW(HKEY_CURRENT_USER
, szName
, NULL
, RRF_RT_REG_SZ
, lpName
, cbData
);
589 if (res
== ERROR_SUCCESS
)
591 *pPidlOut
= ILClone(pidl
);
599 HRESULT WINAPI
CRegFolder::GetDefaultSearchGUID(GUID
*pguid
)
604 HRESULT WINAPI
CRegFolder::EnumSearches(IEnumExtraSearch
** ppenum
)
609 HRESULT WINAPI
CRegFolder::GetDefaultColumn(DWORD dwRes
, ULONG
*pSort
, ULONG
*pDisplay
)
619 HRESULT WINAPI
CRegFolder::GetDefaultColumnState(UINT iColumn
, DWORD
*pcsFlags
)
623 *pcsFlags
= SHCOLSTATE_TYPE_STR
| SHCOLSTATE_ONBYDEFAULT
;
627 HRESULT WINAPI
CRegFolder::GetDetailsEx(PCUITEMID_CHILD pidl
, const SHCOLUMNID
*pscid
, VARIANT
*pv
)
632 HRESULT WINAPI
CRegFolder::GetDetailsOf(PCUITEMID_CHILD pidl
, UINT iColumn
, SHELLDETAILS
*psd
)
637 GUID
const *clsid
= _ILGetGUIDPointer (pidl
);
641 ERR("Pidl is not reg item!\n");
647 /* Return an empty string when we area asked for a column we don't support.
648 Only the regfolder is supposed to do this as it supports less columns compared to other folder
649 and its contents are supposed to be presented alongside items that support more columns. */
650 return SHSetStrRet(&psd
->str
, "");
656 return GetDisplayNameOf(pidl
, SHGDN_NORMAL
| SHGDN_INFOLDER
, &psd
->str
);
657 case 1: /* comments */
659 if (!HCR_RegOpenClassIDKey(*clsid
, &hKey
))
660 return SHSetStrRet(&psd
->str
, "");
662 psd
->str
.cStr
[0] = 0x00;
663 psd
->str
.uType
= STRRET_CSTR
;
664 RegLoadMUIStringA(hKey
, "InfoTip", psd
->str
.cStr
, MAX_PATH
, NULL
, 0, NULL
);
668 //return SHSetStrRet(&psd->str, resource_id); /* FIXME: translate */
669 return SHSetStrRet(&psd
->str
, "System Folder");
674 HRESULT WINAPI
CRegFolder::MapColumnToSCID(UINT column
, SHCOLUMNID
*pscid
)
679 /* In latest windows version this is exported but it takes different arguments! */
680 HRESULT
CRegFolder_CreateInstance(const GUID
*pGuid
, LPCITEMIDLIST pidlRoot
, LPCWSTR lpszPath
, REFIID riid
, void **ppv
)
682 return ShellObjectCreatorInit
<CRegFolder
>(pGuid
, pidlRoot
, lpszPath
, riid
, ppv
);