2 * Copyright 2003, 2004 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, MAX_PATH)))
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 void ShellDirectory::read_directory(int scan_flags
)
233 CONTEXT("ShellDirectory::read_directory()");
235 int level
= _level
+ 1;
237 Entry
* first_entry
= NULL
;
240 /*if (_folder.empty())
243 TCHAR buffer
[MAX_PATH
];
245 if ((scan_flags
&SCAN_FILESYSTEM
) && get_path(buffer
, COUNTOF(buffer
)) && _tcsncmp(buffer
,TEXT("::{"),3)) {
246 Entry
* entry
= NULL
; // eliminate useless GCC warning by initializing entry
248 LPTSTR p
= buffer
+ _tcslen(buffer
);
250 // TODO FIXME - this can overflow
251 lstrcpy(p
, TEXT("\\*"));
253 WIN32_FIND_DATA w32fd
;
254 HANDLE hFind
= FindFirstFile(buffer
, &w32fd
);
256 if (hFind
!= INVALID_HANDLE_VALUE
) {
258 // ignore hidden files (usefull in the start menu)
259 if (w32fd
.dwFileAttributes
& FILE_ATTRIBUTE_HIDDEN
)
262 // ignore directory entries "." and ".."
263 if ((w32fd
.dwFileAttributes
&FILE_ATTRIBUTE_DIRECTORY
) &&
264 w32fd
.cFileName
[0]==TEXT('.') &&
265 (w32fd
.cFileName
[1]==TEXT('\0') ||
266 (w32fd
.cFileName
[1]==TEXT('.') && w32fd
.cFileName
[2]==TEXT('\0'))))
269 lstrcpy(p
+1, w32fd
.cFileName
);
271 if (w32fd
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
272 entry
= new WinDirectory(this, buffer
);
274 entry
= new WinEntry(this);
282 memcpy(&entry
->_data
, &w32fd
, sizeof(WIN32_FIND_DATA
));
284 entry
->_level
= level
;
286 if (scan_flags
& SCAN_DO_ACCESS
) {
287 HANDLE hFile
= CreateFile(buffer
, GENERIC_READ
, FILE_SHARE_READ
|FILE_SHARE_WRITE
|FILE_SHARE_DELETE
,
288 0, OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, 0);
290 if (hFile
!= INVALID_HANDLE_VALUE
) {
291 if (GetFileInformationByHandle(hFile
, &entry
->_bhfi
))
292 entry
->_bhfi_valid
= true;
294 if (ScanNTFSStreams(entry
, hFile
))
295 entry
->_scanned
= true; // There exist named NTFS sub-streams in this file.
301 // set file type name
302 LPCTSTR ext
= g_Globals
._ftype_mgr
.set_type(entry
);
304 DWORD attribs
= SFGAO_FILESYSTEM
;
306 if (w32fd
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
307 attribs
|= SFGAO_FOLDER
;
309 if (w32fd
.dwFileAttributes
& FILE_ATTRIBUTE_READONLY
)
310 attribs
|= SFGAO_READONLY
;
312 //if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
313 // attribs |= SFGAO_HIDDEN;
315 if (w32fd
.dwFileAttributes
& FILE_ATTRIBUTE_COMPRESSED
)
316 attribs
|= SFGAO_COMPRESSED
;
318 if (ext
&& !_tcsicmp(ext
, _T(".lnk"))) {
319 attribs
|= SFGAO_LINK
;
320 w32fd
.dwFileAttributes
|= ATTRIBUTE_SYMBOLIC_LINK
;
323 entry
->_shell_attribs
= attribs
;
325 if (w32fd
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
326 entry
->_icon_id
= ICID_FOLDER
;
327 /* else if (scan_flags & SCAN_EXTRACT_ICONS)
329 entry->extract_icon(big_icons);
330 } catch(COMException&) {
331 // ignore unexpected exceptions while extracting icons
335 } while(FindNextFile(hFind
, &w32fd
));
340 } else { // !SCAN_FILESYSTEM
342 ShellItemEnumerator
enumerator(_folder
, SHCONTF_FOLDERS
|SHCONTF_NONFOLDERS
|SHCONTF_INCLUDEHIDDEN
|SHCONTF_SHAREABLE
|SHCONTF_STORAGE
);
344 TCHAR name
[MAX_PATH
];
345 HRESULT hr_next
= S_OK
;
348 #define FETCH_ITEM_COUNT 32
349 LPITEMIDLIST pidls
[FETCH_ITEM_COUNT
];
352 memset(pidls
, 0, sizeof(pidls
));
354 hr_next
= enumerator
->Next(FETCH_ITEM_COUNT
, pidls
, &cnt
);
356 /* don't break yet now: Registry Explorer Plugin returns E_FAIL!
357 if (!SUCCEEDED(hr_next))
360 if (hr_next
== S_FALSE
)
363 for(ULONG n
=0; n
<cnt
; ++n
) {
364 WIN32_FIND_DATA w32fd
;
365 BY_HANDLE_FILE_INFORMATION bhfi
;
366 bool bhfi_valid
= false;
368 memset(&w32fd
, 0, sizeof(WIN32_FIND_DATA
));
370 SFGAOF attribs_before
= ~SFGAO_READONLY
& ~SFGAO_VALIDATE
;
371 SFGAOF attribs
= attribs_before
;
372 HRESULT hr
= _folder
->GetAttributesOf(1, (LPCITEMIDLIST
*)&pidls
[n
], &attribs
);
373 bool removeable
= false;
375 if (SUCCEEDED(hr
) && attribs
!=attribs_before
) {
376 // avoid accessing floppy drives when browsing "My Computer"
377 if (attribs
& SFGAO_REMOVABLE
) {
378 attribs
|= SFGAO_HASSUBFOLDER
;
380 } else if (scan_flags
& SCAN_DO_ACCESS
) {
381 DWORD attribs2
= SFGAO_READONLY
;
383 HRESULT hr
= _folder
->GetAttributesOf(1, (LPCITEMIDLIST
*)&pidls
[n
], &attribs2
);
391 bhfi_valid
= fill_w32fdata_shell(pidls
[n
], attribs
, &w32fd
, &bhfi
,
392 (scan_flags
&SCAN_DO_ACCESS
) && !removeable
);
395 Entry
* entry
= NULL
; // eliminate useless GCC warning by initializing entry
397 if (w32fd
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
398 entry
= new ShellDirectory(this, pidls
[n
], _hwnd
);
400 entry
= new ShellEntry(this, pidls
[n
]);
408 memcpy(&entry
->_data
, &w32fd
, sizeof(WIN32_FIND_DATA
));
411 memcpy(&entry
->_bhfi
, &bhfi
, sizeof(BY_HANDLE_FILE_INFORMATION
));
413 if (SUCCEEDED(name_from_pidl(_folder
, pidls
[n
], name
, MAX_PATH
, SHGDN_INFOLDER
|0x2000/*0x2000=SHGDN_INCLUDE_NONFILESYS*/))) {
414 if (!entry
->_data
.cFileName
[0])
415 _tcscpy(entry
->_data
.cFileName
, name
);
416 else if (_tcscmp(entry
->_display_name
, name
))
417 entry
->_display_name
= _tcsdup(name
); // store display name separate from file name; sort display by file name
420 if (attribs
& SFGAO_LINK
)
421 w32fd
.dwFileAttributes
|= ATTRIBUTE_SYMBOLIC_LINK
;
423 entry
->_level
= level
;
424 entry
->_shell_attribs
= attribs
;
425 entry
->_bhfi_valid
= bhfi_valid
;
427 // set file type name
428 g_Globals
._ftype_mgr
.set_type(entry
);
430 // get icons for files and virtual objects
431 if (!(entry
->_data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) ||
432 !(attribs
& SFGAO_FILESYSTEM
)) {
433 /* if (scan_flags & SCAN_EXTRACT_ICONS)
435 entry->extract_icon(big_icons);
436 } catch(COMException&) {
437 // ignore unexpected exceptions while extracting icons
439 */ } else if (entry
->_data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
440 entry
->_icon_id
= ICID_FOLDER
;
442 entry
->_icon_id
= ICID_NONE
; // don't try again later
445 } catch(COMException
& e
) {
446 HandleException(e
, _hwnd
);
449 } while(SUCCEEDED(hr_next
));
459 const void* ShellDirectory::get_next_path_component(const void* p
) const
461 LPITEMIDLIST pidl
= (LPITEMIDLIST
)p
;
463 if (!pidl
|| !pidl
->mkid
.cb
)
466 // go to next element
467 pidl
= (LPITEMIDLIST
)((LPBYTE
)pidl
+pidl
->mkid
.cb
);
472 Entry
* ShellDirectory::find_entry(const void* p
)
474 LPITEMIDLIST pidl
= (LPITEMIDLIST
) p
;
476 // handle special case of empty trailing id list entry
480 for(Entry
*entry
=_down
; entry
; entry
=entry
->_next
)
481 if (entry
->_etype
== ET_SHELL
) {
482 ShellEntry
* se
= static_cast<ShellEntry
*>(entry
);
484 if (se
->_pidl
&& se
->_pidl
->mkid
.cb
==pidl
->mkid
.cb
&& !memcmp(se
->_pidl
, pidl
, se
->_pidl
->mkid
.cb
))
491 int ShellDirectory::extract_icons(bool big_icons
)
495 for(Entry
*entry
=_down
; entry
; entry
=entry
->_next
)
496 if (entry
->_icon_id
== ICID_UNKNOWN
) {
497 entry
->extract_icon(big_icons
);
499 if (entry
->_icon_id
!= ICID_NONE
)