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 UINT col = LOWORD(lParam); // Column index without SHCIDS_* flags 244 245 hres = isf->GetDetailsOf(pidl1, col, &sd); 246 if (FAILED(hres)) 247 return MAKE_COMPARE_HRESULT(1); 248 249 hres = StrRetToBufW(&sd.str, pidl1, wszItem1, MAX_PATH); 250 if (FAILED(hres)) 251 return MAKE_COMPARE_HRESULT(1); 252 253 hres = isf->GetDetailsOf(pidl2, col, &sd); 254 if (FAILED(hres)) 255 return MAKE_COMPARE_HRESULT(1); 256 257 hres = StrRetToBufW(&sd.str, pidl2, wszItem2, MAX_PATH); 258 if (FAILED(hres)) 259 return MAKE_COMPARE_HRESULT(1); 260 261 int ret = wcsicmp(wszItem1, wszItem2); 262 if (ret == 0) 263 return SHELL32_CompareChildren(isf, lParam, pidl1, pidl2); 264 265 return MAKE_COMPARE_HRESULT(ret); 266 } 267 268 void AddClassKeyToArray(const WCHAR * szClass, HKEY* array, UINT* cKeys) 269 { 270 if (*cKeys >= 16) 271 return; 272 273 HKEY hkey; 274 LSTATUS result = RegOpenKeyExW(HKEY_CLASSES_ROOT, szClass, 0, KEY_READ | KEY_QUERY_VALUE, &hkey); 275 if (result != ERROR_SUCCESS) 276 return; 277 278 array[*cKeys] = hkey; 279 *cKeys += 1; 280 } 281 282 void AddFSClassKeysToArray(PCUITEMID_CHILD pidl, HKEY* array, UINT* cKeys) 283 { 284 if (_ILIsValue(pidl)) 285 { 286 FileStructW* pFileData = _ILGetFileStructW(pidl); 287 LPWSTR extension = PathFindExtension(pFileData->wszName); 288 289 if (extension) 290 { 291 AddClassKeyToArray(extension, array, cKeys); 292 293 WCHAR wszClass[MAX_PATH], wszClass2[MAX_PATH]; 294 DWORD dwSize = sizeof(wszClass); 295 if (RegGetValueW(HKEY_CLASSES_ROOT, extension, NULL, RRF_RT_REG_SZ, NULL, wszClass, &dwSize) == ERROR_SUCCESS) 296 { 297 swprintf(wszClass2, L"%s//%s", extension, wszClass); 298 299 AddClassKeyToArray(wszClass, array, cKeys); 300 AddClassKeyToArray(wszClass2, array, cKeys); 301 } 302 303 swprintf(wszClass2, L"SystemFileAssociations//%s", extension); 304 AddClassKeyToArray(wszClass2, array, cKeys); 305 306 if (RegGetValueW(HKEY_CLASSES_ROOT, extension, L"PerceivedType ", RRF_RT_REG_SZ, NULL, wszClass, &dwSize) == ERROR_SUCCESS) 307 { 308 swprintf(wszClass2, L"SystemFileAssociations//%s", wszClass); 309 AddClassKeyToArray(wszClass2, array, cKeys); 310 } 311 } 312 313 AddClassKeyToArray(L"AllFilesystemObjects", array, cKeys); 314 AddClassKeyToArray(L"*", array, cKeys); 315 } 316 else if (_ILIsFolder(pidl)) 317 { 318 AddClassKeyToArray(L"AllFilesystemObjects", array, cKeys); 319 AddClassKeyToArray(L"Directory", array, cKeys); 320 AddClassKeyToArray(L"Folder", array, cKeys); 321 } 322 else 323 { 324 ERR("Got non FS pidl\n"); 325 } 326 } 327 328 HRESULT SH_GetApidlFromDataObject(IDataObject *pDataObject, PIDLIST_ABSOLUTE* ppidlfolder, PUITEMID_CHILD **apidlItems, UINT *pcidl) 329 { 330 CDataObjectHIDA cida(pDataObject); 331 332 if (FAILED_UNEXPECTEDLY(cida.hr())) 333 return cida.hr(); 334 335 /* convert the data into pidl */ 336 LPITEMIDLIST pidl; 337 LPITEMIDLIST *apidl = _ILCopyCidaToaPidl(&pidl, cida); 338 if (!apidl) 339 { 340 return E_OUTOFMEMORY; 341 } 342 343 *ppidlfolder = pidl; 344 *apidlItems = apidl; 345 *pcidl = cida->cidl; 346 347 return S_OK; 348 } 349 350 /*********************************************************************** 351 * SHCreateLinks 352 * 353 * Undocumented. 354 */ 355 HRESULT WINAPI SHCreateLinks( HWND hWnd, LPCSTR lpszDir, IDataObject * lpDataObject, 356 UINT uFlags, LPITEMIDLIST *lppidlLinks) 357 { 358 FIXME("%p %s %p %08x %p\n", hWnd, lpszDir, lpDataObject, uFlags, lppidlLinks); 359 return E_NOTIMPL; 360 } 361 362 /*********************************************************************** 363 * SHOpenFolderAndSelectItems 364 * 365 * Unimplemented. 366 */ 367 EXTERN_C HRESULT 368 WINAPI 369 SHOpenFolderAndSelectItems(PCIDLIST_ABSOLUTE pidlFolder, 370 UINT cidl, 371 PCUITEMID_CHILD_ARRAY apidl, 372 DWORD dwFlags) 373 { 374 ERR("SHOpenFolderAndSelectItems() is hackplemented\n"); 375 PCIDLIST_ABSOLUTE pidlItem; 376 if (cidl) 377 { 378 /* Firefox sends a full pidl here dispite the fact it is a PCUITEMID_CHILD_ARRAY -_- */ 379 if (ILGetNext(apidl[0]) != NULL) 380 { 381 pidlItem = apidl[0]; 382 } 383 else 384 { 385 pidlItem = ILCombine(pidlFolder, apidl[0]); 386 } 387 } 388 else 389 { 390 pidlItem = pidlFolder; 391 } 392 393 CComPtr<IShellFolder> psfDesktop; 394 395 HRESULT hr = SHGetDesktopFolder(&psfDesktop); 396 if (FAILED_UNEXPECTEDLY(hr)) 397 return hr; 398 399 STRRET strret; 400 hr = psfDesktop->GetDisplayNameOf(pidlItem, SHGDN_FORPARSING, &strret); 401 if (FAILED_UNEXPECTEDLY(hr)) 402 return hr; 403 404 WCHAR wszBuf[MAX_PATH]; 405 hr = StrRetToBufW(&strret, pidlItem, wszBuf, _countof(wszBuf)); 406 if (FAILED_UNEXPECTEDLY(hr)) 407 return hr; 408 409 WCHAR wszParams[MAX_PATH]; 410 wcscpy(wszParams, L"/select,"); 411 wcscat(wszParams, wszBuf); 412 413 SHELLEXECUTEINFOW sei; 414 memset(&sei, 0, sizeof sei); 415 sei.cbSize = sizeof sei; 416 sei.fMask = SEE_MASK_WAITFORINPUTIDLE; 417 sei.lpFile = L"explorer.exe"; 418 sei.lpParameters = wszParams; 419 420 if (ShellExecuteExW(&sei)) 421 return S_OK; 422 else 423 return E_FAIL; 424 } 425 426 static 427 DWORD WINAPI 428 _ShowPropertiesDialogThread(LPVOID lpParameter) 429 { 430 CComPtr<IDataObject> pDataObject; 431 pDataObject.Attach((IDataObject*)lpParameter); 432 433 CDataObjectHIDA cida(pDataObject); 434 435 if (FAILED_UNEXPECTEDLY(cida.hr())) 436 return cida.hr(); 437 438 if (cida->cidl > 1) 439 { 440 ERR("SHMultiFileProperties is not yet implemented\n"); 441 return E_FAIL; 442 } 443 444 CComHeapPtr<ITEMIDLIST> completePidl(ILCombine(HIDA_GetPIDLFolder(cida), HIDA_GetPIDLItem(cida, 0))); 445 CComHeapPtr<WCHAR> wszName; 446 if (FAILED_UNEXPECTEDLY(SHGetNameFromIDList(completePidl, SIGDN_PARENTRELATIVEPARSING, &wszName))) 447 return 0; 448 449 BOOL bSuccess = SH_ShowPropertiesDialog(wszName, pDataObject); 450 if (!bSuccess) 451 ERR("SH_ShowPropertiesDialog failed\n"); 452 453 return 0; 454 } 455 456 /* 457 * for internal use 458 */ 459 HRESULT WINAPI 460 Shell_DefaultContextMenuCallBack(IShellFolder *psf, IDataObject *pdtobj) 461 { 462 pdtobj->AddRef(); 463 if (!SHCreateThread(_ShowPropertiesDialogThread, pdtobj, CTF_INSIST | CTF_COINIT, NULL)) 464 { 465 pdtobj->Release(); 466 return HRESULT_FROM_WIN32(GetLastError()); 467 } 468 else 469 { 470 return S_OK; 471 } 472 } 473