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