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