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