move explorer and cmd. Add a few .rbuild files
[reactos.git] / reactos / base / shell / explorer / utility / shellclasses.cpp
1 /*
2 * Copyright 2003, 2004, 2005 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 // shellclasses.cpp
24 //
25 // C++ wrapper classes for COM interfaces and shell objects
26 //
27 // Martin Fuchs, 20.07.2003
28 //
29
30
31 #include <precomp.h>
32
33
34 #ifdef _MS_VER
35 #pragma comment(lib, "shell32") // link to shell32.dll
36 #endif
37
38
39 // work around GCC's wide string constant bug
40 #ifdef __GNUC__
41 const LPCTSTR sCFSTR_SHELLIDLIST = TEXT("Shell IDList Array");
42 #endif
43
44
45 // helper functions for string copying
46
47 /*LPSTR strcpyn(LPSTR dest, LPCSTR source, size_t count)
48 {
49 LPCSTR s;
50 LPSTR d = dest;
51
52 for(s=source; count&&(*d++=*s++); )
53 count--;
54
55 return dest;
56 }
57
58 LPWSTR wcscpyn(LPWSTR dest, LPCWSTR source, size_t count)
59 {
60 LPCWSTR s;
61 LPWSTR d = dest;
62
63 for(s=source; count&&(*d++=*s++); )
64 count--;
65
66 return dest;
67 }*/
68
69
70 String COMException::toString() const
71 {
72 TCHAR msg[4*BUFFER_LEN];
73 int l = 4*BUFFER_LEN;
74 LPTSTR p = msg;
75
76 int n = _stprintf_s2(p, l, TEXT("%s\nContext: %s"), super::ErrorMessage(), (LPCTSTR)_context.toString());
77 p += n;
78 l -= n;
79
80 if (_file)
81 p += _stprintf_s2(p, l, TEXT("\nLocation: %hs:%d"), _file, _line);
82
83 return msg;
84 }
85
86
87 /// Exception Handler for COM exceptions
88
89 void HandleException(COMException& e, HWND hwnd)
90 {
91 String msg = e.toString();
92
93 SetLastError(0);
94
95 if (hwnd && !IsWindowVisible(hwnd))
96 hwnd = 0;
97
98 MessageBox(hwnd, msg, TEXT("ShellClasses Exception"), MB_ICONHAND|MB_OK);
99
100 // If displaying the error message box _with_ parent was not successfull, display it now without a parent window.
101 if (GetLastError() == ERROR_INVALID_WINDOW_HANDLE)
102 MessageBox(0, msg, TEXT("ShellClasses Exception"), MB_ICONHAND|MB_OK);
103 }
104
105
106 // common IMalloc object
107
108 CommonShellMalloc ShellMalloc::s_cmn_shell_malloc;
109
110
111 // common desktop object
112
113 ShellFolder& GetDesktopFolder()
114 {
115 static CommonDesktop s_desktop;
116
117 // initialize s_desktop
118 s_desktop.init();
119
120 return s_desktop;
121 }
122
123
124 void CommonDesktop::init()
125 {
126 CONTEXT("CommonDesktop::init()");
127
128 if (!_desktop)
129 _desktop = new ShellFolder;
130 }
131
132 CommonDesktop::~CommonDesktop()
133 {
134 if (_desktop)
135 delete _desktop;
136 }
137
138
139 HRESULT path_from_pidlA(IShellFolder* folder, LPCITEMIDLIST pidl, LPSTR buffer, int len)
140 {
141 CONTEXT("path_from_pidlA()");
142
143 StrRetA str;
144
145 HRESULT hr = folder->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &str);
146
147 if (SUCCEEDED(hr))
148 str.GetString(pidl->mkid, buffer, len);
149 else
150 buffer[0] = '\0';
151
152 return hr;
153 }
154
155 HRESULT path_from_pidlW(IShellFolder* folder, LPCITEMIDLIST pidl, LPWSTR buffer, int len)
156 {
157 CONTEXT("path_from_pidlW()");
158
159 StrRetW str;
160
161 HRESULT hr = folder->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &str);
162
163 if (SUCCEEDED(hr))
164 str.GetString(pidl->mkid, buffer, len);
165 else
166 buffer[0] = '\0';
167
168 return hr;
169 }
170
171 HRESULT name_from_pidl(IShellFolder* folder, LPCITEMIDLIST pidl, LPTSTR buffer, int len, SHGDNF flags)
172 {
173 CONTEXT("name_from_pidl()");
174
175 StrRet str;
176
177 HRESULT hr = folder->GetDisplayNameOf(pidl, flags, &str);
178
179 if (SUCCEEDED(hr))
180 str.GetString(pidl->mkid, buffer, len);
181 else
182 buffer[0] = '\0';
183
184 return hr;
185 }
186
187
188 #ifndef _NO_COMUTIL
189
190 ShellFolder::ShellFolder()
191 {
192 CONTEXT("ShellFolder::ShellFolder()");
193
194 IShellFolder* desktop;
195
196 CHECKERROR(SHGetDesktopFolder(&desktop));
197
198 super::Attach(desktop);
199 desktop->AddRef();
200 }
201
202 ShellFolder::ShellFolder(IShellFolder* p)
203 : super(p)
204 {
205 CONTEXT("ShellFolder::ShellFolder(IShellFolder*)");
206
207 p->AddRef();
208 }
209
210 ShellFolder::ShellFolder(IShellFolder* parent, LPCITEMIDLIST pidl)
211 {
212 CONTEXT("ShellFolder::ShellFolder(IShellFolder*, LPCITEMIDLIST)");
213
214 IShellFolder* ptr;
215
216 if (!pidl)
217 CHECKERROR(E_INVALIDARG);
218
219 if (pidl && pidl->mkid.cb)
220 CHECKERROR(parent->BindToObject(pidl, 0, IID_IShellFolder, (LPVOID*)&ptr));
221 else
222 ptr = parent;
223
224 super::Attach(ptr);
225 ptr->AddRef();
226 }
227
228 ShellFolder::ShellFolder(LPCITEMIDLIST pidl)
229 {
230 CONTEXT("ShellFolder::ShellFolder(LPCITEMIDLIST)");
231
232 IShellFolder* ptr;
233 IShellFolder* parent = GetDesktopFolder();
234
235 if (pidl && pidl->mkid.cb)
236 CHECKERROR(parent->BindToObject(pidl, 0, IID_IShellFolder, (LPVOID*)&ptr));
237 else
238 ptr = parent;
239
240 super::Attach(ptr);
241 ptr->AddRef();
242 }
243
244 void ShellFolder::attach(IShellFolder* parent, LPCITEMIDLIST pidl)
245 {
246 CONTEXT("ShellFolder::attach(IShellFolder*, LPCITEMIDLIST)");
247
248 IShellFolder* ptr;
249
250 if (pidl && pidl->mkid.cb)
251 CHECKERROR(parent->BindToObject(pidl, 0, IID_IShellFolder, (LPVOID*)&ptr));
252 else
253 ptr = parent;
254
255 super::Attach(ptr);
256 ptr->AddRef();
257 }
258
259 #else // _com_ptr not available -> use SIfacePtr
260
261 ShellFolder::ShellFolder()
262 {
263 CONTEXT("ShellFolder::ShellFolder()");
264
265 CHECKERROR(SHGetDesktopFolder(&_p));
266
267 _p->AddRef();
268 }
269
270 ShellFolder::ShellFolder(IShellFolder* p)
271 : super(p)
272 {
273 CONTEXT("ShellFolder::ShellFolder(IShellFolder*)");
274
275 _p->AddRef();
276 }
277
278 ShellFolder::ShellFolder(IShellFolder* parent, LPCITEMIDLIST pidl)
279 {
280 CONTEXT("ShellFolder::ShellFolder(IShellFolder*, LPCITEMIDLIST)");
281
282 if (pidl && pidl->mkid.cb)
283 CHECKERROR(parent->BindToObject(pidl, 0, IID_IShellFolder, (LPVOID*)&_p));
284 else
285 _p = GetDesktopFolder();
286
287 _p->AddRef();
288 }
289
290 ShellFolder::ShellFolder(LPCITEMIDLIST pidl)
291 {
292 CONTEXT("ShellFolder::ShellFolder(LPCITEMIDLIST)");
293
294 if (pidl && pidl->mkid.cb)
295 CHECKERROR(GetDesktopFolder()->BindToObject(pidl, 0, IID_IShellFolder, (LPVOID*)&_p));
296 else
297 _p = GetDesktopFolder();
298
299 _p->AddRef();
300 }
301
302 void ShellFolder::attach(IShellFolder* parent, LPCITEMIDLIST pidl)
303 {
304 CONTEXT("ShellFolder::ShellFolder(IShellFolder*, LPCITEMIDLIST)");
305
306 IShellFolder* h = _p;
307
308 CHECKERROR(parent->BindToObject(pidl, 0, IID_IShellFolder, (LPVOID*)&_p));
309
310 _p->AddRef();
311 h->Release();
312 }
313
314 #endif
315
316 String ShellFolder::get_name(LPCITEMIDLIST pidl, SHGDNF flags) const
317 {
318 CONTEXT("ShellFolder::get_name()");
319
320 TCHAR buffer[MAX_PATH];
321 StrRet strret;
322
323 HRESULT hr = ((IShellFolder*)*const_cast<ShellFolder*>(this))->GetDisplayNameOf(pidl, flags, &strret);
324
325 if (hr == S_OK)
326 strret.GetString(pidl->mkid, buffer, COUNTOF(buffer));
327 else {
328 CHECKERROR(hr);
329 *buffer = TEXT('\0');
330 }
331
332 return buffer;
333 }
334
335
336 void ShellPath::split(ShellPath& parent, ShellPath& obj) const
337 {
338 SHITEMID *piid, *piidLast;
339 int size = 0;
340
341 // find last item-id and calculate total size of pidl
342 for(piid=piidLast=&_p->mkid; piid->cb; ) {
343 piidLast = piid;
344 size += (piid->cb);
345 piid = (SHITEMID*)((LPBYTE)piid + (piid->cb));
346 }
347
348 // copy parent folder portion
349 size -= piidLast->cb; // don't count "object" item-id
350
351 if (size > 0)
352 parent.assign(_p, size);
353
354 // copy "object" portion
355 obj.assign((ITEMIDLIST*)piidLast, piidLast->cb);
356 }
357
358 void ShellPath::GetUIObjectOf(REFIID riid, LPVOID* ppvOut, HWND hWnd, ShellFolder& sf)
359 {
360 CONTEXT("ShellPath::GetUIObjectOf()");
361
362 ShellPath parent, obj;
363
364 split(parent, obj);
365
366 LPCITEMIDLIST idl = obj;
367
368 if (parent && parent->mkid.cb)
369 // use the IShellFolder of the parent
370 CHECKERROR(ShellFolder((IShellFolder*)sf,parent)->GetUIObjectOf(hWnd, 1, &idl, riid, 0, ppvOut));
371 else // else use desktop folder
372 CHECKERROR(sf->GetUIObjectOf(hWnd, 1, &idl, riid, 0, ppvOut));
373 }
374
375 #if 0 // ILCombine() was missing in previous versions of MinGW and is not exported from shell32.dll on Windows 2000.
376
377 // convert an item id list from relative to absolute (=relative to the desktop) format
378 ShellPath ShellPath::create_absolute_pidl(LPCITEMIDLIST parent_pidl) const
379 {
380 CONTEXT("ShellPath::create_absolute_pidl()");
381
382 return ILCombine(parent_pidl, _p);
383
384 /* seems to work only for NT upwards
385 // create a new item id list with _p append behind parent_pidl
386 int l1 = ILGetSize(parent_pidl) - sizeof(USHORT/ SHITEMID::cb /);
387 int l2 = ILGetSize(_p);
388
389 LPITEMIDLIST p = (LPITEMIDLIST) _malloc->Alloc(l1+l2);
390
391 memcpy(p, parent_pidl, l1);
392 memcpy((LPBYTE)p+l1, _p, l2);
393
394 return p;
395 */
396 }
397
398 #else
399
400 ShellPath ShellPath::create_absolute_pidl(LPCITEMIDLIST parent_pidl) const
401 {
402 CONTEXT("ShellPath::create_absolute_pidl()");
403
404 static DynamicFct<LPITEMIDLIST(WINAPI*)(LPCITEMIDLIST, LPCITEMIDLIST)> ILCombine(TEXT("SHELL32"), 25);
405
406 if (ILCombine)
407 return (*ILCombine)(parent_pidl, _p);
408
409 // create a new item id list with _p append behind parent_pidl
410 int l1 = ILGetSize(parent_pidl) - sizeof(USHORT/*SHITEMID::cb*/);
411 int l2 = ILGetSize(_p);
412
413 LPITEMIDLIST p = (LPITEMIDLIST) _malloc->Alloc(l1+l2);
414
415 memcpy(p, parent_pidl, l1);
416 memcpy((LPBYTE)p+l1, _p, l2);
417
418 return p;
419 }
420
421 #endif
422
423 // local implementation of ILGetSize() to replace missing export on Windows 2000
424 UINT ILGetSize_local(LPCITEMIDLIST pidl)
425 {
426 if (!pidl)
427 return 0;
428
429 int l = sizeof(USHORT/*SHITEMID::cb*/);
430
431 while(pidl->mkid.cb) {
432 l += pidl->mkid.cb;
433 pidl = LPCITEMIDLIST((LPBYTE)pidl+pidl->mkid.cb);
434 }
435
436 return l;
437 }
438
439
440 #ifndef _SHFOLDER_H_
441 #define CSIDL_FLAG_CREATE 0x8000
442 #endif
443
444 /// file system path of special folder
445 SpecialFolderFSPath::SpecialFolderFSPath(int folder, HWND hwnd)
446 {
447 _fullpath[0] = '\0';
448
449 #ifdef UNICODE
450 static DynamicFct<BOOL (__stdcall*)(HWND hwnd, LPTSTR pszPath, int csidl, BOOL fCreate)> s_pSHGetSpecialFolderPath(TEXT("shell32"), "SHGetSpecialFolderPathW");
451 #else
452 static DynamicFct<BOOL (__stdcall*)(HWND hwnd, LPTSTR pszPath, int csidl, BOOL fCreate)> s_pSHGetSpecialFolderPath(TEXT("shell32"), "SHGetSpecialFolderPathA");
453 #endif
454 if (*s_pSHGetSpecialFolderPath)
455 (*s_pSHGetSpecialFolderPath)(hwnd, _fullpath, folder, TRUE);
456 else {
457 // SHGetSpecialFolderPath() is not compatible to WIN95/NT4
458 #ifdef UNICODE
459 static DynamicFct<HRESULT (__stdcall*)(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath)> s_pSHGetFolderPath_shell32(TEXT("shell32"), "SHGetFolderPathW");
460 #else
461 static DynamicFct<HRESULT (__stdcall*)(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath)> s_pSHGetFolderPath_shell32(TEXT("shell32"), "SHGetFolderPathA");
462 #endif
463 if (*s_pSHGetFolderPath_shell32)
464 (*s_pSHGetFolderPath_shell32)(hwnd, folder|CSIDL_FLAG_CREATE, 0, 0, _fullpath);
465 else {
466 // SHGetFolderPath() is only present in shfolder.dll on some platforms.
467 #ifdef UNICODE
468 static DynamicLoadLibFct<HRESULT (__stdcall*)(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath)> s_pSHGetFolderPath_shfolder(TEXT("shfolder"), "SHGetFolderPathW");
469 #else
470 static DynamicLoadLibFct<HRESULT (__stdcall*)(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath)> s_pSHGetFolderPath_shfolder(TEXT("shfolder"), "SHGetFolderPathA");
471 #endif
472 if (*s_pSHGetFolderPath_shfolder)
473 (*s_pSHGetFolderPath_shfolder)(hwnd, folder|CSIDL_FLAG_CREATE, 0, 0, _fullpath);
474 }
475 }
476 }
477
478
479 void CtxMenuInterfaces::reset()
480 {
481 _pctxmenu2 = NULL;
482
483 #ifndef __MINGW32__ // IContextMenu3 missing in MinGW (as of 6.2.2005)
484 _pctxmenu3 = NULL;
485 #endif
486 }
487
488 bool CtxMenuInterfaces::HandleMenuMsg(UINT nmsg, WPARAM wparam, LPARAM lparam)
489 {
490 #ifndef __MINGW32__ // IContextMenu3 missing in MinGW (as of 6.2.2005)
491 if (_pctxmenu3) {
492 if (SUCCEEDED(_pctxmenu3->HandleMenuMsg(nmsg, wparam, lparam)))
493 return true;
494 }
495 #endif
496
497 if (_pctxmenu2)
498 if (SUCCEEDED(_pctxmenu2->HandleMenuMsg(nmsg, wparam, lparam)))
499 return true;
500
501 return false;
502 }
503
504 IContextMenu* CtxMenuInterfaces::query_interfaces(IContextMenu* pcm1)
505 {
506 IContextMenu* pcm = NULL;
507
508 reset();
509
510 // Get the higher version context menu interfaces.
511 #ifndef __MINGW32__ // IContextMenu3 missing in MinGW (as of 6.2.2005)
512 if (pcm1->QueryInterface(IID_IContextMenu3, (void**)&pcm) == NOERROR)
513 _pctxmenu3 = (LPCONTEXTMENU3)pcm;
514 else
515 #endif
516 if (pcm1->QueryInterface(IID_IContextMenu2, (void**)&pcm) == NOERROR)
517 _pctxmenu2 = (LPCONTEXTMENU2)pcm;
518
519 if (pcm) {
520 pcm1->Release();
521 return pcm;
522 } else
523 return pcm1;
524 }
525
526
527 HRESULT ShellFolderContextMenu(IShellFolder* shell_folder, HWND hwndParent, int cidl,
528 LPCITEMIDLIST* apidl, int x, int y, CtxMenuInterfaces& cm_ifs)
529 {
530 IContextMenu* pcm;
531
532 HRESULT hr = shell_folder->GetUIObjectOf(hwndParent, cidl, apidl, IID_IContextMenu, NULL, (LPVOID*)&pcm);
533 // HRESULT hr = CDefFolderMenu_Create2(dir?dir->_pidl:DesktopFolder(), hwndParent, 1, &pidl, shell_folder, NULL, 0, NULL, &pcm);
534
535 if (SUCCEEDED(hr)) {
536 pcm = cm_ifs.query_interfaces(pcm);
537
538 HMENU hmenu = CreatePopupMenu();
539
540 if (hmenu) {
541 hr = pcm->QueryContextMenu(hmenu, 0, FCIDM_SHVIEWFIRST, FCIDM_SHVIEWLAST, CMF_NORMAL|CMF_EXPLORE);
542
543 if (SUCCEEDED(hr)) {
544 UINT idCmd = TrackPopupMenu(hmenu, TPM_LEFTALIGN|TPM_RETURNCMD|TPM_RIGHTBUTTON, x, y, 0, hwndParent, NULL);
545
546 cm_ifs.reset();
547
548 if (idCmd) {
549 CMINVOKECOMMANDINFO cmi;
550
551 cmi.cbSize = sizeof(CMINVOKECOMMANDINFO);
552 cmi.fMask = 0;
553 cmi.hwnd = hwndParent;
554 cmi.lpVerb = (LPCSTR)(INT_PTR)(idCmd - FCIDM_SHVIEWFIRST);
555 cmi.lpParameters = NULL;
556 cmi.lpDirectory = NULL;
557 cmi.nShow = SW_SHOWNORMAL;
558 cmi.dwHotKey = 0;
559 cmi.hIcon = 0;
560
561 hr = pcm->InvokeCommand(&cmi);
562 }
563 } else
564 cm_ifs.reset();
565 }
566
567 pcm->Release();
568 }
569
570 return hr;
571 }