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