2 * Copyright 2003, 2004, 2005 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 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())
244 TCHAR buffer
[MAX_PATH
];
246 if ((scan_flags
&SCAN_FILESYSTEM
) && get_path(buffer
, COUNTOF(buffer
)) && _tcsncmp(buffer
,TEXT("::{"),3)) {
247 Entry
* entry
= NULL
; // eliminate useless GCC warning by initializing entry
249 LPTSTR p
= buffer
+ _tcslen(buffer
);
251 // TODO FIXME - this can overflow
252 lstrcpy(p
, TEXT("\\*"));
254 WIN32_FIND_DATA w32fd
;
255 HANDLE hFind
= FindFirstFile(buffer
, &w32fd
);
257 if (hFind
!= INVALID_HANDLE_VALUE
) {
259 // ignore hidden files (usefull in the start menu)
260 if (w32fd
.dwFileAttributes
& FILE_ATTRIBUTE_HIDDEN
)
263 // ignore directory entries "." and ".."
264 if ((w32fd
.dwFileAttributes
&FILE_ATTRIBUTE_DIRECTORY
) &&
265 w32fd
.cFileName
[0]==TEXT('.') &&
266 (w32fd
.cFileName
[1]==TEXT('\0') ||
267 (w32fd
.cFileName
[1]==TEXT('.') && w32fd
.cFileName
[2]==TEXT('\0'))))
270 lstrcpy(p
+1, w32fd
.cFileName
);
272 if (w32fd
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
273 entry
= new WinDirectory(this, buffer
);
275 entry
= new WinEntry(this);
283 memcpy(&entry
->_data
, &w32fd
, sizeof(WIN32_FIND_DATA
));
285 entry
->_level
= level
;
287 if (scan_flags
& SCAN_DO_ACCESS
) {
288 HANDLE hFile
= CreateFile(buffer
, GENERIC_READ
, FILE_SHARE_READ
|FILE_SHARE_WRITE
|FILE_SHARE_DELETE
,
289 0, OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, 0);
291 if (hFile
!= INVALID_HANDLE_VALUE
) {
292 if (GetFileInformationByHandle(hFile
, &entry
->_bhfi
))
293 entry
->_bhfi_valid
= true;
295 if (ScanNTFSStreams(entry
, hFile
))
296 entry
->_scanned
= true; // There exist named NTFS sub-streams in this file.
302 // set file type name
303 LPCTSTR ext
= g_Globals
._ftype_mgr
.set_type(entry
);
305 DWORD attribs
= SFGAO_FILESYSTEM
;
307 if (w32fd
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
308 attribs
|= SFGAO_FOLDER
|SFGAO_HASSUBFOLDER
;
310 if (w32fd
.dwFileAttributes
& FILE_ATTRIBUTE_READONLY
)
311 attribs
|= SFGAO_READONLY
;
313 //if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
314 // attribs |= SFGAO_HIDDEN;
316 if (w32fd
.dwFileAttributes
& FILE_ATTRIBUTE_COMPRESSED
)
317 attribs
|= SFGAO_COMPRESSED
;
319 if (ext
&& !_tcsicmp(ext
, _T(".lnk"))) {
320 attribs
|= SFGAO_LINK
;
321 w32fd
.dwFileAttributes
|= ATTRIBUTE_SYMBOLIC_LINK
;
324 entry
->_shell_attribs
= attribs
;
326 if (w32fd
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
327 entry
->_icon_id
= ICID_FOLDER
;
328 /* else if (scan_flags & SCAN_EXTRACT_ICONS)
329 entry->safe_extract_icon(large_icons);
332 } while(FindNextFile(hFind
, &w32fd
));
337 else // !SCAN_FILESYSTEM
341 ShellItemEnumerator
enumerator(_folder
, SHCONTF_FOLDERS
|SHCONTF_NONFOLDERS
|SHCONTF_INCLUDEHIDDEN
|SHCONTF_SHAREABLE
|SHCONTF_STORAGE
);
343 TCHAR name
[MAX_PATH
];
344 HRESULT hr_next
= S_OK
;
347 #define FETCH_ITEM_COUNT 32
348 LPITEMIDLIST pidls
[FETCH_ITEM_COUNT
];
351 memset(pidls
, 0, sizeof(pidls
));
353 hr_next
= enumerator
->Next(FETCH_ITEM_COUNT
, pidls
, &cnt
);
355 /* don't break yet now: Registry Explorer Plugin returns E_FAIL!
356 if (!SUCCEEDED(hr_next))
359 if (hr_next
== S_FALSE
)
362 for(ULONG n
=0; n
<cnt
; ++n
) {
363 WIN32_FIND_DATA w32fd
;
364 BY_HANDLE_FILE_INFORMATION bhfi
;
365 bool bhfi_valid
= false;
367 memset(&w32fd
, 0, sizeof(WIN32_FIND_DATA
));
369 SFGAOF attribs_before
= ~SFGAO_READONLY
& ~SFGAO_VALIDATE
;
370 SFGAOF attribs
= attribs_before
;
371 HRESULT hr
= _folder
->GetAttributesOf(1, (LPCITEMIDLIST
*)&pidls
[n
], &attribs
);
372 bool removeable
= false;
374 if (SUCCEEDED(hr
) && attribs
!=attribs_before
) {
375 // avoid accessing floppy drives when browsing "My Computer"
376 if (attribs
& SFGAO_REMOVABLE
) {
377 attribs
|= SFGAO_HASSUBFOLDER
;
379 } else if (scan_flags
& SCAN_DO_ACCESS
) {
380 DWORD attribs2
= SFGAO_READONLY
;
382 HRESULT hr
= _folder
->GetAttributesOf(1, (LPCITEMIDLIST
*)&pidls
[n
], &attribs2
);
390 bhfi_valid
= fill_w32fdata_shell(pidls
[n
], attribs
, &w32fd
, &bhfi
,
391 (scan_flags
&SCAN_DO_ACCESS
) && !removeable
);
394 Entry
* entry
= NULL
; // eliminate useless GCC warning by initializing entry
396 if (w32fd
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
397 entry
= new ShellDirectory(this, pidls
[n
], _hwnd
);
399 entry
= new ShellEntry(this, pidls
[n
]);
407 memcpy(&entry
->_data
, &w32fd
, sizeof(WIN32_FIND_DATA
));
410 memcpy(&entry
->_bhfi
, &bhfi
, sizeof(BY_HANDLE_FILE_INFORMATION
));
412 if (SUCCEEDED(name_from_pidl(_folder
, pidls
[n
], name
, COUNTOF(name
), SHGDN_INFOLDER
|0x2000/*0x2000=SHGDN_INCLUDE_NONFILESYS*/))) {
413 if (!entry
->_data
.cFileName
[0])
414 _tcscpy(entry
->_data
.cFileName
, name
);
415 else if (_tcscmp(entry
->_display_name
, name
))
416 entry
->_display_name
= _tcsdup(name
); // store display name separate from file name; sort display by file name
419 if (attribs
& SFGAO_LINK
)
420 w32fd
.dwFileAttributes
|= ATTRIBUTE_SYMBOLIC_LINK
;
422 entry
->_level
= level
;
423 entry
->_shell_attribs
= attribs
;
424 entry
->_bhfi_valid
= bhfi_valid
;
426 // set file type name
427 g_Globals
._ftype_mgr
.set_type(entry
);
429 // get icons for files and virtual objects
430 if (!(entry
->_data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) ||
431 !(attribs
& SFGAO_FILESYSTEM
)) {
432 /* if (scan_flags & SCAN_EXTRACT_ICONS)
433 entry->extract_icon(large_icons);
434 */ } else if (entry
->_data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
435 entry
->_icon_id
= ICID_FOLDER
;
437 entry
->_icon_id
= ICID_NONE
; // don't try again later
440 } catch(COMException
& e
) {
441 HandleException(e
, _hwnd
);
444 } while(SUCCEEDED(hr_next
));
454 const void* ShellDirectory::get_next_path_component(const void* p
) const
456 LPITEMIDLIST pidl
= (LPITEMIDLIST
)p
;
458 if (!pidl
|| !pidl
->mkid
.cb
)
461 // go to next element
462 pidl
= (LPITEMIDLIST
)((LPBYTE
)pidl
+pidl
->mkid
.cb
);
467 Entry
* ShellDirectory::find_entry(const void* p
)
469 LPITEMIDLIST pidl
= (LPITEMIDLIST
) p
;
471 // handle special case of empty trailing id list entry
475 for(Entry
*entry
=_down
; entry
; entry
=entry
->_next
)
476 if (entry
->_etype
== ET_SHELL
) {
477 ShellEntry
* se
= static_cast<ShellEntry
*>(entry
);
479 if (se
->_pidl
&& se
->_pidl
->mkid
.cb
==pidl
->mkid
.cb
&& !memcmp(se
->_pidl
, pidl
, se
->_pidl
->mkid
.cb
))
486 int ShellDirectory::extract_icons(ICONCACHE_FLAGS flags
)
490 for(Entry
*entry
=_down
; entry
; entry
=entry
->_next
)
491 if (entry
->_icon_id
== ICID_UNKNOWN
) {
492 entry
->_icon_id
= entry
->extract_icon(flags
);
494 if (entry
->_icon_id
!= ICID_NONE
)