explorer:
[reactos.git] / reactos / subsys / system / explorer / shell / shellfs.cpp
1 /*
2 * Copyright 2003, 2004, 2005, 2006 Martin Fuchs
3 *
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.
8 *
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.
13 *
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
17 */
18
19
20 //
21 // Explorer clone
22 //
23 // shellfs.cpp
24 //
25 // Martin Fuchs, 23.07.2003
26 //
27
28
29 #include <precomp.h>
30
31 //#include "shellfs.h"
32 //#include "winfs.h"
33
34 #include <shlwapi.h>
35
36
37 bool ShellDirectory::fill_w32fdata_shell(LPCITEMIDLIST pidl, SFGAOF attribs, WIN32_FIND_DATA* pw32fdata, BY_HANDLE_FILE_INFORMATION* pbhfi, bool do_access)
38 {
39 CONTEXT("ShellDirectory::fill_w32fdata_shell()");
40
41 bool bhfi_valid = false;
42
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;
47
48 STGMEDIUM medium = {0, {0}, 0};
49 FORMATETC fmt = {g_Globals._cfStrFName, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
50
51 HRESULT hr = _folder->GetUIObjectOf(0, 1, &pidl, IID_IDataObject, 0, (LPVOID*)&pDataObj);
52
53 if (SUCCEEDED(hr)) {
54 hr = pDataObj->GetData(&fmt, &medium);
55
56 pDataObj->Release();
57
58 if (SUCCEEDED(hr)) {
59 LPCTSTR path = (LPCTSTR)GlobalLock(medium.UNION_MEMBER(hGlobal));
60
61 if (path) {
62 // fill with drive names "C:", ...
63 assert(_tcslen(path) < GlobalSize(medium.UNION_MEMBER(hGlobal)));
64 _tcscpy(pw32fdata->cFileName, path);
65
66 UINT sem_org = SetErrorMode(SEM_FAILCRITICALERRORS);
67
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;
73
74 if (!(fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
75 pw32fdata->nFileSizeLow = fad.nFileSizeLow;
76 pw32fdata->nFileSizeHigh = fad.nFileSizeHigh;
77 }
78 }
79
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);
82
83 if (hFile != INVALID_HANDLE_VALUE) {
84 if (GetFileInformationByHandle(hFile, pbhfi))
85 bhfi_valid = true;
86
87 CloseHandle(hFile);
88 }
89
90 SetErrorMode(sem_org);
91
92 GlobalUnlock(medium.UNION_MEMBER(hGlobal));
93 GlobalFree(medium.UNION_MEMBER(hGlobal));
94 }
95 }
96 }
97 }
98
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;
102
103 if (attribs & SFGAO_READONLY)
104 pw32fdata->dwFileAttributes |= FILE_ATTRIBUTE_READONLY;
105
106 if (attribs & SFGAO_COMPRESSED)
107 pw32fdata->dwFileAttributes |= FILE_ATTRIBUTE_COMPRESSED;
108
109 return bhfi_valid;
110 }
111
112
113 ShellPath ShellEntry::create_absolute_pidl() const
114 {
115 CONTEXT("ShellEntry::create_absolute_pidl()");
116
117 if (_up)
118 if (_up->_etype == ET_SHELL) {
119 ShellDirectory* dir = static_cast<ShellDirectory*>(_up);
120
121 if (dir->_pidl->mkid.cb) // Caching of absolute PIDLs could enhance performance.
122 return _pidl.create_absolute_pidl(dir->create_absolute_pidl());
123 } else
124 return _pidl.create_absolute_pidl(_up->create_absolute_pidl());
125
126 return _pidl;
127 }
128
129
130 // get full path of a shell entry
131 bool ShellEntry::get_path(PTSTR path, size_t path_count) const
132 {
133 if (!path || path_count==0)
134 return false;
135 /*
136 path[0] = TEXT('\0');
137
138 if (FAILED(path_from_pidl(get_parent_folder(), &*_pidl, path, path_count)))
139 return false;
140 */
141 FileSysShellPath fs_path(create_absolute_pidl());
142 LPCTSTR ret = fs_path;
143
144 if (ret) {
145 lstrcpyn(path, ret, path_count);
146 return true;
147 } else
148 return false;
149 }
150
151
152 // get full path of a shell folder
153 bool ShellDirectory::get_path(PTSTR path, size_t path_count) const
154 {
155 CONTEXT("ShellDirectory::get_path()");
156
157 if (!path || path_count==0)
158 return false;
159
160 path[0] = TEXT('\0');
161
162 if (_folder.empty())
163 return false;
164
165 SFGAOF attribs = SFGAO_FILESYSTEM;
166
167 if (FAILED(const_cast<ShellFolder&>(_folder)->GetAttributesOf(1, (LPCITEMIDLIST*)&_pidl, &attribs)))
168 return false;
169
170 if (!(attribs & SFGAO_FILESYSTEM))
171 return false;
172
173 if (FAILED(path_from_pidl(get_parent_folder(), &*_pidl, path, path_count)))
174 return false;
175
176 return true;
177 }
178
179
180 BOOL ShellEntry::launch_entry(HWND hwnd, UINT nCmdShow)
181 {
182 CONTEXT("ShellEntry::launch_entry()");
183
184 SHELLEXECUTEINFO shexinfo;
185
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;
194
195 ShellPath shell_path = create_absolute_pidl();
196 shexinfo.lpIDList = &*shell_path;
197
198 // add PIDL to the recent file list
199 SHAddToRecentDocs(SHARD_PIDL, shexinfo.lpIDList);
200
201 BOOL ret = TRUE;
202
203 if (!ShellExecuteEx(&shexinfo)) {
204 display_error(hwnd, GetLastError());
205 ret = FALSE;
206 }
207
208 return ret;
209 }
210
211
212 HRESULT ShellEntry::do_context_menu(HWND hwnd, LPPOINT pptScreen, CtxMenuInterfaces& cm_ifs)
213 {
214 ShellDirectory* dir = static_cast<ShellDirectory*>(_up);
215
216 ShellFolder folder = dir? dir->_folder: GetDesktopFolder();
217 LPCITEMIDLIST pidl = _pidl;
218
219 return ShellFolderContextMenu(folder, hwnd, 1, &pidl, pptScreen->x, pptScreen->y, cm_ifs);
220 }
221
222
223 HRESULT ShellEntry::GetUIObjectOf(HWND hWnd, REFIID riid, LPVOID* ppvOut)
224 {
225 LPCITEMIDLIST pidl = _pidl;
226
227 return get_parent_folder()->GetUIObjectOf(hWnd, 1, &pidl, riid, NULL, ppvOut);
228 }
229
230
231 ShellFolder Entry::get_shell_folder() const
232 {
233 return ShellFolder(create_absolute_pidl());
234 }
235
236 ShellFolder ShellEntry::get_shell_folder() const
237 {
238 return get_parent_folder();
239 }
240
241 ShellFolder ShellDirectory::get_shell_folder() const
242 {
243 return _folder;
244 }
245
246
247 void ShellDirectory::read_directory(int scan_flags)
248 {
249 CONTEXT("ShellDirectory::read_directory()");
250
251 int level = _level + 1;
252
253 Entry* first_entry = NULL;
254 Entry* last = NULL;
255
256 /*if (_folder.empty())
257 return;*/
258
259 #ifndef _NO_WIN_FS
260 TCHAR buffer[_MAX_PATH+_MAX_FNAME];
261
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
264
265 LPTSTR p = buffer + _tcslen(buffer);
266
267 lstrcpy(p, TEXT("\\*"));
268
269 WIN32_FIND_DATA w32fd;
270 HANDLE hFind = FindFirstFile(buffer, &w32fd);
271
272 if (hFind != INVALID_HANDLE_VALUE) {
273 do {
274 // ignore hidden files (usefull in the start menu)
275 if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
276 continue;
277
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'))))
283 continue;
284
285 lstrcpy(p+1, w32fd.cFileName);
286
287 if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
288 entry = new WinDirectory(this, buffer);
289 else
290 entry = new WinEntry(this);
291
292 if (!first_entry)
293 first_entry = entry;
294
295 if (last)
296 last->_next = entry;
297
298 memcpy(&entry->_data, &w32fd, sizeof(WIN32_FIND_DATA));
299
300 entry->_level = level;
301
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);
305
306 if (hFile != INVALID_HANDLE_VALUE) {
307 if (GetFileInformationByHandle(hFile, &entry->_bhfi))
308 entry->_bhfi_valid = true;
309
310 if (ScanNTFSStreams(entry, hFile))
311 entry->_scanned = true; // There exist named NTFS sub-streams in this file.
312
313 CloseHandle(hFile);
314 }
315 }
316
317 // set file type name
318 LPCTSTR ext = g_Globals._ftype_mgr.set_type(entry);
319
320 DWORD attribs = SFGAO_FILESYSTEM;
321
322 if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
323 attribs |= SFGAO_FOLDER|SFGAO_HASSUBFOLDER;
324
325 if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
326 attribs |= SFGAO_READONLY;
327
328 //if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
329 // attribs |= SFGAO_HIDDEN;
330
331 if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED)
332 attribs |= SFGAO_COMPRESSED;
333
334 if (ext && !_tcsicmp(ext, _T(".lnk"))) {
335 attribs |= SFGAO_LINK;
336 w32fd.dwFileAttributes |= ATTRIBUTE_SYMBOLIC_LINK;
337 }
338
339 entry->_shell_attribs = attribs;
340
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.
345
346 last = entry;
347 } while(FindNextFile(hFind, &w32fd));
348
349 FindClose(hFind);
350 }
351 }
352 else // SCAN_NO_FILESYSTEM
353 #endif
354 {
355 ShellItemEnumerator enumerator(_folder, SHCONTF_FOLDERS|SHCONTF_NONFOLDERS|SHCONTF_INCLUDEHIDDEN|SHCONTF_SHAREABLE|SHCONTF_STORAGE);
356
357 TCHAR name[MAX_PATH];
358 HRESULT hr_next = S_OK;
359
360 do {
361 #define FETCH_ITEM_COUNT 32
362 LPITEMIDLIST pidls[FETCH_ITEM_COUNT];
363 ULONG cnt = 0;
364
365 memset(pidls, 0, sizeof(pidls));
366
367 hr_next = enumerator->Next(FETCH_ITEM_COUNT, pidls, &cnt);
368
369 /* don't break yet now: Registry Explorer Plugin returns E_FAIL!
370 if (!SUCCEEDED(hr_next))
371 break; */
372
373 if (hr_next == S_FALSE)
374 break;
375
376 for(ULONG n=0; n<cnt; ++n) {
377 WIN32_FIND_DATA w32fd;
378 BY_HANDLE_FILE_INFORMATION bhfi;
379 bool bhfi_valid = false;
380
381 memset(&w32fd, 0, sizeof(WIN32_FIND_DATA));
382
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;
387
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;
392 removeable = true;
393 } else if (!(scan_flags & SCAN_DONT_ACCESS)) {
394 DWORD attribs2 = SFGAO_READONLY;
395
396 HRESULT hr = _folder->GetAttributesOf(1, (LPCITEMIDLIST*)&pidls[n], &attribs2);
397
398 if (SUCCEEDED(hr))
399 attribs |= attribs2;
400 }
401 } else
402 attribs = 0;
403
404 bhfi_valid = fill_w32fdata_shell(pidls[n], attribs, &w32fd, &bhfi,
405 !(scan_flags&SCAN_DONT_ACCESS) && !removeable);
406
407 try {
408 Entry* entry = NULL; // eliminate useless GCC warning by initializing entry
409
410 if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
411 entry = new ShellDirectory(this, pidls[n], _hwnd);
412 else
413 entry = new ShellEntry(this, pidls[n]);
414
415 if (!first_entry)
416 first_entry = entry;
417
418 if (last)
419 last->_next = entry;
420
421 memcpy(&entry->_data, &w32fd, sizeof(WIN32_FIND_DATA));
422
423 if (bhfi_valid)
424 memcpy(&entry->_bhfi, &bhfi, sizeof(BY_HANDLE_FILE_INFORMATION));
425
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
431 }
432
433 if (attribs & SFGAO_LINK)
434 w32fd.dwFileAttributes |= ATTRIBUTE_SYMBOLIC_LINK;
435
436 entry->_level = level;
437 entry->_shell_attribs = attribs;
438 entry->_bhfi_valid = bhfi_valid;
439
440 // set file type name
441 g_Globals._ftype_mgr.set_type(entry);
442
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;
450 else
451 entry->_icon_id = ICID_NONE; // don't try again later
452
453 last = entry;
454 } catch(COMException& e) {
455 HandleException(e, _hwnd);
456 }
457 }
458 } while(SUCCEEDED(hr_next));
459 }
460
461 if (last)
462 last->_next = NULL;
463
464 _down = first_entry;
465 _scanned = true;
466 }
467
468 const void* ShellDirectory::get_next_path_component(const void* p) const
469 {
470 LPITEMIDLIST pidl = (LPITEMIDLIST)p;
471
472 if (!pidl || !pidl->mkid.cb)
473 return NULL;
474
475 // go to next element
476 pidl = (LPITEMIDLIST)((LPBYTE)pidl+pidl->mkid.cb);
477
478 return pidl;
479 }
480
481 Entry* ShellDirectory::find_entry(const void* p)
482 {
483 LPITEMIDLIST pidl = (LPITEMIDLIST) p;
484
485 // handle special case of empty trailing id list entry
486 if (!pidl->mkid.cb)
487 return this;
488
489 for(Entry*entry=_down; entry; entry=entry->_next)
490 if (entry->_etype == ET_SHELL) {
491 ShellEntry* se = static_cast<ShellEntry*>(entry);
492
493 if (se->_pidl && se->_pidl->mkid.cb==pidl->mkid.cb && !memcmp(se->_pidl, pidl, se->_pidl->mkid.cb))
494 return entry;
495 } else {
496 const ShellPath& sp = entry->create_absolute_pidl();
497 static DynamicFct<LPITEMIDLIST(WINAPI*)(LPCITEMIDLIST)> ILFindLastID(TEXT("SHELL32"), "ILFindLastID");
498
499 if (ILFindLastID) {
500 LPCITEMIDLIST entry_pidl = (*ILFindLastID)(sp);
501
502 if (entry_pidl && entry_pidl->mkid.cb==pidl->mkid.cb && !memcmp(entry_pidl, pidl, entry_pidl->mkid.cb))
503 return entry;
504 }
505 }
506
507 return NULL;
508 }
509
510 int ShellDirectory::extract_icons(ICONCACHE_FLAGS flags)
511 {
512 int cnt = 0;
513
514 for(Entry*entry=_down; entry; entry=entry->_next)
515 if (entry->_icon_id == ICID_UNKNOWN) {
516 entry->_icon_id = entry->extract_icon(flags);
517
518 if (entry->_icon_id != ICID_NONE)
519 ++cnt;
520 }
521
522 return cnt;
523 }