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