[SHELL32]
[reactos.git] / reactos / dll / win32 / shell32 / folders / CRegFolder.cpp
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
21 #include <precomp.h>
22
23 WINE_DEFAULT_DEBUG_CHANNEL (shell);
24
25 HRESULT CGuidItemExtractIcon_CreateInstance(LPCITEMIDLIST pidl, REFIID iid, LPVOID * ppvOut)
26 {
27 CComPtr<IDefaultExtractIconInit> initIcon;
28 HRESULT hr;
29 GUID const * riid;
30 int icon_idx;
31 WCHAR wTemp[MAX_PATH];
32
33 hr = SHCreateDefaultExtractIcon(IID_PPV_ARG(IDefaultExtractIconInit,&initIcon));
34 if (FAILED(hr))
35 return hr;
36
37 if (_ILIsDesktop(pidl))
38 {
39 initIcon->SetNormalIcon(swShell32Name, -IDI_SHELL_DESKTOP);
40 return initIcon->QueryInterface(iid, ppvOut);
41 }
42
43 riid = _ILGetGUIDPointer(pidl);
44 if (!riid)
45 return E_FAIL;
46
47 /* my computer and other shell extensions */
48 static const WCHAR fmt[] = { 'C', 'L', 'S', 'I', 'D', '\\',
49 '{', '%', '0', '8', 'l', 'x', '-', '%', '0', '4', 'x', '-', '%', '0', '4', 'x', '-',
50 '%', '0', '2', 'x', '%', '0', '2', 'x', '-', '%', '0', '2', 'x', '%', '0', '2', 'x',
51 '%', '0', '2', 'x', '%', '0', '2', 'x', '%', '0', '2', 'x', '%', '0', '2', 'x', '}', 0
52 };
53 WCHAR xriid[50];
54
55 swprintf(xriid, fmt,
56 riid->Data1, riid->Data2, riid->Data3,
57 riid->Data4[0], riid->Data4[1], riid->Data4[2], riid->Data4[3],
58 riid->Data4[4], riid->Data4[5], riid->Data4[6], riid->Data4[7]);
59
60 const WCHAR* iconname = NULL;
61 if (_ILIsBitBucket(pidl))
62 {
63 static const WCHAR szFull[] = {'F','u','l','l',0};
64 static const WCHAR szEmpty[] = {'E','m','p','t','y',0};
65 CComPtr<IEnumIDList> EnumIDList;
66 CoInitialize(NULL);
67
68 CComPtr<IShellFolder2> psfRecycleBin;
69 CComPtr<IShellFolder> psfDesktop;
70 hr = SHGetDesktopFolder(&psfDesktop);
71
72 if (SUCCEEDED(hr))
73 hr = psfDesktop->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder2, &psfRecycleBin));
74 if (SUCCEEDED(hr))
75 hr = psfRecycleBin->EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &EnumIDList);
76
77 ULONG itemcount;
78 LPITEMIDLIST pidl = NULL;
79 if (SUCCEEDED(hr) && (hr = EnumIDList->Next(1, &pidl, &itemcount)) == S_OK)
80 {
81 CoTaskMemFree(pidl);
82 iconname = szFull;
83 } else {
84 iconname = szEmpty;
85 }
86 }
87
88 if (HCR_GetIconW(xriid, wTemp, iconname, MAX_PATH, &icon_idx))
89 {
90 initIcon->SetNormalIcon(wTemp, icon_idx);
91 }
92 else
93 {
94 if (IsEqualGUID(*riid, CLSID_MyComputer))
95 initIcon->SetNormalIcon(swShell32Name, -IDI_SHELL_MY_COMPUTER);
96 else if (IsEqualGUID(*riid, CLSID_MyDocuments))
97 initIcon->SetNormalIcon(swShell32Name, -IDI_SHELL_MY_DOCUMENTS);
98 else if (IsEqualGUID(*riid, CLSID_NetworkPlaces))
99 initIcon->SetNormalIcon(swShell32Name, -IDI_SHELL_MY_NETWORK_PLACES);
100 else
101 initIcon->SetNormalIcon(swShell32Name, -IDI_SHELL_FOLDER);
102 }
103
104 return initIcon->QueryInterface(iid, ppvOut);
105 }
106
107 class CRegFolder :
108 public CComObjectRootEx<CComMultiThreadModelNoCS>,
109 public IShellFolder2
110 {
111 private:
112 GUID m_guid;
113 CAtlStringW m_rootPath;
114 CComHeapPtr<ITEMIDLIST> m_pidlRoot;
115
116 HRESULT GetGuidItemAttributes (LPCITEMIDLIST pidl, LPDWORD pdwAttributes);
117 public:
118 CRegFolder();
119 ~CRegFolder();
120 HRESULT WINAPI Initialize(const GUID *pGuid, LPCITEMIDLIST pidlRoot, LPCWSTR lpszPath);
121
122 // IShellFolder
123 virtual HRESULT WINAPI ParseDisplayName(HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName, ULONG *pchEaten, PIDLIST_RELATIVE *ppidl, ULONG *pdwAttributes);
124 virtual HRESULT WINAPI EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList);
125 virtual HRESULT WINAPI BindToObject(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut);
126 virtual HRESULT WINAPI BindToStorage(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut);
127 virtual HRESULT WINAPI CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2);
128 virtual HRESULT WINAPI CreateViewObject(HWND hwndOwner, REFIID riid, LPVOID *ppvOut);
129 virtual HRESULT WINAPI GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, DWORD *rgfInOut);
130 virtual HRESULT WINAPI GetUIObjectOf(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, REFIID riid, UINT * prgfInOut, LPVOID * ppvOut);
131 virtual HRESULT WINAPI GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET strRet);
132 virtual HRESULT WINAPI SetNameOf(HWND hwndOwner, PCUITEMID_CHILD pidl, LPCOLESTR lpName, DWORD dwFlags, PITEMID_CHILD *pPidlOut);
133
134 /* ShellFolder2 */
135 virtual HRESULT WINAPI GetDefaultSearchGUID(GUID *pguid);
136 virtual HRESULT WINAPI EnumSearches(IEnumExtraSearch **ppenum);
137 virtual HRESULT WINAPI GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay);
138 virtual HRESULT WINAPI GetDefaultColumnState(UINT iColumn, DWORD *pcsFlags);
139 virtual HRESULT WINAPI GetDetailsEx(PCUITEMID_CHILD pidl, const SHCOLUMNID *pscid, VARIANT *pv);
140 virtual HRESULT WINAPI GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDETAILS *psd);
141 virtual HRESULT WINAPI MapColumnToSCID(UINT column, SHCOLUMNID *pscid);
142
143 DECLARE_NOT_AGGREGATABLE(CRegFolder)
144
145 DECLARE_PROTECT_FINAL_CONSTRUCT()
146
147 BEGIN_COM_MAP(CRegFolder)
148 COM_INTERFACE_ENTRY_IID(IID_IShellFolder2, IShellFolder2)
149 COM_INTERFACE_ENTRY_IID(IID_IShellFolder, IShellFolder)
150 END_COM_MAP()
151 };
152
153 CRegFolder::CRegFolder()
154 {
155 }
156
157 CRegFolder::~CRegFolder()
158 {
159 }
160
161 HRESULT WINAPI CRegFolder::Initialize(const GUID *pGuid, LPCITEMIDLIST pidlRoot, LPCWSTR lpszPath)
162 {
163 memcpy(&m_guid, pGuid, sizeof(m_guid));
164
165 m_rootPath = lpszPath;
166 if (!m_rootPath)
167 return E_OUTOFMEMORY;
168
169 m_pidlRoot.Attach(ILClone(pidlRoot));
170 if (!m_pidlRoot)
171 return E_OUTOFMEMORY;
172
173 return S_OK;
174 }
175
176 HRESULT CRegFolder::GetGuidItemAttributes (LPCITEMIDLIST pidl, LPDWORD pdwAttributes)
177 {
178 /* First try to get them from the registry */
179 if (HCR_GetFolderAttributes(pidl, pdwAttributes) && *pdwAttributes)
180 {
181 return S_OK;
182 }
183 else
184 {
185 /* If we can't get it from the registry we have to query the child */
186 CComPtr<IShellFolder> psf2;
187 if (SUCCEEDED(BindToObject(pidl, 0, IID_PPV_ARG(IShellFolder, &psf2))))
188 {
189 return psf2->GetAttributesOf(0, NULL, pdwAttributes);
190 }
191 }
192
193 *pdwAttributes &= SFGAO_CANLINK;
194 return S_OK;
195 }
196
197 HRESULT WINAPI CRegFolder::ParseDisplayName(HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName,
198 ULONG *pchEaten, PIDLIST_RELATIVE *ppidl, ULONG *pdwAttributes)
199 {
200 LPITEMIDLIST pidl;
201
202 if (!lpszDisplayName || !ppidl)
203 return E_INVALIDARG;
204
205 *ppidl = 0;
206
207 if (pchEaten)
208 *pchEaten = 0;
209
210 UINT cch = wcslen(lpszDisplayName);
211 if (cch < 39 || lpszDisplayName[0] != L':' || lpszDisplayName[1] != L':')
212 return E_FAIL;
213
214 pidl = _ILCreateGuidFromStrW(lpszDisplayName + 2);
215 if (pidl == NULL)
216 return E_FAIL;
217
218 if (cch < 41)
219 {
220 *ppidl = pidl;
221 if (pdwAttributes && *pdwAttributes)
222 {
223 GetGuidItemAttributes(*ppidl, pdwAttributes);
224 }
225 }
226 else
227 {
228 HRESULT hr = SHELL32_ParseNextElement(this, hwndOwner, pbc, &pidl, lpszDisplayName + 41, pchEaten, pdwAttributes);
229 if (SUCCEEDED(hr))
230 {
231 *ppidl = pidl;
232 }
233 return hr;
234 }
235
236 return S_OK;
237 }
238
239 HRESULT WINAPI CRegFolder::EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList)
240 {
241 return E_NOTIMPL;
242 }
243
244 HRESULT WINAPI CRegFolder::BindToObject(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut)
245 {
246 CComPtr<IPersistFolder> pFolder;
247 HRESULT hr;
248
249 if (!ppvOut || !pidl || !pidl->mkid.cb)
250 return E_INVALIDARG;
251
252 *ppvOut = NULL;
253
254 GUID *pGUID = _ILGetGUIDPointer(pidl);
255 if (!pGUID)
256 {
257 ERR("CRegFolder::BindToObject called for non guid item!\n");
258 return E_INVALIDARG;
259 }
260
261 LPITEMIDLIST pidlChild = ILCloneFirst (pidl);
262 if (!pidlChild)
263 return E_OUTOFMEMORY;
264
265 CComPtr<IShellFolder> psf;
266 hr = SHELL32_CoCreateInitSF(m_pidlRoot, NULL, pidlChild, pGUID, -1, IID_PPV_ARG(IShellFolder, &psf));
267 ILFree(pidlChild);
268 if (FAILED(hr))
269 return hr;
270
271 if (_ILIsPidlSimple (pidl))
272 {
273 return psf->QueryInterface(riid, ppvOut);
274 }
275 else
276 {
277 return psf->BindToObject(ILGetNext (pidl), pbcReserved, riid, ppvOut);
278 }
279 }
280
281 HRESULT WINAPI CRegFolder::BindToStorage(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut)
282 {
283 return E_NOTIMPL;
284 }
285
286 HRESULT WINAPI CRegFolder::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2)
287 {
288 if (!pidl1 || !pidl2 || pidl1->mkid.cb == 0 || pidl2->mkid.cb == 0)
289 {
290 ERR("Got an empty pidl!\n");
291 return E_INVALIDARG;
292 }
293
294 BOOL bIsGuidFolder1 = _ILIsSpecialFolder(pidl1);
295 BOOL bIsGuidFolder2 = _ILIsSpecialFolder(pidl2);
296
297 if (!bIsGuidFolder1 && !bIsGuidFolder2)
298 {
299 ERR("Got no guid pidl!\n");
300 return E_INVALIDARG;
301 }
302 else if (bIsGuidFolder1 && bIsGuidFolder2)
303 {
304 return SHELL32_CompareDetails(this, lParam, pidl1, pidl2);
305 }
306
307 /* Guid folders come first compared to everything else */
308 return MAKE_COMPARE_HRESULT(bIsGuidFolder1 ? -1 : 1);
309 }
310
311 HRESULT WINAPI CRegFolder::CreateViewObject(HWND hwndOwner, REFIID riid, LPVOID *ppvOut)
312 {
313 return E_NOTIMPL;
314 }
315
316 HRESULT WINAPI CRegFolder::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, DWORD *rgfInOut)
317 {
318 if (!rgfInOut || !cidl || !apidl)
319 return E_INVALIDARG;
320
321 if (*rgfInOut == 0)
322 *rgfInOut = ~0;
323
324 while(cidl > 0 && *apidl)
325 {
326 if (_ILIsSpecialFolder(*apidl))
327 GetGuidItemAttributes(*apidl, rgfInOut);
328 else
329 ERR("Got an unkown pidl here!\n");
330 apidl++;
331 cidl--;
332 }
333
334 /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */
335 *rgfInOut &= ~SFGAO_VALIDATE;
336
337 return S_OK;
338 }
339
340 HRESULT WINAPI CRegFolder::GetUIObjectOf(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
341 REFIID riid, UINT * prgfInOut, LPVOID * ppvOut)
342 {
343 LPVOID pObj = NULL;
344 HRESULT hr = E_INVALIDARG;
345
346 if (!ppvOut)
347 return hr;
348
349 *ppvOut = NULL;
350
351 if ((IsEqualIID (riid, IID_IExtractIconA) || IsEqualIID (riid, IID_IExtractIconW)) && (cidl == 1))
352 {
353 hr = CGuidItemExtractIcon_CreateInstance(apidl[0], riid, &pObj);
354 }
355 else
356 hr = E_NOINTERFACE;
357
358 *ppvOut = pObj;
359 return hr;
360
361 }
362
363 HRESULT WINAPI CRegFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET strRet)
364 {
365 if (!strRet || !_ILIsSpecialFolder(pidl))
366 return E_INVALIDARG;
367
368 if (!pidl->mkid.cb)
369 {
370 LPWSTR pszPath = (LPWSTR)CoTaskMemAlloc((MAX_PATH + 1) * sizeof(WCHAR));
371 if (!pszPath)
372 return E_OUTOFMEMORY;
373
374 /* parsing name like ::{...} */
375 pszPath[0] = ':';
376 pszPath[1] = ':';
377 SHELL32_GUIDToStringW(m_guid, &pszPath[2]);
378
379 strRet->uType = STRRET_WSTR;
380 strRet->pOleStr = pszPath;
381
382 return S_OK;
383 }
384
385 HRESULT hr;
386 GUID const *clsid = _ILGetGUIDPointer (pidl);
387
388 /* First of all check if we need to query the name from the child item */
389 if (GET_SHGDN_FOR (dwFlags) == SHGDN_FORPARSING &&
390 GET_SHGDN_RELATION (dwFlags) == SHGDN_NORMAL)
391 {
392 int bWantsForParsing = FALSE;
393
394 /*
395 * We can only get a filesystem path from a shellfolder if the
396 * value WantsFORPARSING in CLSID\\{...}\\shellfolder exists.
397 *
398 * Exception: The MyComputer folder doesn't have this key,
399 * but any other filesystem backed folder it needs it.
400 */
401 if (IsEqualIID (*clsid, CLSID_MyComputer))
402 {
403 bWantsForParsing = TRUE;
404 }
405 else
406 {
407 HKEY hkeyClass;
408 if (HCR_RegOpenClassIDKey(*clsid, &hkeyClass))
409 {
410 LONG res = SHGetValueW(hkeyClass, L"Shellfolder", L"WantsForParsing", NULL, NULL, NULL);
411 bWantsForParsing = (res == ERROR_SUCCESS);
412 RegCloseKey(hkeyClass);
413 }
414 }
415
416 if (bWantsForParsing)
417 {
418 /*
419 * we need the filesystem path to the destination folder.
420 * Only the folder itself can know it
421 */
422 return SHELL32_GetDisplayNameOfChild (this, pidl, dwFlags, strRet);
423 }
424 }
425
426 /* Allocate the buffer for the result */
427 LPWSTR pszPath = (LPWSTR)CoTaskMemAlloc((MAX_PATH + 1) * sizeof(WCHAR));
428 if (!pszPath)
429 return E_OUTOFMEMORY;
430
431 hr = S_OK;
432
433 if (GET_SHGDN_FOR (dwFlags) == SHGDN_FORPARSING)
434 {
435 wcscpy(pszPath, m_rootPath);
436 PWCHAR pItemName = &pszPath[wcslen(pszPath)];
437
438 /* parsing name like ::{...} */
439 pItemName[0] = ':';
440 pItemName[1] = ':';
441 SHELL32_GUIDToStringW (*clsid, &pItemName[2]);
442 }
443 else
444 {
445 /* user friendly name */
446 if (!HCR_GetClassNameW (*clsid, pszPath, MAX_PATH))
447 hr = E_FAIL;
448 }
449
450 if (SUCCEEDED(hr))
451 {
452 strRet->uType = STRRET_WSTR;
453 strRet->pOleStr = pszPath;
454 }
455 else
456 {
457 CoTaskMemFree(pszPath);
458 }
459
460 return hr;
461 }
462
463 HRESULT WINAPI CRegFolder::SetNameOf(HWND hwndOwner, PCUITEMID_CHILD pidl, /* simple pidl */
464 LPCOLESTR lpName, DWORD dwFlags, PITEMID_CHILD *pPidlOut)
465 {
466 GUID const *clsid = _ILGetGUIDPointer (pidl);
467 LPOLESTR pStr;
468 HRESULT hr;
469 WCHAR szName[100];
470
471 if (!clsid)
472 {
473 ERR("Pidl is not reg item!\n");
474 return E_FAIL;
475 }
476
477 hr = StringFromCLSID(*clsid, &pStr);
478 if (FAILED_UNEXPECTEDLY(hr))
479 return hr;
480
481 swprintf(szName, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\CLSID\\%s", pStr);
482
483 DWORD cbData = (wcslen(lpName) + 1) * sizeof(WCHAR);
484 LONG res = SHSetValueW(HKEY_CURRENT_USER, szName, NULL, RRF_RT_REG_SZ, lpName, cbData);
485
486 CoTaskMemFree(pStr);
487
488 if (res == ERROR_SUCCESS)
489 {
490 *pPidlOut = ILClone(pidl);
491 return S_OK;
492 }
493
494 return E_FAIL;
495 }
496
497
498 HRESULT WINAPI CRegFolder::GetDefaultSearchGUID(GUID *pguid)
499 {
500 return E_NOTIMPL;
501 }
502
503 HRESULT WINAPI CRegFolder::EnumSearches(IEnumExtraSearch ** ppenum)
504 {
505 return E_NOTIMPL;
506 }
507
508 HRESULT WINAPI CRegFolder::GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
509 {
510 if (pSort)
511 *pSort = 0;
512 if (pDisplay)
513 *pDisplay = 0;
514
515 return S_OK;
516 }
517
518 HRESULT WINAPI CRegFolder::GetDefaultColumnState(UINT iColumn, DWORD *pcsFlags)
519 {
520 if (iColumn >= 2)
521 return E_INVALIDARG;
522 *pcsFlags = SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT;
523 return S_OK;
524 }
525
526 HRESULT WINAPI CRegFolder::GetDetailsEx(PCUITEMID_CHILD pidl, const SHCOLUMNID *pscid, VARIANT *pv)
527 {
528 return E_NOTIMPL;
529 }
530
531 HRESULT WINAPI CRegFolder::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDETAILS *psd)
532 {
533 if (!psd || iColumn >= 2)
534 return E_INVALIDARG;
535
536 GUID const *clsid = _ILGetGUIDPointer (pidl);
537
538 if (!clsid)
539 {
540 ERR("Pidl is not reg item!\n");
541 return E_INVALIDARG;
542 }
543
544 switch(iColumn)
545 {
546 case 0: /* name */
547 return GetDisplayNameOf(pidl, SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str);
548 case 1: /* comment */
549 HKEY hKey;
550 if (HCR_RegOpenClassIDKey(*clsid, &hKey))
551 {
552 psd->str.cStr[0] = 0x00;
553 psd->str.uType = STRRET_CSTR;
554 RegLoadMUIStringA(hKey, "InfoTip", psd->str.cStr, MAX_PATH, NULL, 0, NULL);
555 RegCloseKey(hKey);
556 return S_OK;
557 }
558 }
559 return E_FAIL;
560 }
561
562 HRESULT WINAPI CRegFolder::MapColumnToSCID(UINT column, SHCOLUMNID *pscid)
563 {
564 return E_NOTIMPL;
565 }
566
567 /* In latest windows version this is exported but it takes different arguments! */
568 HRESULT CRegFolder_CreateInstance(const GUID *pGuid, LPCITEMIDLIST pidlRoot, LPCWSTR lpszPath, REFIID riid, void **ppv)
569 {
570 return ShellObjectCreatorInit<CRegFolder>(pGuid, pidlRoot, lpszPath, riid, ppv);
571 }