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  // explorer.cpp
24  //
25  // Martin Fuchs, 23.07.2003
26  //
27  // Credits: Thanks to Leon Finker for his explorer cabinet window example
28  //
29 
30 
31 #include <precomp.h>    // <precomp.h> instead of "precomp.h" because the ROS build system needs this to find the precompiled header file (*.gch) in the output directory tree
32 
33 #include <shlwapi.h>
34 #include <locale.h>    // for setlocale()
35 
36 #ifndef __WINE__
37 #include <io.h>        // for dup2()
38 #include <fcntl.h>    // for _O_RDONLY
39 #endif
40 
41 //#include "dialogs/settings.h"    // for MdiSdiDlg
42 
43 #include "services/shellservices.h"
44 
45 
46 extern "C" int initialize_gdb_stub();    // start up GDB stub
47 
48 
49 DynamicLoadLibFct<void(__stdcall*)(BOOL)> g_SHDOCVW_ShellDDEInit(TEXT("SHDOCVW"), 118);
50 
51 
52 ExplorerGlobals g_Globals;
53 boolean SelectOpt=FALSE;
54 
55 
ExplorerGlobals()56 ExplorerGlobals::ExplorerGlobals()
57 {
58     _hInstance = 0;
59     _cfStrFName = 0;
60 
61 #ifndef ROSSHELL
62     _hframeClass = 0;
63     _hMainWnd = 0;
64     _desktop_mode = false;
65     _prescan_nodes = false;
66 #endif
67 
68     _log = NULL;
69     _SHRestricted = 0;
70     _hwndDesktopBar = 0;
71     _hwndShellView = 0;
72     _hwndDesktop = 0;
73 }
74 
75 
init(HINSTANCE hInstance)76 void ExplorerGlobals::init(HINSTANCE hInstance)
77 {
78     _hInstance = hInstance;
79     _SHRestricted = (DWORD(STDAPICALLTYPE*)(RESTRICTIONS)) GetProcAddress(GetModuleHandle(TEXT("SHELL32")), "SHRestricted");
80     _icon_cache.init();
81 }
82 
83 
read_persistent()84 void ExplorerGlobals::read_persistent()
85 {
86     // read configuration file
87     _cfg_dir.printf(TEXT("%s\\ReactOS"), (LPCTSTR)SpecialFolderFSPath(CSIDL_APPDATA,0));
88     _cfg_path.printf(TEXT("%s\\ros-explorer-cfg.xml"), _cfg_dir.c_str());
89 
90     if (!_cfg.read_file(_cfg_path)) {
91         if (!_cfg._errors.empty()) {
92             MessageBox(_hwndDesktop,
93                        _cfg._errors.str(),
94                        TEXT("ROS Explorer - reading user settings"),
95                        MB_OK);
96         }
97         _cfg.read_file(TEXT("explorer-cfg-template.xml"));
98     }
99 
100      // read bookmarks
101     _favorites_path.printf(TEXT("%s\\ros-explorer-bookmarks.xml"), _cfg_dir.c_str());
102 
103     if (!_favorites.read(_favorites_path)) {
104         _favorites.import_IE_favorites(0);
105         _favorites.write(_favorites_path);
106     }
107 }
108 
write_persistent()109 void ExplorerGlobals::write_persistent()
110 {
111      // write configuration file
112     RecursiveCreateDirectory(_cfg_dir);
113 
114     _cfg.write_file(_cfg_path);
115     _favorites.write(_favorites_path);
116 }
117 
118 
get_cfg()119 XMLPos ExplorerGlobals::get_cfg()
120 {
121     XMLPos cfg_pos(&_cfg);
122 
123     cfg_pos.smart_create("explorer-cfg");
124 
125     return cfg_pos;
126 }
127 
get_cfg(const char * path)128 XMLPos ExplorerGlobals::get_cfg(const char* path)
129 {
130     XMLPos cfg_pos(&_cfg);
131 
132     cfg_pos.smart_create("explorer-cfg");
133     cfg_pos.create_relative(path);
134 
135     return cfg_pos;
136 }
137 
138 
_log_(LPCTSTR txt)139 void _log_(LPCTSTR txt)
140 {
141     FmtString msg(TEXT("%s\n"), txt);
142 
143     if (g_Globals._log)
144         _fputts(msg, g_Globals._log);
145 
146     OutputDebugString(msg);
147 }
148 
149 
is_exe_file(LPCTSTR ext)150 bool FileTypeManager::is_exe_file(LPCTSTR ext)
151 {
152     static const LPCTSTR s_executable_extensions[] = {
153         TEXT("COM"),
154         TEXT("EXE"),
155         TEXT("BAT"),
156         TEXT("CMD"),
157         TEXT("CMM"),
158         TEXT("BTM"),
159         TEXT("AWK"),
160         0
161     };
162 
163     TCHAR ext_buffer[_MAX_EXT];
164     const LPCTSTR* p;
165     LPCTSTR s;
166     LPTSTR d;
167 
168     for(s=ext+1,d=ext_buffer; (*d=toupper(*s)); s++)
169         ++d;
170 
171     for(p=s_executable_extensions; *p; p++)
172         if (!lstrcmp(ext_buffer, *p))
173             return true;
174 
175     return false;
176 }
177 
178 
operator [](String ext)179 const FileTypeInfo& FileTypeManager::operator[](String ext)
180 {
181     ext.toLower();
182 
183     iterator found = find(ext);
184     if (found != end())
185         return found->second;
186 
187     FileTypeInfo& ftype = super::operator[](ext);
188 
189     ftype._neverShowExt = false;
190 
191     HKEY hkey;
192     TCHAR value[MAX_PATH], display_name[MAX_PATH];
193     LONG valuelen = sizeof(value);
194 
195     if (!RegQueryValue(HKEY_CLASSES_ROOT, ext, value, &valuelen)) {
196         ftype._classname = value;
197 
198         valuelen = sizeof(display_name);
199         if (!RegQueryValue(HKEY_CLASSES_ROOT, ftype._classname, display_name, &valuelen))
200             ftype._displayname = display_name;
201 
202         if (!RegOpenKey(HKEY_CLASSES_ROOT, ftype._classname, &hkey)) {
203             if (!RegQueryValueEx(hkey, TEXT("NeverShowExt"), 0, NULL, NULL, NULL))
204                 ftype._neverShowExt = true;
205 
206             RegCloseKey(hkey);
207         }
208     }
209 
210     return ftype;
211 }
212 
set_type(Entry * entry,bool dont_hide_ext)213 LPCTSTR FileTypeManager::set_type(Entry* entry, bool dont_hide_ext)
214 {
215     LPCTSTR ext = _tcsrchr(entry->_data.cFileName, TEXT('.'));
216 
217     if (ext) {
218         const FileTypeInfo& type = (*this)[ext];
219 
220         if (!type._displayname.empty())
221             entry->_type_name = _tcsdup(type._displayname);
222 
223          // hide some file extensions
224         if (type._neverShowExt && !dont_hide_ext) {
225             int len = ext - entry->_data.cFileName;
226 
227             if (entry->_display_name != entry->_data.cFileName)
228                 free(entry->_display_name);
229 
230             entry->_display_name = (LPTSTR) malloc((len+1)*sizeof(TCHAR));
231             lstrcpyn(entry->_display_name, entry->_data.cFileName, len + 1);
232         }
233 
234         if (is_exe_file(ext))
235             entry->_data.dwFileAttributes |= ATTRIBUTE_EXECUTABLE;
236     }
237 
238     return ext;
239 }
240 
241 
Icon()242 Icon::Icon()
243  : _id(ICID_UNKNOWN),
244     _itype(IT_STATIC),
245     _hicon(0)
246 {
247 }
248 
Icon(ICON_ID id,UINT nid)249 Icon::Icon(ICON_ID id, UINT nid)    //, int cx, int cy
250  :    _id(id),
251     _itype(IT_STATIC),
252     _hicon(ResIcon(nid))    // ResIconEx(nid, cx, cy)
253 {
254 }
255 
Icon(ICON_ID id,UINT nid,int icon_size)256 Icon::Icon(ICON_ID id, UINT nid, int icon_size)
257  :    _id(id),
258     _itype(IT_STATIC),
259     _hicon(ResIconEx(nid, icon_size, icon_size))
260 {
261 }
262 
Icon(ICON_TYPE itype,int id,HICON hIcon)263 Icon::Icon(ICON_TYPE itype, int id, HICON hIcon)
264  :    _id((ICON_ID)id),
265     _itype(itype),
266     _hicon(hIcon)
267 {
268 }
269 
Icon(ICON_TYPE itype,int id,int sys_idx)270 Icon::Icon(ICON_TYPE itype, int id, int sys_idx)
271  :    _id((ICON_ID)id),
272     _itype(itype),
273     _sys_idx(sys_idx)
274 {
275 }
276 
draw(HDC hdc,int x,int y,int cx,int cy,COLORREF bk_color,HBRUSH bk_brush) const277 void Icon::draw(HDC hdc, int x, int y, int cx, int cy, COLORREF bk_color, HBRUSH bk_brush) const
278 {
279     if (_itype == IT_SYSCACHE)
280         ImageList_DrawEx(g_Globals._icon_cache.get_sys_imagelist(), _sys_idx, hdc, x, y, cx, cy, bk_color, CLR_DEFAULT, ILD_NORMAL);
281     else
282         DrawIconEx(hdc, x, y, _hicon, cx, cy, 0, bk_brush, DI_NORMAL);
283 }
284 
create_bitmap(COLORREF bk_color,HBRUSH hbrBkgnd,HDC hdc_wnd) const285 HBITMAP    Icon::create_bitmap(COLORREF bk_color, HBRUSH hbrBkgnd, HDC hdc_wnd) const
286 {
287     if (_itype == IT_SYSCACHE) {
288         HIMAGELIST himl = g_Globals._icon_cache.get_sys_imagelist();
289 
290         int cx, cy;
291         ImageList_GetIconSize(himl, &cx, &cy);
292 
293         HBITMAP hbmp = CreateCompatibleBitmap(hdc_wnd, cx, cy);
294         HDC hdc = CreateCompatibleDC(hdc_wnd);
295         HBITMAP hbmp_old = SelectBitmap(hdc, hbmp);
296         ImageList_DrawEx(himl, _sys_idx, hdc, 0, 0, cx, cy, bk_color, CLR_DEFAULT, ILD_NORMAL);
297         SelectBitmap(hdc, hbmp_old);
298         DeleteDC(hdc);
299 
300         return hbmp;
301     } else
302         return create_bitmap_from_icon(_hicon, hbrBkgnd, hdc_wnd);
303 }
304 
305 
add_to_imagelist(HIMAGELIST himl,HDC hdc_wnd,COLORREF bk_color,HBRUSH bk_brush) const306 int Icon::add_to_imagelist(HIMAGELIST himl, HDC hdc_wnd, COLORREF bk_color, HBRUSH bk_brush) const
307 {
308     int ret;
309 
310     if (_itype == IT_SYSCACHE) {
311         HIMAGELIST himl = g_Globals._icon_cache.get_sys_imagelist();
312 
313         int cx, cy;
314         ImageList_GetIconSize(himl, &cx, &cy);
315 
316         HBITMAP hbmp = CreateCompatibleBitmap(hdc_wnd, cx, cy);
317         HDC hdc = CreateCompatibleDC(hdc_wnd);
318         HBITMAP hbmp_old = SelectBitmap(hdc, hbmp);
319         ImageList_DrawEx(himl, _sys_idx, hdc, 0, 0, cx, cy, bk_color, CLR_DEFAULT, ILD_NORMAL);
320         SelectBitmap(hdc, hbmp_old);
321         DeleteDC(hdc);
322 
323         ret = ImageList_Add(himl, hbmp, 0);
324 
325         DeleteObject(hbmp);
326     } else
327         ret = ImageList_AddAlphaIcon(himl, _hicon, bk_brush, hdc_wnd);
328 
329     return ret;
330 }
331 
create_bitmap_from_icon(HICON hIcon,HBRUSH hbrush_bkgnd,HDC hdc_wnd)332 HBITMAP create_bitmap_from_icon(HICON hIcon, HBRUSH hbrush_bkgnd, HDC hdc_wnd/*, int icon_size*/)
333 {
334     int cx = ICON_SIZE_SMALL;
335     int cy = ICON_SIZE_SMALL;
336     HBITMAP hbmp = CreateCompatibleBitmap(hdc_wnd, cx, cy);
337 
338     MemCanvas canvas;
339     BitmapSelection sel(canvas, hbmp);
340 
341     RECT rect = {0, 0, cx, cy};
342     FillRect(canvas, &rect, hbrush_bkgnd);
343 
344     DrawIconEx(canvas, 0, 0, hIcon, cx, cy, 0, hbrush_bkgnd, DI_NORMAL);
345 
346     return hbmp;
347 }
348 
create_small_bitmap_from_icon(HICON hIcon,HBRUSH hbrush_bkgnd,HDC hdc_wnd)349 HBITMAP create_small_bitmap_from_icon(HICON hIcon, HBRUSH hbrush_bkgnd, HDC hdc_wnd)
350 {
351     int cx = GetSystemMetrics(SM_CXSMICON);
352     int cy = GetSystemMetrics(SM_CYSMICON);
353     HBITMAP hbmp = CreateCompatibleBitmap(hdc_wnd, cx, cy);
354 
355     MemCanvas canvas;
356     BitmapSelection sel(canvas, hbmp);
357 
358     RECT rect = {0, 0, cx, cy};
359     FillRect(canvas, &rect, hbrush_bkgnd);
360 
361     DrawIconEx(canvas, 0, 0, hIcon, cx, cy, 0, hbrush_bkgnd, DI_NORMAL);
362 
363     return hbmp;
364 }
365 
ImageList_AddAlphaIcon(HIMAGELIST himl,HICON hIcon,HBRUSH hbrush_bkgnd,HDC hdc_wnd)366 int ImageList_AddAlphaIcon(HIMAGELIST himl, HICON hIcon, HBRUSH hbrush_bkgnd, HDC hdc_wnd)
367 {
368     HBITMAP hbmp = create_bitmap_from_icon(hIcon, hbrush_bkgnd, hdc_wnd);
369 
370     int ret = ImageList_Add(himl, hbmp, 0);
371 
372     DeleteObject(hbmp);
373 
374     return ret;
375 }
376 
377 
378 int IconCache::s_next_id = ICID_DYNAMIC;
379 
380 
init()381 void IconCache::init()
382 {
383     int icon_size = STARTMENUROOT_ICON_SIZE;
384 
385     _icons[ICID_NONE]        = Icon(IT_STATIC, ICID_NONE, (HICON)0);
386 
387     _icons[ICID_FOLDER]        = Icon(ICID_FOLDER,        IDI_FOLDER);
388     //_icons[ICID_DOCUMENT]    = Icon(ICID_DOCUMENT,    IDI_DOCUMENT);
389     _icons[ICID_EXPLORER]    = Icon(ICID_EXPLORER,    IDI_EXPLORER);
390     //_icons[ICID_APP]        = Icon(ICID_APP,        IDI_APPICON);
391 
392     _icons[ICID_CONFIG]        = Icon(ICID_CONFIG,        IDI_CONFIG,        icon_size);
393     _icons[ICID_DOCUMENTS]    = Icon(ICID_DOCUMENTS,    IDI_DOCUMENTS,    icon_size);
394     _icons[ICID_FAVORITES]    = Icon(ICID_FAVORITES,    IDI_FAVORITES,    icon_size);
395     _icons[ICID_INFO]        = Icon(ICID_INFO,        IDI_INFO,        icon_size);
396     _icons[ICID_APPS]        = Icon(ICID_APPS,        IDI_APPS,        icon_size);
397     _icons[ICID_SEARCH]     = Icon(ICID_SEARCH,     IDI_SEARCH,        icon_size);
398     _icons[ICID_ACTION]     = Icon(ICID_ACTION,     IDI_ACTION,        icon_size);
399     _icons[ICID_SEARCH_DOC] = Icon(ICID_SEARCH_DOC, IDI_SEARCH_DOC,    icon_size);
400     _icons[ICID_PRINTER]    = Icon(ICID_PRINTER,    IDI_PRINTER,    icon_size);
401     _icons[ICID_NETWORK]    = Icon(ICID_NETWORK,    IDI_NETWORK,    icon_size);
402     _icons[ICID_COMPUTER]    = Icon(ICID_COMPUTER,    IDI_COMPUTER,    icon_size);
403     _icons[ICID_LOGOFF]     = Icon(ICID_LOGOFF,     IDI_LOGOFF,        icon_size);
404     _icons[ICID_SHUTDOWN]    = Icon(ICID_SHUTDOWN,    IDI_SHUTDOWN,    icon_size);
405     _icons[ICID_RESTART]    = Icon(ICID_RESTART,    IDI_RESTART,    icon_size);
406     _icons[ICID_BOOKMARK]    = Icon(ICID_BOOKMARK,    IDI_DOT_TRANS,    icon_size);
407     _icons[ICID_MINIMIZE]    = Icon(ICID_MINIMIZE,    IDI_MINIMIZE,    icon_size);
408     _icons[ICID_CONTROLPAN] = Icon(ICID_CONTROLPAN, IDI_CONTROLPAN,    icon_size);
409     _icons[ICID_DESKSETTING]= Icon(ICID_DESKSETTING,IDI_DESKSETTING,icon_size);
410     _icons[ICID_NETCONNS]    = Icon(ICID_NETCONNS,    IDI_NETCONNS,    icon_size);
411     _icons[ICID_ADMIN]        = Icon(ICID_ADMIN,        IDI_ADMIN,        icon_size);
412     _icons[ICID_RECENT]     = Icon(ICID_RECENT,     IDI_RECENT,        icon_size);
413 }
414 
415 
extract(LPCTSTR path,ICONCACHE_FLAGS flags)416 const Icon& IconCache::extract(LPCTSTR path, ICONCACHE_FLAGS flags)
417 {
418     // search for matching icon with unchanged flags in the cache
419     CacheKey mapkey(path, flags);
420     PathCacheMap::iterator found = _pathCache.find(mapkey);
421 
422     if (found != _pathCache.end())
423         return _icons[found->second];
424 
425     // search for matching icon with handle
426     CacheKey mapkey_hicon(path, flags|ICF_HICON);
427     if (flags != mapkey_hicon.second) {
428         found = _pathCache.find(mapkey_hicon);
429 
430         if (found != _pathCache.end())
431             return _icons[found->second];
432     }
433 
434      // search for matching icon in the system image list cache
435     CacheKey mapkey_syscache(path, flags|ICF_SYSCACHE);
436     if (flags != mapkey_syscache.second) {
437         found = _pathCache.find(mapkey_syscache);
438 
439         if (found != _pathCache.end())
440             return _icons[found->second];
441     }
442 
443     SHFILEINFO sfi;
444 
445     int shgfi_flags = 0;
446 
447     if (flags & ICF_OPEN)
448         shgfi_flags |= SHGFI_OPENICON;
449 
450     if ((flags&(ICF_LARGE|ICF_MIDDLE|ICF_OVERLAYS|ICF_HICON)) && !(flags&ICF_SYSCACHE)) {
451         shgfi_flags |= SHGFI_ICON;
452 
453         if (!(flags & (ICF_LARGE|ICF_MIDDLE)))
454             shgfi_flags |= SHGFI_SMALLICON;
455 
456         if (flags & ICF_OVERLAYS)
457             shgfi_flags |= SHGFI_ADDOVERLAYS;
458 
459          // get small/big icons with/without overlays
460         if (SHGetFileInfo(path, 0, &sfi, sizeof(sfi), shgfi_flags)) {
461             const Icon& icon = add(sfi.hIcon, IT_CACHED);
462 
463             ///@todo limit cache size
464             _pathCache[mapkey_hicon] = icon;
465 
466             return icon;
467         }
468     } else {
469         assert(!(flags&ICF_OVERLAYS));
470 
471         shgfi_flags |= SHGFI_SYSICONINDEX|SHGFI_SMALLICON;
472 
473          // use system image list - the "search program dialog" needs it
474         HIMAGELIST himlSys_small = (HIMAGELIST) SHGetFileInfo(path, 0, &sfi, sizeof(sfi), shgfi_flags);
475 
476         if (himlSys_small) {
477             _himlSys_small = himlSys_small;
478 
479             const Icon& icon = add(sfi.iIcon/*, IT_SYSCACHE*/);
480 
481             ///@todo limit cache size
482             _pathCache[mapkey_syscache] = icon;
483 
484             return icon;
485         }
486     }
487 
488     return _icons[ICID_NONE];
489 }
490 
extract(LPCTSTR path,int icon_idx,ICONCACHE_FLAGS flags)491 const Icon& IconCache::extract(LPCTSTR path, int icon_idx, ICONCACHE_FLAGS flags)
492 {
493     IdxCacheKey key(path, make_pair(icon_idx, (flags|ICF_HICON)&~ICF_SYSCACHE));
494 
495     key.first.toLower();
496 
497     IdxCacheMap::iterator found = _idxCache.find(key);
498 
499     if (found != _idxCache.end())
500         return _icons[found->second];
501 
502     HICON hIcon;
503 
504     if ((int)ExtractIconEx(path, icon_idx, NULL, &hIcon, 1) > 0) {
505         const Icon& icon = add(hIcon, IT_CACHED);
506 
507         _idxCache[key] = icon;
508 
509         return icon;
510     } else {
511 
512         ///@todo retreive "http://.../favicon.ico" format icons
513 
514         return _icons[ICID_NONE];
515     }
516 }
517 
extract(IExtractIcon * pExtract,LPCTSTR path,int icon_idx,ICONCACHE_FLAGS flags)518 const Icon& IconCache::extract(IExtractIcon* pExtract, LPCTSTR path, int icon_idx, ICONCACHE_FLAGS flags)
519 {
520     HICON hIconLarge = 0;
521     HICON hIcon;
522 
523     int icon_size = ICON_SIZE_FROM_ICF(flags);
524     HRESULT hr = pExtract->Extract(path, icon_idx, &hIconLarge, &hIcon, MAKELONG(GetSystemMetrics(SM_CXICON), icon_size));
525 
526     if (hr == NOERROR) {    //@@ oder SUCCEEDED(hr) ?
527         if (icon_size > ICON_SIZE_SMALL) {    //@@ OK?
528             if (hIcon)
529                 DestroyIcon(hIcon);
530 
531             hIcon = hIconLarge;
532         } else {
533             if (hIconLarge)
534                 DestroyIcon(hIconLarge);
535         }
536 
537         if (hIcon)
538             return add(hIcon);    //@@ When do we want not to free this icons?
539     }
540 
541     return _icons[ICID_NONE];
542 }
543 
extract(LPCITEMIDLIST pidl,ICONCACHE_FLAGS flags)544 const Icon& IconCache::extract(LPCITEMIDLIST pidl, ICONCACHE_FLAGS flags)
545 {
546      // search for matching icon with unchanged flags in the cache
547     PidlCacheKey mapkey(pidl, flags);
548     PidlCacheMap::iterator found = _pidlcache.find(mapkey);
549 
550     if (found != _pidlcache.end())
551         return _icons[found->second];
552 
553      // search for matching icon with handle
554     PidlCacheKey mapkey_hicon(pidl, flags|ICF_HICON);
555     if (flags != mapkey_hicon.second) {
556         found = _pidlcache.find(mapkey_hicon);
557 
558         if (found != _pidlcache.end())
559             return _icons[found->second];
560     }
561 
562      // search for matching icon in the system image list cache
563     PidlCacheKey mapkey_syscache(pidl, flags|ICF_SYSCACHE);
564     if (flags != mapkey_syscache.second) {
565         found = _pidlcache.find(mapkey_syscache);
566 
567         if (found != _pidlcache.end())
568             return _icons[found->second];
569     }
570 
571     SHFILEINFO sfi;
572 
573     int shgfi_flags = SHGFI_PIDL;
574 
575     if (!(flags & (ICF_LARGE|ICF_MIDDLE)))
576         shgfi_flags |= SHGFI_SMALLICON;
577 
578     if (flags & ICF_OPEN)
579         shgfi_flags |= SHGFI_OPENICON;
580 
581     if (flags & ICF_SYSCACHE) {
582         assert(!(flags&ICF_OVERLAYS));
583 
584         HIMAGELIST himlSys = (HIMAGELIST) SHGetFileInfo((LPCTSTR)pidl, 0, &sfi, sizeof(sfi), SHGFI_SYSICONINDEX|shgfi_flags);
585         if (himlSys) {
586             const Icon& icon = add(sfi.iIcon/*, IT_SYSCACHE*/);
587 
588             ///@todo limit cache size
589             _pidlcache[mapkey_syscache] = icon;
590 
591             return icon;
592         }
593     } else {
594         if (flags & ICF_OVERLAYS)
595             shgfi_flags |= SHGFI_ADDOVERLAYS;
596 
597         if (SHGetFileInfo((LPCTSTR)pidl, 0, &sfi, sizeof(sfi), SHGFI_ICON|shgfi_flags)) {
598             const Icon& icon = add(sfi.hIcon, IT_CACHED);
599 
600             ///@todo limit cache size
601             _pidlcache[mapkey_hicon] = icon;
602 
603             return icon;
604         }
605     }
606 
607     return _icons[ICID_NONE];
608 }
609 
610 
add(HICON hIcon,ICON_TYPE type)611 const Icon& IconCache::add(HICON hIcon, ICON_TYPE type)
612 {
613     int id = ++s_next_id;
614 
615     return _icons[id] = Icon(type, id, hIcon);
616 }
617 
add(int sys_idx)618 const Icon&    IconCache::add(int sys_idx/*, ICON_TYPE type=IT_SYSCACHE*/)
619 {
620     int id = ++s_next_id;
621 
622     return _icons[id] = SysCacheIcon(id, sys_idx);
623 }
624 
get_icon(int id)625 const Icon& IconCache::get_icon(int id)
626 {
627     return _icons[id];
628 }
629 
~IconCache()630 IconCache::~IconCache()
631 {
632 /* We don't need to free cached resources - they are automatically freed at process termination
633     for (int index = s_next_id; index >= 0; index--) {
634         IconMap::iterator found = _icons.find(index);
635 
636         if (found != _icons.end()) {
637             Icon& icon = found->second;
638 
639             if ((icon.get_icontype() == IT_DYNAMIC) ||
640                 (icon.get_icontype() == IT_CACHED))
641             {
642                 DestroyIcon(icon.get_hicon());
643                 _icons.erase(found);
644             }
645         }
646     }
647 */
648 }
649 
free_icon(int icon_id)650 void IconCache::free_icon(int icon_id)
651 {
652     IconMap::iterator found = _icons.find(icon_id);
653 
654     if (found != _icons.end()) {
655         Icon& icon = found->second;
656 
657         if (icon.destroy())
658             _icons.erase(found);
659     }
660 }
661 
662 
ResString(UINT nid)663 ResString::ResString(UINT nid)
664 {
665     TCHAR buffer[BUFFER_LEN];
666 
667     int len = LoadString(g_Globals._hInstance, nid, buffer, sizeof(buffer)/sizeof(TCHAR));
668 
669     super::assign(buffer, len);
670 }
671 
672 
ResIcon(UINT nid)673 ResIcon::ResIcon(UINT nid)
674 {
675     _hicon = LoadIcon(g_Globals._hInstance, MAKEINTRESOURCE(nid));
676 }
677 
SmallIcon(UINT nid)678 SmallIcon::SmallIcon(UINT nid)
679 {
680     _hicon = (HICON)LoadImage(g_Globals._hInstance, MAKEINTRESOURCE(nid), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED);
681 }
682 
ResIconEx(UINT nid,int w,int h)683 ResIconEx::ResIconEx(UINT nid, int w, int h)
684 {
685     _hicon = (HICON)LoadImage(g_Globals._hInstance, MAKEINTRESOURCE(nid), IMAGE_ICON, w, h, LR_SHARED);
686 }
687 
688 
SetWindowIcon(HWND hwnd,UINT nid)689 void SetWindowIcon(HWND hwnd, UINT nid)
690 {
691     HICON hIcon = ResIcon(nid);
692     (void)Window_SetIcon(hwnd, ICON_BIG, hIcon);
693 
694     HICON hIconSmall = SmallIcon(nid);
695     (void)Window_SetIcon(hwnd, ICON_SMALL, hIconSmall);
696 }
697 
698 
ResBitmap(UINT nid)699 ResBitmap::ResBitmap(UINT nid)
700 {
701     _hBmp = LoadBitmap(g_Globals._hInstance, MAKEINTRESOURCE(nid));
702 }
703 
704 
705 #ifndef ROSSHELL
706 
explorer_show_frame(int cmdShow,LPTSTR lpCmdLine)707 void explorer_show_frame(int cmdShow, LPTSTR lpCmdLine)
708 {
709     ExplorerCmd cmd;
710 
711     if (g_Globals._hMainWnd) {
712         if (IsIconic(g_Globals._hMainWnd))
713             ShowWindow(g_Globals._hMainWnd, SW_RESTORE);
714         else
715             SetForegroundWindow(g_Globals._hMainWnd);
716 
717         return;
718     }
719 
720     g_Globals._prescan_nodes = false;
721 
722     cmd._mdi =  true;
723     cmd._cmdShow = cmdShow;
724 
725      // parse command line options, which may overwrite the MDI flag
726     if (lpCmdLine)
727         cmd.ParseCmdLine(lpCmdLine);
728 
729      // create main window
730     MainFrameBase::Create(cmd);
731 }
732 
ParseCmdLine(LPCTSTR lpCmdLine)733 bool ExplorerCmd::ParseCmdLine(LPCTSTR lpCmdLine)
734 {
735     bool ok = true;
736 
737     LPCTSTR b = lpCmdLine;
738     LPCTSTR p = b;
739 
740     while(*b) {
741          // remove leading space
742         while(_istspace((unsigned)*b))
743             ++b;
744 
745         p = b;
746 
747         bool quote = false;
748 
749          // options are separated by ','
750         for(; *p; ++p) {
751             if (*p == '"')    // Quote characters may appear at any position in the command line.
752                 quote = !quote;
753             else if (*p==',' && !quote)
754                 break;
755         }
756 
757         if (p > b) {
758             int l = p - b;
759 
760              // remove trailing space
761             while(l>0 && _istspace((unsigned)b[l-1]))
762                 --l;
763 
764             if (!EvaluateOption(String(b, l)))
765                 ok = false;
766 
767             if (*p)
768                 ++p;
769 
770             b = p;
771         }
772     }
773 
774     return ok;
775 }
776 
EvaluateOption(LPCTSTR option)777 bool ExplorerCmd::EvaluateOption(LPCTSTR option)
778 {
779     String opt_str;
780 
781      // Remove quote characters, as they are evaluated at this point.
782     for(; *option; ++option)
783         if (*option != '"')
784             opt_str += *option;
785 
786     option = opt_str;
787 
788     if (option[0] == '/') {
789         ++option;
790 
791          // option /e for windows in explorer mode
792         if (!_tcsicmp(option, TEXT("e")))
793             _flags |= OWM_EXPLORE;
794          // option /root for rooted explorer windows
795         else if (!_tcsicmp(option, TEXT("root")))
796             _flags |= OWM_ROOTED;
797          // non-standard options: /mdi, /sdi
798         else if (!_tcsicmp(option, TEXT("mdi")))
799             _mdi = true;
800         else if (!_tcsicmp(option, TEXT("sdi")))
801             _mdi = false;
802         else if (!_tcsicmp(option, TEXT("n")))
803         {
804             // Do nothing
805         }
806         else if (!_tcsicmp(option, TEXT("select")))
807         {
808             SelectOpt = TRUE;
809         }
810         else
811             return false;
812 
813     } else {
814         if (!_path.empty())
815             return false;
816 
817         if((SelectOpt == TRUE) && (PathFileExists(option)))
818         {
819             WCHAR szDir[MAX_PATH];
820 
821             _wsplitpath(option, szPath, szDir, NULL, NULL);
822             wcscat(szPath, szDir);
823             PathRemoveBackslash(szPath);
824             _path = szPath;
825             SelectOpt = FALSE;
826         }
827         else
828             _path = opt_str;
829     }
830 
831     return true;
832 }
833 
IsValidPath() const834 bool ExplorerCmd::IsValidPath() const
835 {
836     if (!_path.empty()) {
837         DWORD attribs = GetFileAttributes(_path);
838 
839         if (attribs!=INVALID_FILE_ATTRIBUTES && (attribs&FILE_ATTRIBUTE_DIRECTORY))
840             return true;    // file system path
841         else if (*_path==':' && _path.at(1)==':')
842             return true;    // text encoded IDL
843     }
844 
845     return false;
846 }
847 
848 #else
849 
explorer_show_frame(int cmdShow,LPTSTR lpCmdLine)850 void explorer_show_frame(int cmdShow, LPTSTR lpCmdLine)
851 {
852     if (!lpCmdLine)
853         lpCmdLine = TEXT("explorer.exe");
854 
855     launch_file(GetDesktopWindow(), lpCmdLine, cmdShow);
856 }
857 
858 #endif
859 
860 
PopupMenu(UINT nid)861 PopupMenu::PopupMenu(UINT nid)
862 {
863     HMENU hMenu = LoadMenu(g_Globals._hInstance, MAKEINTRESOURCE(nid));
864     _hmenu = GetSubMenu(hMenu, 0);
865     RemoveMenu(hMenu, 0, MF_BYPOSITION);
866     DestroyMenu(hMenu);
867 }
868 
869 
870  /// "About Explorer" Dialog
871 struct ExplorerAboutDlg : public
872             CtlColorParent<
873                 OwnerDrawParent<Dialog>
874             >
875 {
876     typedef CtlColorParent<
877                 OwnerDrawParent<Dialog>
878             > super;
879 
ExplorerAboutDlgExplorerAboutDlg880     ExplorerAboutDlg(HWND hwnd)
881      :    super(hwnd)
882     {
883         SetWindowIcon(hwnd, IDI_REACTOS);
884 
885         new FlatButton(hwnd, IDOK);
886 
887         _hfont = CreateFont(20, 0, 0, 0, FW_BOLD, TRUE, 0, 0, 0, 0, 0, 0, 0, TEXT("Sans Serif"));
888         new ColorStatic(hwnd, IDC_ROS_EXPLORER, RGB(32,32,128), 0, _hfont);
889 
890         new HyperlinkCtrl(hwnd, IDC_WWW);
891 
892         FmtString ver_txt(ResString(IDS_EXPLORER_VERSION_STR), (LPCTSTR)ResString(IDS_VERSION_STR));
893         SetWindowText(GetDlgItem(hwnd, IDC_VERSION_TXT), ver_txt);
894 
895         HWND hwnd_winver = GetDlgItem(hwnd, IDC_WIN_VERSION);
896         SetWindowText(hwnd_winver, get_windows_version_str());
897         SetWindowFont(hwnd_winver, GetStockFont(DEFAULT_GUI_FONT), FALSE);
898 
899         CenterWindow(hwnd);
900     }
901 
~ExplorerAboutDlgExplorerAboutDlg902     ~ExplorerAboutDlg()
903     {
904         DeleteObject(_hfont);
905     }
906 
WndProcExplorerAboutDlg907     LRESULT WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
908     {
909         switch(nmsg) {
910           case WM_PAINT:
911             Paint();
912             break;
913 
914           default:
915             return super::WndProc(nmsg, wparam, lparam);
916         }
917 
918         return 0;
919     }
920 
PaintExplorerAboutDlg921     void Paint()
922     {
923         PaintCanvas canvas(_hwnd);
924 
925         HICON hicon = (HICON) LoadImage(g_Globals._hInstance, MAKEINTRESOURCE(IDI_REACTOS_BIG), IMAGE_ICON, 0, 0, LR_SHARED);
926 
927         DrawIconEx(canvas, 20, 10, hicon, 0, 0, 0, 0, DI_NORMAL);
928     }
929 
930 protected:
931     HFONT    _hfont;
932 };
933 
explorer_about(HWND hwndParent)934 void explorer_about(HWND hwndParent)
935 {
936     Dialog::DoModal(IDD_ABOUT_EXPLORER, WINDOW_CREATOR(ExplorerAboutDlg), hwndParent);
937 }
938 
939 
InitInstance(HINSTANCE hInstance)940 static void InitInstance(HINSTANCE hInstance)
941 {
942     CONTEXT("InitInstance");
943 
944     setlocale(LC_COLLATE, "");    // set collating rules to local settings for compareName
945 
946 #ifndef ROSSHELL
947      // register frame window class
948     g_Globals._hframeClass = IconWindowClass(CLASSNAME_FRAME,IDI_EXPLORER);
949 
950      // register child window class
951     WindowClass(CLASSNAME_CHILDWND, CS_CLASSDC|CS_DBLCLKS).Register();
952 
953      // register tree window class
954     WindowClass(CLASSNAME_WINEFILETREE, CS_CLASSDC|CS_DBLCLKS).Register();
955 #endif
956 
957     g_Globals._cfStrFName = RegisterClipboardFormat(CFSTR_FILENAME);
958 }
959 
960 
explorer_main(HINSTANCE hInstance,LPTSTR lpCmdLine,int cmdShow)961 int explorer_main(HINSTANCE hInstance, LPTSTR lpCmdLine, int cmdShow)
962 {
963     CONTEXT("explorer_main");
964 
965      // initialize Common Controls library
966     CommonControlInit usingCmnCtrl;
967 
968     try {
969         InitInstance(hInstance);
970     } catch(COMException& e) {
971         HandleException(e, GetDesktopWindow());
972         return -1;
973     }
974 
975 #ifndef ROSSHELL
976     if (cmdShow != SW_HIDE) {
977 /*    // don't maximize if being called from the ROS desktop
978         if (cmdShow == SW_SHOWNORMAL)
979                 ///@todo read window placement from registry
980             cmdShow = SW_MAXIMIZE;
981 */
982 
983         explorer_show_frame(cmdShow, lpCmdLine);
984     }
985 #endif
986 
987     Window::MessageLoop();
988 
989     return 1;
990 }
991 
992 
SetShellReadyEvent(LPCTSTR evtName)993 static bool SetShellReadyEvent(LPCTSTR evtName)
994 {
995     HANDLE hEvent = OpenEvent(EVENT_MODIFY_STATE, FALSE, evtName);
996     if (!hEvent)
997         return false;
998 
999     SetEvent(hEvent);
1000     CloseHandle(hEvent);
1001 
1002     return true;
1003 }
1004 
1005 
_tWinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPTSTR lpCmdLine,int nShowCmd)1006 int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nShowCmd)
1007 {
1008     CONTEXT("WinMain()");
1009 
1010     BOOL any_desktop_running = IsAnyDesktopRunning();
1011 
1012     BOOL startup_desktop;
1013 
1014     // strip extended options from the front of the command line
1015     String ext_options;
1016 
1017     while(*lpCmdLine == '-') {
1018         while(*lpCmdLine && !_istspace((unsigned)*lpCmdLine))
1019             ext_options += *lpCmdLine++;
1020 
1021         while(_istspace((unsigned)*lpCmdLine))
1022             ++lpCmdLine;
1023     }
1024 
1025      // command line option "-install" to replace previous shell application with ROS Explorer
1026     if (_tcsstr(ext_options,TEXT("-install"))) {
1027         // install ROS Explorer into the registry
1028         TCHAR path[MAX_PATH];
1029 
1030         int l = GetModuleFileName(0, path, COUNTOF(path));
1031         if (l) {
1032             HKEY hkey;
1033 
1034             if (!RegOpenKey(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon"), &hkey)) {
1035 
1036                 ///@todo save previous shell application in config file
1037 
1038                 RegSetValueEx(hkey, TEXT("Shell"), 0, REG_SZ, (LPBYTE)path, l*sizeof(TCHAR));
1039                 RegCloseKey(hkey);
1040             }
1041 
1042             if (!RegOpenKey(HKEY_CURRENT_USER, TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon"), &hkey)) {
1043 
1044                 ///@todo save previous shell application in config file
1045 
1046                 RegSetValueEx(hkey, TEXT("Shell"), 0, REG_SZ, (LPBYTE)TEXT(""), l*sizeof(TCHAR));
1047                 RegCloseKey(hkey);
1048             }
1049         }
1050 
1051         HWND shellWindow = GetShellWindow();
1052 
1053         if (shellWindow) {
1054             DWORD pid;
1055 
1056             // terminate shell process for NT like systems
1057             GetWindowThreadProcessId(shellWindow, &pid);
1058             HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
1059 
1060              // On Win 9x it's sufficient to destroy the shell window.
1061             DestroyWindow(shellWindow);
1062 
1063             if (TerminateProcess(hProcess, 0))
1064                 WaitForSingleObject(hProcess, INFINITE);
1065 
1066             CloseHandle(hProcess);
1067         }
1068 
1069         startup_desktop = TRUE;
1070     } else {
1071          // create desktop window and task bar only, if there is no other shell and we are
1072          // the first explorer instance
1073          // MS Explorer looks additionally into the registry entry HKCU\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\shell,
1074          // to decide wether it is currently configured as shell application.
1075         startup_desktop = !any_desktop_running;
1076     }
1077 
1078 
1079     bool autostart = !any_desktop_running;
1080 
1081     // disable autostart if the SHIFT key is pressed
1082     if (GetAsyncKeyState(VK_SHIFT) < 0)
1083         autostart = false;
1084 
1085 #ifdef _DEBUG    //MF: disabled for debugging
1086     autostart = false;
1087 #endif
1088 
1089     // If there is given the command line option "-desktop", create desktop window anyways
1090     if (_tcsstr(ext_options,TEXT("-desktop")))
1091         startup_desktop = TRUE;
1092 #ifndef ROSSHELL
1093     else if (_tcsstr(ext_options,TEXT("-nodesktop")))
1094         startup_desktop = FALSE;
1095 
1096     // Don't display cabinet window in desktop mode
1097     if (startup_desktop && !_tcsstr(ext_options,TEXT("-explorer")))
1098         nShowCmd = SW_HIDE;
1099 #endif
1100 
1101     if (_tcsstr(ext_options,TEXT("-noautostart")))
1102         autostart = false;
1103     else if (_tcsstr(ext_options,TEXT("-autostart")))
1104         autostart = true;
1105 
1106 #ifndef __WINE__
1107     if (_tcsstr(ext_options,TEXT("-console"))) {
1108         AllocConsole();
1109 
1110         _dup2(_open_osfhandle((long)GetStdHandle(STD_INPUT_HANDLE), _O_RDONLY), 0);
1111         _dup2(_open_osfhandle((long)GetStdHandle(STD_OUTPUT_HANDLE), 0), 1);
1112         _dup2(_open_osfhandle((long)GetStdHandle(STD_ERROR_HANDLE), 0), 2);
1113 
1114         g_Globals._log = _fdopen(1, "w");
1115         setvbuf(g_Globals._log, 0, _IONBF, 0);
1116 
1117         LOG(TEXT("starting explorer debug log\n"));
1118     }
1119 #endif
1120 
1121 
1122     if (startup_desktop) {
1123          // hide the XP login screen (Credit to Nicolas Escuder)
1124          // another undocumented event: "Global\\msgina: ReturnToWelcome"
1125         if (!SetShellReadyEvent(TEXT("msgina: ShellReadyEvent")))
1126             SetShellReadyEvent(TEXT("Global\\msgina: ShellReadyEvent"));
1127     }
1128 #ifdef ROSSHELL
1129     else
1130         return 0;    // no shell to launch, so exit immediatelly
1131 #endif
1132 
1133 
1134     if (!any_desktop_running) {
1135         // launch the shell DDE server
1136         if (g_SHDOCVW_ShellDDEInit)
1137             (*g_SHDOCVW_ShellDDEInit)(TRUE);
1138     }
1139 
1140 
1141     bool use_gdb_stub = false;    // !IsDebuggerPresent();
1142 
1143     if (_tcsstr(ext_options,TEXT("-debug")))
1144         use_gdb_stub = true;
1145 
1146     if (_tcsstr(ext_options,TEXT("-break"))) {
1147         LOG(TEXT("debugger breakpoint"));
1148         __debugbreak();
1149     }
1150 
1151 #ifdef _M_IX86
1152     // activate GDB remote debugging stub if no other debugger is running
1153     if (use_gdb_stub) {
1154         LOG(TEXT("waiting for debugger connection...\n"));
1155 
1156         initialize_gdb_stub();
1157     }
1158 #endif
1159 
1160     g_Globals.init(hInstance);
1161 
1162     // initialize COM and OLE before creating the desktop window
1163     OleInit usingCOM;
1164 
1165     // init common controls library
1166     CommonControlInit usingCmnCtrl;
1167 
1168     g_Globals.read_persistent();
1169 
1170     if (startup_desktop) {
1171         WaitCursor wait;
1172 
1173         g_Globals._desktops.init();
1174 
1175         g_Globals._hwndDesktop = DesktopWindow::Create();
1176 #ifdef _USE_HDESK
1177         g_Globals._desktops.get_current_Desktop()->_hwndDesktop = g_Globals._hwndDesktop;
1178 #endif
1179     }
1180 
1181     if (_tcsstr(ext_options,TEXT("-?"))) {
1182         MessageBoxA(g_Globals._hwndDesktop,
1183             "/e        open cabinet window in explorer mode\r\n"
1184             "/root        open cabinet window in rooted mode\r\n"
1185             "/mdi        open cabinet window in MDI mode\r\n"
1186             "/sdi        open cabinet window in SDI mode\r\n"
1187             "\r\n"
1188             "-?        display command line options\r\n"
1189             "\r\n"
1190             "-desktop        start in desktop mode regardless of an already running shell\r\n"
1191             "-nodesktop    disable desktop mode\r\n"
1192             "-explorer        display cabinet window regardless of enabled desktop mode\r\n"
1193             "\r\n"
1194             "-install        replace previous shell application with ROS Explorer\r\n"
1195             "\r\n"
1196             "-noautostart    disable autostarts\r\n"
1197             "-autostart    enable autostarts regardless of debug build\r\n"
1198             "\r\n"
1199             "-console        open debug console\r\n"
1200             "\r\n"
1201             "-debug        activate GDB remote debugging stub\r\n"
1202             "-break        activate debugger breakpoint\r\n",
1203             "ROS Explorer - command line options", MB_OK);
1204     }
1205 
1206     /*
1207      * Set our shutdown parameters: we want to shutdown the very last,
1208      * but before any TaskMgr instance (which has a shutdown level of 1).
1209      */
1210     SetProcessShutdownParameters(2, 0);
1211 
1212     Thread* pSSOThread = NULL;
1213 
1214     if (startup_desktop) {
1215         // launch SSO thread to allow message processing independent from the explorer main thread
1216         pSSOThread = new SSOThread;
1217         pSSOThread->Start();
1218     }
1219 
1220     /**TODO launching autostart programs can be moved into a background thread. */
1221     if (autostart) {
1222         const char* argv[] = {"", "s"};    // call startup routine in SESSION_START mode
1223         startup(2, argv);
1224     }
1225 
1226 #ifndef ROSSHELL
1227     if (g_Globals._hwndDesktop)
1228         g_Globals._desktop_mode = true;
1229 #endif
1230 
1231 
1232     int ret = explorer_main(hInstance, lpCmdLine, nShowCmd);
1233 
1234 
1235     // write configuration file
1236     g_Globals.write_persistent();
1237 
1238     if (pSSOThread) {
1239         pSSOThread->Stop();
1240         delete pSSOThread;
1241     }
1242 
1243     if (!any_desktop_running) {
1244         // shutdown the shell DDE server
1245         if (g_SHDOCVW_ShellDDEInit)
1246             (*g_SHDOCVW_ShellDDEInit)(FALSE);
1247     }
1248 
1249     return ret;
1250 }
1251