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