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 245 hres = isf->GetDetailsOf(pidl1, lParam, &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, lParam, &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 427 428 static 429 DWORD WINAPI 430 _ShowPropertiesDialogThread(LPVOID lpParameter) 431 { 432 CComPtr<IDataObject> pDataObject; 433 pDataObject.Attach((IDataObject*)lpParameter); 434 435 CDataObjectHIDA cida(pDataObject); 436 437 if (FAILED_UNEXPECTEDLY(cida.hr())) 438 return cida.hr(); 439 440 if (cida->cidl > 1) 441 { 442 ERR("SHMultiFileProperties is not yet implemented\n"); 443 return E_FAIL; 444 } 445 446 CComHeapPtr<ITEMIDLIST> completePidl(ILCombine(HIDA_GetPIDLFolder(cida), HIDA_GetPIDLItem(cida, 0))); 447 CComHeapPtr<WCHAR> wszName; 448 if (FAILED_UNEXPECTEDLY(SHGetNameFromIDList(completePidl, SIGDN_PARENTRELATIVEPARSING, &wszName))) 449 return 0; 450 451 BOOL bSuccess = SH_ShowPropertiesDialog(wszName, pDataObject); 452 if (!bSuccess) 453 ERR("SH_ShowPropertiesDialog failed\n"); 454 455 return 0; 456 } 457 458 /* 459 * for internal use 460 */ 461 HRESULT WINAPI 462 Shell_DefaultContextMenuCallBack(IShellFolder *psf, IDataObject *pdtobj) 463 { 464 pdtobj->AddRef(); 465 if (!SHCreateThread(_ShowPropertiesDialogThread, pdtobj, CTF_INSIST | CTF_COINIT, NULL)) 466 { 467 pdtobj->Release(); 468 return HRESULT_FROM_WIN32(GetLastError()); 469 } 470 else 471 { 472 return S_OK; 473 } 474 } 475