1 2 /* 3 * file system folder 4 * 5 * Copyright 1997 Marcus Meissner 6 * Copyright 1998, 1999, 2002 Juergen Schmied 7 * 8 * This library is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU Lesser General Public 10 * License as published by the Free Software Foundation; either 11 * version 2.1 of the License, or (at your option) any later version. 12 * 13 * This library is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * Lesser General Public License for more details. 17 * 18 * You should have received a copy of the GNU Lesser General Public 19 * License along with this library; if not, write to the Free Software 20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 21 */ 22 23 #include <precomp.h> 24 25 WINE_DEFAULT_DEBUG_CHANNEL (shell); 26 27 HKEY OpenKeyFromFileType(PCUIDLIST_RELATIVE pidl, LPCWSTR KeyName) 28 { 29 HKEY hkey; 30 31 if (!_ILIsValue(pidl)) 32 { 33 ERR("Invalid pidl!\n"); 34 return NULL; 35 } 36 37 FileStructW* pDataW = _ILGetFileStructW(pidl); 38 if (!pDataW) 39 { 40 ERR("Invalid pidl!\n"); 41 return NULL; 42 } 43 44 LPWSTR pExtension = PathFindExtensionW(pDataW->wszName); 45 if (!pExtension || *pExtension == NULL) 46 { 47 WARN("No extension for %S!\n", pDataW->wszName); 48 return NULL; 49 } 50 51 WCHAR FullName[MAX_PATH]; 52 DWORD dwSize = sizeof(FullName); 53 wsprintf(FullName, L"%s\\%s", pExtension, KeyName); 54 55 LONG res = RegOpenKeyExW(HKEY_CLASSES_ROOT, FullName, 0, KEY_READ, &hkey); 56 if (!res) 57 return hkey; 58 59 res = RegGetValueW(HKEY_CLASSES_ROOT, pExtension, NULL, RRF_RT_REG_SZ, NULL, FullName, &dwSize); 60 if (res) 61 { 62 WARN("Failed to get progid for file %S, extension %S (%x), address %x, pidl: %x, error %d\n", pDataW->wszName, pExtension, pExtension, &dwSize, pidl, res); 63 return NULL; 64 } 65 66 wcscat(FullName, L"\\"); 67 wcscat(FullName, KeyName); 68 69 hkey = NULL; 70 res = RegOpenKeyExW(HKEY_CLASSES_ROOT, FullName, 0, KEY_READ, &hkey); 71 if (res) 72 WARN("Could not open key %S for extension %S\n", KeyName, pExtension); 73 74 return hkey; 75 } 76 77 HRESULT GetCLSIDForFileType(PCUIDLIST_RELATIVE pidl, LPCWSTR KeyName, CLSID* pclsid) 78 { 79 HKEY hkeyProgId = OpenKeyFromFileType(pidl, KeyName); 80 if (!hkeyProgId) 81 { 82 WARN("OpenKeyFromFileType failed for key %S\n", KeyName); 83 return S_FALSE; 84 } 85 86 WCHAR wszCLSIDValue[CHARS_IN_GUID]; 87 DWORD dwSize = sizeof(wszCLSIDValue); 88 LONG res = RegGetValueW(hkeyProgId, NULL, NULL, RRF_RT_REG_SZ, NULL, wszCLSIDValue, &dwSize); 89 RegCloseKey(hkeyProgId); 90 if (res) 91 { 92 ERR("OpenKeyFromFileType succeeded but RegGetValueW failed\n"); 93 return S_FALSE; 94 } 95 96 #if 0 97 { 98 res = RegGetValueW(HKEY_LOCAL_MACHINE, 99 L"Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved", 100 wszCLSIDValue, 101 RRF_RT_REG_SZ, 102 NULL, 103 NULL, 104 NULL); 105 if (res != ERROR_SUCCESS) 106 { 107 ERR("DropHandler extension %S not approved\n", wszName); 108 return E_ACCESSDENIED; 109 } 110 } 111 #endif 112 113 if (RegGetValueW(HKEY_LOCAL_MACHINE, 114 L"Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Blocked", 115 wszCLSIDValue, 116 RRF_RT_REG_SZ, 117 NULL, 118 NULL, 119 NULL) == ERROR_SUCCESS) 120 { 121 ERR("Extension %S not approved\n", wszCLSIDValue); 122 return E_ACCESSDENIED; 123 } 124 125 HRESULT hres = CLSIDFromString (wszCLSIDValue, pclsid); 126 if (FAILED_UNEXPECTEDLY(hres)) 127 return hres; 128 129 return S_OK; 130 } 131 132 static HRESULT getIconLocationForFolder(IShellFolder * psf, LPCITEMIDLIST pidl, UINT uFlags, 133 LPWSTR szIconFile, UINT cchMax, int *piIndex, UINT *pwFlags) 134 { 135 static const WCHAR shellClassInfo[] = { '.', 'S', 'h', 'e', 'l', 'l', 'C', 'l', 'a', 's', 's', 'I', 'n', 'f', 'o', 0 }; 136 static const WCHAR iconFile[] = { 'I', 'c', 'o', 'n', 'F', 'i', 'l', 'e', 0 }; 137 static const WCHAR clsid[] = { 'C', 'L', 'S', 'I', 'D', 0 }; 138 static const WCHAR clsid2[] = { 'C', 'L', 'S', 'I', 'D', '2', 0 }; 139 static const WCHAR iconIndex[] = { 'I', 'c', 'o', 'n', 'I', 'n', 'd', 'e', 'x', 0 }; 140 static const WCHAR wszDesktopIni[] = { 'd','e','s','k','t','o','p','.','i','n','i',0 }; 141 int icon_idx; 142 143 if (!(uFlags & GIL_DEFAULTICON) && (_ILGetFileAttributes(ILFindLastID(pidl), NULL, 0) & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)) != 0 ) 144 { 145 WCHAR wszFolderPath[MAX_PATH]; 146 147 if (!ILGetDisplayNameExW(psf, pidl, wszFolderPath, 0)) 148 return FALSE; 149 150 PathAppendW(wszFolderPath, wszDesktopIni); 151 152 if (PathFileExistsW(wszFolderPath)) 153 { 154 WCHAR wszPath[MAX_PATH]; 155 WCHAR wszCLSIDValue[CHARS_IN_GUID]; 156 157 if (GetPrivateProfileStringW(shellClassInfo, iconFile, NULL, wszPath, MAX_PATH, wszFolderPath)) 158 { 159 ExpandEnvironmentStringsW(wszPath, szIconFile, cchMax); 160 161 *piIndex = GetPrivateProfileIntW(shellClassInfo, iconIndex, 0, wszFolderPath); 162 return S_OK; 163 } 164 else if (GetPrivateProfileStringW(shellClassInfo, clsid, NULL, wszCLSIDValue, CHARS_IN_GUID, wszFolderPath) && 165 HCR_GetIconW(wszCLSIDValue, szIconFile, NULL, cchMax, &icon_idx)) 166 { 167 *piIndex = icon_idx; 168 return S_OK; 169 } 170 else if (GetPrivateProfileStringW(shellClassInfo, clsid2, NULL, wszCLSIDValue, CHARS_IN_GUID, wszFolderPath) && 171 HCR_GetIconW(wszCLSIDValue, szIconFile, NULL, cchMax, &icon_idx)) 172 { 173 *piIndex = icon_idx; 174 return S_OK; 175 } 176 } 177 } 178 179 static const WCHAR folder[] = { 'F', 'o', 'l', 'd', 'e', 'r', 0 }; 180 181 if (!HCR_GetIconW(folder, szIconFile, NULL, cchMax, &icon_idx)) 182 { 183 lstrcpynW(szIconFile, swShell32Name, cchMax); 184 icon_idx = -IDI_SHELL_FOLDER; 185 } 186 187 if (uFlags & GIL_OPENICON) 188 *piIndex = icon_idx < 0 ? icon_idx - 1 : icon_idx + 1; 189 else 190 *piIndex = icon_idx; 191 192 return S_OK; 193 } 194 195 HRESULT CFSExtractIcon_CreateInstance(IShellFolder * psf, LPCITEMIDLIST pidl, REFIID iid, LPVOID * ppvOut) 196 { 197 CComPtr<IDefaultExtractIconInit> initIcon; 198 HRESULT hr; 199 int icon_idx = 0; 200 UINT flags = 0; // FIXME: Use it! 201 WCHAR wTemp[MAX_PATH] = L""; 202 203 hr = SHCreateDefaultExtractIcon(IID_PPV_ARG(IDefaultExtractIconInit,&initIcon)); 204 if (FAILED(hr)) 205 return hr; 206 207 if (_ILIsFolder (pidl)) 208 { 209 if (SUCCEEDED(getIconLocationForFolder(psf, 210 pidl, 0, wTemp, _countof(wTemp), 211 &icon_idx, 212 &flags))) 213 { 214 initIcon->SetNormalIcon(wTemp, icon_idx); 215 // FIXME: if/when getIconLocationForFolder does something for 216 // GIL_FORSHORTCUT, code below should be uncommented. and 217 // the following line removed. 218 initIcon->SetShortcutIcon(wTemp, icon_idx); 219 } 220 if (SUCCEEDED(getIconLocationForFolder(psf, 221 pidl, GIL_DEFAULTICON, wTemp, _countof(wTemp), 222 &icon_idx, 223 &flags))) 224 { 225 initIcon->SetDefaultIcon(wTemp, icon_idx); 226 } 227 // if (SUCCEEDED(getIconLocationForFolder(psf, 228 // pidl, GIL_FORSHORTCUT, wTemp, _countof(wTemp), 229 // &icon_idx, 230 // &flags))) 231 // { 232 // initIcon->SetShortcutIcon(wTemp, icon_idx); 233 // } 234 if (SUCCEEDED(getIconLocationForFolder(psf, 235 pidl, GIL_OPENICON, wTemp, _countof(wTemp), 236 &icon_idx, 237 &flags))) 238 { 239 initIcon->SetOpenIcon(wTemp, icon_idx); 240 } 241 } 242 else 243 { 244 HKEY hkey = OpenKeyFromFileType(pidl, L"DefaultIcon"); 245 if (!hkey) 246 WARN("Could not open DefaultIcon key!\n"); 247 248 DWORD dwSize = sizeof(wTemp); 249 if (hkey && !SHQueryValueExW(hkey, NULL, NULL, NULL, wTemp, &dwSize)) 250 { 251 WCHAR sNum[5]; 252 if (ParseFieldW (wTemp, 2, sNum, 5)) 253 icon_idx = _wtoi(sNum); 254 else 255 icon_idx = 0; /* sometimes the icon number is missing */ 256 ParseFieldW (wTemp, 1, wTemp, MAX_PATH); 257 PathUnquoteSpacesW(wTemp); 258 259 if (!wcscmp(L"%1", wTemp)) /* icon is in the file */ 260 { 261 ILGetDisplayNameExW(psf, pidl, wTemp, 0); 262 icon_idx = 0; 263 } 264 265 initIcon->SetNormalIcon(wTemp, icon_idx); 266 } 267 else 268 { 269 initIcon->SetNormalIcon(swShell32Name, 0); 270 } 271 272 if (hkey) 273 RegCloseKey(hkey); 274 } 275 276 return initIcon->QueryInterface(iid, ppvOut); 277 } 278 279 /* 280 CFileSysEnum should do an initial FindFirstFile and do a FindNextFile as each file is 281 returned by Next. When the enumerator is created, it can do numerous additional operations 282 including formatting a drive, reconnecting a network share drive, and requesting a disk 283 be inserted in a removable drive. 284 */ 285 286 /*********************************************************************** 287 * IShellFolder implementation 288 */ 289 290 class CFileSysEnum : 291 public CEnumIDListBase 292 { 293 private: 294 public: 295 CFileSysEnum(); 296 ~CFileSysEnum(); 297 HRESULT WINAPI Initialize(LPWSTR sPathTarget, DWORD dwFlags); 298 299 BEGIN_COM_MAP(CFileSysEnum) 300 COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList) 301 END_COM_MAP() 302 }; 303 304 CFileSysEnum::CFileSysEnum() 305 { 306 } 307 308 CFileSysEnum::~CFileSysEnum() 309 { 310 } 311 312 HRESULT WINAPI CFileSysEnum::Initialize(LPWSTR lpszPath, DWORD dwFlags) 313 { 314 WIN32_FIND_DATAW stffile; 315 HANDLE hFile; 316 WCHAR szPath[MAX_PATH]; 317 BOOL succeeded = TRUE; 318 static const WCHAR stars[] = { '*','.','*',0 }; 319 static const WCHAR dot[] = { '.',0 }; 320 static const WCHAR dotdot[] = { '.','.',0 }; 321 322 TRACE("(%p)->(path=%s flags=0x%08x)\n", this, debugstr_w(lpszPath), dwFlags); 323 324 if(!lpszPath || !lpszPath[0]) return FALSE; 325 326 wcscpy(szPath, lpszPath); 327 PathAddBackslashW(szPath); 328 wcscat(szPath,stars); 329 330 hFile = FindFirstFileW(szPath,&stffile); 331 if ( hFile != INVALID_HANDLE_VALUE ) 332 { 333 BOOL findFinished = FALSE; 334 335 do 336 { 337 if ( !(stffile.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) 338 || (dwFlags & SHCONTF_INCLUDEHIDDEN) ) 339 { 340 LPITEMIDLIST pidl = NULL; 341 342 if ( (stffile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && 343 dwFlags & SHCONTF_FOLDERS && 344 strcmpW(stffile.cFileName, dot) && strcmpW(stffile.cFileName, dotdot)) 345 { 346 pidl = _ILCreateFromFindDataW(&stffile); 347 succeeded = succeeded && AddToEnumList(pidl); 348 } 349 else if (!(stffile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 350 && dwFlags & SHCONTF_NONFOLDERS) 351 { 352 pidl = _ILCreateFromFindDataW(&stffile); 353 succeeded = succeeded && AddToEnumList(pidl); 354 } 355 } 356 if (succeeded) 357 { 358 if (!FindNextFileW(hFile, &stffile)) 359 { 360 if (GetLastError() == ERROR_NO_MORE_FILES) 361 findFinished = TRUE; 362 else 363 succeeded = FALSE; 364 } 365 } 366 } while (succeeded && !findFinished); 367 FindClose(hFile); 368 } 369 370 return succeeded; 371 } 372 373 CFSFolder::CFSFolder() 374 { 375 pclsid = (CLSID *)&CLSID_ShellFSFolder; 376 sPathTarget = NULL; 377 pidlRoot = NULL; 378 m_bGroupPolicyActive = 0; 379 } 380 381 CFSFolder::~CFSFolder() 382 { 383 TRACE("-- destroying IShellFolder(%p)\n", this); 384 385 SHFree(pidlRoot); 386 SHFree(sPathTarget); 387 } 388 389 390 static const shvheader GenericSFHeader[] = { 391 {IDS_SHV_COLUMN_NAME, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 15}, 392 {IDS_SHV_COLUMN_COMMENTS, SHCOLSTATE_TYPE_STR, LVCFMT_LEFT, 0}, 393 {IDS_SHV_COLUMN_TYPE, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 10}, 394 {IDS_SHV_COLUMN_SIZE, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10}, 395 {IDS_SHV_COLUMN_MODIFIED, SHCOLSTATE_TYPE_DATE | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 12}, 396 {IDS_SHV_COLUMN_ATTRIBUTES, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 10} 397 }; 398 399 #define GENERICSHELLVIEWCOLUMNS 6 400 401 /************************************************************************** 402 * SHELL32_CreatePidlFromBindCtx [internal] 403 * 404 * If the caller bound File System Bind Data, assume it is the 405 * find data for the path. 406 * This allows binding of paths that don't exist. 407 */ 408 LPITEMIDLIST SHELL32_CreatePidlFromBindCtx(IBindCtx *pbc, LPCWSTR path) 409 { 410 IFileSystemBindData *fsbd = NULL; 411 LPITEMIDLIST pidl = NULL; 412 IUnknown *param = NULL; 413 WIN32_FIND_DATAW wfd; 414 HRESULT r; 415 416 TRACE("%p %s\n", pbc, debugstr_w(path)); 417 418 if (!pbc) 419 return NULL; 420 421 /* see if the caller bound File System Bind Data */ 422 r = pbc->GetObjectParam((LPOLESTR)STR_FILE_SYS_BIND_DATA, ¶m); 423 if (FAILED(r)) 424 return NULL; 425 426 r = param->QueryInterface(IID_PPV_ARG(IFileSystemBindData,&fsbd)); 427 if (SUCCEEDED(r)) 428 { 429 r = fsbd->GetFindData(&wfd); 430 if (SUCCEEDED(r)) 431 { 432 lstrcpynW(&wfd.cFileName[0], path, MAX_PATH); 433 pidl = _ILCreateFromFindDataW(&wfd); 434 } 435 fsbd->Release(); 436 } 437 438 return pidl; 439 } 440 441 void SHELL32_GetCLSIDForDirectory(LPCWSTR pwszDir, CLSID* pclsidFolder) 442 { 443 WCHAR wszCLSIDValue[CHARS_IN_GUID]; 444 WCHAR wszDesktopIni[MAX_PATH]; 445 StringCchCopyW(wszDesktopIni, MAX_PATH, pwszDir); 446 StringCchCatW(wszDesktopIni, MAX_PATH, L"\\desktop.ini"); 447 448 if (GetPrivateProfileStringW(L".ShellClassInfo", 449 L"CLSID", 450 L"", 451 wszCLSIDValue, 452 CHARS_IN_GUID, 453 wszDesktopIni)) 454 { 455 CLSIDFromString (wszCLSIDValue, pclsidFolder); 456 } 457 } 458 459 HRESULT SHELL32_GetFSItemAttributes(IShellFolder * psf, LPCITEMIDLIST pidl, LPDWORD pdwAttributes) 460 { 461 DWORD dwFileAttributes, dwShellAttributes; 462 463 if (!_ILIsFolder(pidl) && !_ILIsValue(pidl)) 464 { 465 ERR("Got wrong type of pidl!\n"); 466 *pdwAttributes &= SFGAO_CANLINK; 467 return S_OK; 468 } 469 470 dwFileAttributes = _ILGetFileAttributes(pidl, NULL, 0); 471 472 /* Set common attributes */ 473 dwShellAttributes = SFGAO_CANCOPY | SFGAO_CANMOVE | SFGAO_CANLINK | SFGAO_CANRENAME | SFGAO_CANDELETE | 474 SFGAO_HASPROPSHEET | SFGAO_DROPTARGET | SFGAO_FILESYSTEM; 475 476 if (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 477 dwShellAttributes |= (SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_FILESYSANCESTOR | SFGAO_STORAGEANCESTOR | SFGAO_STORAGE); 478 else 479 dwShellAttributes |= SFGAO_STREAM; 480 481 if (dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) 482 dwShellAttributes |= SFGAO_HIDDEN; 483 484 if (dwFileAttributes & FILE_ATTRIBUTE_READONLY) 485 dwShellAttributes |= SFGAO_READONLY; 486 487 if (SFGAO_LINK & *pdwAttributes) 488 { 489 char ext[MAX_PATH]; 490 491 if (_ILGetExtension(pidl, ext, MAX_PATH) && !lstrcmpiA(ext, "lnk")) 492 dwShellAttributes |= SFGAO_LINK; 493 } 494 495 if (SFGAO_HASSUBFOLDER & *pdwAttributes) 496 { 497 CComPtr<IShellFolder> psf2; 498 if (SUCCEEDED(psf->BindToObject(pidl, 0, IID_PPV_ARG(IShellFolder, &psf2)))) 499 { 500 CComPtr<IEnumIDList> pEnumIL; 501 if (SUCCEEDED(psf2->EnumObjects(0, SHCONTF_FOLDERS, &pEnumIL))) 502 { 503 if (pEnumIL->Skip(1) == S_OK) 504 dwShellAttributes |= SFGAO_HASSUBFOLDER; 505 } 506 } 507 } 508 509 *pdwAttributes = dwShellAttributes; 510 511 TRACE ("-- 0x%08x\n", *pdwAttributes); 512 return S_OK; 513 } 514 515 /************************************************************************** 516 * CFSFolder::ParseDisplayName {SHELL32} 517 * 518 * Parse a display name. 519 * 520 * PARAMS 521 * hwndOwner [in] Parent window for any message's 522 * pbc [in] optional FileSystemBindData context 523 * lpszDisplayName [in] Unicode displayname. 524 * pchEaten [out] (unicode) characters processed 525 * ppidl [out] complex pidl to item 526 * pdwAttributes [out] items attributes 527 * 528 * NOTES 529 * Every folder tries to parse only its own (the leftmost) pidl and creates a 530 * subfolder to evaluate the remaining parts. 531 * Now we can parse into namespaces implemented by shell extensions 532 * 533 * Behaviour on win98: lpszDisplayName=NULL -> crash 534 * lpszDisplayName="" -> returns mycoputer-pidl 535 * 536 * FIXME 537 * pdwAttributes is not set 538 * pchEaten is not set like in windows 539 */ 540 HRESULT WINAPI CFSFolder::ParseDisplayName(HWND hwndOwner, 541 LPBC pbc, 542 LPOLESTR lpszDisplayName, 543 DWORD *pchEaten, PIDLIST_RELATIVE *ppidl, 544 DWORD *pdwAttributes) 545 { 546 HRESULT hr = E_INVALIDARG; 547 LPCWSTR szNext = NULL; 548 WCHAR szElement[MAX_PATH]; 549 WCHAR szPath[MAX_PATH]; 550 LPITEMIDLIST pidlTemp = NULL; 551 DWORD len; 552 553 TRACE ("(%p)->(HWND=%p,%p,%p=%s,%p,pidl=%p,%p)\n", 554 this, hwndOwner, pbc, lpszDisplayName, debugstr_w (lpszDisplayName), 555 pchEaten, ppidl, pdwAttributes); 556 557 if (!ppidl) 558 return E_INVALIDARG; 559 560 if (!lpszDisplayName) 561 { 562 *ppidl = NULL; 563 return E_INVALIDARG; 564 } 565 566 *ppidl = NULL; 567 568 if (pchEaten) 569 *pchEaten = 0; /* strange but like the original */ 570 571 if (*lpszDisplayName) 572 { 573 /* get the next element */ 574 szNext = GetNextElementW (lpszDisplayName, szElement, MAX_PATH); 575 576 pidlTemp = SHELL32_CreatePidlFromBindCtx(pbc, szElement); 577 if (pidlTemp != NULL) 578 { 579 /* We are creating an id list without ensuring that the items exist. 580 If we have a remaining path, this must be a folder. 581 We have to do it now because it is set as a file by default */ 582 if (szNext) 583 { 584 pidlTemp->mkid.abID[0] = PT_FOLDER; 585 } 586 hr = S_OK; 587 } 588 else 589 { 590 /* build the full pathname to the element */ 591 lstrcpynW(szPath, sPathTarget, MAX_PATH - 1); 592 PathAddBackslashW(szPath); 593 len = wcslen(szPath); 594 lstrcpynW(szPath + len, szElement, MAX_PATH - len); 595 596 /* get the pidl */ 597 hr = _ILCreateFromPathW(szPath, &pidlTemp); 598 } 599 600 if (SUCCEEDED(hr)) 601 { 602 if (szNext && *szNext) 603 { 604 /* try to analyse the next element */ 605 hr = SHELL32_ParseNextElement(this, hwndOwner, pbc, 606 &pidlTemp, (LPOLESTR) szNext, pchEaten, pdwAttributes); 607 } 608 else 609 { 610 /* it's the last element */ 611 if (pdwAttributes && *pdwAttributes) 612 hr = SHELL32_GetFSItemAttributes(this, pidlTemp, pdwAttributes); 613 } 614 } 615 } 616 617 if (SUCCEEDED(hr)) 618 *ppidl = pidlTemp; 619 else 620 *ppidl = NULL; 621 622 TRACE("(%p)->(-- pidl=%p ret=0x%08x)\n", this, ppidl ? *ppidl : 0, hr); 623 624 return hr; 625 } 626 627 /************************************************************************** 628 * CFSFolder::EnumObjects 629 * PARAMETERS 630 * HWND hwndOwner, //[in ] Parent Window 631 * DWORD grfFlags, //[in ] SHCONTF enumeration mask 632 * LPENUMIDLIST* ppenumIDList //[out] IEnumIDList interface 633 */ 634 HRESULT WINAPI CFSFolder::EnumObjects( 635 HWND hwndOwner, 636 DWORD dwFlags, 637 LPENUMIDLIST *ppEnumIDList) 638 { 639 return ShellObjectCreatorInit<CFileSysEnum>(sPathTarget, dwFlags, IID_PPV_ARG(IEnumIDList, ppEnumIDList)); 640 } 641 642 /************************************************************************** 643 * CFSFolder::BindToObject 644 * PARAMETERS 645 * LPCITEMIDLIST pidl, //[in ] relative pidl to open 646 * LPBC pbc, //[in ] optional FileSystemBindData context 647 * REFIID riid, //[in ] Initial Interface 648 * LPVOID* ppvObject //[out] Interface* 649 */ 650 HRESULT WINAPI CFSFolder::BindToObject( 651 PCUIDLIST_RELATIVE pidl, 652 LPBC pbc, 653 REFIID riid, 654 LPVOID * ppvOut) 655 { 656 TRACE("(%p)->(pidl=%p,%p,%s,%p)\n", this, pidl, pbc, 657 shdebugstr_guid(&riid), ppvOut); 658 659 CComPtr<IShellFolder> pSF; 660 HRESULT hr; 661 662 if (!pidlRoot || !ppvOut || !pidl || !pidl->mkid.cb) 663 { 664 ERR("CFSFolder::BindToObject: Invalid parameters\n"); 665 return E_INVALIDARG; 666 } 667 668 /* Get the pidl data */ 669 FileStruct* pData = &_ILGetDataPointer(pidl)->u.file; 670 FileStructW* pDataW = _ILGetFileStructW(pidl); 671 672 if (!pDataW) 673 { 674 ERR("CFSFolder::BindToObject: Invalid pidl!\n"); 675 return E_INVALIDARG; 676 } 677 678 *ppvOut = NULL; 679 680 /* Create the target folder info */ 681 PERSIST_FOLDER_TARGET_INFO pfti = {0}; 682 pfti.dwAttributes = -1; 683 pfti.csidl = -1; 684 PathCombineW(pfti.szTargetParsingName, sPathTarget, pDataW->wszName); 685 686 /* Get the CLSID to bind to */ 687 CLSID clsidFolder; 688 if (_ILIsFolder(pidl)) 689 { 690 clsidFolder = CLSID_ShellFSFolder; 691 692 if ((pData->uFileAttribs & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)) != 0) 693 SHELL32_GetCLSIDForDirectory(pfti.szTargetParsingName, &clsidFolder); 694 } 695 else 696 { 697 hr = GetCLSIDForFileType(pidl, L"CLSID", &clsidFolder); 698 if (hr == S_FALSE) 699 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); 700 if (hr != S_OK) 701 return hr; 702 } 703 704 hr = SHELL32_BindToSF(pidlRoot, &pfti, pidl, &clsidFolder, riid, ppvOut); 705 if (FAILED_UNEXPECTEDLY(hr)) 706 return hr; 707 708 TRACE ("-- returning (%p) %08x\n", *ppvOut, hr); 709 710 return S_OK; 711 712 } 713 714 /************************************************************************** 715 * CFSFolder::BindToStorage 716 * PARAMETERS 717 * LPCITEMIDLIST pidl, //[in ] complex pidl to store 718 * LPBC pbc, //[in ] reserved 719 * REFIID riid, //[in ] Initial storage interface 720 * LPVOID* ppvObject //[out] Interface* returned 721 */ 722 HRESULT WINAPI CFSFolder::BindToStorage( 723 PCUIDLIST_RELATIVE pidl, 724 LPBC pbcReserved, 725 REFIID riid, 726 LPVOID *ppvOut) 727 { 728 FIXME("(%p)->(pidl=%p,%p,%s,%p) stub\n", this, pidl, pbcReserved, 729 shdebugstr_guid (&riid), ppvOut); 730 731 *ppvOut = NULL; 732 return E_NOTIMPL; 733 } 734 735 /************************************************************************** 736 * CFSFolder::CompareIDs 737 */ 738 739 HRESULT WINAPI CFSFolder::CompareIDs(LPARAM lParam, 740 PCUIDLIST_RELATIVE pidl1, 741 PCUIDLIST_RELATIVE pidl2) 742 { 743 LPPIDLDATA pData1 = _ILGetDataPointer(pidl1); 744 LPPIDLDATA pData2 = _ILGetDataPointer(pidl2); 745 FileStructW* pDataW1 = _ILGetFileStructW(pidl1); 746 FileStructW* pDataW2 = _ILGetFileStructW(pidl2); 747 BOOL bIsFolder1 = _ILIsFolder(pidl1); 748 BOOL bIsFolder2 = _ILIsFolder(pidl2); 749 LPWSTR pExtension1, pExtension2; 750 751 if (!pDataW1 || !pDataW2 || LOWORD(lParam) >= GENERICSHELLVIEWCOLUMNS) 752 return E_INVALIDARG; 753 754 /* When sorting between a File and a Folder, the Folder gets sorted first */ 755 if (bIsFolder1 != bIsFolder2) 756 { 757 return MAKE_COMPARE_HRESULT(bIsFolder1 ? -1 : 1); 758 } 759 760 int result; 761 switch (LOWORD(lParam)) 762 { 763 case 0: /* Name */ 764 result = wcsicmp(pDataW1->wszName, pDataW2->wszName); 765 break; 766 case 1: /* Comments */ 767 result = 0; 768 break; 769 case 2: /* Type */ 770 pExtension1 = PathFindExtensionW(pDataW1->wszName); 771 pExtension2 = PathFindExtensionW(pDataW2->wszName); 772 result = wcsicmp(pExtension1, pExtension2); 773 break; 774 case 3: /* Size */ 775 result = pData1->u.file.dwFileSize - pData2->u.file.dwFileSize; 776 break; 777 case 4: /* Modified */ 778 result = pData1->u.file.uFileDate - pData2->u.file.uFileDate; 779 if (result == 0) 780 result = pData1->u.file.uFileTime - pData2->u.file.uFileTime; 781 break; 782 case 5: /* Attributes */ 783 return SHELL32_CompareDetails(this, lParam, pidl1, pidl2); 784 } 785 786 if (result == 0) 787 return SHELL32_CompareChildren(this, lParam, pidl1, pidl2); 788 789 return MAKE_COMPARE_HRESULT(result); 790 } 791 792 /************************************************************************** 793 * CFSFolder::CreateViewObject 794 */ 795 HRESULT WINAPI CFSFolder::CreateViewObject(HWND hwndOwner, 796 REFIID riid, LPVOID * ppvOut) 797 { 798 CComPtr<IShellView> pShellView; 799 HRESULT hr = E_INVALIDARG; 800 801 TRACE ("(%p)->(hwnd=%p,%s,%p)\n", this, hwndOwner, shdebugstr_guid (&riid), 802 ppvOut); 803 804 if (ppvOut) 805 { 806 *ppvOut = NULL; 807 808 if (IsEqualIID (riid, IID_IDropTarget)) 809 hr = CFSDropTarget_CreateInstance(sPathTarget, riid, ppvOut); 810 else if (IsEqualIID (riid, IID_IContextMenu)) 811 { 812 HKEY hKeys[16]; 813 UINT cKeys = 0; 814 AddClassKeyToArray(L"Directory\\Background", hKeys, &cKeys); 815 816 DEFCONTEXTMENU dcm; 817 dcm.hwnd = hwndOwner; 818 dcm.pcmcb = this; 819 dcm.pidlFolder = pidlRoot; 820 dcm.psf = this; 821 dcm.cidl = 0; 822 dcm.apidl = NULL; 823 dcm.cKeys = cKeys; 824 dcm.aKeys = hKeys; 825 dcm.punkAssociationInfo = NULL; 826 hr = SHCreateDefaultContextMenu (&dcm, riid, ppvOut); 827 } 828 else if (IsEqualIID (riid, IID_IShellView)) 829 { 830 SFV_CREATE sfvparams = {sizeof(SFV_CREATE), this}; 831 hr = SHCreateShellFolderView(&sfvparams, (IShellView**)ppvOut); 832 } 833 } 834 TRACE("-- (%p)->(interface=%p)\n", this, ppvOut); 835 return hr; 836 } 837 838 /************************************************************************** 839 * CFSFolder::GetAttributesOf 840 * 841 * PARAMETERS 842 * UINT cidl, //[in ] num elements in pidl array 843 * LPCITEMIDLIST* apidl, //[in ] simple pidl array 844 * ULONG* rgfInOut) //[out] result array 845 * 846 */ 847 HRESULT WINAPI CFSFolder::GetAttributesOf(UINT cidl, 848 PCUITEMID_CHILD_ARRAY apidl, DWORD * rgfInOut) 849 { 850 HRESULT hr = S_OK; 851 852 if (!rgfInOut) 853 return E_INVALIDARG; 854 if (cidl && !apidl) 855 return E_INVALIDARG; 856 857 if (*rgfInOut == 0) 858 *rgfInOut = ~0; 859 860 if(cidl == 0) 861 { 862 LPCITEMIDLIST rpidl = ILFindLastID(pidlRoot); 863 864 if (_ILIsFolder(rpidl) || _ILIsValue(rpidl)) 865 { 866 SHELL32_GetFSItemAttributes(this, rpidl, rgfInOut); 867 } 868 else if (_ILIsDrive(rpidl)) 869 { 870 IShellFolder *psfParent = NULL; 871 hr = SHBindToParent(pidlRoot, IID_PPV_ARG(IShellFolder, &psfParent), NULL); 872 if(SUCCEEDED(hr)) 873 { 874 hr = psfParent->GetAttributesOf(1, &rpidl, (SFGAOF*)rgfInOut); 875 psfParent->Release(); 876 } 877 } 878 else 879 { 880 ERR("Got and unknown pidl!\n"); 881 } 882 } 883 else 884 { 885 while (cidl > 0 && *apidl) 886 { 887 pdump(*apidl); 888 if(_ILIsFolder(*apidl) || _ILIsValue(*apidl)) 889 SHELL32_GetFSItemAttributes(this, *apidl, rgfInOut); 890 else 891 ERR("Got an unknown type of pidl!!!\n"); 892 apidl++; 893 cidl--; 894 } 895 } 896 /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */ 897 *rgfInOut &= ~SFGAO_VALIDATE; 898 899 TRACE("-- result=0x%08x\n", *rgfInOut); 900 901 return hr; 902 } 903 904 /************************************************************************** 905 * CFSFolder::GetUIObjectOf 906 * 907 * PARAMETERS 908 * HWND hwndOwner, //[in ] Parent window for any output 909 * UINT cidl, //[in ] array size 910 * LPCITEMIDLIST* apidl, //[in ] simple pidl array 911 * REFIID riid, //[in ] Requested Interface 912 * UINT* prgfInOut, //[ ] reserved 913 * LPVOID* ppvObject) //[out] Resulting Interface 914 * 915 * NOTES 916 * This function gets asked to return "view objects" for one or more (multiple 917 * select) items: 918 * The viewobject typically is an COM object with one of the following 919 * interfaces: 920 * IExtractIcon,IDataObject,IContextMenu 921 * In order to support icon positions in the default Listview your DataObject 922 * must implement the SetData method (in addition to GetData :) - the shell 923 * passes a barely documented "Icon positions" structure to SetData when the 924 * drag starts, and GetData's it if the drop is in another explorer window that 925 * needs the positions. 926 */ 927 HRESULT WINAPI CFSFolder::GetUIObjectOf(HWND hwndOwner, 928 UINT cidl, PCUITEMID_CHILD_ARRAY apidl, 929 REFIID riid, UINT * prgfInOut, 930 LPVOID * ppvOut) 931 { 932 LPVOID pObj = NULL; 933 HRESULT hr = E_INVALIDARG; 934 935 TRACE ("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n", 936 this, hwndOwner, cidl, apidl, shdebugstr_guid (&riid), prgfInOut, ppvOut); 937 938 if (ppvOut) 939 { 940 *ppvOut = NULL; 941 942 if (cidl == 1 && _ILIsValue(apidl[0])) 943 { 944 hr = _CreateExtensionUIObject(apidl[0], riid, ppvOut); 945 if(hr != S_FALSE) 946 return hr; 947 } 948 949 if (IsEqualIID(riid, IID_IContextMenu) && (cidl >= 1)) 950 { 951 HKEY hKeys[16]; 952 UINT cKeys = 0; 953 AddFSClassKeysToArray(apidl[0], hKeys, &cKeys); 954 955 DEFCONTEXTMENU dcm; 956 dcm.hwnd = hwndOwner; 957 dcm.pcmcb = this; 958 dcm.pidlFolder = pidlRoot; 959 dcm.psf = this; 960 dcm.cidl = cidl; 961 dcm.apidl = apidl; 962 dcm.cKeys = cKeys; 963 dcm.aKeys = hKeys; 964 dcm.punkAssociationInfo = NULL; 965 hr = SHCreateDefaultContextMenu (&dcm, riid, &pObj); 966 } 967 else if (IsEqualIID (riid, IID_IDataObject)) 968 { 969 if (cidl >= 1) 970 { 971 hr = IDataObject_Constructor (hwndOwner, pidlRoot, apidl, cidl, (IDataObject **)&pObj); 972 } 973 else 974 { 975 hr = E_INVALIDARG; 976 } 977 } 978 else if ((IsEqualIID (riid, IID_IExtractIconA) || IsEqualIID (riid, IID_IExtractIconW)) && (cidl == 1)) 979 { 980 if (_ILIsValue(apidl[0])) 981 hr = _GetIconHandler(apidl[0], riid, (LPVOID*)&pObj); 982 if (hr != S_OK) 983 hr = CFSExtractIcon_CreateInstance(this, apidl[0], riid, &pObj); 984 } 985 else if (IsEqualIID (riid, IID_IDropTarget)) 986 { 987 /* only interested in attempting to bind to shell folders, not files (except exe), so if we fail, rebind to root */ 988 if (cidl != 1 || FAILED(hr = this->_GetDropTarget(apidl[0], (LPVOID*) &pObj))) 989 { 990 hr = CFSDropTarget_CreateInstance(sPathTarget, riid, (LPVOID*) &pObj); 991 } 992 } 993 else 994 hr = E_NOINTERFACE; 995 996 if (SUCCEEDED(hr) && !pObj) 997 hr = E_OUTOFMEMORY; 998 999 *ppvOut = pObj; 1000 } 1001 TRACE("(%p)->hr=0x%08x\n", this, hr); 1002 return hr; 1003 } 1004 1005 static const WCHAR AdvancedW[] = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced"; 1006 static const WCHAR HideFileExtW[] = L"HideFileExt"; 1007 static const WCHAR NeverShowExtW[] = L"NeverShowExt"; 1008 1009 /****************************************************************************** 1010 * SHELL_FS_HideExtension [Internal] 1011 * 1012 * Query the registry if the filename extension of a given path should be 1013 * hidden. 1014 * 1015 * PARAMS 1016 * szPath [I] Relative or absolute path of a file 1017 * 1018 * RETURNS 1019 * TRUE, if the filename's extension should be hidden 1020 * FALSE, otherwise. 1021 */ 1022 BOOL SHELL_FS_HideExtension(LPWSTR szPath) 1023 { 1024 HKEY hKey; 1025 DWORD dwData; 1026 DWORD dwDataSize = sizeof (DWORD); 1027 BOOL doHide = FALSE; /* The default value is FALSE (win98 at least) */ 1028 1029 if (!RegCreateKeyExW(HKEY_CURRENT_USER, AdvancedW, 0, 0, 0, KEY_ALL_ACCESS, 0, &hKey, 0)) { 1030 if (!RegQueryValueExW(hKey, HideFileExtW, 0, 0, (LPBYTE) &dwData, &dwDataSize)) 1031 doHide = dwData; 1032 RegCloseKey (hKey); 1033 } 1034 1035 if (!doHide) { 1036 LPWSTR ext = PathFindExtensionW(szPath); 1037 1038 if (*ext != '\0') { 1039 WCHAR classname[MAX_PATH]; 1040 LONG classlen = sizeof(classname); 1041 1042 if (!RegQueryValueW(HKEY_CLASSES_ROOT, ext, classname, &classlen)) 1043 if (!RegOpenKeyW(HKEY_CLASSES_ROOT, classname, &hKey)) { 1044 if (!RegQueryValueExW(hKey, NeverShowExtW, 0, NULL, NULL, NULL)) 1045 doHide = TRUE; 1046 RegCloseKey(hKey); 1047 } 1048 } 1049 } 1050 return doHide; 1051 } 1052 1053 void SHELL_FS_ProcessDisplayFilename(LPWSTR szPath, DWORD dwFlags) 1054 { 1055 /*FIXME: MSDN also mentions SHGDN_FOREDITING which is not yet handled. */ 1056 if (!(dwFlags & SHGDN_FORPARSING) && 1057 ((dwFlags & SHGDN_INFOLDER) || (dwFlags == SHGDN_NORMAL))) { 1058 if (SHELL_FS_HideExtension(szPath) && szPath[0] != '.') 1059 PathRemoveExtensionW(szPath); 1060 } 1061 } 1062 1063 /************************************************************************** 1064 * CFSFolder::GetDisplayNameOf 1065 * Retrieves the display name for the specified file object or subfolder 1066 * 1067 * PARAMETERS 1068 * LPCITEMIDLIST pidl, //[in ] complex pidl to item 1069 * DWORD dwFlags, //[in ] SHGNO formatting flags 1070 * LPSTRRET lpName) //[out] Returned display name 1071 * 1072 * FIXME 1073 * if the name is in the pidl the ret value should be a STRRET_OFFSET 1074 */ 1075 1076 HRESULT WINAPI CFSFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl, 1077 DWORD dwFlags, LPSTRRET strRet) 1078 { 1079 if (!strRet) 1080 return E_INVALIDARG; 1081 1082 /* If it is a complex pidl, let the child handle it */ 1083 if (!_ILIsPidlSimple (pidl)) /* complex pidl */ 1084 { 1085 return SHELL32_GetDisplayNameOfChild(this, pidl, dwFlags, strRet); 1086 } 1087 else if (pidl && !pidl->mkid.cb) /* empty pidl */ 1088 { 1089 /* If it is an empty pidl return only the path of the folder */ 1090 if ((GET_SHGDN_FOR(dwFlags) & SHGDN_FORPARSING) && 1091 (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER) && 1092 sPathTarget) 1093 { 1094 return SHSetStrRet(strRet, sPathTarget); 1095 } 1096 return E_INVALIDARG; 1097 } 1098 1099 int len = 0; 1100 LPWSTR pszPath = (LPWSTR)CoTaskMemAlloc((MAX_PATH + 1) * sizeof(WCHAR)); 1101 if (!pszPath) 1102 return E_OUTOFMEMORY; 1103 1104 if ((GET_SHGDN_FOR(dwFlags) & SHGDN_FORPARSING) && 1105 (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER) && 1106 sPathTarget) 1107 { 1108 lstrcpynW(pszPath, sPathTarget, MAX_PATH); 1109 PathAddBackslashW(pszPath); 1110 len = wcslen(pszPath); 1111 } 1112 _ILSimpleGetTextW(pidl, pszPath + len, MAX_PATH + 1 - len); 1113 if (!_ILIsFolder(pidl)) SHELL_FS_ProcessDisplayFilename(pszPath, dwFlags); 1114 1115 strRet->uType = STRRET_WSTR; 1116 strRet->pOleStr = pszPath; 1117 1118 TRACE ("-- (%p)->(%s)\n", this, strRet->uType == STRRET_CSTR ? strRet->cStr : debugstr_w(strRet->pOleStr)); 1119 return S_OK; 1120 } 1121 1122 /************************************************************************** 1123 * CFSFolder::SetNameOf 1124 * Changes the name of a file object or subfolder, possibly changing its item 1125 * identifier in the process. 1126 * 1127 * PARAMETERS 1128 * HWND hwndOwner, //[in ] Owner window for output 1129 * LPCITEMIDLIST pidl, //[in ] simple pidl of item to change 1130 * LPCOLESTR lpszName, //[in ] the items new display name 1131 * DWORD dwFlags, //[in ] SHGNO formatting flags 1132 * LPITEMIDLIST* ppidlOut) //[out] simple pidl returned 1133 */ 1134 HRESULT WINAPI CFSFolder::SetNameOf( 1135 HWND hwndOwner, 1136 PCUITEMID_CHILD pidl, 1137 LPCOLESTR lpName, 1138 DWORD dwFlags, 1139 PITEMID_CHILD *pPidlOut) 1140 { 1141 WCHAR szSrc[MAX_PATH + 1], szDest[MAX_PATH + 1]; 1142 BOOL bIsFolder = _ILIsFolder (ILFindLastID (pidl)); 1143 1144 TRACE ("(%p)->(%p,pidl=%p,%s,%u,%p)\n", this, hwndOwner, pidl, 1145 debugstr_w (lpName), dwFlags, pPidlOut); 1146 1147 FileStructW* pDataW = _ILGetFileStructW(pidl); 1148 if (!pDataW) 1149 { 1150 ERR("Got garbage pidl\n"); 1151 return E_INVALIDARG; 1152 } 1153 1154 /* build source path */ 1155 PathCombineW(szSrc, sPathTarget, pDataW->wszName); 1156 1157 /* build destination path */ 1158 if (dwFlags == SHGDN_NORMAL || dwFlags & SHGDN_INFOLDER) 1159 PathCombineW(szDest, sPathTarget, lpName); 1160 else 1161 lstrcpynW(szDest, lpName, MAX_PATH); 1162 1163 if(!(dwFlags & SHGDN_FORPARSING) && SHELL_FS_HideExtension(szSrc)) { 1164 WCHAR *ext = PathFindExtensionW(szSrc); 1165 if(*ext != '\0') { 1166 INT len = wcslen(szDest); 1167 lstrcpynW(szDest + len, ext, MAX_PATH - len); 1168 } 1169 } 1170 1171 TRACE ("src=%s dest=%s\n", debugstr_w(szSrc), debugstr_w(szDest)); 1172 if (!wcscmp(szSrc, szDest)) 1173 { 1174 /* src and destination is the same */ 1175 HRESULT hr = S_OK; 1176 if (pPidlOut) 1177 hr = _ILCreateFromPathW(szDest, pPidlOut); 1178 1179 return hr; 1180 } 1181 1182 if (MoveFileW (szSrc, szDest)) 1183 { 1184 HRESULT hr = S_OK; 1185 1186 if (pPidlOut) 1187 hr = _ILCreateFromPathW(szDest, pPidlOut); 1188 1189 SHChangeNotify (bIsFolder ? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM, 1190 SHCNF_PATHW, szSrc, szDest); 1191 1192 return hr; 1193 } 1194 1195 return E_FAIL; 1196 } 1197 1198 HRESULT WINAPI CFSFolder::GetDefaultSearchGUID(GUID * pguid) 1199 { 1200 FIXME ("(%p)\n", this); 1201 return E_NOTIMPL; 1202 } 1203 1204 HRESULT WINAPI CFSFolder::EnumSearches(IEnumExtraSearch ** ppenum) 1205 { 1206 FIXME ("(%p)\n", this); 1207 return E_NOTIMPL; 1208 } 1209 1210 HRESULT WINAPI CFSFolder::GetDefaultColumn(DWORD dwRes, 1211 ULONG * pSort, ULONG * pDisplay) 1212 { 1213 TRACE ("(%p)\n", this); 1214 1215 if (pSort) 1216 *pSort = 0; 1217 if (pDisplay) 1218 *pDisplay = 0; 1219 1220 return S_OK; 1221 } 1222 1223 HRESULT WINAPI CFSFolder::GetDefaultColumnState(UINT iColumn, 1224 DWORD * pcsFlags) 1225 { 1226 TRACE ("(%p)\n", this); 1227 1228 if (!pcsFlags || iColumn >= GENERICSHELLVIEWCOLUMNS) 1229 return E_INVALIDARG; 1230 1231 *pcsFlags = GenericSFHeader[iColumn].pcsFlags; 1232 1233 return S_OK; 1234 } 1235 1236 HRESULT WINAPI CFSFolder::GetDetailsEx(PCUITEMID_CHILD pidl, 1237 const SHCOLUMNID * pscid, VARIANT * pv) 1238 { 1239 FIXME ("(%p)\n", this); 1240 1241 return E_NOTIMPL; 1242 } 1243 1244 HRESULT WINAPI CFSFolder::GetDetailsOf(PCUITEMID_CHILD pidl, 1245 UINT iColumn, SHELLDETAILS * psd) 1246 { 1247 HRESULT hr = E_FAIL; 1248 1249 TRACE ("(%p)->(%p %i %p)\n", this, pidl, iColumn, psd); 1250 1251 if (!psd || iColumn >= GENERICSHELLVIEWCOLUMNS) 1252 return E_INVALIDARG; 1253 1254 if (!pidl) 1255 { 1256 /* the header titles */ 1257 psd->fmt = GenericSFHeader[iColumn].fmt; 1258 psd->cxChar = GenericSFHeader[iColumn].cxChar; 1259 return SHSetStrRet(&psd->str, GenericSFHeader[iColumn].colnameid); 1260 } 1261 else 1262 { 1263 hr = S_OK; 1264 psd->str.uType = STRRET_CSTR; 1265 /* the data from the pidl */ 1266 switch (iColumn) 1267 { 1268 case 0: /* name */ 1269 hr = GetDisplayNameOf (pidl, SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str); 1270 break; 1271 case 1: /* FIXME: comments */ 1272 psd->str.cStr[0] = 0; 1273 break; 1274 case 2: /* type */ 1275 _ILGetFileType(pidl, psd->str.cStr, MAX_PATH); 1276 break; 1277 case 3: /* size */ 1278 _ILGetFileSize(pidl, psd->str.cStr, MAX_PATH); 1279 break; 1280 case 4: /* date */ 1281 _ILGetFileDate(pidl, psd->str.cStr, MAX_PATH); 1282 break; 1283 case 5: /* attributes */ 1284 _ILGetFileAttributes(pidl, psd->str.cStr, MAX_PATH); 1285 break; 1286 } 1287 } 1288 1289 return hr; 1290 } 1291 1292 HRESULT WINAPI CFSFolder::MapColumnToSCID (UINT column, 1293 SHCOLUMNID * pscid) 1294 { 1295 FIXME ("(%p)\n", this); 1296 return E_NOTIMPL; 1297 } 1298 1299 /************************************************************************ 1300 * CFSFolder::GetClassID 1301 */ 1302 HRESULT WINAPI CFSFolder::GetClassID(CLSID * lpClassId) 1303 { 1304 TRACE ("(%p)\n", this); 1305 1306 if (!lpClassId) 1307 return E_POINTER; 1308 1309 *lpClassId = *pclsid; 1310 1311 return S_OK; 1312 } 1313 1314 /************************************************************************ 1315 * CFSFolder::Initialize 1316 * 1317 * NOTES 1318 * sPathTarget is not set. Don't know how to handle in a non rooted environment. 1319 */ 1320 HRESULT WINAPI CFSFolder::Initialize(LPCITEMIDLIST pidl) 1321 { 1322 WCHAR wszTemp[MAX_PATH]; 1323 1324 TRACE ("(%p)->(%p)\n", this, pidl); 1325 1326 SHFree (pidlRoot); /* free the old pidl */ 1327 pidlRoot = ILClone (pidl); /* set my pidl */ 1328 1329 SHFree (sPathTarget); 1330 sPathTarget = NULL; 1331 1332 /* set my path */ 1333 if (SHGetPathFromIDListW (pidl, wszTemp)) 1334 { 1335 int len = wcslen(wszTemp); 1336 sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR)); 1337 if (!sPathTarget) 1338 return E_OUTOFMEMORY; 1339 memcpy(sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR)); 1340 } 1341 1342 TRACE ("--(%p)->(%s)\n", this, debugstr_w(sPathTarget)); 1343 return S_OK; 1344 } 1345 1346 /************************************************************************** 1347 * CFSFolder::GetCurFolder 1348 */ 1349 HRESULT WINAPI CFSFolder::GetCurFolder(LPITEMIDLIST * pidl) 1350 { 1351 TRACE ("(%p)->(%p)\n", this, pidl); 1352 1353 if (!pidl) 1354 return E_POINTER; 1355 1356 *pidl = ILClone(pidlRoot); 1357 return S_OK; 1358 } 1359 1360 /************************************************************************** 1361 * CFSFolder::InitializeEx 1362 * 1363 * FIXME: error handling 1364 */ 1365 HRESULT WINAPI CFSFolder::InitializeEx(IBindCtx * pbc, LPCITEMIDLIST pidlRootx, 1366 const PERSIST_FOLDER_TARGET_INFO * ppfti) 1367 { 1368 WCHAR wszTemp[MAX_PATH]; 1369 1370 TRACE("(%p)->(%p,%p,%p)\n", this, pbc, pidlRootx, ppfti); 1371 if (ppfti) 1372 TRACE("--%p %s %s 0x%08x 0x%08x\n", 1373 ppfti->pidlTargetFolder, debugstr_w (ppfti->szTargetParsingName), 1374 debugstr_w (ppfti->szNetworkProvider), ppfti->dwAttributes, 1375 ppfti->csidl); 1376 1377 pdump (pidlRootx); 1378 if (ppfti && ppfti->pidlTargetFolder) 1379 pdump(ppfti->pidlTargetFolder); 1380 1381 if (pidlRoot) 1382 __SHFreeAndNil(&pidlRoot); /* free the old */ 1383 if (sPathTarget) 1384 __SHFreeAndNil(&sPathTarget); 1385 1386 /* 1387 * Root path and pidl 1388 */ 1389 pidlRoot = ILClone(pidlRootx); 1390 1391 /* 1392 * the target folder is spezified in csidl OR pidlTargetFolder OR 1393 * szTargetParsingName 1394 */ 1395 if (ppfti) 1396 { 1397 if (ppfti->csidl != -1) 1398 { 1399 if (SHGetSpecialFolderPathW(0, wszTemp, ppfti->csidl, 1400 ppfti->csidl & CSIDL_FLAG_CREATE)) { 1401 int len = wcslen(wszTemp); 1402 sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR)); 1403 if (!sPathTarget) 1404 return E_OUTOFMEMORY; 1405 memcpy(sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR)); 1406 } 1407 } 1408 else if (ppfti->szTargetParsingName[0]) 1409 { 1410 int len = wcslen(ppfti->szTargetParsingName); 1411 sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR)); 1412 if (!sPathTarget) 1413 return E_OUTOFMEMORY; 1414 memcpy(sPathTarget, ppfti->szTargetParsingName, 1415 (len + 1) * sizeof(WCHAR)); 1416 } 1417 else if (ppfti->pidlTargetFolder) 1418 { 1419 if (SHGetPathFromIDListW(ppfti->pidlTargetFolder, wszTemp)) 1420 { 1421 int len = wcslen(wszTemp); 1422 sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR)); 1423 if (!sPathTarget) 1424 return E_OUTOFMEMORY; 1425 memcpy(sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR)); 1426 } 1427 } 1428 } 1429 1430 TRACE("--(%p)->(target=%s)\n", this, debugstr_w(sPathTarget)); 1431 pdump(pidlRoot); 1432 return (sPathTarget) ? S_OK : E_FAIL; 1433 } 1434 1435 HRESULT WINAPI CFSFolder::GetFolderTargetInfo(PERSIST_FOLDER_TARGET_INFO * ppfti) 1436 { 1437 FIXME("(%p)->(%p)\n", this, ppfti); 1438 ZeroMemory(ppfti, sizeof (*ppfti)); 1439 return E_NOTIMPL; 1440 } 1441 1442 HRESULT CFSFolder::_CreateExtensionUIObject(PCUIDLIST_RELATIVE pidl, REFIID riid, LPVOID *ppvOut) 1443 { 1444 static const WCHAR formatW[] = {'S','h','e','l','l','E','x','\\', 1445 '{','%','0','8','x','-','%','0','4','x','-','%','0','4','x','-', 1446 '%','0','2','x','%','0','2','x','-','%','0','2','x','%','0','2','x', 1447 '%','0','2','x','%','0','2','x','%','0','2','x','%','0','2','x','}',0}; 1448 WCHAR buf[MAX_PATH]; 1449 1450 sprintfW(buf, formatW, riid.Data1, riid.Data2, riid.Data3, 1451 riid.Data4[0], riid.Data4[1], riid.Data4[2], riid.Data4[3], 1452 riid.Data4[4], riid.Data4[5], riid.Data4[6], riid.Data4[7]); 1453 1454 CLSID clsid; 1455 HRESULT hr; 1456 1457 hr = GetCLSIDForFileType(pidl, buf, &clsid); 1458 if (hr != S_OK) 1459 return hr; 1460 1461 hr = _CreateShellExtInstance(&clsid, pidl, riid, ppvOut); 1462 if (FAILED_UNEXPECTEDLY(hr)) 1463 return hr; 1464 1465 return S_OK; 1466 } 1467 1468 HRESULT CFSFolder::_GetDropTarget(LPCITEMIDLIST pidl, LPVOID *ppvOut) 1469 { 1470 HRESULT hr; 1471 1472 TRACE("CFSFolder::_GetDropTarget entered\n"); 1473 1474 if (_ILIsFolder (pidl)) 1475 { 1476 CComPtr<IShellFolder> psfChild; 1477 hr = this->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfChild)); 1478 if (FAILED_UNEXPECTEDLY(hr)) 1479 return hr; 1480 1481 return psfChild->CreateViewObject(NULL, IID_IDropTarget, ppvOut); 1482 } 1483 1484 CLSID clsid; 1485 hr = GetCLSIDForFileType(pidl, L"shellex\\DropHandler", &clsid); 1486 if (hr != S_OK) 1487 return hr; 1488 1489 hr = _CreateShellExtInstance(&clsid, pidl, IID_IDropTarget, ppvOut); 1490 if (FAILED_UNEXPECTEDLY(hr)) 1491 return S_FALSE; 1492 1493 return S_OK; 1494 } 1495 1496 HRESULT CFSFolder::_GetIconHandler(LPCITEMIDLIST pidl, REFIID riid, LPVOID *ppvOut) 1497 { 1498 CLSID clsid; 1499 HRESULT hr; 1500 1501 hr = GetCLSIDForFileType(pidl, L"shellex\\IconHandler", &clsid); 1502 if (hr != S_OK) 1503 return hr; 1504 1505 hr = _CreateShellExtInstance(&clsid, pidl, riid, ppvOut); 1506 if (FAILED_UNEXPECTEDLY(hr)) 1507 return S_FALSE; 1508 1509 return S_OK; 1510 } 1511 1512 HRESULT CFSFolder::_CreateShellExtInstance(const CLSID *pclsid, LPCITEMIDLIST pidl, REFIID riid, LPVOID *ppvOut) 1513 { 1514 HRESULT hr; 1515 WCHAR wszPath[MAX_PATH]; 1516 1517 FileStructW* pDataW = _ILGetFileStructW(pidl); 1518 if (!pDataW) 1519 { 1520 ERR("Got garbage pidl\n"); 1521 return E_INVALIDARG; 1522 } 1523 1524 PathCombineW(wszPath, sPathTarget, pDataW->wszName); 1525 1526 CComPtr<IPersistFile> pp; 1527 hr = SHCoCreateInstance(NULL, pclsid, NULL, IID_PPV_ARG(IPersistFile, &pp)); 1528 if (FAILED_UNEXPECTEDLY(hr)) 1529 return hr; 1530 1531 pp->Load(wszPath, 0); 1532 1533 hr = pp->QueryInterface(riid, ppvOut); 1534 if (hr != S_OK) 1535 { 1536 ERR("Failed to query for interface IID_IShellExtInit hr %x pclsid %s\n", hr, wine_dbgstr_guid(pclsid)); 1537 return hr; 1538 } 1539 return hr; 1540 } 1541 1542 HRESULT WINAPI CFSFolder::CallBack(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam) 1543 { 1544 if (uMsg != DFM_MERGECONTEXTMENU && uMsg != DFM_INVOKECOMMAND) 1545 return S_OK; 1546 1547 /* no data object means no selection */ 1548 if (!pdtobj) 1549 { 1550 if (uMsg == DFM_INVOKECOMMAND && wParam == 0) 1551 { 1552 PUITEMID_CHILD pidlChild = ILClone(ILFindLastID(pidlRoot)); 1553 LPITEMIDLIST pidlParent = ILClone(pidlRoot); 1554 ILRemoveLastID(pidlParent); 1555 HRESULT hr = SH_ShowPropertiesDialog(sPathTarget, pidlParent, &pidlChild); 1556 if (FAILED(hr)) 1557 ERR("SH_ShowPropertiesDialog failed\n"); 1558 ILFree(pidlChild); 1559 ILFree(pidlParent); 1560 } 1561 else if (uMsg == DFM_MERGECONTEXTMENU) 1562 { 1563 QCMINFO *pqcminfo = (QCMINFO *)lParam; 1564 HMENU hpopup = CreatePopupMenu(); 1565 _InsertMenuItemW(hpopup, 0, TRUE, 0, MFT_STRING, MAKEINTRESOURCEW(IDS_PROPERTIES), MFS_ENABLED); 1566 Shell_MergeMenus(pqcminfo->hmenu, hpopup, pqcminfo->indexMenu++, pqcminfo->idCmdFirst, pqcminfo->idCmdLast, MM_ADDSEPARATOR); 1567 DestroyMenu(hpopup); 1568 } 1569 1570 return S_OK; 1571 } 1572 1573 if (uMsg != DFM_INVOKECOMMAND || wParam != DFM_CMD_PROPERTIES) 1574 return S_OK; 1575 1576 PIDLIST_ABSOLUTE pidlFolder; 1577 PUITEMID_CHILD *apidl; 1578 UINT cidl; 1579 HRESULT hr = SH_GetApidlFromDataObject(pdtobj, &pidlFolder, &apidl, &cidl); 1580 if (FAILED_UNEXPECTEDLY(hr)) 1581 return hr; 1582 1583 if (cidl > 1) 1584 ERR("SHMultiFileProperties is not yet implemented\n"); 1585 1586 STRRET strFile; 1587 hr = GetDisplayNameOf(apidl[0], SHGDN_FORPARSING, &strFile); 1588 if (SUCCEEDED(hr)) 1589 { 1590 hr = SH_ShowPropertiesDialog(strFile.pOleStr, pidlFolder, apidl); 1591 if (FAILED(hr)) 1592 ERR("SH_ShowPropertiesDialog failed\n"); 1593 } 1594 else 1595 { 1596 ERR("Failed to get display name\n"); 1597 } 1598 1599 SHFree(pidlFolder); 1600 _ILFreeaPidl(apidl, cidl); 1601 1602 return hr; 1603 } 1604