1 /*
2  * ReactOS Shell
3  *
4  * Copyright 2016 Giannis Adamopoulos
5  *
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.
10  *
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.
15  *
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
19  *
20  * The required-regitem design is based on the research by Geoff Chappell
21  * https://www.geoffchappell.com/studies/windows/shell/shell32/classes/regfolder.htm
22  */
23 
24 #include <precomp.h>
25 
26 WINE_DEFAULT_DEBUG_CHANNEL (shell);
27 
28 #define DEFAULTSORTORDERINDEX 0x80 // The default for registry items according to Geoff Chappell
29 
30 static HRESULT CRegItemContextMenu_CreateInstance(PCIDLIST_ABSOLUTE pidlFolder, HWND hwnd, UINT cidl,
31                                                   PCUITEMID_CHILD_ARRAY apidl, IShellFolder *psf, IContextMenu **ppcm);
32 
GetRegItemCLSIDOffset(PIDLTYPE type)33 static inline UINT GetRegItemCLSIDOffset(PIDLTYPE type)
34 {
35     return type == PT_CONTROLS_NEWREGITEM ? 14 : 4;
36 }
37 
CreateRegItem(PIDLTYPE type,REFCLSID clsid,BYTE order=0)38 static LPITEMIDLIST CreateRegItem(PIDLTYPE type, REFCLSID clsid, BYTE order = 0)
39 {
40 #if 1 // FIXME: CControlPanelFolder is not ready for this yet
41     if (type == PT_CONTROLS_NEWREGITEM)
42         type = PT_CONTROLS_OLDREGITEM;
43 #endif
44     const UINT offset = GetRegItemCLSIDOffset(type);
45     const UINT cb = offset + sizeof(CLSID), cbTotal = cb + sizeof(WORD);
46     LPITEMIDLIST pidl = (LPITEMIDLIST)SHAlloc(cbTotal);
47     if (pidl)
48     {
49         ZeroMemory(pidl, cbTotal); // Note: This also initializes the terminator WORD
50         pidl->mkid.cb = cb;
51         pidl->mkid.abID[0] = type;
52         pidl->mkid.abID[1] = order;
53         *(CLSID*)(SIZE_T(pidl) + offset) = clsid;
54     }
55     return pidl;
56 }
57 
CreateRegItem(PIDLTYPE type,LPCWSTR clsidstr)58 static LPITEMIDLIST CreateRegItem(PIDLTYPE type, LPCWSTR clsidstr)
59 {
60     CLSID clsid;
61     return SUCCEEDED(CLSIDFromString(clsidstr, &clsid)) ? CreateRegItem(type, clsid) : NULL;
62 }
63 
FormatGUIDKey(LPWSTR KeyName,SIZE_T KeySize,LPCWSTR RegPath,const GUID * riid)64 HRESULT FormatGUIDKey(LPWSTR KeyName, SIZE_T KeySize, LPCWSTR RegPath, const GUID* riid)
65 {
66     WCHAR xriid[CHARS_IN_GUID];
67     StringFromGUID2(*riid, xriid, _countof(xriid));
68     return StringCchPrintfW(KeyName, KeySize, RegPath, xriid);
69 }
70 
SHELL_QueryCLSIDValue(_In_ REFCLSID clsid,_In_opt_ LPCWSTR SubKey,_In_opt_ LPCWSTR Value,_In_opt_ PVOID pData,_In_opt_ PDWORD pSize)71 static DWORD SHELL_QueryCLSIDValue(_In_ REFCLSID clsid, _In_opt_ LPCWSTR SubKey, _In_opt_ LPCWSTR Value, _In_opt_ PVOID pData, _In_opt_ PDWORD pSize)
72 {
73     WCHAR Path[MAX_PATH];
74     wcscpy(Path, L"CLSID\\");
75     StringFromGUID2(clsid, Path + 6, 39);
76     if (SubKey)
77     {
78         wcscpy(Path + 6 + 38, L"\\");
79         wcscpy(Path + 6 + 39, SubKey);
80     }
81     return RegGetValueW(HKEY_CLASSES_ROOT, Path, Value, RRF_RT_ANY, NULL, pData, pSize);
82 }
83 
HasCLSIDShellFolderValue(REFCLSID clsid,LPCWSTR Value)84 static bool HasCLSIDShellFolderValue(REFCLSID clsid, LPCWSTR Value)
85 {
86     return SHELL_QueryCLSIDValue(clsid, L"ShellFolder", Value, NULL, NULL) == ERROR_SUCCESS;
87 }
88 
89 struct CRegFolderInfo
90 {
91     const REGFOLDERINFO *m_pInfo;
92 
InitializeFolderInfoCRegFolderInfo93     void InitializeFolderInfo(const REGFOLDERINFO *pInfo)
94     {
95         m_pInfo = pInfo;
96     }
97 
IsRegItemCRegFolderInfo98     const CLSID* IsRegItem(LPCITEMIDLIST pidl) const
99     {
100         if (pidl && pidl->mkid.cb >= sizeof(WORD) + 1 + 1 + sizeof(GUID))
101         {
102             if (pidl->mkid.abID[0] == m_pInfo->PidlType)
103                 return (CLSID*)(SIZE_T(pidl) + GetCLSIDOffset());
104             if (pidl->mkid.abID[0] == PT_CONTROLS_OLDREGITEM)
105                 return (CLSID*)(SIZE_T(pidl) + GetRegItemCLSIDOffset(PT_CONTROLS_OLDREGITEM));
106         }
107         if (const IID* pIID = _ILGetGUIDPointer(pidl))
108         {
109             FIXME("Unexpected GUID PIDL type %#x\n", pidl->mkid.abID[0]);
110             return pIID; // FIXME: Remove this when all folders have been fixed
111         }
112         return NULL;
113     }
114 
CreateItemCRegFolderInfo115     LPITEMIDLIST CreateItem(size_t i) const
116     {
117         const REQUIREDREGITEM &item = GetAt(i);
118         return CreateRegItem(GetPidlType(), item.clsid, item.Order);
119     }
120 
GetParsingPathCRegFolderInfo121     LPCWSTR GetParsingPath() const { return m_pInfo->pszParsingPath; }
GetCLSIDOffsetCRegFolderInfo122     UINT GetCLSIDOffset() const { return GetRegItemCLSIDOffset(m_pInfo->PidlType); }
GetPidlTypeCRegFolderInfo123     PIDLTYPE GetPidlType() const { return m_pInfo->PidlType; }
GetRequiredItemsCountCRegFolderInfo124     UINT GetRequiredItemsCount() const { return m_pInfo->Count; }
GetAtCRegFolderInfo125     const REQUIREDREGITEM& GetAt(size_t i) const { return m_pInfo->Items[i]; }
126 };
127 
CGuidItemExtractIcon_CreateInstance(LPCITEMIDLIST pidl,REFIID iid,LPVOID * ppvOut)128 HRESULT CGuidItemExtractIcon_CreateInstance(LPCITEMIDLIST pidl, REFIID iid, LPVOID * ppvOut)
129 {
130     CComPtr<IDefaultExtractIconInit>    initIcon;
131     HRESULT hr;
132     GUID const * riid;
133     int icon_idx;
134     WCHAR wTemp[MAX_PATH];
135 
136     hr = SHCreateDefaultExtractIcon(IID_PPV_ARG(IDefaultExtractIconInit,&initIcon));
137     if (FAILED(hr))
138         return hr;
139 
140     if (_ILIsDesktop(pidl))
141     {
142         initIcon->SetNormalIcon(swShell32Name, -IDI_SHELL_DESKTOP);
143         return initIcon->QueryInterface(iid, ppvOut);
144     }
145 
146     riid = _ILGetGUIDPointer(pidl);
147     if (!riid)
148         return E_FAIL;
149 
150     /* Choose a correct icon for Recycle Bin (full or empty) */
151     const WCHAR* iconname = NULL;
152     if (_ILIsBitBucket(pidl))
153     {
154         CComPtr<IEnumIDList> EnumIDList;
155         CoInitialize(NULL);
156 
157         CComPtr<IShellFolder2> psfRecycleBin;
158         CComPtr<IShellFolder> psfDesktop;
159         hr = SHGetDesktopFolder(&psfDesktop);
160 
161         if (SUCCEEDED(hr))
162             hr = psfDesktop->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder2, &psfRecycleBin));
163         if (SUCCEEDED(hr))
164             hr = psfRecycleBin->EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &EnumIDList);
165 
166         ULONG itemcount;
167         LPITEMIDLIST pidl = NULL;
168         if (SUCCEEDED(hr) && (hr = EnumIDList->Next(1, &pidl, &itemcount)) == S_OK)
169         {
170             CoTaskMemFree(pidl);
171             iconname = L"Full";
172         } else {
173             iconname = L"Empty";
174         }
175     }
176 
177     /* Prepare registry path for loading icons of My Computer and other shell extensions */
178     WCHAR KeyName[MAX_PATH];
179 
180     hr = FormatGUIDKey(KeyName, _countof(KeyName),
181                        L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\CLSID\\%s",
182                        riid);
183     if (FAILED_UNEXPECTEDLY(hr))
184         return hr;
185 
186     /* Load icon for the current user */
187     BOOL ret = HCU_GetIconW(KeyName, wTemp, iconname, _countof(wTemp), &icon_idx);
188     if (!ret)
189     {
190         /* Failed, load default system-wide icon */
191         hr = FormatGUIDKey(KeyName, _countof(KeyName), L"CLSID\\%s", riid);
192         if (FAILED_UNEXPECTEDLY(hr))
193             return hr;
194 
195         ret = HCR_GetIconW(KeyName, wTemp, iconname, _countof(wTemp), &icon_idx);
196     }
197 
198     if (ret)
199     {
200         /* Success, set loaded icon */
201         initIcon->SetNormalIcon(wTemp, icon_idx);
202     }
203     else
204     {
205         /* Everything has failed, set blank paper icon */
206         WARN("Failed to load an icon for the item, setting blank icon\n");
207         initIcon->SetNormalIcon(swShell32Name, IDI_SHELL_DOCUMENT - 1);
208     }
209 
210     return initIcon->QueryInterface(iid, ppvOut);
211 }
212 
213 class CRegFolderEnum :
214     public CEnumIDListBase,
215     public CRegFolderInfo
216 {
217     SHCONTF m_SHCTF;
218     public:
219         HRESULT Initialize(const REGFOLDERINFO *pInfo, IShellFolder *pSF, DWORD dwFlags);
220         HRESULT AddItemsFromKey(IShellFolder *pSF, HKEY hkey_root, LPCWSTR szRepPath);
221 
GetPidlClsid(PCUITEMID_CHILD pidl)222         const CLSID* GetPidlClsid(PCUITEMID_CHILD pidl) { return IsRegItem(pidl); }
HasItemWithCLSID(LPCITEMIDLIST pidl)223         BOOL HasItemWithCLSID(LPCITEMIDLIST pidl) { return HasItemWithCLSIDImpl<CRegFolderEnum>(pidl); }
224 
225         BEGIN_COM_MAP(CRegFolderEnum)
226         COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList)
227         END_COM_MAP()
228 };
229 
Initialize(const REGFOLDERINFO * pInfo,IShellFolder * pSF,DWORD dwFlags)230 HRESULT CRegFolderEnum::Initialize(const REGFOLDERINFO *pInfo, IShellFolder *pSF, DWORD dwFlags)
231 {
232     InitializeFolderInfo(pInfo);
233     m_SHCTF = (SHCONTF)dwFlags;
234     if (!(dwFlags & SHCONTF_FOLDERS))
235         return S_OK;
236 
237     WCHAR KeyName[MAX_PATH];
238     HRESULT hr = StringCchPrintfW(KeyName, _countof(KeyName),
239                                   L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\%s\\Namespace",
240                                   pInfo->pszEnumKeyName);
241     if (FAILED_UNEXPECTEDLY(hr))
242         return hr;
243 
244     // First add the required items and then the items from the registry
245     SFGAOF query = SHELL_CreateFolderEnumItemAttributeQuery(m_SHCTF, TRUE);
246     for (size_t i = 0; i < GetRequiredItemsCount(); ++i)
247     {
248         LPITEMIDLIST pidl = CreateItem(i);
249         if (pidl && SHELL_IncludeItemInFolderEnum(pSF, pidl, query, m_SHCTF))
250             AddToEnumList(pidl);
251         else
252             ILFree(pidl);
253     }
254     AddItemsFromKey(pSF, HKEY_LOCAL_MACHINE, KeyName);
255     AddItemsFromKey(pSF, HKEY_CURRENT_USER, KeyName);
256     return S_OK;
257 }
258 
AddItemsFromKey(IShellFolder * pSF,HKEY hkey_root,LPCWSTR szRepPath)259 HRESULT CRegFolderEnum::AddItemsFromKey(IShellFolder *pSF, HKEY hkey_root, LPCWSTR szRepPath)
260 {
261     WCHAR name[MAX_PATH];
262     HKEY hkey;
263 
264     if (RegOpenKeyW(hkey_root, szRepPath, &hkey) != ERROR_SUCCESS)
265         return S_FALSE;
266 
267     for (int idx = 0; ; idx++)
268     {
269         if (RegEnumKeyW(hkey, idx, name, MAX_PATH) != ERROR_SUCCESS)
270             break;
271 
272         /* If the name of the key is not a guid try to get the default value of the key */
273         if (name[0] != L'{')
274         {
275             DWORD dwSize = sizeof(name);
276             RegGetValueW(hkey, name, NULL, RRF_RT_REG_SZ, NULL, name, &dwSize);
277         }
278 
279         if (*name == '{')
280         {
281             if (LPITEMIDLIST pidl = CreateRegItem(GetPidlType(), name))
282             {
283                 SFGAOF query = SHELL_CreateFolderEnumItemAttributeQuery(m_SHCTF, TRUE);
284                 if (SHELL_IncludeItemInFolderEnum(pSF, pidl, query, m_SHCTF) && !HasItemWithCLSID(pidl))
285                     AddToEnumList(pidl);
286                 else
287                     ILFree(pidl);
288             }
289         }
290     }
291     RegCloseKey(hkey);
292 
293     return S_OK;
294 }
295 
296 /*
297  * These columns try to map to CFSFolder's columns because the CDesktopFolder
298  * displays CFSFolder and CRegFolder items in the same view.
299  */
300 enum REGFOLDERCOLUMNINDEX
301 {
302     COL_NAME = SHFSF_COL_NAME,
303     COL_TYPE = SHFSF_COL_TYPE,
304     COL_INFOTIP = SHFSF_COL_COMMENT,
305     REGFOLDERCOLUMNCOUNT = max(COL_INFOTIP, COL_TYPE) + 1
306 };
307 
308 class CRegFolder :
309     public CComObjectRootEx<CComMultiThreadModelNoCS>,
310     public IShellFolder2,
311     public CRegFolderInfo
312 {
313     private:
314         IShellFolder *m_pOuterFolder; // Not ref-counted
315         CComHeapPtr<ITEMIDLIST> m_pidlRoot;
316 
317         HRESULT GetGuidItemAttributes (LPCITEMIDLIST pidl, LPDWORD pdwAttributes);
318         BOOL _IsInNameSpace(_In_ LPCITEMIDLIST pidl);
319 
320     public:
321         CRegFolder();
322         ~CRegFolder();
323         HRESULT WINAPI Initialize(PREGFOLDERINITDATA pInit, LPCITEMIDLIST pidlRoot);
324 
IsRequiredItem(LPCITEMIDLIST pidl) const325         const REQUIREDREGITEM* IsRequiredItem(LPCITEMIDLIST pidl) const
326         {
327             const CLSID* const pCLSID = IsRegItem(pidl);
328             for (size_t i = 0; pCLSID && i < GetRequiredItemsCount(); ++i)
329             {
330                 const REQUIREDREGITEM &item = GetAt(i);
331                 if (item.clsid == *pCLSID)
332                     return &item;
333             }
334             return NULL;
335         }
336 
337         // IShellFolder
338         STDMETHOD(ParseDisplayName)(HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName, ULONG *pchEaten, PIDLIST_RELATIVE *ppidl, ULONG *pdwAttributes) override;
339         STDMETHOD(EnumObjects)(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList) override;
340         STDMETHOD(BindToObject)(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut) override;
341         STDMETHOD(BindToStorage)(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut) override;
342         STDMETHOD(CompareIDs)(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2) override;
343         STDMETHOD(CreateViewObject)(HWND hwndOwner, REFIID riid, LPVOID *ppvOut) override;
344         STDMETHOD(GetAttributesOf)(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, DWORD *rgfInOut) override;
345         STDMETHOD(GetUIObjectOf)(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, REFIID riid, UINT * prgfInOut, LPVOID * ppvOut) override;
346         STDMETHOD(GetDisplayNameOf)(PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET strRet) override;
347         STDMETHOD(SetNameOf)(HWND hwndOwner, PCUITEMID_CHILD pidl, LPCOLESTR lpName, DWORD dwFlags, PITEMID_CHILD *pPidlOut) override;
348 
349         /* ShellFolder2 */
350         STDMETHOD(GetDefaultSearchGUID)(GUID *pguid) override;
351         STDMETHOD(EnumSearches)(IEnumExtraSearch **ppenum) override;
352         STDMETHOD(GetDefaultColumn)(DWORD dwRes, ULONG *pSort, ULONG *pDisplay) override;
353         STDMETHOD(GetDefaultColumnState)(UINT iColumn, DWORD *pcsFlags) override;
354         STDMETHOD(GetDetailsEx)(PCUITEMID_CHILD pidl, const SHCOLUMNID *pscid, VARIANT *pv) override;
355         STDMETHOD(GetDetailsOf)(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDETAILS *psd) override;
356         STDMETHOD(MapColumnToSCID)(UINT column, SHCOLUMNID *pscid) override;
357 
358         DECLARE_NOT_AGGREGATABLE(CRegFolder)
359 
360         DECLARE_PROTECT_FINAL_CONSTRUCT()
361 
362         BEGIN_COM_MAP(CRegFolder)
363         COM_INTERFACE_ENTRY_IID(IID_IShellFolder2, IShellFolder2)
364         COM_INTERFACE_ENTRY_IID(IID_IShellFolder, IShellFolder)
365         END_COM_MAP()
366 };
367 
CRegFolder()368 CRegFolder::CRegFolder()
369 {
370 }
371 
~CRegFolder()372 CRegFolder::~CRegFolder()
373 {
374 }
375 
Initialize(PREGFOLDERINITDATA pInit,LPCITEMIDLIST pidlRoot)376 HRESULT WINAPI CRegFolder::Initialize(PREGFOLDERINITDATA pInit, LPCITEMIDLIST pidlRoot)
377 {
378     InitializeFolderInfo(pInit->pInfo);
379     m_pOuterFolder = pInit->psfOuter;
380 
381     m_pidlRoot.Attach(ILClone(pidlRoot));
382     if (!m_pidlRoot)
383         return E_OUTOFMEMORY;
384 
385     return S_OK;
386 }
387 
GetGuidItemAttributes(LPCITEMIDLIST pidl,LPDWORD pdwAttributes)388 HRESULT CRegFolder::GetGuidItemAttributes (LPCITEMIDLIST pidl, LPDWORD pdwAttributes)
389 {
390     DWORD dwAttributes = *pdwAttributes;
391 
392     /* First try to get them from the registry */
393     if (!HCR_GetFolderAttributes(pidl, pdwAttributes))
394     {
395         /* We couldn't get anything */
396         *pdwAttributes = 0;
397     }
398 
399     /* Items have more attributes when on desktop */
400     if (_ILIsDesktop(m_pidlRoot))
401     {
402         *pdwAttributes |= (dwAttributes & (SFGAO_CANLINK|SFGAO_CANDELETE|SFGAO_CANRENAME|SFGAO_HASPROPSHEET));
403     }
404 
405     /* In any case, links can be created */
406     *pdwAttributes |= (dwAttributes & SFGAO_CANLINK);
407     return S_OK;
408 }
409 
_IsInNameSpace(_In_ LPCITEMIDLIST pidl)410 BOOL CRegFolder::_IsInNameSpace(_In_ LPCITEMIDLIST pidl)
411 {
412     CLSID clsid = *_ILGetGUIDPointer(pidl);
413     if (IsEqualGUID(clsid, CLSID_Printers))
414         return TRUE;
415     if (IsEqualGUID(clsid, CLSID_ConnectionFolder))
416         return TRUE;
417     FIXME("Check registry\n");
418     return TRUE;
419 }
420 
ParseDisplayName(HWND hwndOwner,LPBC pbc,LPOLESTR lpszDisplayName,ULONG * pchEaten,PIDLIST_RELATIVE * ppidl,ULONG * pdwAttributes)421 HRESULT WINAPI CRegFolder::ParseDisplayName(HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName,
422         ULONG *pchEaten, PIDLIST_RELATIVE *ppidl, ULONG *pdwAttributes)
423 {
424     if (!ppidl)
425         return E_INVALIDARG;
426 
427     *ppidl = NULL;
428 
429     if (!lpszDisplayName)
430         return E_INVALIDARG;
431 
432     if (lpszDisplayName[0] != L':' || lpszDisplayName[1] != L':')
433     {
434         FIXME("What should we do here?\n");
435         return E_FAIL;
436     }
437 
438     LPWSTR pch, pchNextOfComma = NULL;
439     for (pch = &lpszDisplayName[2]; *pch && *pch != L'\\'; ++pch)
440     {
441         if (*pch == L',' && !pchNextOfComma)
442             pchNextOfComma = pch + 1;
443     }
444 
445     CLSID clsid;
446     if (!GUIDFromStringW(&lpszDisplayName[2], &clsid))
447         return CO_E_CLASSSTRING;
448 
449     if (pchNextOfComma)
450     {
451         FIXME("Delegate folder\n");
452         return E_FAIL;
453     }
454 
455     CComHeapPtr<ITEMIDLIST> pidlTemp(CreateRegItem(GetPidlType(), clsid));
456     if (!pidlTemp)
457         return E_OUTOFMEMORY;
458 
459     if (!_IsInNameSpace(pidlTemp) && !(BindCtx_GetMode(pbc, 0) & STGM_CREATE))
460         return E_INVALIDARG;
461 
462     *ppidl = pidlTemp.Detach();
463 
464     if (!*pch)
465     {
466         if (pdwAttributes && *pdwAttributes)
467             GetGuidItemAttributes(*ppidl, pdwAttributes);
468 
469         return S_OK;
470     }
471 
472     HRESULT hr = SHELL32_ParseNextElement(this, hwndOwner, pbc, ppidl, pch + 1, pchEaten,
473                                           pdwAttributes);
474     if (FAILED(hr))
475     {
476         ILFree(*ppidl);
477         *ppidl = NULL;
478     }
479     return hr;
480 }
481 
EnumObjects(HWND hwndOwner,DWORD dwFlags,LPENUMIDLIST * ppEnumIDList)482 HRESULT WINAPI CRegFolder::EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList)
483 {
484     return ShellObjectCreatorInit<CRegFolderEnum>(m_pInfo, m_pOuterFolder, dwFlags,
485                                                   IID_PPV_ARG(IEnumIDList, ppEnumIDList));
486 }
487 
BindToObject(PCUIDLIST_RELATIVE pidl,LPBC pbcReserved,REFIID riid,LPVOID * ppvOut)488 HRESULT WINAPI CRegFolder::BindToObject(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut)
489 {
490     CComPtr<IPersistFolder> pFolder;
491     HRESULT hr;
492 
493     if (!ppvOut || !pidl || !pidl->mkid.cb)
494         return E_INVALIDARG;
495 
496     *ppvOut = NULL;
497 
498     GUID *pGUID = _ILGetGUIDPointer(pidl);
499     if (!pGUID)
500     {
501         ERR("CRegFolder::BindToObject called for non guid item!\n");
502         return E_INVALIDARG;
503     }
504 
505     hr = SHELL32_BindToSF(m_pidlRoot, NULL, pidl, pGUID, riid, ppvOut);
506     if (FAILED_UNEXPECTEDLY(hr))
507         return hr;
508 
509     return S_OK;
510 }
511 
BindToStorage(PCUIDLIST_RELATIVE pidl,LPBC pbcReserved,REFIID riid,LPVOID * ppvOut)512 HRESULT WINAPI CRegFolder::BindToStorage(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut)
513 {
514     return E_NOTIMPL;
515 }
516 
CompareIDs(LPARAM lParam,PCUIDLIST_RELATIVE pidl1,PCUIDLIST_RELATIVE pidl2)517 HRESULT WINAPI CRegFolder::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2)
518 {
519     if (!pidl1 || !pidl2 || pidl1->mkid.cb == 0 || pidl2->mkid.cb == 0)
520     {
521         ERR("Got an empty pidl!\n");
522         return E_INVALIDARG;
523     }
524 
525     GUID const *clsid1 = _ILGetGUIDPointer (pidl1);
526     GUID const *clsid2 = _ILGetGUIDPointer (pidl2);
527 
528     if (!clsid1 && !clsid2)
529     {
530         ERR("Got no guid pidl!\n");
531         return E_INVALIDARG;
532     }
533     else if (clsid1 && clsid2)
534     {
535         if (memcmp(clsid1, clsid2, sizeof(GUID)) == 0)
536             return SHELL32_CompareChildren(this, lParam, pidl1, pidl2);
537 
538         return SHELL32_CompareDetails(this, lParam, pidl1, pidl2);
539     }
540 
541     /* Guid folders come first compared to everything else */
542     /* And Drives come before folders in My Computer */
543     if (GetPidlType() == PT_COMPUTER_REGITEM)
544         return MAKE_COMPARE_HRESULT(clsid1 ? 1 : -1);
545     else
546         return MAKE_COMPARE_HRESULT(clsid1 ? -1 : 1);
547 }
548 
CreateViewObject(HWND hwndOwner,REFIID riid,LPVOID * ppvOut)549 HRESULT WINAPI CRegFolder::CreateViewObject(HWND hwndOwner, REFIID riid, LPVOID *ppvOut)
550 {
551     return E_NOTIMPL;
552 }
553 
GetAttributesOf(UINT cidl,PCUITEMID_CHILD_ARRAY apidl,DWORD * rgfInOut)554 HRESULT WINAPI CRegFolder::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, DWORD *rgfInOut)
555 {
556     if (!rgfInOut || !cidl || !apidl)
557         return E_INVALIDARG;
558 
559     if (*rgfInOut == 0)
560         *rgfInOut = ~0;
561 
562     while(cidl > 0 && *apidl)
563     {
564         if (_ILIsSpecialFolder(*apidl))
565             GetGuidItemAttributes(*apidl, rgfInOut);
566         else
567             ERR("Got unknown pidl\n");
568         apidl++;
569         cidl--;
570     }
571 
572     /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */
573     *rgfInOut &= ~SFGAO_VALIDATE;
574 
575     return S_OK;
576 }
577 
GetUIObjectOf(HWND hwndOwner,UINT cidl,PCUITEMID_CHILD_ARRAY apidl,REFIID riid,UINT * prgfInOut,LPVOID * ppvOut)578 HRESULT WINAPI CRegFolder::GetUIObjectOf(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
579         REFIID riid, UINT * prgfInOut, LPVOID * ppvOut)
580 {
581     LPVOID pObj = NULL;
582     HRESULT hr = E_INVALIDARG;
583 
584     if (!ppvOut)
585         return hr;
586 
587     *ppvOut = NULL;
588 
589     if ((IsEqualIID (riid, IID_IExtractIconA) || IsEqualIID (riid, IID_IExtractIconW)) && (cidl == 1))
590     {
591         hr = CGuidItemExtractIcon_CreateInstance(apidl[0], riid, &pObj);
592     }
593     else if (IsEqualIID (riid, IID_IContextMenu) && (cidl >= 1))
594     {
595         if (!_ILGetGUIDPointer (apidl[0]))
596         {
597             ERR("Got non guid item!\n");
598             return E_FAIL;
599         }
600 
601         hr = CRegItemContextMenu_CreateInstance(m_pidlRoot, hwndOwner, cidl, apidl, static_cast<IShellFolder*>(this), (IContextMenu**)&pObj);
602     }
603     else if (IsEqualIID (riid, IID_IDataObject) && (cidl >= 1))
604     {
605         hr = IDataObject_Constructor (hwndOwner, m_pidlRoot, apidl, cidl, TRUE, (IDataObject **)&pObj);
606     }
607     else
608     {
609         hr = E_NOINTERFACE;
610     }
611 
612     *ppvOut = pObj;
613     return hr;
614 
615 }
616 
GetDisplayNameOf(PCUITEMID_CHILD pidl,DWORD dwFlags,LPSTRRET strRet)617 HRESULT WINAPI CRegFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET strRet)
618 {
619     if (!strRet || (!_ILIsSpecialFolder(pidl) && pidl != NULL))
620         return E_INVALIDARG;
621 
622     if (!pidl || !pidl->mkid.cb)
623     {
624         if (IS_SHGDN_FOR_PARSING(dwFlags))
625         {
626             if (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER)
627             {
628                 TRACE("GDNO returning INFOLDER instead of %#x\n", GET_SHGDN_RELATION(dwFlags));
629             }
630             LPWSTR pszPath = (LPWSTR)CoTaskMemAlloc((2 + 38 + 1) * sizeof(WCHAR));
631             if (!pszPath)
632                 return E_OUTOFMEMORY;
633             /* parsing name like ::{...} */
634             pszPath[0] = ':';
635             pszPath[1] = ':';
636             SHELL32_GUIDToStringW(m_pInfo->clsid, &pszPath[2]);
637             strRet->uType = STRRET_WSTR;
638             strRet->pOleStr = pszPath;
639             return S_OK;
640         }
641         else
642         {
643             BOOL bRet;
644             WCHAR wstrName[MAX_PATH+1];
645             bRet = HCR_GetClassNameW(m_pInfo->clsid, wstrName, MAX_PATH);
646             if (!bRet)
647                 return E_FAIL;
648 
649             return SHSetStrRet(strRet, wstrName);
650         }
651     }
652 
653     HRESULT hr;
654     GUID const *clsid = _ILGetGUIDPointer (pidl);
655 
656     /* First of all check if we need to query the name from the child item */
657     if (GET_SHGDN_FOR (dwFlags) == SHGDN_FORPARSING &&
658         GET_SHGDN_RELATION (dwFlags) == SHGDN_NORMAL)
659     {
660         int bWantsForParsing = FALSE;
661 
662         /*
663             * We can only get a filesystem path from a shellfolder if the
664             *  value WantsFORPARSING in CLSID\\{...}\\shellfolder exists.
665             *
666             * Exception: The MyComputer folder doesn't have this key,
667             *   but any other filesystem backed folder it needs it.
668             */
669         if (IsEqualIID (*clsid, CLSID_MyComputer))
670         {
671             bWantsForParsing = TRUE;
672         }
673         else
674         {
675             HKEY hkeyClass;
676             if (HCR_RegOpenClassIDKey(*clsid, &hkeyClass))
677             {
678                 LONG res = SHGetValueW(hkeyClass, L"Shellfolder", L"WantsForParsing", NULL, NULL, NULL);
679                 bWantsForParsing = (res == ERROR_SUCCESS);
680                 RegCloseKey(hkeyClass);
681             }
682         }
683 
684         if (bWantsForParsing)
685         {
686             /*
687              * we need the filesystem path to the destination folder.
688              * Only the folder itself can know it
689              */
690             return SHELL32_GetDisplayNameOfChild (this, pidl, dwFlags, strRet);
691         }
692     }
693 
694     /* Allocate the buffer for the result */
695     SIZE_T cchPath = MAX_PATH + 1;
696     LPWSTR pszPath = (LPWSTR)CoTaskMemAlloc(cchPath * sizeof(WCHAR));
697     if (!pszPath)
698         return E_OUTOFMEMORY;
699 
700     hr = S_OK;
701 
702     if (GET_SHGDN_FOR (dwFlags) == SHGDN_FORPARSING)
703     {
704         SIZE_T pathlen = 0;
705         PWCHAR pItemName = pszPath; // GET_SHGDN_RELATION(dwFlags) == SHGDN_INFOLDER
706         if (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER)
707         {
708             hr = StringCchCopyW(pszPath, cchPath, GetParsingPath());
709             if (SUCCEEDED(hr))
710             {
711                 pathlen = wcslen(pszPath);
712                 pItemName = &pszPath[pathlen];
713                 if (pathlen)
714                 {
715                     if (++pathlen < cchPath)
716                         *pItemName++ = L'\\';
717                     else
718                         hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
719                 }
720             }
721         }
722 
723         if (SUCCEEDED(hr) && pathlen + 2 + 38 + 1 < cchPath)
724         {
725             /* parsing name like ::{...} */
726             pItemName[0] = L':';
727             pItemName[1] = L':';
728             SHELL32_GUIDToStringW(*clsid, &pItemName[2]);
729         }
730         else
731         {
732             hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
733         }
734     }
735     else
736     {
737         /* user friendly name */
738         if (!HCR_GetClassNameW(*clsid, pszPath, cchPath))
739             hr = E_FAIL;
740     }
741 
742     if (SUCCEEDED(hr))
743     {
744         strRet->uType = STRRET_WSTR;
745         strRet->pOleStr = pszPath;
746     }
747     else
748     {
749         CoTaskMemFree(pszPath);
750     }
751 
752     return hr;
753 }
754 
SetNameOf(HWND hwndOwner,PCUITEMID_CHILD pidl,LPCOLESTR lpName,DWORD dwFlags,PITEMID_CHILD * pPidlOut)755 HRESULT WINAPI CRegFolder::SetNameOf(HWND hwndOwner, PCUITEMID_CHILD pidl,    /* simple pidl */
756         LPCOLESTR lpName, DWORD dwFlags, PITEMID_CHILD *pPidlOut)
757 {
758     GUID const *clsid = _ILGetGUIDPointer (pidl);
759     LPOLESTR pStr;
760     HRESULT hr;
761     WCHAR szName[100];
762 
763     if (!clsid)
764     {
765         ERR("Pidl is not reg item!\n");
766         return E_FAIL;
767     }
768 
769     hr = StringFromCLSID(*clsid, &pStr);
770     if (FAILED_UNEXPECTEDLY(hr))
771         return hr;
772 
773     swprintf(szName, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\CLSID\\%s", pStr);
774 
775     DWORD cbData = (wcslen(lpName) + 1) * sizeof(WCHAR);
776     LONG res = SHSetValueW(HKEY_CURRENT_USER, szName, NULL, RRF_RT_REG_SZ, lpName, cbData);
777 
778     CoTaskMemFree(pStr);
779 
780     if (res == ERROR_SUCCESS)
781     {
782         return pPidlOut ? SHILClone(pidl, pPidlOut) : S_OK;
783     }
784 
785     return E_FAIL;
786 }
787 
788 
GetDefaultSearchGUID(GUID * pguid)789 HRESULT WINAPI CRegFolder::GetDefaultSearchGUID(GUID *pguid)
790 {
791     return E_NOTIMPL;
792 }
793 
EnumSearches(IEnumExtraSearch ** ppenum)794 HRESULT WINAPI CRegFolder::EnumSearches(IEnumExtraSearch ** ppenum)
795 {
796     return E_NOTIMPL;
797 }
798 
GetDefaultColumn(DWORD dwRes,ULONG * pSort,ULONG * pDisplay)799 HRESULT WINAPI CRegFolder::GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
800 {
801     if (pSort)
802         *pSort = 0;
803     if (pDisplay)
804         *pDisplay = 0;
805 
806     return S_OK;
807 }
808 
GetDefaultColumnState(UINT iColumn,DWORD * pcsFlags)809 HRESULT WINAPI CRegFolder::GetDefaultColumnState(UINT iColumn, DWORD *pcsFlags)
810 {
811     if (iColumn >= REGFOLDERCOLUMNCOUNT)
812         return E_INVALIDARG;
813     *pcsFlags = SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT;
814     return S_OK;
815 }
816 
GetDetailsEx(PCUITEMID_CHILD pidl,const SHCOLUMNID * pscid,VARIANT * pv)817 HRESULT WINAPI CRegFolder::GetDetailsEx(PCUITEMID_CHILD pidl, const SHCOLUMNID *pscid, VARIANT *pv)
818 {
819     return E_NOTIMPL;
820 }
821 
GetDetailsOf(PCUITEMID_CHILD pidl,UINT iColumn,SHELLDETAILS * psd)822 HRESULT WINAPI CRegFolder::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDETAILS *psd)
823 {
824     if (!psd)
825         return E_INVALIDARG;
826 
827     if (!pidl)
828     {
829         TRACE("CRegFolder has no column info\n");
830         return E_INVALIDARG;
831     }
832 
833     GUID const *clsid = _ILGetGUIDPointer (pidl);
834 
835     if (!clsid)
836     {
837         ERR("Pidl is not reg item!\n");
838         return E_INVALIDARG;
839     }
840 
841     switch(iColumn)
842     {
843         case COL_NAME:
844             return GetDisplayNameOf(pidl, SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str);
845         case COL_TYPE:
846             return SHSetStrRet(&psd->str, IDS_SYSTEMFOLDER);
847         case COL_INFOTIP:
848             HKEY hKey;
849             if (!HCR_RegOpenClassIDKey(*clsid, &hKey))
850                 return SHSetStrRet(&psd->str, "");
851 
852             psd->str.cStr[0] = 0x00;
853             psd->str.uType = STRRET_CSTR;
854             RegLoadMUIStringA(hKey, "InfoTip", psd->str.cStr, MAX_PATH, NULL, 0, NULL);
855             RegCloseKey(hKey);
856             return S_OK;
857         default:
858             /* Return an empty string when we area asked for a column we don't support.
859                Only  the regfolder is supposed to do this as it supports less columns compared to other folder
860                and its contents are supposed to be presented alongside items that support more columns. */
861             return SHSetStrRet(&psd->str, "");
862     }
863     return E_FAIL;
864 }
865 
MapColumnToSCID(UINT column,SHCOLUMNID * pscid)866 HRESULT WINAPI CRegFolder::MapColumnToSCID(UINT column, SHCOLUMNID *pscid)
867 {
868     return E_NOTIMPL;
869 }
870 
RegFolderContextMenuCallback(IShellFolder * psf,HWND hwnd,IDataObject * pdtobj,UINT uMsg,WPARAM wParam,LPARAM lParam)871 static HRESULT CALLBACK RegFolderContextMenuCallback(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj,
872                                                      UINT uMsg, WPARAM wParam, LPARAM lParam)
873 {
874     if (uMsg != DFM_INVOKECOMMAND || wParam != DFM_CMD_PROPERTIES)
875         return SHELL32_DefaultContextMenuCallBack(psf, pdtobj, uMsg);
876 
877     PIDLIST_ABSOLUTE pidlFolder;
878     PUITEMID_CHILD *apidl;
879     UINT cidl;
880     HRESULT hr = SH_GetApidlFromDataObject(pdtobj, &pidlFolder, &apidl, &cidl);
881     if (FAILED_UNEXPECTEDLY(hr))
882         return hr;
883 
884     CRegFolder *pRegFolder = static_cast<CRegFolder*>(psf);
885     const REQUIREDREGITEM* pRequired = pRegFolder->IsRequiredItem(apidl[0]);
886     if (pRequired && pRequired->pszCpl)
887     {
888         WCHAR buf[MAX_PATH];
889         wsprintfW(buf, L"%hs", const_cast<LPCSTR>(pRequired->pszCpl));
890         hr = SHELL_ExecuteControlPanelCPL(hwnd, buf) ? S_OK : E_FAIL;
891     }
892 #if 0 // Should never happen, CDesktopFolder.cpp handles this
893     else if (_ILIsDesktop(pidlFolder) && _ILIsDesktop(apidl[0]))
894     {
895         hr = SHELL_ExecuteControlPanelCPL(hwnd, L"desk.cpl") ? S_OK : E_FAIL;
896     }
897 #endif
898     else if (_ILIsDesktop(pidlFolder) && _ILIsBitBucket(apidl[0]))
899     {
900         FIXME("Use SHOpenPropSheet on Recyclers PropertySheetHandlers from the registry\n");
901         hr = SH_ShowRecycleBinProperties(L'C') ? S_OK : E_FAIL;
902     }
903     else
904     {
905         hr = S_FALSE; // Tell the caller to run the default action
906     }
907 
908     SHFree(pidlFolder);
909     _ILFreeaPidl(apidl, cidl);
910     return hr;
911 }
912 
CRegItemContextMenu_CreateInstance(PCIDLIST_ABSOLUTE pidlFolder,HWND hwnd,UINT cidl,PCUITEMID_CHILD_ARRAY apidl,IShellFolder * psf,IContextMenu ** ppcm)913 static HRESULT CRegItemContextMenu_CreateInstance(PCIDLIST_ABSOLUTE pidlFolder, HWND hwnd, UINT cidl,
914                                                   PCUITEMID_CHILD_ARRAY apidl, IShellFolder *psf, IContextMenu **ppcm)
915 {
916     HKEY hKeys[3];
917     UINT cKeys = 0;
918 
919     const GUID *pGuid = _ILGetGUIDPointer(apidl[0]);
920     if (pGuid)
921     {
922         WCHAR key[sizeof("CLSID\\") + 38];
923         wcscpy(key, L"CLSID\\");
924         StringFromGUID2(*pGuid, &key[6], 39);
925         AddClassKeyToArray(key, hKeys, &cKeys);
926     }
927 
928     // FIXME: CRegFolder should be aggregated by its outer folder and should
929     // provide the attributes for all required non-registry folders.
930     // It currently does not so we have to ask the outer folder ourself so
931     // that we get the correct attributes for My Computer etc.
932     CComPtr<IShellFolder> pOuterSF;
933     SHBindToObject(NULL, pidlFolder, IID_PPV_ARG(IShellFolder, &pOuterSF));
934 
935     SFGAOF att = (psf && cidl) ? SHGetAttributes(pOuterSF ? pOuterSF.p : psf, apidl[0], SFGAO_FOLDER) : 0;
936     if ((att & SFGAO_FOLDER) && (!pGuid || !HasCLSIDShellFolderValue(*pGuid, L"HideFolderVerbs")))
937         AddClassKeyToArray(L"Folder", hKeys, &cKeys);
938 
939     return CDefFolderMenu_Create2(pidlFolder, hwnd, cidl, apidl, psf, RegFolderContextMenuCallback, cKeys, hKeys, ppcm);
940 }
941 
942 /* In latest windows version this is exported but it takes different arguments! */
CRegFolder_CreateInstance(PREGFOLDERINITDATA pInit,LPCITEMIDLIST pidlRoot,REFIID riid,void ** ppv)943 HRESULT CRegFolder_CreateInstance(PREGFOLDERINITDATA pInit, LPCITEMIDLIST pidlRoot, REFIID riid, void **ppv)
944 {
945     return ShellObjectCreatorInit<CRegFolder>(pInit, pidlRoot, riid, ppv);
946 }
947