4a53117c4f22432344a99571e930026c056acc1d
[reactos.git] / reactos / dll / win32 / shell32 / shlfolder.cpp
1 /*
2 * Shell Folder stuff
3 *
4 * Copyright 1997 Marcus Meissner
5 * Copyright 1998, 1999, 2002 Juergen Schmied
6 *
7 * IShellFolder2 and related interfaces
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24 #include "precomp.h"
25
26 WINE_DEFAULT_DEBUG_CHANNEL(shell);
27
28 /***************************************************************************
29 * GetNextElement (internal function)
30 *
31 * Gets a part of a string till the first backslash.
32 *
33 * PARAMETERS
34 * pszNext [IN] string to get the element from
35 * pszOut [IN] pointer to buffer which receives string
36 * dwOut [IN] length of pszOut
37 *
38 * RETURNS
39 * LPSTR pointer to first, not yet parsed char
40 */
41
42 LPCWSTR GetNextElementW (LPCWSTR pszNext, LPWSTR pszOut, DWORD dwOut)
43 {
44 LPCWSTR pszTail = pszNext;
45 DWORD dwCopy;
46
47 TRACE ("(%s %p 0x%08x)\n", debugstr_w (pszNext), pszOut, dwOut);
48
49 *pszOut = 0x0000;
50
51 if (!pszNext || !*pszNext)
52 return NULL;
53
54 while (*pszTail && (*pszTail != (WCHAR) '\\'))
55 pszTail++;
56
57 dwCopy = pszTail - pszNext + 1;
58 lstrcpynW (pszOut, pszNext, (dwOut < dwCopy) ? dwOut : dwCopy);
59
60 if (*pszTail)
61 pszTail++;
62 else
63 pszTail = NULL;
64
65 TRACE ("--(%s %s 0x%08x %p)\n", debugstr_w (pszNext), debugstr_w (pszOut), dwOut, pszTail);
66 return pszTail;
67 }
68
69 HRESULT SHELL32_ParseNextElement (IShellFolder2 * psf, HWND hwndOwner, LPBC pbc,
70 LPITEMIDLIST * pidlInOut, LPOLESTR szNext, DWORD * pEaten, DWORD * pdwAttributes)
71 {
72 HRESULT hr = E_INVALIDARG;
73 LPITEMIDLIST pidlIn = pidlInOut ? *pidlInOut : NULL;
74 LPITEMIDLIST pidlOut = NULL;
75 LPITEMIDLIST pidlTemp = NULL;
76 CComPtr<IShellFolder> psfChild;
77
78 TRACE ("(%p, %p, %p, %s)\n", psf, pbc, pidlIn, debugstr_w (szNext));
79
80 /* get the shellfolder for the child pidl and let it analyse further */
81 hr = psf->BindToObject(pidlIn, pbc, IID_PPV_ARG(IShellFolder, &psfChild));
82 if (FAILED(hr))
83 return hr;
84
85 hr = psfChild->ParseDisplayName(hwndOwner, pbc, szNext, pEaten, &pidlOut, pdwAttributes);
86 if (FAILED(hr))
87 return hr;
88
89 pidlTemp = ILCombine (pidlIn, pidlOut);
90 if (!pidlTemp)
91 {
92 hr = E_OUTOFMEMORY;
93 if (pidlOut)
94 ILFree(pidlOut);
95 return hr;
96 }
97
98 if (pidlOut)
99 ILFree (pidlOut);
100
101 if (pidlIn)
102 ILFree (pidlIn);
103
104 *pidlInOut = pidlTemp;
105
106 TRACE ("-- pidl=%p ret=0x%08x\n", pidlInOut ? *pidlInOut : NULL, hr);
107 return S_OK;
108 }
109
110 /***********************************************************************
111 * SHELL32_CoCreateInitSF
112 *
113 * Creates a shell folder and initializes it with a pidl and a root folder
114 * via IPersistFolder3 or IPersistFolder.
115 *
116 * NOTES
117 * pathRoot can be NULL for Folders being a drive.
118 * In this case the absolute path is built from pidlChild (eg. C:)
119 */
120 HRESULT SHELL32_CoCreateInitSF (LPCITEMIDLIST pidlRoot, PERSIST_FOLDER_TARGET_INFO* ppfti,
121 LPCITEMIDLIST pidlChild, const GUID* clsid, REFIID riid, LPVOID *ppvOut)
122 {
123 HRESULT hr;
124 CComPtr<IShellFolder> pShellFolder;
125
126 hr = SHCoCreateInstance(NULL, clsid, NULL, IID_PPV_ARG(IShellFolder, &pShellFolder));
127 if (FAILED(hr))
128 return hr;
129
130 LPITEMIDLIST pidlAbsolute = ILCombine (pidlRoot, pidlChild);
131 CComPtr<IPersistFolder> ppf;
132 CComPtr<IPersistFolder3> ppf3;
133
134 if (ppfti && SUCCEEDED(pShellFolder->QueryInterface(IID_PPV_ARG(IPersistFolder3, &ppf3))))
135 {
136 ppf3->InitializeEx(NULL, pidlAbsolute, ppfti);
137 }
138 else if (SUCCEEDED(pShellFolder->QueryInterface(IID_PPV_ARG(IPersistFolder, &ppf))))
139 {
140 ppf->Initialize(pidlAbsolute);
141 }
142 ILFree (pidlAbsolute);
143
144 return pShellFolder->QueryInterface(riid, ppvOut);
145 }
146
147 HRESULT SHELL32_CoCreateInitSF (LPCITEMIDLIST pidlRoot, const GUID* clsid,
148 int csidl, REFIID riid, LPVOID *ppvOut)
149 {
150 /* fill the PERSIST_FOLDER_TARGET_INFO */
151 PERSIST_FOLDER_TARGET_INFO pfti = {0};
152 pfti.dwAttributes = -1;
153 pfti.csidl = csidl;
154
155 return SHELL32_CoCreateInitSF(pidlRoot, &pfti, NULL, clsid, riid, ppvOut);
156 }
157
158 HRESULT SHELL32_BindToSF (LPCITEMIDLIST pidlRoot, PERSIST_FOLDER_TARGET_INFO* ppfti,
159 LPCITEMIDLIST pidl, const GUID* clsid, REFIID riid, LPVOID *ppvOut)
160 {
161 PITEMID_CHILD pidlChild = ILCloneFirst (pidl);
162 if (!pidlChild)
163 return E_FAIL;
164
165 CComPtr<IShellFolder> psf;
166 HRESULT hr = SHELL32_CoCreateInitSF(pidlRoot,
167 ppfti,
168 pidlChild,
169 clsid,
170 IID_PPV_ARG(IShellFolder, &psf));
171 ILFree(pidlChild);
172
173 if (FAILED_UNEXPECTEDLY(hr))
174 return hr;
175
176 if (_ILIsPidlSimple (pidl))
177 return psf->QueryInterface(riid, ppvOut);
178 else
179 return psf->BindToObject(ILGetNext (pidl), NULL, riid, ppvOut);
180 }
181
182 /***********************************************************************
183 * SHELL32_GetDisplayNameOfChild
184 *
185 * Retrieves the display name of a child object of a shellfolder.
186 *
187 * For a pidl eg. [subpidl1][subpidl2][subpidl3]:
188 * - it binds to the child shellfolder [subpidl1]
189 * - asks it for the displayname of [subpidl2][subpidl3]
190 *
191 * Is possible the pidl is a simple pidl. In this case it asks the
192 * subfolder for the displayname of an empty pidl. The subfolder
193 * returns the own displayname eg. "::{guid}". This is used for
194 * virtual folders with the registry key WantsFORPARSING set.
195 */
196 HRESULT SHELL32_GetDisplayNameOfChild (IShellFolder2 * psf,
197 LPCITEMIDLIST pidl, DWORD dwFlags, LPSTRRET strRet)
198 {
199 LPITEMIDLIST pidlFirst = ILCloneFirst(pidl);
200 if (!pidlFirst)
201 return E_OUTOFMEMORY;
202
203 CComPtr<IShellFolder> psfChild;
204 HRESULT hr = psf->BindToObject(pidlFirst, NULL, IID_PPV_ARG(IShellFolder, &psfChild));
205 if (SUCCEEDED (hr))
206 {
207 hr = psfChild->GetDisplayNameOf(ILGetNext (pidl), dwFlags, strRet);
208 }
209 ILFree (pidlFirst);
210
211 return hr;
212 }
213
214 HRESULT SHELL32_CompareChildren(IShellFolder2* psf, LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
215 {
216 PUIDLIST_RELATIVE nextpidl1 = ILGetNext (pidl1);
217 PUIDLIST_RELATIVE nextpidl2 = ILGetNext (pidl2);
218
219 bool isEmpty1 = _ILIsDesktop(nextpidl1);
220 bool isEmpty2 = _ILIsDesktop(nextpidl2);
221 if (isEmpty1 || isEmpty2)
222 return MAKE_COMPARE_HRESULT(isEmpty2 - isEmpty1);
223
224 PITEMID_CHILD firstpidl = ILCloneFirst (pidl1);
225 if (!firstpidl)
226 return E_OUTOFMEMORY;
227
228 CComPtr<IShellFolder> psf2;
229 HRESULT hr = psf->BindToObject(firstpidl, 0, IID_PPV_ARG(IShellFolder, &psf2));
230 ILFree(firstpidl);
231 if (FAILED(hr))
232 return MAKE_COMPARE_HRESULT(0);
233
234 return psf2->CompareIDs(lParam, nextpidl1, nextpidl2);
235 }
236
237 HRESULT SHELL32_CompareDetails(IShellFolder2* isf, LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
238 {
239 SHELLDETAILS sd;
240 WCHAR wszItem1[MAX_PATH], wszItem2[MAX_PATH];
241 HRESULT hres;
242
243 hres = isf->GetDetailsOf(pidl1, lParam, &sd);
244 if (FAILED(hres))
245 return MAKE_COMPARE_HRESULT(1);
246
247 hres = StrRetToBufW(&sd.str, pidl1, wszItem1, MAX_PATH);
248 if (FAILED(hres))
249 return MAKE_COMPARE_HRESULT(1);
250
251 hres = isf->GetDetailsOf(pidl2, lParam, &sd);
252 if (FAILED(hres))
253 return MAKE_COMPARE_HRESULT(1);
254
255 hres = StrRetToBufW(&sd.str, pidl2, wszItem2, MAX_PATH);
256 if (FAILED(hres))
257 return MAKE_COMPARE_HRESULT(1);
258
259 int ret = wcsicmp(wszItem1, wszItem2);
260 if (ret == 0)
261 return SHELL32_CompareChildren(isf, lParam, pidl1, pidl2);
262
263 return MAKE_COMPARE_HRESULT(ret);
264 }
265
266 void AddClassKeyToArray(const WCHAR * szClass, HKEY* array, UINT* cKeys)
267 {
268 if (*cKeys >= 16)
269 return;
270
271 HKEY hkey;
272 LSTATUS result = RegOpenKeyExW(HKEY_CLASSES_ROOT, szClass, 0, KEY_READ | KEY_QUERY_VALUE, &hkey);
273 if (result != ERROR_SUCCESS)
274 return;
275
276 array[*cKeys] = hkey;
277 *cKeys += 1;
278 }
279
280 void AddFSClassKeysToArray(PCUITEMID_CHILD pidl, HKEY* array, UINT* cKeys)
281 {
282 if (_ILIsValue(pidl))
283 {
284 FileStructW* pFileData = _ILGetFileStructW(pidl);
285 LPWSTR extension = PathFindExtension(pFileData->wszName);
286
287 if (extension)
288 {
289 AddClassKeyToArray(extension, array, cKeys);
290
291 WCHAR wszClass[40], wszClass2[40];
292 DWORD dwSize = sizeof(wszClass);
293 if (RegGetValueW(HKEY_CLASSES_ROOT, extension, NULL, RRF_RT_REG_SZ, NULL, wszClass, &dwSize) == ERROR_SUCCESS)
294 {
295 swprintf(wszClass2, L"%s//%s", extension, wszClass);
296
297 AddClassKeyToArray(wszClass, array, cKeys);
298 AddClassKeyToArray(wszClass2, array, cKeys);
299 }
300
301 swprintf(wszClass2, L"SystemFileAssociations//%s", extension);
302 AddClassKeyToArray(wszClass2, array, cKeys);
303
304 if (RegGetValueW(HKEY_CLASSES_ROOT, extension, L"PerceivedType ", RRF_RT_REG_SZ, NULL, wszClass, &dwSize) == ERROR_SUCCESS)
305 {
306 swprintf(wszClass2, L"SystemFileAssociations//%s", wszClass);
307 AddClassKeyToArray(wszClass2, array, cKeys);
308 }
309 }
310
311 AddClassKeyToArray(L"AllFilesystemObjects", array, cKeys);
312 AddClassKeyToArray(L"*", array, cKeys);
313 }
314 else if (_ILIsFolder(pidl))
315 {
316 AddClassKeyToArray(L"AllFilesystemObjects", array, cKeys);
317 AddClassKeyToArray(L"Directory", array, cKeys);
318 AddClassKeyToArray(L"Folder", array, cKeys);
319 }
320 else
321 {
322 ERR("Got non FS pidl\n");
323 }
324 }
325
326 HRESULT SH_GetApidlFromDataObject(IDataObject *pDataObject, PIDLIST_ABSOLUTE* ppidlfolder, PUITEMID_CHILD **apidlItems, UINT *pcidl)
327 {
328 UINT cfShellIDList = RegisterClipboardFormatW(CFSTR_SHELLIDLIST);
329 if (!cfShellIDList)
330 return E_FAIL;
331
332 FORMATETC fmt;
333 InitFormatEtc (fmt, cfShellIDList, TYMED_HGLOBAL);
334
335 HRESULT hr = pDataObject->QueryGetData(&fmt);
336 if (FAILED_UNEXPECTEDLY(hr))
337 return hr;
338
339 STGMEDIUM medium;
340 hr = pDataObject->GetData(&fmt, &medium);
341 if (FAILED_UNEXPECTEDLY(hr))
342 return hr;
343
344 /* lock the handle */
345 LPIDA lpcida = (LPIDA)GlobalLock(medium.hGlobal);
346 if (!lpcida)
347 {
348 ReleaseStgMedium(&medium);
349 return E_FAIL;
350 }
351
352 /* convert the data into pidl */
353 LPITEMIDLIST pidl;
354 LPITEMIDLIST *apidl = _ILCopyCidaToaPidl(&pidl, lpcida);
355 if (!apidl)
356 {
357 ReleaseStgMedium(&medium);
358 return E_OUTOFMEMORY;
359 }
360
361 *ppidlfolder = pidl;
362 *apidlItems = apidl;
363 *pcidl = lpcida->cidl;
364
365 ReleaseStgMedium(&medium);
366 return S_OK;
367 }
368
369 /***********************************************************************
370 * SHCreateLinks
371 *
372 * Undocumented.
373 */
374 HRESULT WINAPI SHCreateLinks( HWND hWnd, LPCSTR lpszDir, IDataObject * lpDataObject,
375 UINT uFlags, LPITEMIDLIST *lppidlLinks)
376 {
377 FIXME("%p %s %p %08x %p\n", hWnd, lpszDir, lpDataObject, uFlags, lppidlLinks);
378 return E_NOTIMPL;
379 }
380
381 /***********************************************************************
382 * SHOpenFolderAndSelectItems
383 *
384 * Unimplemented.
385 */
386 EXTERN_C HRESULT
387 WINAPI
388 SHOpenFolderAndSelectItems(LPITEMIDLIST pidlFolder,
389 UINT cidl,
390 PCUITEMID_CHILD_ARRAY apidl,
391 DWORD dwFlags)
392 {
393 ERR("SHOpenFolderAndSelectItems() is hackplemented\n");
394 PCIDLIST_ABSOLUTE pidlItem;
395 if (cidl)
396 {
397 /* Firefox sends a full pidl here dispite the fact it is a PCUITEMID_CHILD_ARRAY -_- */
398 if (ILGetNext(apidl[0]) != NULL)
399 {
400 pidlItem = apidl[0];
401 }
402 else
403 {
404 pidlItem = ILCombine(pidlFolder, apidl[0]);
405 }
406 }
407 else
408 {
409 pidlItem = pidlFolder;
410 }
411
412 CComPtr<IShellFolder> psfDesktop;
413
414 HRESULT hr = SHGetDesktopFolder(&psfDesktop);
415 if (FAILED_UNEXPECTEDLY(hr))
416 return hr;
417
418 STRRET strret;
419 hr = psfDesktop->GetDisplayNameOf(pidlItem, SHGDN_FORPARSING, &strret);
420 if (FAILED_UNEXPECTEDLY(hr))
421 return hr;
422
423 WCHAR wszBuf[MAX_PATH];
424 hr = StrRetToBufW(&strret, pidlItem, wszBuf, _countof(wszBuf));
425 if (FAILED_UNEXPECTEDLY(hr))
426 return hr;
427
428 WCHAR wszParams[MAX_PATH];
429 wcscpy(wszParams, L"/select,");
430 wcscat(wszParams, wszBuf);
431
432 SHELLEXECUTEINFOW sei;
433 memset(&sei, 0, sizeof sei);
434 sei.cbSize = sizeof sei;
435 sei.fMask = SEE_MASK_WAITFORINPUTIDLE;
436 sei.lpFile = L"explorer.exe";
437 sei.lpParameters = wszParams;
438
439 if (ShellExecuteExW(&sei))
440 return S_OK;
441 else
442 return E_FAIL;
443 }