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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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
)) {
75 pw32fdata
->nFileSizeLow
= fad
.nFileSizeLow
;
76 pw32fdata
->nFileSizeHigh
= fad
.nFileSizeHigh
;
80 HANDLE hFile
= CreateFile(path
, GENERIC_READ
, FILE_SHARE_READ
|FILE_SHARE_WRITE
|FILE_SHARE_DELETE
,
81 0, OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, 0);
83 if (hFile
!= INVALID_HANDLE_VALUE
) {
84 if (GetFileInformationByHandle(hFile
, pbhfi
))
90 SetErrorMode(sem_org
);
92 GlobalUnlock(medium
.UNION_MEMBER(hGlobal
));
93 GlobalFree(medium
.UNION_MEMBER(hGlobal
));
99 if (!do_access
|| !(attribs
&SFGAO_FILESYSTEM
)) // Archiv files should not be displayed as folders in explorer view.
100 if (attribs
& (SFGAO_FOLDER
|SFGAO_HASSUBFOLDER
))
101 pw32fdata
->dwFileAttributes
|= FILE_ATTRIBUTE_DIRECTORY
;
103 if (attribs
& SFGAO_READONLY
)
104 pw32fdata
->dwFileAttributes
|= FILE_ATTRIBUTE_READONLY
;
106 if (attribs
& SFGAO_COMPRESSED
)
107 pw32fdata
->dwFileAttributes
|= FILE_ATTRIBUTE_COMPRESSED
;
113 ShellPath
ShellEntry::create_absolute_pidl() const
115 CONTEXT("ShellEntry::create_absolute_pidl()");
118 if (_up
->_etype
== ET_SHELL
) {
119 ShellDirectory
* dir
= static_cast<ShellDirectory
*>(_up
);
121 if (dir
->_pidl
->mkid
.cb
) // Caching of absolute PIDLs could enhance performance.
122 return _pidl
.create_absolute_pidl(dir
->create_absolute_pidl());
124 return _pidl
.create_absolute_pidl(_up
->create_absolute_pidl());
130 // get full path of a shell entry
131 bool ShellEntry::get_path(PTSTR path
, size_t path_count
) const
133 if (!path
|| path_count
==0)
136 path[0] = TEXT('\0');
138 if (FAILED(path_from_pidl(get_parent_folder(), &*_pidl, path, path_count)))
141 FileSysShellPath
fs_path(create_absolute_pidl());
142 LPCTSTR ret
= fs_path
;
145 lstrcpyn(path
, ret
, path_count
);
152 // get full path of a shell folder
153 bool ShellDirectory::get_path(PTSTR path
, size_t path_count
) const
155 CONTEXT("ShellDirectory::get_path()");
157 if (!path
|| path_count
==0)
160 path
[0] = TEXT('\0');
165 SFGAOF attribs
= SFGAO_FILESYSTEM
;
167 if (FAILED(const_cast<ShellFolder
&>(_folder
)->GetAttributesOf(1, (LPCITEMIDLIST
*)&_pidl
, &attribs
)))
170 if (!(attribs
& SFGAO_FILESYSTEM
))
173 if (FAILED(path_from_pidl(get_parent_folder(), &*_pidl
, path
, path_count
)))
180 BOOL
ShellEntry::launch_entry(HWND hwnd
, UINT nCmdShow
)
182 CONTEXT("ShellEntry::launch_entry()");
184 SHELLEXECUTEINFO shexinfo
;
186 shexinfo
.cbSize
= sizeof(SHELLEXECUTEINFO
);
187 shexinfo
.fMask
= SEE_MASK_INVOKEIDLIST
; // SEE_MASK_IDLIST is also possible.
188 shexinfo
.hwnd
= hwnd
;
189 shexinfo
.lpVerb
= NULL
;
190 shexinfo
.lpFile
= NULL
;
191 shexinfo
.lpParameters
= NULL
;
192 shexinfo
.lpDirectory
= NULL
;
193 shexinfo
.nShow
= nCmdShow
;
195 ShellPath shell_path
= create_absolute_pidl();
196 shexinfo
.lpIDList
= &*shell_path
;
198 // add PIDL to the recent file list
199 SHAddToRecentDocs(SHARD_PIDL
, shexinfo
.lpIDList
);
203 if (!ShellExecuteEx(&shexinfo
)) {
204 display_error(hwnd
, GetLastError());
212 HRESULT
ShellEntry::do_context_menu(HWND hwnd
, LPPOINT pptScreen
, CtxMenuInterfaces
& cm_ifs
)
214 ShellDirectory
* dir
= static_cast<ShellDirectory
*>(_up
);
216 ShellFolder folder
= dir
? dir
->_folder
: GetDesktopFolder();
217 LPCITEMIDLIST pidl
= _pidl
;
219 return ShellFolderContextMenu(folder
, hwnd
, 1, &pidl
, pptScreen
->x
, pptScreen
->y
, cm_ifs
);
223 HRESULT
ShellEntry::GetUIObjectOf(HWND hWnd
, REFIID riid
, LPVOID
* ppvOut
)
225 LPCITEMIDLIST pidl
= _pidl
;
227 return get_parent_folder()->GetUIObjectOf(hWnd
, 1, &pidl
, riid
, NULL
, ppvOut
);
231 ShellFolder
Entry::get_shell_folder() const
233 return ShellFolder(create_absolute_pidl());
236 ShellFolder
ShellEntry::get_shell_folder() const
238 return get_parent_folder();
241 ShellFolder
ShellDirectory::get_shell_folder() const
247 void ShellDirectory::read_directory(int scan_flags
)
249 CONTEXT("ShellDirectory::read_directory()");
251 int level
= _level
+ 1;
253 Entry
* first_entry
= NULL
;
256 /*if (_folder.empty())
260 TCHAR buffer
[_MAX_PATH
+_MAX_FNAME
];
262 if (!(scan_flags
&SCAN_NO_FILESYSTEM
) && get_path(buffer
, COUNTOF(buffer
)) && _tcsncmp(buffer
,TEXT("::{"),3)) {
263 Entry
* entry
= NULL
; // eliminate useless GCC warning by initializing entry
265 LPTSTR p
= buffer
+ _tcslen(buffer
);
267 lstrcpy(p
, TEXT("\\*"));
269 WIN32_FIND_DATA w32fd
;
270 HANDLE hFind
= FindFirstFile(buffer
, &w32fd
);
272 if (hFind
!= INVALID_HANDLE_VALUE
) {
274 // ignore hidden files (usefull in the start menu)
275 if (w32fd
.dwFileAttributes
& FILE_ATTRIBUTE_HIDDEN
)
278 // ignore directory entries "." and ".."
279 if ((w32fd
.dwFileAttributes
&FILE_ATTRIBUTE_DIRECTORY
) &&
280 w32fd
.cFileName
[0]==TEXT('.') &&
281 (w32fd
.cFileName
[1]==TEXT('\0') ||
282 (w32fd
.cFileName
[1]==TEXT('.') && w32fd
.cFileName
[2]==TEXT('\0'))))
285 lstrcpy(p
+1, w32fd
.cFileName
);
287 if (w32fd
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
288 entry
= new WinDirectory(this, buffer
);
290 entry
= new WinEntry(this);
298 memcpy(&entry
->_data
, &w32fd
, sizeof(WIN32_FIND_DATA
));
300 entry
->_level
= level
;
302 if (!(scan_flags
& SCAN_DONT_ACCESS
)) {
303 HANDLE hFile
= CreateFile(buffer
, GENERIC_READ
, FILE_SHARE_READ
|FILE_SHARE_WRITE
|FILE_SHARE_DELETE
,
304 0, OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, 0);
306 if (hFile
!= INVALID_HANDLE_VALUE
) {
307 if (GetFileInformationByHandle(hFile
, &entry
->_bhfi
))
308 entry
->_bhfi_valid
= true;
310 if (ScanNTFSStreams(entry
, hFile
))
311 entry
->_scanned
= true; // There exist named NTFS sub-streams in this file.
317 // set file type name
318 LPCTSTR ext
= g_Globals
._ftype_mgr
.set_type(entry
);
320 DWORD attribs
= SFGAO_FILESYSTEM
;
322 if (w32fd
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
323 attribs
|= SFGAO_FOLDER
|SFGAO_HASSUBFOLDER
;
325 if (w32fd
.dwFileAttributes
& FILE_ATTRIBUTE_READONLY
)
326 attribs
|= SFGAO_READONLY
;
328 //if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
329 // attribs |= SFGAO_HIDDEN;
331 if (w32fd
.dwFileAttributes
& FILE_ATTRIBUTE_COMPRESSED
)
332 attribs
|= SFGAO_COMPRESSED
;
334 if (ext
&& !_tcsicmp(ext
, _T(".lnk"))) {
335 attribs
|= SFGAO_LINK
;
336 w32fd
.dwFileAttributes
|= ATTRIBUTE_SYMBOLIC_LINK
;
339 entry
->_shell_attribs
= attribs
;
341 if (w32fd
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
342 entry
->_icon_id
= ICID_FOLDER
;
343 else if (!(scan_flags
& SCAN_DONT_EXTRACT_ICONS
))
344 entry
->_icon_id
= entry
->safe_extract_icon(); // Assume small icon, we can extract the large icon later on demand.
347 } while(FindNextFile(hFind
, &w32fd
));
352 else // SCAN_NO_FILESYSTEM
355 ShellItemEnumerator
enumerator(_folder
, SHCONTF_FOLDERS
|SHCONTF_NONFOLDERS
|SHCONTF_INCLUDEHIDDEN
|SHCONTF_SHAREABLE
|SHCONTF_STORAGE
);
357 TCHAR name
[MAX_PATH
];
358 HRESULT hr_next
= S_OK
;
361 #define FETCH_ITEM_COUNT 32
362 LPITEMIDLIST pidls
[FETCH_ITEM_COUNT
];
365 memset(pidls
, 0, sizeof(pidls
));
367 hr_next
= enumerator
->Next(FETCH_ITEM_COUNT
, pidls
, &cnt
);
369 /* don't break yet now: Registry Explorer Plugin returns E_FAIL!
370 if (!SUCCEEDED(hr_next))
373 if (hr_next
== S_FALSE
)
376 for(ULONG n
=0; n
<cnt
; ++n
) {
377 WIN32_FIND_DATA w32fd
;
378 BY_HANDLE_FILE_INFORMATION bhfi
;
379 bool bhfi_valid
= false;
381 memset(&w32fd
, 0, sizeof(WIN32_FIND_DATA
));
383 SFGAOF attribs_before
= ~SFGAO_READONLY
& ~SFGAO_VALIDATE
;
384 SFGAOF attribs
= attribs_before
;
385 HRESULT hr
= _folder
->GetAttributesOf(1, (LPCITEMIDLIST
*)&pidls
[n
], &attribs
);
386 bool removeable
= false;
388 if (SUCCEEDED(hr
) && attribs
!=attribs_before
) {
389 // avoid accessing floppy drives when browsing "My Computer"
390 if (attribs
& SFGAO_REMOVABLE
) {
391 attribs
|= SFGAO_HASSUBFOLDER
;
393 } else if (!(scan_flags
& SCAN_DONT_ACCESS
)) {
394 DWORD attribs2
= SFGAO_READONLY
;
396 HRESULT hr
= _folder
->GetAttributesOf(1, (LPCITEMIDLIST
*)&pidls
[n
], &attribs2
);
404 bhfi_valid
= fill_w32fdata_shell(pidls
[n
], attribs
, &w32fd
, &bhfi
,
405 !(scan_flags
&SCAN_DONT_ACCESS
) && !removeable
);
408 Entry
* entry
= NULL
; // eliminate useless GCC warning by initializing entry
410 if (w32fd
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
411 entry
= new ShellDirectory(this, pidls
[n
], _hwnd
);
413 entry
= new ShellEntry(this, pidls
[n
]);
421 memcpy(&entry
->_data
, &w32fd
, sizeof(WIN32_FIND_DATA
));
424 memcpy(&entry
->_bhfi
, &bhfi
, sizeof(BY_HANDLE_FILE_INFORMATION
));
426 if (SUCCEEDED(name_from_pidl(_folder
, pidls
[n
], name
, COUNTOF(name
), SHGDN_INFOLDER
|0x2000/*0x2000=SHGDN_INCLUDE_NONFILESYS*/))) {
427 if (!entry
->_data
.cFileName
[0])
428 _tcscpy(entry
->_data
.cFileName
, name
);
429 else if (_tcscmp(entry
->_display_name
, name
))
430 entry
->_display_name
= _tcsdup(name
); // store display name separate from file name; sort display by file name
433 if (attribs
& SFGAO_LINK
)
434 w32fd
.dwFileAttributes
|= ATTRIBUTE_SYMBOLIC_LINK
;
436 entry
->_level
= level
;
437 entry
->_shell_attribs
= attribs
;
438 entry
->_bhfi_valid
= bhfi_valid
;
440 // set file type name
441 g_Globals
._ftype_mgr
.set_type(entry
);
443 // get icons for files and virtual objects
444 if (!(entry
->_data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) ||
445 !(attribs
& SFGAO_FILESYSTEM
)) {
446 if (!(scan_flags
& SCAN_DONT_EXTRACT_ICONS
))
447 entry
->_icon_id
= entry
->safe_extract_icon(); // Assume small icon, we can extract the large icon later on demand.
448 } else if (entry
->_data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
449 entry
->_icon_id
= ICID_FOLDER
;
451 entry
->_icon_id
= ICID_NONE
; // don't try again later
454 } catch(COMException
& e
) {
455 HandleException(e
, _hwnd
);
458 } while(SUCCEEDED(hr_next
));
468 const void* ShellDirectory::get_next_path_component(const void* p
) const
470 LPITEMIDLIST pidl
= (LPITEMIDLIST
)p
;
472 if (!pidl
|| !pidl
->mkid
.cb
)
475 // go to next element
476 pidl
= (LPITEMIDLIST
)((LPBYTE
)pidl
+pidl
->mkid
.cb
);
481 Entry
* ShellDirectory::find_entry(const void* p
)
483 LPITEMIDLIST pidl
= (LPITEMIDLIST
) p
;
485 // handle special case of empty trailing id list entry
489 for(Entry
*entry
=_down
; entry
; entry
=entry
->_next
)
490 if (entry
->_etype
== ET_SHELL
) {
491 ShellEntry
* se
= static_cast<ShellEntry
*>(entry
);
493 if (se
->_pidl
&& se
->_pidl
->mkid
.cb
==pidl
->mkid
.cb
&& !memcmp(se
->_pidl
, pidl
, se
->_pidl
->mkid
.cb
))
496 const ShellPath
& sp
= entry
->create_absolute_pidl();
497 static DynamicFct
<LPITEMIDLIST(WINAPI
*)(LPCITEMIDLIST
)> ILFindLastID(TEXT("SHELL32"), "ILFindLastID");
500 LPCITEMIDLIST entry_pidl
= (*ILFindLastID
)(sp
);
502 if (entry_pidl
&& entry_pidl
->mkid
.cb
==pidl
->mkid
.cb
&& !memcmp(entry_pidl
, pidl
, entry_pidl
->mkid
.cb
))
510 int ShellDirectory::extract_icons(ICONCACHE_FLAGS flags
)
514 for(Entry
*entry
=_down
; entry
; entry
=entry
->_next
)
515 if (entry
->_icon_id
== ICID_UNKNOWN
) {
516 entry
->_icon_id
= entry
->extract_icon(flags
);
518 if (entry
->_icon_id
!= ICID_NONE
)