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 LPITEMIDLIST pidlChild
= ILCloneFirst (pidl
);
344 return E_OUTOFMEMORY
;
346 CComPtr
<IShellFolder
> psf
;
347 hr
= SHELL32_CoCreateInitSF(m_pidlRoot
, NULL
, pidlChild
, pGUID
, -1, IID_PPV_ARG(IShellFolder
, &psf
));
352 if (_ILIsPidlSimple (pidl
))
354 return psf
->QueryInterface(riid
, ppvOut
);
358 return psf
->BindToObject(ILGetNext (pidl
), pbcReserved
, riid
, ppvOut
);
362 HRESULT WINAPI
CRegFolder::BindToStorage(PCUIDLIST_RELATIVE pidl
, LPBC pbcReserved
, REFIID riid
, LPVOID
*ppvOut
)
367 HRESULT WINAPI
CRegFolder::CompareIDs(LPARAM lParam
, PCUIDLIST_RELATIVE pidl1
, PCUIDLIST_RELATIVE pidl2
)
369 if (!pidl1
|| !pidl2
|| pidl1
->mkid
.cb
== 0 || pidl2
->mkid
.cb
== 0)
371 ERR("Got an empty pidl!\n");
375 GUID
const *clsid1
= _ILGetGUIDPointer (pidl1
);
376 GUID
const *clsid2
= _ILGetGUIDPointer (pidl2
);
378 if (!clsid1
&& !clsid2
)
380 ERR("Got no guid pidl!\n");
383 else if (clsid1
&& clsid2
)
385 if (memcmp(clsid1
, clsid2
, sizeof(GUID
)) == 0)
386 return SHELL32_CompareChildren(this, lParam
, pidl1
, pidl2
);
388 return SHELL32_CompareDetails(this, lParam
, pidl1
, pidl2
);
391 /* Guid folders come first compared to everything else */
392 return MAKE_COMPARE_HRESULT(clsid1
? -1 : 1);
395 HRESULT WINAPI
CRegFolder::CreateViewObject(HWND hwndOwner
, REFIID riid
, LPVOID
*ppvOut
)
400 HRESULT WINAPI
CRegFolder::GetAttributesOf(UINT cidl
, PCUITEMID_CHILD_ARRAY apidl
, DWORD
*rgfInOut
)
402 if (!rgfInOut
|| !cidl
|| !apidl
)
408 while(cidl
> 0 && *apidl
)
410 if (_ILIsSpecialFolder(*apidl
))
411 GetGuidItemAttributes(*apidl
, rgfInOut
);
413 ERR("Got an unkown pidl here!\n");
418 /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */
419 *rgfInOut
&= ~SFGAO_VALIDATE
;
424 HRESULT WINAPI
CRegFolder::GetUIObjectOf(HWND hwndOwner
, UINT cidl
, PCUITEMID_CHILD_ARRAY apidl
,
425 REFIID riid
, UINT
* prgfInOut
, LPVOID
* ppvOut
)
428 HRESULT hr
= E_INVALIDARG
;
435 if ((IsEqualIID (riid
, IID_IExtractIconA
) || IsEqualIID (riid
, IID_IExtractIconW
)) && (cidl
== 1))
437 hr
= CGuidItemExtractIcon_CreateInstance(apidl
[0], riid
, &pObj
);
439 else if (IsEqualIID (riid
, IID_IContextMenu
) && (cidl
>= 1))
441 if (!_ILGetGUIDPointer (apidl
[0]))
443 ERR("Got non guid item!\n");
447 hr
= CGuidItemContextMenu_CreateInstance(m_pidlRoot
, hwndOwner
, cidl
, apidl
, static_cast<IShellFolder
*>(this), (IContextMenu
**)&pObj
);
449 else if (IsEqualIID (riid
, IID_IDataObject
) && (cidl
>= 1))
451 hr
= IDataObject_Constructor (hwndOwner
, m_pidlRoot
, apidl
, cidl
, (IDataObject
**)&pObj
);
463 HRESULT WINAPI
CRegFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl
, DWORD dwFlags
, LPSTRRET strRet
)
465 if (!strRet
|| (!_ILIsSpecialFolder(pidl
) && pidl
!= NULL
))
468 if (!pidl
|| !pidl
->mkid
.cb
)
470 if ((GET_SHGDN_RELATION(dwFlags
) == SHGDN_NORMAL
) && (GET_SHGDN_FOR(dwFlags
) & SHGDN_FORPARSING
))
472 LPWSTR pszPath
= (LPWSTR
)CoTaskMemAlloc((MAX_PATH
+ 1) * sizeof(WCHAR
));
474 return E_OUTOFMEMORY
;
476 /* parsing name like ::{...} */
479 SHELL32_GUIDToStringW(m_guid
, &pszPath
[2]);
481 strRet
->uType
= STRRET_WSTR
;
482 strRet
->pOleStr
= pszPath
;
488 return HCR_GetClassName(m_guid
, strRet
);
493 GUID
const *clsid
= _ILGetGUIDPointer (pidl
);
495 /* First of all check if we need to query the name from the child item */
496 if (GET_SHGDN_FOR (dwFlags
) == SHGDN_FORPARSING
&&
497 GET_SHGDN_RELATION (dwFlags
) == SHGDN_NORMAL
)
499 int bWantsForParsing
= FALSE
;
502 * We can only get a filesystem path from a shellfolder if the
503 * value WantsFORPARSING in CLSID\\{...}\\shellfolder exists.
505 * Exception: The MyComputer folder doesn't have this key,
506 * but any other filesystem backed folder it needs it.
508 if (IsEqualIID (*clsid
, CLSID_MyComputer
))
510 bWantsForParsing
= TRUE
;
515 if (HCR_RegOpenClassIDKey(*clsid
, &hkeyClass
))
517 LONG res
= SHGetValueW(hkeyClass
, L
"Shellfolder", L
"WantsForParsing", NULL
, NULL
, NULL
);
518 bWantsForParsing
= (res
== ERROR_SUCCESS
);
519 RegCloseKey(hkeyClass
);
523 if (bWantsForParsing
)
526 * we need the filesystem path to the destination folder.
527 * Only the folder itself can know it
529 return SHELL32_GetDisplayNameOfChild (this, pidl
, dwFlags
, strRet
);
533 /* Allocate the buffer for the result */
534 LPWSTR pszPath
= (LPWSTR
)CoTaskMemAlloc((MAX_PATH
+ 1) * sizeof(WCHAR
));
536 return E_OUTOFMEMORY
;
540 if (GET_SHGDN_FOR (dwFlags
) == SHGDN_FORPARSING
)
542 wcscpy(pszPath
, m_rootPath
);
543 PWCHAR pItemName
= &pszPath
[wcslen(pszPath
)];
545 /* parsing name like ::{...} */
548 SHELL32_GUIDToStringW (*clsid
, &pItemName
[2]);
552 /* user friendly name */
553 if (!HCR_GetClassNameW (*clsid
, pszPath
, MAX_PATH
))
559 strRet
->uType
= STRRET_WSTR
;
560 strRet
->pOleStr
= pszPath
;
564 CoTaskMemFree(pszPath
);
570 HRESULT WINAPI
CRegFolder::SetNameOf(HWND hwndOwner
, PCUITEMID_CHILD pidl
, /* simple pidl */
571 LPCOLESTR lpName
, DWORD dwFlags
, PITEMID_CHILD
*pPidlOut
)
573 GUID
const *clsid
= _ILGetGUIDPointer (pidl
);
580 ERR("Pidl is not reg item!\n");
584 hr
= StringFromCLSID(*clsid
, &pStr
);
585 if (FAILED_UNEXPECTEDLY(hr
))
588 swprintf(szName
, L
"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\CLSID\\%s", pStr
);
590 DWORD cbData
= (wcslen(lpName
) + 1) * sizeof(WCHAR
);
591 LONG res
= SHSetValueW(HKEY_CURRENT_USER
, szName
, NULL
, RRF_RT_REG_SZ
, lpName
, cbData
);
595 if (res
== ERROR_SUCCESS
)
597 *pPidlOut
= ILClone(pidl
);
605 HRESULT WINAPI
CRegFolder::GetDefaultSearchGUID(GUID
*pguid
)
610 HRESULT WINAPI
CRegFolder::EnumSearches(IEnumExtraSearch
** ppenum
)
615 HRESULT WINAPI
CRegFolder::GetDefaultColumn(DWORD dwRes
, ULONG
*pSort
, ULONG
*pDisplay
)
625 HRESULT WINAPI
CRegFolder::GetDefaultColumnState(UINT iColumn
, DWORD
*pcsFlags
)
629 *pcsFlags
= SHCOLSTATE_TYPE_STR
| SHCOLSTATE_ONBYDEFAULT
;
633 HRESULT WINAPI
CRegFolder::GetDetailsEx(PCUITEMID_CHILD pidl
, const SHCOLUMNID
*pscid
, VARIANT
*pv
)
638 HRESULT WINAPI
CRegFolder::GetDetailsOf(PCUITEMID_CHILD pidl
, UINT iColumn
, SHELLDETAILS
*psd
)
640 if (!psd
|| iColumn
>= 2)
643 GUID
const *clsid
= _ILGetGUIDPointer (pidl
);
647 ERR("Pidl is not reg item!\n");
654 return GetDisplayNameOf(pidl
, SHGDN_NORMAL
| SHGDN_INFOLDER
, &psd
->str
);
655 case 1: /* comment */
657 if (HCR_RegOpenClassIDKey(*clsid
, &hKey
))
659 psd
->str
.cStr
[0] = 0x00;
660 psd
->str
.uType
= STRRET_CSTR
;
661 RegLoadMUIStringA(hKey
, "InfoTip", psd
->str
.cStr
, MAX_PATH
, NULL
, 0, NULL
);
669 HRESULT WINAPI
CRegFolder::MapColumnToSCID(UINT column
, SHCOLUMNID
*pscid
)
674 /* In latest windows version this is exported but it takes different arguments! */
675 HRESULT
CRegFolder_CreateInstance(const GUID
*pGuid
, LPCITEMIDLIST pidlRoot
, LPCWSTR lpszPath
, REFIID riid
, void **ppv
)
677 return ShellObjectCreatorInit
<CRegFolder
>(pGuid
, pidlRoot
, lpszPath
, riid
, ppv
);