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 bool ShellDirectory::fill_w32fdata_shell(LPCITEMIDLIST pidl
, SFGAOF attribs
, WIN32_FIND_DATA
* pw32fdata
, BY_HANDLE_FILE_INFORMATION
* pbhfi
, bool do_access
)
33 CONTEXT("ShellDirectory::fill_w32fdata_shell()");
35 bool bhfi_valid
= false;
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
;
42 STGMEDIUM medium
= {0, {0}, 0};
43 FORMATETC fmt
= {(USHORT
)g_Globals
._cfStrFName
, 0, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
};
45 HRESULT hr
= _folder
->GetUIObjectOf(0, 1, &pidl
, IID_IDataObject
, 0, (LPVOID
*)&pDataObj
);
48 hr
= pDataObj
->GetData(&fmt
, &medium
);
53 LPCTSTR path
= (LPCTSTR
)GlobalLock(medium
.UNION_MEMBER(hGlobal
));
56 // fill with drive names "C:", ...
57 assert(_tcslen(path
) < GlobalSize(medium
.UNION_MEMBER(hGlobal
)));
58 _tcscpy(pw32fdata
->cFileName
, path
);
60 UINT sem_org
= SetErrorMode(SEM_FAILCRITICALERRORS
);
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
;
68 if (!(fad
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)) {
70 pw32fdata
->nFileSizeLow
= fad
.nFileSizeLow
;
71 pw32fdata
->nFileSizeHigh
= fad
.nFileSizeHigh
;
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
;
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);
84 if (hFile
!= INVALID_HANDLE_VALUE
) {
85 if (GetFileInformationByHandle(hFile
, pbhfi
))
91 SetErrorMode(sem_org
);
93 GlobalUnlock(medium
.UNION_MEMBER(hGlobal
));
94 GlobalFree(medium
.UNION_MEMBER(hGlobal
));
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
;
104 if (attribs
& SFGAO_READONLY
)
105 pw32fdata
->dwFileAttributes
|= FILE_ATTRIBUTE_READONLY
;
107 if (attribs
& SFGAO_COMPRESSED
)
108 pw32fdata
->dwFileAttributes
|= FILE_ATTRIBUTE_COMPRESSED
;
114 ShellPath
ShellEntry::create_absolute_pidl() const
116 CONTEXT("ShellEntry::create_absolute_pidl()");
120 if (_up
->_etype
== ET_SHELL
) {
121 ShellDirectory
* dir
= static_cast<ShellDirectory
*>(_up
);
123 if (dir
->_pidl
->mkid
.cb
) // Caching of absolute PIDLs could enhance performance.
124 return _pidl
.create_absolute_pidl(dir
->create_absolute_pidl());
126 return _pidl
.create_absolute_pidl(_up
->create_absolute_pidl());
132 // get full path of a shell entry
133 bool ShellEntry::get_path(PTSTR path
, size_t path_count
) const
135 if (!path
|| path_count
==0)
138 path[0] = TEXT('\0');
140 if (FAILED(path_from_pidl(get_parent_folder(), &*_pidl, path, path_count)))
143 FileSysShellPath
fs_path(create_absolute_pidl());
144 LPCTSTR ret
= fs_path
;
147 lstrcpyn(path
, ret
, path_count
);
154 // get full path of a shell folder
155 bool ShellDirectory::get_path(PTSTR path
, size_t path_count
) const
157 CONTEXT("ShellDirectory::get_path()");
159 if (!path
|| path_count
==0)
162 path
[0] = TEXT('\0');
167 SFGAOF attribs
= SFGAO_FILESYSTEM
;
169 // Split pidl into current and parent folder PIDLs
170 ShellPath pidlParent
, pidlFolder
;
171 _pidl
.split(pidlParent
, pidlFolder
);
173 if (FAILED(const_cast<ShellFolder
&>(_folder
)->GetAttributesOf(1, (LPCITEMIDLIST
*)&pidlFolder
, &attribs
)))
176 if (!(attribs
& SFGAO_FILESYSTEM
))
179 if (FAILED(path_from_pidl(get_parent_folder(), &*_pidl
, path
, path_count
)))
186 BOOL
ShellEntry::launch_entry(HWND hwnd
, UINT nCmdShow
)
188 CONTEXT("ShellEntry::launch_entry()");
190 SHELLEXECUTEINFO shexinfo
;
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
;
201 ShellPath shell_path
= create_absolute_pidl();
202 shexinfo
.lpIDList
= &*shell_path
;
204 // add PIDL to the recent file list
205 SHAddToRecentDocs(SHARD_PIDL
, shexinfo
.lpIDList
);
209 if (!ShellExecuteEx(&shexinfo
)) {
210 display_error(hwnd
, GetLastError());
218 HRESULT
ShellEntry::do_context_menu(HWND hwnd
, const POINT
& pptScreen
, CtxMenuInterfaces
& cm_ifs
)
220 ShellDirectory
* dir
= static_cast<ShellDirectory
*>(_up
);
222 ShellFolder folder
= dir
? dir
->_folder
: GetDesktopFolder();
223 LPCITEMIDLIST pidl
= _pidl
;
225 return ShellFolderContextMenu(folder
, hwnd
, 1, &pidl
, pptScreen
.x
, pptScreen
.y
, cm_ifs
);
229 HRESULT
ShellEntry::GetUIObjectOf(HWND hWnd
, REFIID riid
, LPVOID
* ppvOut
)
231 LPCITEMIDLIST pidl
= _pidl
;
233 return get_parent_folder()->GetUIObjectOf(hWnd
, 1, &pidl
, riid
, NULL
, ppvOut
);
237 ShellFolder
Entry::get_shell_folder() const
239 return ShellFolder(create_absolute_pidl());
242 ShellFolder
ShellEntry::get_shell_folder() const
244 return get_parent_folder();
247 ShellFolder
ShellDirectory::get_shell_folder() const
253 void ShellDirectory::read_directory(int scan_flags
)
255 CONTEXT("ShellDirectory::read_directory()");
257 int level
= _level
+ 1;
259 Entry
* first_entry
= NULL
;
262 /*if (_folder.empty())
266 TCHAR buffer
[_MAX_PATH
+_MAX_FNAME
];
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
271 LPTSTR p
= buffer
+ _tcslen(buffer
);
273 lstrcpy(p
, TEXT("\\*"));
275 WIN32_FIND_DATA w32fd
;
276 HANDLE hFind
= FindFirstFile(buffer
, &w32fd
);
278 if (hFind
!= INVALID_HANDLE_VALUE
) {
280 // ignore hidden files (usefull in the start menu)
281 if (w32fd
.dwFileAttributes
& FILE_ATTRIBUTE_HIDDEN
)
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'))))
291 lstrcpy(p
+1, w32fd
.cFileName
);
293 if (w32fd
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
294 entry
= new WinDirectory(this, buffer
);
296 entry
= new WinEntry(this);
304 memcpy(&entry
->_data
, &w32fd
, sizeof(WIN32_FIND_DATA
));
306 entry
->_level
= level
;
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);
312 if (hFile
!= INVALID_HANDLE_VALUE
) {
313 if (GetFileInformationByHandle(hFile
, &entry
->_bhfi
))
314 entry
->_bhfi_valid
= true;
316 #ifdef BACKUP_READ_IMPLEMENTED
317 if (ScanNTFSStreams(entry
, hFile
))
318 entry
->_scanned
= true; // There exist named NTFS sub-streams in this file.
325 // set file type name
326 LPCTSTR ext
= g_Globals
._ftype_mgr
.set_type(entry
);
328 DWORD attribs
= SFGAO_FILESYSTEM
;
330 if (w32fd
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
331 attribs
|= SFGAO_FOLDER
|SFGAO_HASSUBFOLDER
;
333 if (w32fd
.dwFileAttributes
& FILE_ATTRIBUTE_READONLY
)
334 attribs
|= SFGAO_READONLY
;
336 //if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
337 // attribs |= SFGAO_HIDDEN;
339 if (w32fd
.dwFileAttributes
& FILE_ATTRIBUTE_COMPRESSED
)
340 attribs
|= SFGAO_COMPRESSED
;
342 if (ext
&& !_tcsicmp(ext
, _T(".lnk"))) {
343 attribs
|= SFGAO_LINK
;
344 w32fd
.dwFileAttributes
|= ATTRIBUTE_SYMBOLIC_LINK
;
347 entry
->_shell_attribs
= attribs
;
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.
355 } while(FindNextFile(hFind
, &w32fd
));
360 else // SCAN_NO_FILESYSTEM
363 ShellItemEnumerator
enumerator(_folder
, SHCONTF_FOLDERS
|SHCONTF_NONFOLDERS
|SHCONTF_INCLUDEHIDDEN
|SHCONTF_SHAREABLE
|SHCONTF_STORAGE
);
365 TCHAR name
[MAX_PATH
];
366 TCHAR path
[MAX_PATH
];
367 HRESULT hr_next
= S_OK
;
370 #define FETCH_ITEM_COUNT 32
371 LPITEMIDLIST pidls
[FETCH_ITEM_COUNT
];
374 memset(pidls
, 0, sizeof(pidls
));
376 hr_next
= enumerator
->Next(FETCH_ITEM_COUNT
, pidls
, &cnt
);
378 /* don't break yet now: Registry Explorer Plugin returns E_FAIL!
379 if (!SUCCEEDED(hr_next))
382 if (hr_next
== S_FALSE
)
385 for(ULONG n
=0; n
<cnt
; ++n
) {
386 WIN32_FIND_DATA w32fd
;
387 BY_HANDLE_FILE_INFORMATION bhfi
;
388 bool bhfi_valid
= false;
390 memset(&w32fd
, 0, sizeof(WIN32_FIND_DATA
));
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;
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
;
402 } else if (!(scan_flags
& SCAN_DONT_ACCESS
)) {
403 SFGAOF attribs2
= SFGAO_READONLY
;
405 HRESULT hr
= _folder
->GetAttributesOf(1, (LPCITEMIDLIST
*)&pidls
[n
], &attribs2
);
413 bhfi_valid
= fill_w32fdata_shell(pidls
[n
], attribs
, &w32fd
, &bhfi
,
414 !(scan_flags
&SCAN_DONT_ACCESS
) && !removeable
);
417 Entry
* entry
= NULL
; // eliminate useless GCC warning by initializing entry
419 if (w32fd
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
420 entry
= new ShellDirectory(this, pidls
[n
], _hwnd
);
422 entry
= new ShellEntry(this, pidls
[n
]);
430 memcpy(&entry
->_data
, &w32fd
, sizeof(WIN32_FIND_DATA
));
433 memcpy(&entry
->_bhfi
, &bhfi
, sizeof(BY_HANDLE_FILE_INFORMATION
));
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
);
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
447 if (attribs
& SFGAO_LINK
)
448 w32fd
.dwFileAttributes
|= ATTRIBUTE_SYMBOLIC_LINK
;
450 entry
->_level
= level
;
451 entry
->_shell_attribs
= attribs
;
452 entry
->_bhfi_valid
= bhfi_valid
;
454 // set file type name
455 g_Globals
._ftype_mgr
.set_type(entry
);
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
;
465 entry
->_icon_id
= ICID_NONE
; // don't try again later
468 } catch(COMException
& e
) {
469 HandleException(e
, _hwnd
);
472 } while(SUCCEEDED(hr_next
));
482 const void* ShellDirectory::get_next_path_component(const void* p
) const
484 LPITEMIDLIST pidl
= (LPITEMIDLIST
)p
;
486 if (!pidl
|| !pidl
->mkid
.cb
)
489 // go to next element
490 pidl
= (LPITEMIDLIST
)((LPBYTE
)pidl
+pidl
->mkid
.cb
);
495 Entry
* ShellDirectory::find_entry(const void* p
)
497 LPITEMIDLIST pidl
= (LPITEMIDLIST
) p
;
499 // handle special case of empty trailing id list entry
503 for(Entry
*entry
=_down
; entry
; entry
=entry
->_next
)
504 if (entry
->_etype
== ET_SHELL
) {
505 ShellEntry
* se
= static_cast<ShellEntry
*>(entry
);
507 if (se
->_pidl
&& se
->_pidl
->mkid
.cb
==pidl
->mkid
.cb
&& !memcmp(se
->_pidl
, pidl
, se
->_pidl
->mkid
.cb
))
510 const ShellPath
& sp
= entry
->create_absolute_pidl();
511 static DynamicFct
<LPITEMIDLIST(WINAPI
*)(LPCITEMIDLIST
)> ILFindLastID(TEXT("SHELL32"), "ILFindLastID");
514 LPCITEMIDLIST entry_pidl
= (*ILFindLastID
)(sp
);
516 if (entry_pidl
&& entry_pidl
->mkid
.cb
==pidl
->mkid
.cb
&& !memcmp(entry_pidl
, pidl
, entry_pidl
->mkid
.cb
))
524 int ShellDirectory::extract_icons(ICONCACHE_FLAGS flags
)
528 for(Entry
*entry
=_down
; entry
; entry
=entry
->_next
)
529 if (entry
->_icon_id
== ICID_UNKNOWN
) {
530 entry
->_icon_id
= entry
->extract_icon(flags
);
532 if (entry
->_icon_id
!= ICID_NONE
)