2 * Copyright 2003, 2004, 2005, 2006 Martin Fuchs
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.
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.
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
25 // Martin Fuchs, 23.07.2003
31 //#include "shellfs.h"
37 bool ShellDirectory::fill_w32fdata_shell(LPCITEMIDLIST pidl
, SFGAOF attribs
, WIN32_FIND_DATA
* pw32fdata
, BY_HANDLE_FILE_INFORMATION
* pbhfi
, bool do_access
)
39 CONTEXT("ShellDirectory::fill_w32fdata_shell()");
41 bool bhfi_valid
= false;
43 if (do_access
&& !( (attribs
&SFGAO_FILESYSTEM
) && SUCCEEDED(
44 SHGetDataFromIDList(_folder
, pidl
, SHGDFIL_FINDDATA
, pw32fdata
, sizeof(WIN32_FIND_DATA
))) )) {
45 WIN32_FILE_ATTRIBUTE_DATA fad
;
46 IDataObject
* pDataObj
;
48 STGMEDIUM medium
= {0, {0}, 0};
49 FORMATETC fmt
= {g_Globals
._cfStrFName
, 0, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
};
51 HRESULT hr
= _folder
->GetUIObjectOf(0, 1, &pidl
, IID_IDataObject
, 0, (LPVOID
*)&pDataObj
);
54 hr
= pDataObj
->GetData(&fmt
, &medium
);
59 LPCTSTR path
= (LPCTSTR
)GlobalLock(medium
.UNION_MEMBER(hGlobal
));
62 // fill with drive names "C:", ...
63 assert(_tcslen(path
) < GlobalSize(medium
.UNION_MEMBER(hGlobal
)));
64 _tcscpy(pw32fdata
->cFileName
, path
);
66 UINT sem_org
= SetErrorMode(SEM_FAILCRITICALERRORS
);
68 if (GetFileAttributesEx(path
, GetFileExInfoStandard
, &fad
)) {
69 pw32fdata
->dwFileAttributes
= fad
.dwFileAttributes
;
70 pw32fdata
->ftCreationTime
= fad
.ftCreationTime
;
71 pw32fdata
->ftLastAccessTime
= fad
.ftLastAccessTime
;
72 pw32fdata
->ftLastWriteTime
= fad
.ftLastWriteTime
;
74 if (!(fad
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)) {
76 pw32fdata
->nFileSizeLow
= fad
.nFileSizeLow
;
77 pw32fdata
->nFileSizeHigh
= fad
.nFileSizeHigh
;
79 // ignore FILE_ATTRIBUTE_HIDDEN attribute of NTFS drives - this would hide those drives in ShellBrowser
80 if (pw32fdata
->dwFileAttributes
& FILE_ATTRIBUTE_HIDDEN
) {
81 if (path
[1]==':' && path
[2]=='\\' && !path
[3]) // Is it a drive path?
82 pw32fdata
->dwFileAttributes
&= ~FILE_ATTRIBUTE_HIDDEN
;
87 HANDLE hFile
= CreateFile(path
, GENERIC_READ
, FILE_SHARE_READ
|FILE_SHARE_WRITE
|FILE_SHARE_DELETE
,
88 0, OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, 0);
90 if (hFile
!= INVALID_HANDLE_VALUE
) {
91 if (GetFileInformationByHandle(hFile
, pbhfi
))
97 SetErrorMode(sem_org
);
99 GlobalUnlock(medium
.UNION_MEMBER(hGlobal
));
100 GlobalFree(medium
.UNION_MEMBER(hGlobal
));
106 if (!do_access
|| !(attribs
&SFGAO_FILESYSTEM
)) // Archiv files should not be displayed as folders in explorer view.
107 if (attribs
& (SFGAO_FOLDER
|SFGAO_HASSUBFOLDER
))
108 pw32fdata
->dwFileAttributes
|= FILE_ATTRIBUTE_DIRECTORY
;
110 if (attribs
& SFGAO_READONLY
)
111 pw32fdata
->dwFileAttributes
|= FILE_ATTRIBUTE_READONLY
;
113 if (attribs
& SFGAO_COMPRESSED
)
114 pw32fdata
->dwFileAttributes
|= FILE_ATTRIBUTE_COMPRESSED
;
120 ShellPath
ShellEntry::create_absolute_pidl() const
122 CONTEXT("ShellEntry::create_absolute_pidl()");
126 if (_up
->_etype
== ET_SHELL
) {
127 ShellDirectory
* dir
= static_cast<ShellDirectory
*>(_up
);
129 if (dir
->_pidl
->mkid
.cb
) // Caching of absolute PIDLs could enhance performance.
130 return _pidl
.create_absolute_pidl(dir
->create_absolute_pidl());
132 return _pidl
.create_absolute_pidl(_up
->create_absolute_pidl());
138 // get full path of a shell entry
139 bool ShellEntry::get_path(PTSTR path
, size_t path_count
) const
141 if (!path
|| path_count
==0)
144 path[0] = TEXT('\0');
146 if (FAILED(path_from_pidl(get_parent_folder(), &*_pidl, path, path_count)))
149 FileSysShellPath
fs_path(create_absolute_pidl());
150 LPCTSTR ret
= fs_path
;
153 lstrcpyn(path
, ret
, path_count
);
160 // get full path of a shell folder
161 bool ShellDirectory::get_path(PTSTR path
, size_t path_count
) const
163 CONTEXT("ShellDirectory::get_path()");
165 if (!path
|| path_count
==0)
168 path
[0] = TEXT('\0');
173 SFGAOF attribs
= SFGAO_FILESYSTEM
;
175 // Split pidl into current and parent folder PIDLs
176 ShellPath pidlParent
, pidlFolder
;
177 _pidl
.split(pidlParent
, pidlFolder
);
179 if (FAILED(const_cast<ShellFolder
&>(_folder
)->GetAttributesOf(1, (LPCITEMIDLIST
*)&pidlFolder
, &attribs
)))
182 if (!(attribs
& SFGAO_FILESYSTEM
))
185 if (FAILED(path_from_pidl(get_parent_folder(), &*_pidl
, path
, path_count
)))
192 BOOL
ShellEntry::launch_entry(HWND hwnd
, UINT nCmdShow
)
194 CONTEXT("ShellEntry::launch_entry()");
196 SHELLEXECUTEINFO shexinfo
;
198 shexinfo
.cbSize
= sizeof(SHELLEXECUTEINFO
);
199 shexinfo
.fMask
= SEE_MASK_INVOKEIDLIST
; // SEE_MASK_IDLIST is also possible.
200 shexinfo
.hwnd
= hwnd
;
201 shexinfo
.lpVerb
= NULL
;
202 shexinfo
.lpFile
= NULL
;
203 shexinfo
.lpParameters
= NULL
;
204 shexinfo
.lpDirectory
= NULL
;
205 shexinfo
.nShow
= nCmdShow
;
207 ShellPath shell_path
= create_absolute_pidl();
208 shexinfo
.lpIDList
= &*shell_path
;
210 // add PIDL to the recent file list
211 SHAddToRecentDocs(SHARD_PIDL
, shexinfo
.lpIDList
);
215 if (!ShellExecuteEx(&shexinfo
)) {
216 display_error(hwnd
, GetLastError());
224 HRESULT
ShellEntry::do_context_menu(HWND hwnd
, const POINT
& pptScreen
, CtxMenuInterfaces
& cm_ifs
)
226 ShellDirectory
* dir
= static_cast<ShellDirectory
*>(_up
);
228 ShellFolder folder
= dir
? dir
->_folder
: GetDesktopFolder();
229 LPCITEMIDLIST pidl
= _pidl
;
231 return ShellFolderContextMenu(folder
, hwnd
, 1, &pidl
, pptScreen
.x
, pptScreen
.y
, cm_ifs
);
235 HRESULT
ShellEntry::GetUIObjectOf(HWND hWnd
, REFIID riid
, LPVOID
* ppvOut
)
237 LPCITEMIDLIST pidl
= _pidl
;
239 return get_parent_folder()->GetUIObjectOf(hWnd
, 1, &pidl
, riid
, NULL
, ppvOut
);
243 ShellFolder
Entry::get_shell_folder() const
245 return ShellFolder(create_absolute_pidl());
248 ShellFolder
ShellEntry::get_shell_folder() const
250 return get_parent_folder();
253 ShellFolder
ShellDirectory::get_shell_folder() const
259 void ShellDirectory::read_directory(int scan_flags
)
261 CONTEXT("ShellDirectory::read_directory()");
263 int level
= _level
+ 1;
265 Entry
* first_entry
= NULL
;
268 /*if (_folder.empty())
272 TCHAR buffer
[_MAX_PATH
+_MAX_FNAME
];
274 if (!(scan_flags
&SCAN_NO_FILESYSTEM
) && get_path(buffer
, COUNTOF(buffer
)) && _tcsncmp(buffer
,TEXT("::{"),3)) {
275 Entry
* entry
= NULL
; // eliminate useless GCC warning by initializing entry
277 LPTSTR p
= buffer
+ _tcslen(buffer
);
279 lstrcpy(p
, TEXT("\\*"));
281 WIN32_FIND_DATA w32fd
;
282 HANDLE hFind
= FindFirstFile(buffer
, &w32fd
);
284 if (hFind
!= INVALID_HANDLE_VALUE
) {
286 // ignore hidden files (usefull in the start menu)
287 if (w32fd
.dwFileAttributes
& FILE_ATTRIBUTE_HIDDEN
)
290 // ignore directory entries "." and ".."
291 if ((w32fd
.dwFileAttributes
&FILE_ATTRIBUTE_DIRECTORY
) &&
292 w32fd
.cFileName
[0]==TEXT('.') &&
293 (w32fd
.cFileName
[1]==TEXT('\0') ||
294 (w32fd
.cFileName
[1]==TEXT('.') && w32fd
.cFileName
[2]==TEXT('\0'))))
297 lstrcpy(p
+1, w32fd
.cFileName
);
299 if (w32fd
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
300 entry
= new WinDirectory(this, buffer
);
302 entry
= new WinEntry(this);
310 memcpy(&entry
->_data
, &w32fd
, sizeof(WIN32_FIND_DATA
));
312 entry
->_level
= level
;
314 if (!(scan_flags
& SCAN_DONT_ACCESS
)) {
315 HANDLE hFile
= CreateFile(buffer
, GENERIC_READ
, FILE_SHARE_READ
|FILE_SHARE_WRITE
|FILE_SHARE_DELETE
,
316 0, OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, 0);
318 if (hFile
!= INVALID_HANDLE_VALUE
) {
319 if (GetFileInformationByHandle(hFile
, &entry
->_bhfi
))
320 entry
->_bhfi_valid
= true;
322 if (ScanNTFSStreams(entry
, hFile
))
323 entry
->_scanned
= true; // There exist named NTFS sub-streams in this file.
329 // set file type name
330 LPCTSTR ext
= g_Globals
._ftype_mgr
.set_type(entry
);
332 DWORD attribs
= SFGAO_FILESYSTEM
;
334 if (w32fd
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
335 attribs
|= SFGAO_FOLDER
|SFGAO_HASSUBFOLDER
;
337 if (w32fd
.dwFileAttributes
& FILE_ATTRIBUTE_READONLY
)
338 attribs
|= SFGAO_READONLY
;
340 //if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
341 // attribs |= SFGAO_HIDDEN;
343 if (w32fd
.dwFileAttributes
& FILE_ATTRIBUTE_COMPRESSED
)
344 attribs
|= SFGAO_COMPRESSED
;
346 if (ext
&& !_tcsicmp(ext
, _T(".lnk"))) {
347 attribs
|= SFGAO_LINK
;
348 w32fd
.dwFileAttributes
|= ATTRIBUTE_SYMBOLIC_LINK
;
351 entry
->_shell_attribs
= attribs
;
353 if (w32fd
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
354 entry
->_icon_id
= ICID_FOLDER
;
355 else if (!(scan_flags
& SCAN_DONT_EXTRACT_ICONS
))
356 entry
->_icon_id
= entry
->safe_extract_icon(); // Assume small icon, we can extract the large icon later on demand.
359 } while(FindNextFile(hFind
, &w32fd
));
364 else // SCAN_NO_FILESYSTEM
367 ShellItemEnumerator
enumerator(_folder
, SHCONTF_FOLDERS
|SHCONTF_NONFOLDERS
|SHCONTF_INCLUDEHIDDEN
|SHCONTF_SHAREABLE
|SHCONTF_STORAGE
);
369 TCHAR name
[MAX_PATH
];
370 TCHAR path
[MAX_PATH
];
371 HRESULT hr_next
= S_OK
;
374 #define FETCH_ITEM_COUNT 32
375 LPITEMIDLIST pidls
[FETCH_ITEM_COUNT
];
378 memset(pidls
, 0, sizeof(pidls
));
380 hr_next
= enumerator
->Next(FETCH_ITEM_COUNT
, pidls
, &cnt
);
382 /* don't break yet now: Registry Explorer Plugin returns E_FAIL!
383 if (!SUCCEEDED(hr_next))
386 if (hr_next
== S_FALSE
)
389 for(ULONG n
=0; n
<cnt
; ++n
) {
390 WIN32_FIND_DATA w32fd
;
391 BY_HANDLE_FILE_INFORMATION bhfi
;
392 bool bhfi_valid
= false;
394 memset(&w32fd
, 0, sizeof(WIN32_FIND_DATA
));
396 SFGAOF attribs_before
= ~SFGAO_READONLY
& ~SFGAO_VALIDATE
;
397 SFGAOF attribs
= attribs_before
;
398 HRESULT hr
= _folder
->GetAttributesOf(1, (LPCITEMIDLIST
*)&pidls
[n
], &attribs
);
399 bool removeable
= false;
401 if (SUCCEEDED(hr
) && attribs
!=attribs_before
) {
402 // avoid accessing floppy drives when browsing "My Computer"
403 if (attribs
& SFGAO_REMOVABLE
) {
404 attribs
|= SFGAO_HASSUBFOLDER
;
406 } else if (!(scan_flags
& SCAN_DONT_ACCESS
)) {
407 SFGAOF attribs2
= SFGAO_READONLY
;
409 HRESULT hr
= _folder
->GetAttributesOf(1, (LPCITEMIDLIST
*)&pidls
[n
], &attribs2
);
417 bhfi_valid
= fill_w32fdata_shell(pidls
[n
], attribs
, &w32fd
, &bhfi
,
418 !(scan_flags
&SCAN_DONT_ACCESS
) && !removeable
);
421 Entry
* entry
= NULL
; // eliminate useless GCC warning by initializing entry
423 if (w32fd
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
424 entry
= new ShellDirectory(this, pidls
[n
], _hwnd
);
426 entry
= new ShellEntry(this, pidls
[n
]);
434 memcpy(&entry
->_data
, &w32fd
, sizeof(WIN32_FIND_DATA
));
437 memcpy(&entry
->_bhfi
, &bhfi
, sizeof(BY_HANDLE_FILE_INFORMATION
));
439 // store path in entry->_data.cFileName in case fill_w32fdata_shell() didn't already fill it
440 if (!entry
->_data
.cFileName
[0])
441 if (SUCCEEDED(path_from_pidl(_folder
, pidls
[n
], path
, COUNTOF(path
))))
442 _tcscpy(entry
->_data
.cFileName
, path
);
444 if (SUCCEEDED(name_from_pidl(_folder
, pidls
[n
], name
, COUNTOF(name
), SHGDN_INFOLDER
|0x2000/*0x2000=SHGDN_INCLUDE_NONFILESYS*/))) {
445 if (!entry
->_data
.cFileName
[0])
446 _tcscpy(entry
->_data
.cFileName
, name
);
447 else if (_tcscmp(entry
->_display_name
, name
))
448 entry
->_display_name
= _tcsdup(name
); // store display name separate from file name; sort display by file name
451 if (attribs
& SFGAO_LINK
)
452 w32fd
.dwFileAttributes
|= ATTRIBUTE_SYMBOLIC_LINK
;
454 entry
->_level
= level
;
455 entry
->_shell_attribs
= attribs
;
456 entry
->_bhfi_valid
= bhfi_valid
;
458 // set file type name
459 g_Globals
._ftype_mgr
.set_type(entry
);
461 // get icons for files and virtual objects
462 if (!(entry
->_data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) ||
463 !(attribs
& SFGAO_FILESYSTEM
)) {
464 if (!(scan_flags
& SCAN_DONT_EXTRACT_ICONS
))
465 entry
->_icon_id
= entry
->safe_extract_icon(); // Assume small icon, we can extract the large icon later on demand.
466 } else if (entry
->_data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
467 entry
->_icon_id
= ICID_FOLDER
;
469 entry
->_icon_id
= ICID_NONE
; // don't try again later
472 } catch(COMException
& e
) {
473 HandleException(e
, _hwnd
);
476 } while(SUCCEEDED(hr_next
));
486 const void* ShellDirectory::get_next_path_component(const void* p
) const
488 LPITEMIDLIST pidl
= (LPITEMIDLIST
)p
;
490 if (!pidl
|| !pidl
->mkid
.cb
)
493 // go to next element
494 pidl
= (LPITEMIDLIST
)((LPBYTE
)pidl
+pidl
->mkid
.cb
);
499 Entry
* ShellDirectory::find_entry(const void* p
)
501 LPITEMIDLIST pidl
= (LPITEMIDLIST
) p
;
503 // handle special case of empty trailing id list entry
507 for(Entry
*entry
=_down
; entry
; entry
=entry
->_next
)
508 if (entry
->_etype
== ET_SHELL
) {
509 ShellEntry
* se
= static_cast<ShellEntry
*>(entry
);
511 if (se
->_pidl
&& se
->_pidl
->mkid
.cb
==pidl
->mkid
.cb
&& !memcmp(se
->_pidl
, pidl
, se
->_pidl
->mkid
.cb
))
514 const ShellPath
& sp
= entry
->create_absolute_pidl();
515 static DynamicFct
<LPITEMIDLIST(WINAPI
*)(LPCITEMIDLIST
)> ILFindLastID(TEXT("SHELL32"), "ILFindLastID");
518 LPCITEMIDLIST entry_pidl
= (*ILFindLastID
)(sp
);
520 if (entry_pidl
&& entry_pidl
->mkid
.cb
==pidl
->mkid
.cb
&& !memcmp(entry_pidl
, pidl
, entry_pidl
->mkid
.cb
))
528 int ShellDirectory::extract_icons(ICONCACHE_FLAGS flags
)
532 for(Entry
*entry
=_down
; entry
; entry
=entry
->_next
)
533 if (entry
->_icon_id
== ICID_UNKNOWN
) {
534 entry
->_icon_id
= entry
->extract_icon(flags
);
536 if (entry
->_icon_id
!= ICID_NONE
)