shellfs.cpp

Go to the documentation of this file.
00001 /*
00002  * Copyright 2003, 2004, 2005, 2006 Martin Fuchs
00003  *
00004  * This library is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU Lesser General Public
00006  * License as published by the Free Software Foundation; either
00007  * version 2.1 of the License, or (at your option) any later version.
00008  *
00009  * This library is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * Lesser General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU Lesser General Public
00015  * License along with this library; if not, write to the Free Software
00016  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00017  */
00018 
00019 
00020  //
00021  // Explorer clone
00022  //
00023  // shellfs.cpp
00024  //
00025  // Martin Fuchs, 23.07.2003
00026  //
00027 
00028 
00029 #include <precomp.h>
00030 
00031 //#include "shellfs.h"
00032 //#include "winfs.h"
00033 
00034 #include <shlwapi.h>
00035 
00036 
00037 bool ShellDirectory::fill_w32fdata_shell(LPCITEMIDLIST pidl, SFGAOF attribs, WIN32_FIND_DATA* pw32fdata, BY_HANDLE_FILE_INFORMATION* pbhfi, bool do_access)
00038 {
00039     CONTEXT("ShellDirectory::fill_w32fdata_shell()");
00040 
00041     bool bhfi_valid = false;
00042 
00043     if (do_access && !( (attribs&SFGAO_FILESYSTEM) && SUCCEEDED(
00044                 SHGetDataFromIDList(_folder, pidl, SHGDFIL_FINDDATA, pw32fdata, sizeof(WIN32_FIND_DATA))) )) {
00045         WIN32_FILE_ATTRIBUTE_DATA fad;
00046         IDataObject* pDataObj;
00047 
00048         STGMEDIUM medium = {0, {0}, 0};
00049         FORMATETC fmt = {g_Globals._cfStrFName, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
00050 
00051         HRESULT hr = _folder->GetUIObjectOf(0, 1, &pidl, IID_IDataObject, 0, (LPVOID*)&pDataObj);
00052 
00053         if (SUCCEEDED(hr)) {
00054             hr = pDataObj->GetData(&fmt, &medium);
00055 
00056             pDataObj->Release();
00057 
00058             if (SUCCEEDED(hr)) {
00059                 LPCTSTR path = (LPCTSTR)GlobalLock(medium.UNION_MEMBER(hGlobal));
00060 
00061                 if (path) {
00062                      // fill with drive names "C:", ...
00063                     assert(_tcslen(path) < GlobalSize(medium.UNION_MEMBER(hGlobal)));
00064                     _tcscpy(pw32fdata->cFileName, path);
00065 
00066                     UINT sem_org = SetErrorMode(SEM_FAILCRITICALERRORS);
00067 
00068                     if (GetFileAttributesEx(path, GetFileExInfoStandard, &fad)) {
00069                         pw32fdata->dwFileAttributes = fad.dwFileAttributes;
00070                         pw32fdata->ftCreationTime = fad.ftCreationTime;
00071                         pw32fdata->ftLastAccessTime = fad.ftLastAccessTime;
00072                         pw32fdata->ftLastWriteTime = fad.ftLastWriteTime;
00073 
00074                         if (!(fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
00075                              // copy file size
00076                             pw32fdata->nFileSizeLow = fad.nFileSizeLow;
00077                             pw32fdata->nFileSizeHigh = fad.nFileSizeHigh;
00078                         } else {
00079                              // ignore FILE_ATTRIBUTE_HIDDEN attribute of NTFS drives - this would hide those drives in ShellBrowser
00080                             if (pw32fdata->dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) {
00081                                 if (path[1]==':' && path[2]=='\\' && !path[3])  // Is it a drive path?
00082                                     pw32fdata->dwFileAttributes &= ~FILE_ATTRIBUTE_HIDDEN;
00083                             }
00084                         }
00085                     }
00086 
00087                     HANDLE hFile = CreateFile(path, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
00088                                                 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
00089 
00090                     if (hFile != INVALID_HANDLE_VALUE) {
00091                         if (GetFileInformationByHandle(hFile, pbhfi))
00092                             bhfi_valid = true;
00093 
00094                         CloseHandle(hFile);
00095                     }
00096 
00097                     SetErrorMode(sem_org);
00098 
00099                     GlobalUnlock(medium.UNION_MEMBER(hGlobal));
00100                     GlobalFree(medium.UNION_MEMBER(hGlobal));
00101                 }
00102             }
00103         }
00104     }
00105 
00106     if (!do_access || !(attribs&SFGAO_FILESYSTEM))  // Archiv files should not be displayed as folders in explorer view.
00107         if (attribs & (SFGAO_FOLDER|SFGAO_HASSUBFOLDER))
00108             pw32fdata->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
00109 
00110     if (attribs & SFGAO_READONLY)
00111         pw32fdata->dwFileAttributes |= FILE_ATTRIBUTE_READONLY;
00112 
00113     if (attribs & SFGAO_COMPRESSED)
00114         pw32fdata->dwFileAttributes |= FILE_ATTRIBUTE_COMPRESSED;
00115 
00116     return bhfi_valid;
00117 }
00118 
00119 
00120 ShellPath ShellEntry::create_absolute_pidl() const
00121 {
00122     CONTEXT("ShellEntry::create_absolute_pidl()");
00123 
00124     if (_up)
00125     {
00126         if (_up->_etype == ET_SHELL) {
00127             ShellDirectory* dir = static_cast<ShellDirectory*>(_up);
00128 
00129             if (dir->_pidl->mkid.cb)    // Caching of absolute PIDLs could enhance performance.
00130                 return _pidl.create_absolute_pidl(dir->create_absolute_pidl());
00131         } else
00132             return _pidl.create_absolute_pidl(_up->create_absolute_pidl());
00133     }
00134     return _pidl;
00135 }
00136 
00137 
00138  // get full path of a shell entry
00139 bool ShellEntry::get_path(PTSTR path, size_t path_count) const
00140 {
00141     if (!path || path_count==0)
00142         return false;
00143 /*
00144     path[0] = TEXT('\0');
00145 
00146     if (FAILED(path_from_pidl(get_parent_folder(), &*_pidl, path, path_count)))
00147         return false;
00148 */
00149     FileSysShellPath fs_path(create_absolute_pidl());
00150     LPCTSTR ret = fs_path;
00151 
00152     if (ret) {
00153         lstrcpyn(path, ret, path_count);
00154         return true;
00155     } else
00156         return false;
00157 }
00158 
00159 
00160  // get full path of a shell folder
00161 bool ShellDirectory::get_path(PTSTR path, size_t path_count) const
00162 {
00163     CONTEXT("ShellDirectory::get_path()");
00164 
00165     if (!path || path_count==0)
00166         return false;
00167 
00168     path[0] = TEXT('\0');
00169 
00170     if (_folder.empty())
00171         return false;
00172 
00173     SFGAOF attribs = SFGAO_FILESYSTEM;
00174 
00175     if (FAILED(const_cast<ShellFolder&>(_folder)->GetAttributesOf(1, (LPCITEMIDLIST*)&_pidl, &attribs)))
00176         return false;
00177 
00178     if (!(attribs & SFGAO_FILESYSTEM))
00179         return false;
00180 
00181     if (FAILED(path_from_pidl(get_parent_folder(), &*_pidl, path, path_count)))
00182         return false;
00183 
00184     return true;
00185 }
00186 
00187 
00188 BOOL ShellEntry::launch_entry(HWND hwnd, UINT nCmdShow)
00189 {
00190     CONTEXT("ShellEntry::launch_entry()");
00191 
00192     SHELLEXECUTEINFO shexinfo;
00193 
00194     shexinfo.cbSize = sizeof(SHELLEXECUTEINFO);
00195     shexinfo.fMask = SEE_MASK_INVOKEIDLIST; // SEE_MASK_IDLIST is also possible.
00196     shexinfo.hwnd = hwnd;
00197     shexinfo.lpVerb = NULL;
00198     shexinfo.lpFile = NULL;
00199     shexinfo.lpParameters = NULL;
00200     shexinfo.lpDirectory = NULL;
00201     shexinfo.nShow = nCmdShow;
00202 
00203     ShellPath shell_path = create_absolute_pidl();
00204     shexinfo.lpIDList = &*shell_path;
00205 
00206      // add PIDL to the recent file list
00207     SHAddToRecentDocs(SHARD_PIDL, shexinfo.lpIDList);
00208 
00209     BOOL ret = TRUE;
00210 
00211     if (!ShellExecuteEx(&shexinfo)) {
00212         display_error(hwnd, GetLastError());
00213         ret = FALSE;
00214     }
00215 
00216     return ret;
00217 }
00218 
00219 
00220 HRESULT ShellEntry::do_context_menu(HWND hwnd, LPPOINT pptScreen, CtxMenuInterfaces& cm_ifs)
00221 {
00222     ShellDirectory* dir = static_cast<ShellDirectory*>(_up);
00223 
00224     ShellFolder folder = dir? dir->_folder: GetDesktopFolder();
00225     LPCITEMIDLIST pidl = _pidl;
00226 
00227     return ShellFolderContextMenu(folder, hwnd, 1, &pidl, pptScreen->x, pptScreen->y, cm_ifs);
00228 }
00229 
00230 
00231 HRESULT ShellEntry::GetUIObjectOf(HWND hWnd, REFIID riid, LPVOID* ppvOut)
00232 {
00233     LPCITEMIDLIST pidl = _pidl;
00234 
00235     return get_parent_folder()->GetUIObjectOf(hWnd, 1, &pidl, riid, NULL, ppvOut);
00236 }
00237 
00238 
00239 ShellFolder Entry::get_shell_folder() const
00240 {
00241     return ShellFolder(create_absolute_pidl());
00242 }
00243 
00244 ShellFolder ShellEntry::get_shell_folder() const
00245 {
00246     return get_parent_folder();
00247 }
00248 
00249 ShellFolder ShellDirectory::get_shell_folder() const
00250 {
00251     return _folder;
00252 }
00253 
00254 
00255 void ShellDirectory::read_directory(int scan_flags)
00256 {
00257     CONTEXT("ShellDirectory::read_directory()");
00258 
00259     int level = _level + 1;
00260 
00261     Entry* first_entry = NULL;
00262     Entry* last = NULL;
00263 
00264     /*if (_folder.empty())
00265         return;*/
00266 
00267 #ifndef _NO_WIN_FS
00268     TCHAR buffer[_MAX_PATH+_MAX_FNAME];
00269 
00270     if (!(scan_flags&SCAN_NO_FILESYSTEM) && get_path(buffer, COUNTOF(buffer)) && _tcsncmp(buffer,TEXT("::{"),3)) {
00271         Entry* entry = NULL;    // eliminate useless GCC warning by initializing entry
00272 
00273         LPTSTR p = buffer + _tcslen(buffer);
00274 
00275         lstrcpy(p, TEXT("\\*"));
00276 
00277         WIN32_FIND_DATA w32fd;
00278         HANDLE hFind = FindFirstFile(buffer, &w32fd);
00279 
00280         if (hFind != INVALID_HANDLE_VALUE) {
00281             do {
00282                  // ignore hidden files (usefull in the start menu)
00283                 if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
00284                     continue;
00285 
00286                  // ignore directory entries "." and ".."
00287                 if ((w32fd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) &&
00288                     w32fd.cFileName[0]==TEXT('.') &&
00289                     (w32fd.cFileName[1]==TEXT('\0') ||
00290                     (w32fd.cFileName[1]==TEXT('.') && w32fd.cFileName[2]==TEXT('\0'))))
00291                     continue;
00292 
00293                 lstrcpy(p+1, w32fd.cFileName);
00294 
00295                 if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
00296                     entry = new WinDirectory(this, buffer);
00297                 else
00298                     entry = new WinEntry(this);
00299 
00300                 if (!first_entry)
00301                     first_entry = entry;
00302 
00303                 if (last)
00304                     last->_next = entry;
00305 
00306                 memcpy(&entry->_data, &w32fd, sizeof(WIN32_FIND_DATA));
00307 
00308                 entry->_level = level;
00309 
00310                 if (!(scan_flags & SCAN_DONT_ACCESS)) {
00311                     HANDLE hFile = CreateFile(buffer, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
00312                                                 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
00313 
00314                     if (hFile != INVALID_HANDLE_VALUE) {
00315                         if (GetFileInformationByHandle(hFile, &entry->_bhfi))
00316                             entry->_bhfi_valid = true;
00317 
00318                         if (ScanNTFSStreams(entry, hFile))
00319                             entry->_scanned = true; // There exist named NTFS sub-streams in this file.
00320 
00321                         CloseHandle(hFile);
00322                     }
00323                 }
00324 
00325                  // set file type name
00326                 LPCTSTR ext = g_Globals._ftype_mgr.set_type(entry);
00327 
00328                 DWORD attribs = SFGAO_FILESYSTEM;
00329 
00330                 if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
00331                     attribs |= SFGAO_FOLDER|SFGAO_HASSUBFOLDER;
00332 
00333                 if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
00334                     attribs |= SFGAO_READONLY;
00335 
00336                 //if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
00337                 //  attribs |= SFGAO_HIDDEN;
00338 
00339                 if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED)
00340                     attribs |= SFGAO_COMPRESSED;
00341 
00342                 if (ext && !_tcsicmp(ext, _T(".lnk"))) {
00343                     attribs |= SFGAO_LINK;
00344                     w32fd.dwFileAttributes |= ATTRIBUTE_SYMBOLIC_LINK;
00345                 }
00346 
00347                 entry->_shell_attribs = attribs;
00348 
00349                 if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
00350                     entry->_icon_id = ICID_FOLDER;
00351                 else if (!(scan_flags & SCAN_DONT_EXTRACT_ICONS))
00352                     entry->_icon_id = entry->safe_extract_icon();   // Assume small icon, we can extract the large icon later on demand.
00353 
00354                 last = entry;
00355             } while(FindNextFile(hFind, &w32fd));
00356 
00357             FindClose(hFind);
00358         }
00359     }
00360     else // SCAN_NO_FILESYSTEM
00361 #endif
00362     {
00363         ShellItemEnumerator enumerator(_folder, SHCONTF_FOLDERS|SHCONTF_NONFOLDERS|SHCONTF_INCLUDEHIDDEN|SHCONTF_SHAREABLE|SHCONTF_STORAGE);
00364 
00365         TCHAR name[MAX_PATH];
00366         TCHAR path[MAX_PATH];
00367         HRESULT hr_next = S_OK;
00368 
00369         if (enumerator)
00370           do {
00371 #define FETCH_ITEM_COUNT    32
00372             LPITEMIDLIST pidls[FETCH_ITEM_COUNT];
00373             ULONG cnt = 0;
00374 
00375             memset(pidls, 0, sizeof(pidls));
00376 
00377             hr_next = enumerator->Next(FETCH_ITEM_COUNT, pidls, &cnt);
00378 
00379             /* don't break yet now: Registry Explorer Plugin returns E_FAIL!
00380             if (!SUCCEEDED(hr_next))
00381                 break; */
00382 
00383             if (hr_next == S_FALSE)
00384                 break;
00385 
00386             for(ULONG n=0; n<cnt; ++n) {
00387                 WIN32_FIND_DATA w32fd;
00388                 BY_HANDLE_FILE_INFORMATION bhfi;
00389                 bool bhfi_valid = false;
00390 
00391                 memset(&w32fd, 0, sizeof(WIN32_FIND_DATA));
00392 
00393                 SFGAOF attribs_before = ~SFGAO_READONLY & ~SFGAO_VALIDATE;
00394                 SFGAOF attribs = attribs_before;
00395                 HRESULT hr = _folder->GetAttributesOf(1, (LPCITEMIDLIST*)&pidls[n], &attribs);
00396                 bool removeable = false;
00397 
00398                 if (SUCCEEDED(hr) && attribs!=attribs_before) {
00399                      // avoid accessing floppy drives when browsing "My Computer"
00400                     if (attribs & SFGAO_REMOVABLE) {
00401                         attribs |= SFGAO_HASSUBFOLDER;
00402                         removeable = true;
00403                     } else if (!(scan_flags & SCAN_DONT_ACCESS)) {
00404                         SFGAOF attribs2 = SFGAO_READONLY;
00405 
00406                         HRESULT hr = _folder->GetAttributesOf(1, (LPCITEMIDLIST*)&pidls[n], &attribs2);
00407 
00408                         if (SUCCEEDED(hr))
00409                             attribs |= attribs2;
00410                     }
00411                 } else
00412                     attribs = 0;
00413 
00414                 bhfi_valid = fill_w32fdata_shell(pidls[n], attribs, &w32fd, &bhfi,
00415                                                  !(scan_flags&SCAN_DONT_ACCESS) && !removeable);
00416 
00417                 try {
00418                     Entry* entry = NULL;    // eliminate useless GCC warning by initializing entry
00419 
00420                     if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
00421                         entry = new ShellDirectory(this, pidls[n], _hwnd);
00422                     else
00423                         entry = new ShellEntry(this, pidls[n]);
00424 
00425                     if (!first_entry)
00426                         first_entry = entry;
00427 
00428                     if (last)
00429                         last->_next = entry;
00430 
00431                     memcpy(&entry->_data, &w32fd, sizeof(WIN32_FIND_DATA));
00432 
00433                     if (bhfi_valid)
00434                         memcpy(&entry->_bhfi, &bhfi, sizeof(BY_HANDLE_FILE_INFORMATION));
00435 
00436                      // store path in entry->_data.cFileName in case fill_w32fdata_shell() didn't already fill it
00437                     if (!entry->_data.cFileName[0])
00438                         if (SUCCEEDED(path_from_pidl(_folder, pidls[n], path, COUNTOF(path))))
00439                             _tcscpy(entry->_data.cFileName, path);
00440 
00441                     if (SUCCEEDED(name_from_pidl(_folder, pidls[n], name, COUNTOF(name), SHGDN_INFOLDER|0x2000/*0x2000=SHGDN_INCLUDE_NONFILESYS*/))) {
00442                         if (!entry->_data.cFileName[0])
00443                             _tcscpy(entry->_data.cFileName, name);
00444                         else if (_tcscmp(entry->_display_name, name))
00445                             entry->_display_name = _tcsdup(name);   // store display name separate from file name; sort display by file name
00446                     }
00447 
00448                     if (attribs & SFGAO_LINK)
00449                         w32fd.dwFileAttributes |= ATTRIBUTE_SYMBOLIC_LINK;
00450 
00451                     entry->_level = level;
00452                     entry->_shell_attribs = attribs;
00453                     entry->_bhfi_valid = bhfi_valid;
00454 
00455                      // set file type name
00456                     g_Globals._ftype_mgr.set_type(entry);
00457 
00458                      // get icons for files and virtual objects
00459                     if (!(entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ||
00460                         !(attribs & SFGAO_FILESYSTEM)) {
00461                         if (!(scan_flags & SCAN_DONT_EXTRACT_ICONS))
00462                             entry->_icon_id = entry->safe_extract_icon();   // Assume small icon, we can extract the large icon later on demand.
00463                     } else if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
00464                         entry->_icon_id = ICID_FOLDER;
00465                     else
00466                         entry->_icon_id = ICID_NONE;    // don't try again later
00467 
00468                     last = entry;
00469                 } catch(COMException& e) {
00470                     HandleException(e, _hwnd);
00471                 }
00472             }
00473         } while(SUCCEEDED(hr_next));
00474     }
00475 
00476     if (last)
00477         last->_next = NULL;
00478 
00479     _down = first_entry;
00480     _scanned = true;
00481 }
00482 
00483 const void* ShellDirectory::get_next_path_component(const void* p) const
00484 {
00485     LPITEMIDLIST pidl = (LPITEMIDLIST)p;
00486 
00487     if (!pidl || !pidl->mkid.cb)
00488         return NULL;
00489 
00490      // go to next element
00491     pidl = (LPITEMIDLIST)((LPBYTE)pidl+pidl->mkid.cb);
00492 
00493     return pidl;
00494 }
00495 
00496 Entry* ShellDirectory::find_entry(const void* p)
00497 {
00498     LPITEMIDLIST pidl = (LPITEMIDLIST) p;
00499 
00500      // handle special case of empty trailing id list entry
00501     if (!pidl->mkid.cb)
00502         return this;
00503 
00504     for(Entry*entry=_down; entry; entry=entry->_next)
00505         if (entry->_etype == ET_SHELL) {
00506             ShellEntry* se = static_cast<ShellEntry*>(entry);
00507 
00508             if (se->_pidl && se->_pidl->mkid.cb==pidl->mkid.cb && !memcmp(se->_pidl, pidl, se->_pidl->mkid.cb))
00509                 return entry;
00510         } else {
00511             const ShellPath& sp = entry->create_absolute_pidl();
00512             static DynamicFct<LPITEMIDLIST(WINAPI*)(LPCITEMIDLIST)> ILFindLastID(TEXT("SHELL32"), "ILFindLastID");
00513 
00514             if (ILFindLastID) {
00515                 LPCITEMIDLIST entry_pidl = (*ILFindLastID)(sp);
00516 
00517                 if (entry_pidl && entry_pidl->mkid.cb==pidl->mkid.cb && !memcmp(entry_pidl, pidl, entry_pidl->mkid.cb))
00518                     return entry;
00519             }
00520         }
00521 
00522     return NULL;
00523 }
00524 
00525 int ShellDirectory::extract_icons(ICONCACHE_FLAGS flags)
00526 {
00527     int cnt = 0;
00528 
00529     for(Entry*entry=_down; entry; entry=entry->_next)
00530         if (entry->_icon_id == ICID_UNKNOWN) {
00531             entry->_icon_id = entry->extract_icon(flags);
00532 
00533             if (entry->_icon_id != ICID_NONE)
00534                 ++cnt;
00535         }
00536 
00537     return cnt;
00538 }

ROS Explorer Source Code Documentation
generated on 02.01.2013 by doxygen

ROS Explorer Homepage