1 /* 2 * Copyright 2003, 2004, 2005, 2006 Martin Fuchs 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Lesser General Public 6 * License as published by the Free Software Foundation; either 7 * version 2.1 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Lesser General Public License for more details. 13 * 14 * You should have received a copy of the GNU Lesser General Public 15 * License along with this library; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 */ 18 19 20 // 21 // Explorer clone 22 // 23 // shellfs.cpp 24 // 25 // Martin Fuchs, 23.07.2003 26 // 27 28 29 #include <precomp.h> 30 31 bool ShellDirectory::fill_w32fdata_shell(LPCITEMIDLIST pidl, SFGAOF attribs, WIN32_FIND_DATA* pw32fdata, BY_HANDLE_FILE_INFORMATION* pbhfi, bool do_access) 32 { 33 CONTEXT("ShellDirectory::fill_w32fdata_shell()"); 34 35 bool bhfi_valid = false; 36 37 if (do_access && !( (attribs&SFGAO_FILESYSTEM) && SUCCEEDED( 38 SHGetDataFromIDList(_folder, pidl, SHGDFIL_FINDDATA, pw32fdata, sizeof(WIN32_FIND_DATA))) )) { 39 WIN32_FILE_ATTRIBUTE_DATA fad; 40 IDataObject* pDataObj; 41 42 STGMEDIUM medium = {0, {0}, 0}; 43 FORMATETC fmt = {(USHORT)g_Globals._cfStrFName, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; 44 45 HRESULT hr = _folder->GetUIObjectOf(0, 1, &pidl, IID_IDataObject, 0, (LPVOID*)&pDataObj); 46 47 if (SUCCEEDED(hr)) { 48 hr = pDataObj->GetData(&fmt, &medium); 49 50 pDataObj->Release(); 51 52 if (SUCCEEDED(hr)) { 53 LPCTSTR path = (LPCTSTR)GlobalLock(medium.UNION_MEMBER(hGlobal)); 54 55 if (path) { 56 // fill with drive names "C:", ... 57 assert(_tcslen(path) < GlobalSize(medium.UNION_MEMBER(hGlobal))); 58 _tcscpy(pw32fdata->cFileName, path); 59 60 UINT sem_org = SetErrorMode(SEM_FAILCRITICALERRORS); 61 62 if (GetFileAttributesEx(path, GetFileExInfoStandard, &fad)) { 63 pw32fdata->dwFileAttributes = fad.dwFileAttributes; 64 pw32fdata->ftCreationTime = fad.ftCreationTime; 65 pw32fdata->ftLastAccessTime = fad.ftLastAccessTime; 66 pw32fdata->ftLastWriteTime = fad.ftLastWriteTime; 67 68 if (!(fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { 69 // copy file size 70 pw32fdata->nFileSizeLow = fad.nFileSizeLow; 71 pw32fdata->nFileSizeHigh = fad.nFileSizeHigh; 72 } else { 73 // ignore FILE_ATTRIBUTE_HIDDEN attribute of NTFS drives - this would hide those drives in ShellBrowser 74 if (pw32fdata->dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) { 75 if (path[1]==':' && path[2]=='\\' && !path[3]) // Is it a drive path? 76 pw32fdata->dwFileAttributes &= ~FILE_ATTRIBUTE_HIDDEN; 77 } 78 } 79 } 80 81 HANDLE hFile = CreateFile(path, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, 82 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); 83 84 if (hFile != INVALID_HANDLE_VALUE) { 85 if (GetFileInformationByHandle(hFile, pbhfi)) 86 bhfi_valid = true; 87 88 CloseHandle(hFile); 89 } 90 91 SetErrorMode(sem_org); 92 93 GlobalUnlock(medium.UNION_MEMBER(hGlobal)); 94 GlobalFree(medium.UNION_MEMBER(hGlobal)); 95 } 96 } 97 } 98 } 99 100 if (!do_access || !(attribs&SFGAO_FILESYSTEM)) // Archiv files should not be displayed as folders in explorer view. 101 if (attribs & (SFGAO_FOLDER|SFGAO_HASSUBFOLDER)) 102 pw32fdata->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY; 103 104 if (attribs & SFGAO_READONLY) 105 pw32fdata->dwFileAttributes |= FILE_ATTRIBUTE_READONLY; 106 107 if (attribs & SFGAO_COMPRESSED) 108 pw32fdata->dwFileAttributes |= FILE_ATTRIBUTE_COMPRESSED; 109 110 return bhfi_valid; 111 } 112 113 114 ShellPath ShellEntry::create_absolute_pidl() const 115 { 116 CONTEXT("ShellEntry::create_absolute_pidl()"); 117 118 if (_up) 119 { 120 if (_up->_etype == ET_SHELL) { 121 ShellDirectory* dir = static_cast<ShellDirectory*>(_up); 122 123 if (dir->_pidl->mkid.cb) // Caching of absolute PIDLs could enhance performance. 124 return _pidl.create_absolute_pidl(dir->create_absolute_pidl()); 125 } else 126 return _pidl.create_absolute_pidl(_up->create_absolute_pidl()); 127 } 128 return _pidl; 129 } 130 131 132 // get full path of a shell entry 133 bool ShellEntry::get_path(PTSTR path, size_t path_count) const 134 { 135 if (!path || path_count==0) 136 return false; 137 /* 138 path[0] = TEXT('\0'); 139 140 if (FAILED(path_from_pidl(get_parent_folder(), &*_pidl, path, path_count))) 141 return false; 142 */ 143 FileSysShellPath fs_path(create_absolute_pidl()); 144 LPCTSTR ret = fs_path; 145 146 if (ret) { 147 lstrcpyn(path, ret, path_count); 148 return true; 149 } else 150 return false; 151 } 152 153 154 // get full path of a shell folder 155 bool ShellDirectory::get_path(PTSTR path, size_t path_count) const 156 { 157 CONTEXT("ShellDirectory::get_path()"); 158 159 if (!path || path_count==0) 160 return false; 161 162 path[0] = TEXT('\0'); 163 164 if (_folder.empty()) 165 return false; 166 167 SFGAOF attribs = SFGAO_FILESYSTEM; 168 169 // Split pidl into current and parent folder PIDLs 170 ShellPath pidlParent, pidlFolder; 171 _pidl.split(pidlParent, pidlFolder); 172 173 if (FAILED(const_cast<ShellFolder&>(_folder)->GetAttributesOf(1, (LPCITEMIDLIST*)&pidlFolder, &attribs))) 174 return false; 175 176 if (!(attribs & SFGAO_FILESYSTEM)) 177 return false; 178 179 if (FAILED(path_from_pidl(get_parent_folder(), &*_pidl, path, path_count))) 180 return false; 181 182 return true; 183 } 184 185 186 BOOL ShellEntry::launch_entry(HWND hwnd, UINT nCmdShow) 187 { 188 CONTEXT("ShellEntry::launch_entry()"); 189 190 SHELLEXECUTEINFO shexinfo; 191 192 shexinfo.cbSize = sizeof(SHELLEXECUTEINFO); 193 shexinfo.fMask = SEE_MASK_INVOKEIDLIST; // SEE_MASK_IDLIST is also possible. 194 shexinfo.hwnd = hwnd; 195 shexinfo.lpVerb = NULL; 196 shexinfo.lpFile = NULL; 197 shexinfo.lpParameters = NULL; 198 shexinfo.lpDirectory = NULL; 199 shexinfo.nShow = nCmdShow; 200 201 ShellPath shell_path = create_absolute_pidl(); 202 shexinfo.lpIDList = &*shell_path; 203 204 // add PIDL to the recent file list 205 SHAddToRecentDocs(SHARD_PIDL, shexinfo.lpIDList); 206 207 BOOL ret = TRUE; 208 209 if (!ShellExecuteEx(&shexinfo)) { 210 display_error(hwnd, GetLastError()); 211 ret = FALSE; 212 } 213 214 return ret; 215 } 216 217 218 HRESULT ShellEntry::do_context_menu(HWND hwnd, const POINT& pptScreen, CtxMenuInterfaces& cm_ifs) 219 { 220 ShellDirectory* dir = static_cast<ShellDirectory*>(_up); 221 222 ShellFolder folder = dir? dir->_folder: GetDesktopFolder(); 223 LPCITEMIDLIST pidl = _pidl; 224 225 return ShellFolderContextMenu(folder, hwnd, 1, &pidl, pptScreen.x, pptScreen.y, cm_ifs); 226 } 227 228 229 HRESULT ShellEntry::GetUIObjectOf(HWND hWnd, REFIID riid, LPVOID* ppvOut) 230 { 231 LPCITEMIDLIST pidl = _pidl; 232 233 return get_parent_folder()->GetUIObjectOf(hWnd, 1, &pidl, riid, NULL, ppvOut); 234 } 235 236 237 ShellFolder Entry::get_shell_folder() const 238 { 239 return ShellFolder(create_absolute_pidl()); 240 } 241 242 ShellFolder ShellEntry::get_shell_folder() const 243 { 244 return get_parent_folder(); 245 } 246 247 ShellFolder ShellDirectory::get_shell_folder() const 248 { 249 return _folder; 250 } 251 252 253 void ShellDirectory::read_directory(int scan_flags) 254 { 255 CONTEXT("ShellDirectory::read_directory()"); 256 257 int level = _level + 1; 258 259 Entry* first_entry = NULL; 260 Entry* last = NULL; 261 262 /*if (_folder.empty()) 263 return;*/ 264 265 #ifndef _NO_WIN_FS 266 TCHAR buffer[_MAX_PATH+_MAX_FNAME]; 267 268 if (!(scan_flags&SCAN_NO_FILESYSTEM) && get_path(buffer, COUNTOF(buffer)) && _tcsncmp(buffer,TEXT("::{"),3)) { 269 Entry* entry = NULL; // eliminate useless GCC warning by initializing entry 270 271 LPTSTR p = buffer + _tcslen(buffer); 272 273 lstrcpy(p, TEXT("\\*")); 274 275 WIN32_FIND_DATA w32fd; 276 HANDLE hFind = FindFirstFile(buffer, &w32fd); 277 278 if (hFind != INVALID_HANDLE_VALUE) { 279 do { 280 // ignore hidden files (usefull in the start menu) 281 if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) 282 continue; 283 284 // ignore directory entries "." and ".." 285 if ((w32fd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) && 286 w32fd.cFileName[0]==TEXT('.') && 287 (w32fd.cFileName[1]==TEXT('\0') || 288 (w32fd.cFileName[1]==TEXT('.') && w32fd.cFileName[2]==TEXT('\0')))) 289 continue; 290 291 lstrcpy(p+1, w32fd.cFileName); 292 293 if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 294 entry = new WinDirectory(this, buffer); 295 else 296 entry = new WinEntry(this); 297 298 if (!first_entry) 299 first_entry = entry; 300 301 if (last) 302 last->_next = entry; 303 304 memcpy(&entry->_data, &w32fd, sizeof(WIN32_FIND_DATA)); 305 306 entry->_level = level; 307 308 if (!(scan_flags & SCAN_DONT_ACCESS)) { 309 HANDLE hFile = CreateFile(buffer, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, 310 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); 311 312 if (hFile != INVALID_HANDLE_VALUE) { 313 if (GetFileInformationByHandle(hFile, &entry->_bhfi)) 314 entry->_bhfi_valid = true; 315 316 #ifdef BACKUP_READ_IMPLEMENTED 317 if (ScanNTFSStreams(entry, hFile)) 318 entry->_scanned = true; // There exist named NTFS sub-streams in this file. 319 #endif 320 321 CloseHandle(hFile); 322 } 323 } 324 325 // set file type name 326 LPCTSTR ext = g_Globals._ftype_mgr.set_type(entry); 327 328 DWORD attribs = SFGAO_FILESYSTEM; 329 330 if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 331 attribs |= SFGAO_FOLDER|SFGAO_HASSUBFOLDER; 332 333 if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_READONLY) 334 attribs |= SFGAO_READONLY; 335 336 //if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) 337 // attribs |= SFGAO_HIDDEN; 338 339 if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) 340 attribs |= SFGAO_COMPRESSED; 341 342 if (ext && !_tcsicmp(ext, _T(".lnk"))) { 343 attribs |= SFGAO_LINK; 344 w32fd.dwFileAttributes |= ATTRIBUTE_SYMBOLIC_LINK; 345 } 346 347 entry->_shell_attribs = attribs; 348 349 if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 350 entry->_icon_id = ICID_FOLDER; 351 else if (!(scan_flags & SCAN_DONT_EXTRACT_ICONS)) 352 entry->_icon_id = entry->safe_extract_icon(); // Assume small icon, we can extract the large icon later on demand. 353 354 last = entry; 355 } while(FindNextFile(hFind, &w32fd)); 356 357 FindClose(hFind); 358 } 359 } 360 else // SCAN_NO_FILESYSTEM 361 #endif 362 { 363 ShellItemEnumerator enumerator(_folder, SHCONTF_FOLDERS|SHCONTF_NONFOLDERS|SHCONTF_INCLUDEHIDDEN|SHCONTF_SHAREABLE|SHCONTF_STORAGE); 364 365 TCHAR name[MAX_PATH]; 366 TCHAR path[MAX_PATH]; 367 HRESULT hr_next = S_OK; 368 369 do { 370 #define FETCH_ITEM_COUNT 32 371 LPITEMIDLIST pidls[FETCH_ITEM_COUNT]; 372 ULONG cnt = 0; 373 374 memset(pidls, 0, sizeof(pidls)); 375 376 hr_next = enumerator->Next(FETCH_ITEM_COUNT, pidls, &cnt); 377 378 /* don't break yet now: Registry Explorer Plugin returns E_FAIL! 379 if (!SUCCEEDED(hr_next)) 380 break; */ 381 382 if (hr_next == S_FALSE) 383 break; 384 385 for(ULONG n=0; n<cnt; ++n) { 386 WIN32_FIND_DATA w32fd; 387 BY_HANDLE_FILE_INFORMATION bhfi; 388 bool bhfi_valid = false; 389 390 memset(&w32fd, 0, sizeof(WIN32_FIND_DATA)); 391 392 SFGAOF attribs_before = ~SFGAO_READONLY & ~SFGAO_VALIDATE; 393 SFGAOF attribs = attribs_before; 394 HRESULT hr = _folder->GetAttributesOf(1, (LPCITEMIDLIST*)&pidls[n], &attribs); 395 bool removeable = false; 396 397 if (SUCCEEDED(hr) && attribs!=attribs_before) { 398 // avoid accessing floppy drives when browsing "My Computer" 399 if (attribs & SFGAO_REMOVABLE) { 400 attribs |= SFGAO_HASSUBFOLDER; 401 removeable = true; 402 } else if (!(scan_flags & SCAN_DONT_ACCESS)) { 403 SFGAOF attribs2 = SFGAO_READONLY; 404 405 HRESULT hr = _folder->GetAttributesOf(1, (LPCITEMIDLIST*)&pidls[n], &attribs2); 406 407 if (SUCCEEDED(hr)) 408 attribs |= attribs2; 409 } 410 } else 411 attribs = 0; 412 413 bhfi_valid = fill_w32fdata_shell(pidls[n], attribs, &w32fd, &bhfi, 414 !(scan_flags&SCAN_DONT_ACCESS) && !removeable); 415 416 try { 417 Entry* entry = NULL; // eliminate useless GCC warning by initializing entry 418 419 if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 420 entry = new ShellDirectory(this, pidls[n], _hwnd); 421 else 422 entry = new ShellEntry(this, pidls[n]); 423 424 if (!first_entry) 425 first_entry = entry; 426 427 if (last) 428 last->_next = entry; 429 430 memcpy(&entry->_data, &w32fd, sizeof(WIN32_FIND_DATA)); 431 432 if (bhfi_valid) 433 memcpy(&entry->_bhfi, &bhfi, sizeof(BY_HANDLE_FILE_INFORMATION)); 434 435 // store path in entry->_data.cFileName in case fill_w32fdata_shell() didn't already fill it 436 if (!entry->_data.cFileName[0]) 437 if (SUCCEEDED(path_from_pidl(_folder, pidls[n], path, COUNTOF(path)))) 438 _tcscpy(entry->_data.cFileName, path); 439 440 if (SUCCEEDED(name_from_pidl(_folder, pidls[n], name, COUNTOF(name), SHGDN_INFOLDER|0x2000/*0x2000=SHGDN_INCLUDE_NONFILESYS*/))) { 441 if (!entry->_data.cFileName[0]) 442 _tcscpy(entry->_data.cFileName, name); 443 else if (_tcscmp(entry->_display_name, name)) 444 entry->_display_name = _tcsdup(name); // store display name separate from file name; sort display by file name 445 } 446 447 if (attribs & SFGAO_LINK) 448 w32fd.dwFileAttributes |= ATTRIBUTE_SYMBOLIC_LINK; 449 450 entry->_level = level; 451 entry->_shell_attribs = attribs; 452 entry->_bhfi_valid = bhfi_valid; 453 454 // set file type name 455 g_Globals._ftype_mgr.set_type(entry); 456 457 // get icons for files and virtual objects 458 if (!(entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) || 459 !(attribs & SFGAO_FILESYSTEM)) { 460 if (!(scan_flags & SCAN_DONT_EXTRACT_ICONS)) 461 entry->_icon_id = entry->safe_extract_icon(); // Assume small icon, we can extract the large icon later on demand. 462 } else if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 463 entry->_icon_id = ICID_FOLDER; 464 else 465 entry->_icon_id = ICID_NONE; // don't try again later 466 467 last = entry; 468 } catch(COMException& e) { 469 HandleException(e, _hwnd); 470 } 471 } 472 } while(SUCCEEDED(hr_next)); 473 } 474 475 if (last) 476 last->_next = NULL; 477 478 _down = first_entry; 479 _scanned = true; 480 } 481 482 const void* ShellDirectory::get_next_path_component(const void* p) const 483 { 484 LPITEMIDLIST pidl = (LPITEMIDLIST)p; 485 486 if (!pidl || !pidl->mkid.cb) 487 return NULL; 488 489 // go to next element 490 pidl = (LPITEMIDLIST)((LPBYTE)pidl+pidl->mkid.cb); 491 492 return pidl; 493 } 494 495 Entry* ShellDirectory::find_entry(const void* p) 496 { 497 LPITEMIDLIST pidl = (LPITEMIDLIST) p; 498 499 // handle special case of empty trailing id list entry 500 if (!pidl->mkid.cb) 501 return this; 502 503 for(Entry*entry=_down; entry; entry=entry->_next) 504 if (entry->_etype == ET_SHELL) { 505 ShellEntry* se = static_cast<ShellEntry*>(entry); 506 507 if (se->_pidl && se->_pidl->mkid.cb==pidl->mkid.cb && !memcmp(se->_pidl, pidl, se->_pidl->mkid.cb)) 508 return entry; 509 } else { 510 const ShellPath& sp = entry->create_absolute_pidl(); 511 static DynamicFct<LPITEMIDLIST(WINAPI*)(LPCITEMIDLIST)> ILFindLastID(TEXT("SHELL32"), "ILFindLastID"); 512 513 if (ILFindLastID) { 514 LPCITEMIDLIST entry_pidl = (*ILFindLastID)(sp); 515 516 if (entry_pidl && entry_pidl->mkid.cb==pidl->mkid.cb && !memcmp(entry_pidl, pidl, entry_pidl->mkid.cb)) 517 return entry; 518 } 519 } 520 521 return NULL; 522 } 523 524 int ShellDirectory::extract_icons(ICONCACHE_FLAGS flags) 525 { 526 int cnt = 0; 527 528 for(Entry*entry=_down; entry; entry=entry->_next) 529 if (entry->_icon_id == ICID_UNKNOWN) { 530 entry->_icon_id = entry->extract_icon(flags); 531 532 if (entry->_icon_id != ICID_NONE) 533 ++cnt; 534 } 535 536 return cnt; 537 } 538