87ea0a128b753adb0064f3b619ba482030e1b9ca
[reactos.git] / reactos / base / shell / 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 // copy file size
76 pw32fdata->nFileSizeLow = fad.nFileSizeLow;
77 pw32fdata->nFileSizeHigh = fad.nFileSizeHigh;
78 } else {
79 // ignore FILE_ATTRIBUTE_HIDDEN attribute of NTFS drives - this would hide those drives in ShellBrowser
80 if (pw32fdata->dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) {
81 if (path[1]==':' && path[2]=='\\' && !path[3]) // Is it a drive path?
82 pw32fdata->dwFileAttributes &= ~FILE_ATTRIBUTE_HIDDEN;
83 }
84 }
85 }
86
87 HANDLE hFile = CreateFile(path, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
88 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
89
90 if (hFile != INVALID_HANDLE_VALUE) {
91 if (GetFileInformationByHandle(hFile, pbhfi))
92 bhfi_valid = true;
93
94 CloseHandle(hFile);
95 }
96
97 SetErrorMode(sem_org);
98
99 GlobalUnlock(medium.UNION_MEMBER(hGlobal));
100 GlobalFree(medium.UNION_MEMBER(hGlobal));
101 }
102 }
103 }
104 }
105
106 if (!do_access || !(attribs&SFGAO_FILESYSTEM)) // Archiv files should not be displayed as folders in explorer view.
107 if (attribs & (SFGAO_FOLDER|SFGAO_HASSUBFOLDER))
108 pw32fdata->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
109
110 if (attribs & SFGAO_READONLY)
111 pw32fdata->dwFileAttributes |= FILE_ATTRIBUTE_READONLY;
112
113 if (attribs & SFGAO_COMPRESSED)
114 pw32fdata->dwFileAttributes |= FILE_ATTRIBUTE_COMPRESSED;
115
116 return bhfi_valid;
117 }
118
119
120 ShellPath ShellEntry::create_absolute_pidl() const
121 {
122 CONTEXT("ShellEntry::create_absolute_pidl()");
123
124 if (_up)
125 {
126 if (_up->_etype == ET_SHELL) {
127 ShellDirectory* dir = static_cast<ShellDirectory*>(_up);
128
129 if (dir->_pidl->mkid.cb) // Caching of absolute PIDLs could enhance performance.
130 return _pidl.create_absolute_pidl(dir->create_absolute_pidl());
131 } else
132 return _pidl.create_absolute_pidl(_up->create_absolute_pidl());
133 }
134 return _pidl;
135 }
136
137
138 // get full path of a shell entry
139 bool ShellEntry::get_path(PTSTR path, size_t path_count) const
140 {
141 if (!path || path_count==0)
142 return false;
143 /*
144 path[0] = TEXT('\0');
145
146 if (FAILED(path_from_pidl(get_parent_folder(), &*_pidl, path, path_count)))
147 return false;
148 */
149 FileSysShellPath fs_path(create_absolute_pidl());
150 LPCTSTR ret = fs_path;
151
152 if (ret) {
153 lstrcpyn(path, ret, path_count);
154 return true;
155 } else
156 return false;
157 }
158
159
160 // get full path of a shell folder
161 bool ShellDirectory::get_path(PTSTR path, size_t path_count) const
162 {
163 CONTEXT("ShellDirectory::get_path()");
164
165 if (!path || path_count==0)
166 return false;
167
168 path[0] = TEXT('\0');
169
170 if (_folder.empty())
171 return false;
172
173 SFGAOF attribs = SFGAO_FILESYSTEM;
174
175 // Split pidl into current and parent folder PIDLs
176 ShellPath pidlParent, pidlFolder;
177 _pidl.split(pidlParent, pidlFolder);
178
179 if (FAILED(const_cast<ShellFolder&>(_folder)->GetAttributesOf(1, (LPCITEMIDLIST*)&pidlFolder, &attribs)))
180 return false;
181
182 if (!(attribs & SFGAO_FILESYSTEM))
183 return false;
184
185 if (FAILED(path_from_pidl(get_parent_folder(), &*_pidl, path, path_count)))
186 return false;
187
188 return true;
189 }
190
191
192 BOOL ShellEntry::launch_entry(HWND hwnd, UINT nCmdShow)
193 {
194 CONTEXT("ShellEntry::launch_entry()");
195
196 SHELLEXECUTEINFO shexinfo;
197
198 shexinfo.cbSize = sizeof(SHELLEXECUTEINFO);
199 shexinfo.fMask = SEE_MASK_INVOKEIDLIST; // SEE_MASK_IDLIST is also possible.
200 shexinfo.hwnd = hwnd;
201 shexinfo.lpVerb = NULL;
202 shexinfo.lpFile = NULL;
203 shexinfo.lpParameters = NULL;
204 shexinfo.lpDirectory = NULL;
205 shexinfo.nShow = nCmdShow;
206
207 ShellPath shell_path = create_absolute_pidl();
208 shexinfo.lpIDList = &*shell_path;
209
210 // add PIDL to the recent file list
211 SHAddToRecentDocs(SHARD_PIDL, shexinfo.lpIDList);
212
213 BOOL ret = TRUE;
214
215 if (!ShellExecuteEx(&shexinfo)) {
216 display_error(hwnd, GetLastError());
217 ret = FALSE;
218 }
219
220 return ret;
221 }
222
223
224 HRESULT ShellEntry::do_context_menu(HWND hwnd, const POINT& pptScreen, CtxMenuInterfaces& cm_ifs)
225 {
226 ShellDirectory* dir = static_cast<ShellDirectory*>(_up);
227
228 ShellFolder folder = dir? dir->_folder: GetDesktopFolder();
229 LPCITEMIDLIST pidl = _pidl;
230
231 return ShellFolderContextMenu(folder, hwnd, 1, &pidl, pptScreen.x, pptScreen.y, cm_ifs);
232 }
233
234
235 HRESULT ShellEntry::GetUIObjectOf(HWND hWnd, REFIID riid, LPVOID* ppvOut)
236 {
237 LPCITEMIDLIST pidl = _pidl;
238
239 return get_parent_folder()->GetUIObjectOf(hWnd, 1, &pidl, riid, NULL, ppvOut);
240 }
241
242
243 ShellFolder Entry::get_shell_folder() const
244 {
245 return ShellFolder(create_absolute_pidl());
246 }
247
248 ShellFolder ShellEntry::get_shell_folder() const
249 {
250 return get_parent_folder();
251 }
252
253 ShellFolder ShellDirectory::get_shell_folder() const
254 {
255 return _folder;
256 }
257
258
259 void ShellDirectory::read_directory(int scan_flags)
260 {
261 CONTEXT("ShellDirectory::read_directory()");
262
263 int level = _level + 1;
264
265 Entry* first_entry = NULL;
266 Entry* last = NULL;
267
268 /*if (_folder.empty())
269 return;*/
270
271 #ifndef _NO_WIN_FS
272 TCHAR buffer[_MAX_PATH+_MAX_FNAME];
273
274 if (!(scan_flags&SCAN_NO_FILESYSTEM) && get_path(buffer, COUNTOF(buffer)) && _tcsncmp(buffer,TEXT("::{"),3)) {
275 Entry* entry = NULL; // eliminate useless GCC warning by initializing entry
276
277 LPTSTR p = buffer + _tcslen(buffer);
278
279 lstrcpy(p, TEXT("\\*"));
280
281 WIN32_FIND_DATA w32fd;
282 HANDLE hFind = FindFirstFile(buffer, &w32fd);
283
284 if (hFind != INVALID_HANDLE_VALUE) {
285 do {
286 // ignore hidden files (usefull in the start menu)
287 if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
288 continue;
289
290 // ignore directory entries "." and ".."
291 if ((w32fd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) &&
292 w32fd.cFileName[0]==TEXT('.') &&
293 (w32fd.cFileName[1]==TEXT('\0') ||
294 (w32fd.cFileName[1]==TEXT('.') && w32fd.cFileName[2]==TEXT('\0'))))
295 continue;
296
297 lstrcpy(p+1, w32fd.cFileName);
298
299 if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
300 entry = new WinDirectory(this, buffer);
301 else
302 entry = new WinEntry(this);
303
304 if (!first_entry)
305 first_entry = entry;
306
307 if (last)
308 last->_next = entry;
309
310 memcpy(&entry->_data, &w32fd, sizeof(WIN32_FIND_DATA));
311
312 entry->_level = level;
313
314 if (!(scan_flags & SCAN_DONT_ACCESS)) {
315 HANDLE hFile = CreateFile(buffer, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
316 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
317
318 if (hFile != INVALID_HANDLE_VALUE) {
319 if (GetFileInformationByHandle(hFile, &entry->_bhfi))
320 entry->_bhfi_valid = true;
321
322 if (ScanNTFSStreams(entry, hFile))
323 entry->_scanned = true; // There exist named NTFS sub-streams in this file.
324
325 CloseHandle(hFile);
326 }
327 }
328
329 // set file type name
330 LPCTSTR ext = g_Globals._ftype_mgr.set_type(entry);
331
332 DWORD attribs = SFGAO_FILESYSTEM;
333
334 if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
335 attribs |= SFGAO_FOLDER|SFGAO_HASSUBFOLDER;
336
337 if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
338 attribs |= SFGAO_READONLY;
339
340 //if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
341 // attribs |= SFGAO_HIDDEN;
342
343 if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED)
344 attribs |= SFGAO_COMPRESSED;
345
346 if (ext && !_tcsicmp(ext, _T(".lnk"))) {
347 attribs |= SFGAO_LINK;
348 w32fd.dwFileAttributes |= ATTRIBUTE_SYMBOLIC_LINK;
349 }
350
351 entry->_shell_attribs = attribs;
352
353 if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
354 entry->_icon_id = ICID_FOLDER;
355 else if (!(scan_flags & SCAN_DONT_EXTRACT_ICONS))
356 entry->_icon_id = entry->safe_extract_icon(); // Assume small icon, we can extract the large icon later on demand.
357
358 last = entry;
359 } while(FindNextFile(hFind, &w32fd));
360
361 FindClose(hFind);
362 }
363 }
364 else // SCAN_NO_FILESYSTEM
365 #endif
366 {
367 ShellItemEnumerator enumerator(_folder, SHCONTF_FOLDERS|SHCONTF_NONFOLDERS|SHCONTF_INCLUDEHIDDEN|SHCONTF_SHAREABLE|SHCONTF_STORAGE);
368
369 TCHAR name[MAX_PATH];
370 TCHAR path[MAX_PATH];
371 HRESULT hr_next = S_OK;
372
373 do {
374 #define FETCH_ITEM_COUNT 32
375 LPITEMIDLIST pidls[FETCH_ITEM_COUNT];
376 ULONG cnt = 0;
377
378 memset(pidls, 0, sizeof(pidls));
379
380 hr_next = enumerator->Next(FETCH_ITEM_COUNT, pidls, &cnt);
381
382 /* don't break yet now: Registry Explorer Plugin returns E_FAIL!
383 if (!SUCCEEDED(hr_next))
384 break; */
385
386 if (hr_next == S_FALSE)
387 break;
388
389 for(ULONG n=0; n<cnt; ++n) {
390 WIN32_FIND_DATA w32fd;
391 BY_HANDLE_FILE_INFORMATION bhfi;
392 bool bhfi_valid = false;
393
394 memset(&w32fd, 0, sizeof(WIN32_FIND_DATA));
395
396 SFGAOF attribs_before = ~SFGAO_READONLY & ~SFGAO_VALIDATE;
397 SFGAOF attribs = attribs_before;
398 HRESULT hr = _folder->GetAttributesOf(1, (LPCITEMIDLIST*)&pidls[n], &attribs);
399 bool removeable = false;
400
401 if (SUCCEEDED(hr) && attribs!=attribs_before) {
402 // avoid accessing floppy drives when browsing "My Computer"
403 if (attribs & SFGAO_REMOVABLE) {
404 attribs |= SFGAO_HASSUBFOLDER;
405 removeable = true;
406 } else if (!(scan_flags & SCAN_DONT_ACCESS)) {
407 SFGAOF attribs2 = SFGAO_READONLY;
408
409 HRESULT hr = _folder->GetAttributesOf(1, (LPCITEMIDLIST*)&pidls[n], &attribs2);
410
411 if (SUCCEEDED(hr))
412 attribs |= attribs2;
413 }
414 } else
415 attribs = 0;
416
417 bhfi_valid = fill_w32fdata_shell(pidls[n], attribs, &w32fd, &bhfi,
418 !(scan_flags&SCAN_DONT_ACCESS) && !removeable);
419
420 try {
421 Entry* entry = NULL; // eliminate useless GCC warning by initializing entry
422
423 if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
424 entry = new ShellDirectory(this, pidls[n], _hwnd);
425 else
426 entry = new ShellEntry(this, pidls[n]);
427
428 if (!first_entry)
429 first_entry = entry;
430
431 if (last)
432 last->_next = entry;
433
434 memcpy(&entry->_data, &w32fd, sizeof(WIN32_FIND_DATA));
435
436 if (bhfi_valid)
437 memcpy(&entry->_bhfi, &bhfi, sizeof(BY_HANDLE_FILE_INFORMATION));
438
439 // store path in entry->_data.cFileName in case fill_w32fdata_shell() didn't already fill it
440 if (!entry->_data.cFileName[0])
441 if (SUCCEEDED(path_from_pidl(_folder, pidls[n], path, COUNTOF(path))))
442 _tcscpy(entry->_data.cFileName, path);
443
444 if (SUCCEEDED(name_from_pidl(_folder, pidls[n], name, COUNTOF(name), SHGDN_INFOLDER|0x2000/*0x2000=SHGDN_INCLUDE_NONFILESYS*/))) {
445 if (!entry->_data.cFileName[0])
446 _tcscpy(entry->_data.cFileName, name);
447 else if (_tcscmp(entry->_display_name, name))
448 entry->_display_name = _tcsdup(name); // store display name separate from file name; sort display by file name
449 }
450
451 if (attribs & SFGAO_LINK)
452 w32fd.dwFileAttributes |= ATTRIBUTE_SYMBOLIC_LINK;
453
454 entry->_level = level;
455 entry->_shell_attribs = attribs;
456 entry->_bhfi_valid = bhfi_valid;
457
458 // set file type name
459 g_Globals._ftype_mgr.set_type(entry);
460
461 // get icons for files and virtual objects
462 if (!(entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ||
463 !(attribs & SFGAO_FILESYSTEM)) {
464 if (!(scan_flags & SCAN_DONT_EXTRACT_ICONS))
465 entry->_icon_id = entry->safe_extract_icon(); // Assume small icon, we can extract the large icon later on demand.
466 } else if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
467 entry->_icon_id = ICID_FOLDER;
468 else
469 entry->_icon_id = ICID_NONE; // don't try again later
470
471 last = entry;
472 } catch(COMException& e) {
473 HandleException(e, _hwnd);
474 }
475 }
476 } while(SUCCEEDED(hr_next));
477 }
478
479 if (last)
480 last->_next = NULL;
481
482 _down = first_entry;
483 _scanned = true;
484 }
485
486 const void* ShellDirectory::get_next_path_component(const void* p) const
487 {
488 LPITEMIDLIST pidl = (LPITEMIDLIST)p;
489
490 if (!pidl || !pidl->mkid.cb)
491 return NULL;
492
493 // go to next element
494 pidl = (LPITEMIDLIST)((LPBYTE)pidl+pidl->mkid.cb);
495
496 return pidl;
497 }
498
499 Entry* ShellDirectory::find_entry(const void* p)
500 {
501 LPITEMIDLIST pidl = (LPITEMIDLIST) p;
502
503 // handle special case of empty trailing id list entry
504 if (!pidl->mkid.cb)
505 return this;
506
507 for(Entry*entry=_down; entry; entry=entry->_next)
508 if (entry->_etype == ET_SHELL) {
509 ShellEntry* se = static_cast<ShellEntry*>(entry);
510
511 if (se->_pidl && se->_pidl->mkid.cb==pidl->mkid.cb && !memcmp(se->_pidl, pidl, se->_pidl->mkid.cb))
512 return entry;
513 } else {
514 const ShellPath& sp = entry->create_absolute_pidl();
515 static DynamicFct<LPITEMIDLIST(WINAPI*)(LPCITEMIDLIST)> ILFindLastID(TEXT("SHELL32"), "ILFindLastID");
516
517 if (ILFindLastID) {
518 LPCITEMIDLIST entry_pidl = (*ILFindLastID)(sp);
519
520 if (entry_pidl && entry_pidl->mkid.cb==pidl->mkid.cb && !memcmp(entry_pidl, pidl, entry_pidl->mkid.cb))
521 return entry;
522 }
523 }
524
525 return NULL;
526 }
527
528 int ShellDirectory::extract_icons(ICONCACHE_FLAGS flags)
529 {
530 int cnt = 0;
531
532 for(Entry*entry=_down; entry; entry=entry->_next)
533 if (entry->_icon_id == ICID_UNKNOWN) {
534 entry->_icon_id = entry->extract_icon(flags);
535
536 if (entry->_icon_id != ICID_NONE)
537 ++cnt;
538 }
539
540 return cnt;
541 }