update for HEAD-2003091401
[reactos.git] / subsys / system / explorer / utility / shellclasses.h
1 /*
2  * Copyright 2003 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.h
24  //
25  // C++ wrapper classes for COM interfaces and shell objects
26  //
27  // Martin Fuchs, 20.07.2003
28  //
29
30
31  // windows shell headers
32 #include <shellapi.h>
33 #include <shlobj.h>
34
35 /*@@
36 #if _MSC_VER>=1300      // VS.Net
37 #include <comdefsp.h>
38 using namespace _com_util;
39 #endif
40 */
41
42 #ifndef _INC_COMUTIL    // is comutil.h of MS headers not available?
43 #ifndef _NO_COMUTIL
44 #define _NO_COMUTIL
45 #endif
46 #endif
47
48
49  // COM Exception Handling
50
51 #ifndef _NO_COMUTIL
52
53 #define COMException _com_error
54
55 #else
56
57 struct COMException
58 {
59         COMException(HRESULT hr)
60          :      _hr(hr)
61         {
62         }
63
64         LPCTSTR ErrorMessage() const
65         {
66                 if (_msg.empty()) {
67                         LPTSTR pBuf;
68
69                         if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
70                                 0, _hr, MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), (LPSTR)&pBuf, 0, NULL)) {
71                                 _msg = pBuf;
72                                 LocalFree(pBuf);
73                          } else {
74                                 TCHAR buffer[128];
75                                 _stprintf(buffer, _T("unknown COM Exception: 0x%08X"), _hr);
76                                 _msg = buffer;
77                          }
78                 }
79
80                 return _msg;
81         }
82
83 protected:
84         HRESULT _hr;
85         mutable String _msg;
86 };
87
88 inline void CheckError(HRESULT hr)
89 {
90         if (FAILED(hr)) {
91                 throw COMException(hr);
92         }
93 }
94
95 #endif
96
97
98  // COM Initialisation
99
100 struct ComInit
101 {
102         ComInit()
103         {
104                 CheckError(CoInitialize(0));
105         }
106
107 #if (_WIN32_WINNT>=0x0400) || defined(_WIN32_DCOM)
108         ComInit(DWORD flag)
109         {
110                 CheckError(CoInitializeEx(0, flag));
111         }
112 #endif
113
114         ~ComInit()
115         {
116                 CoUninitialize();
117         }
118 };
119
120
121  // OLE initialisation for drag drop support
122
123 struct OleInit
124 {
125         OleInit()
126         {
127                 CheckError(OleInitialize(0));
128         }
129
130         ~OleInit()
131         {
132                 OleUninitialize();
133         }
134 };
135
136
137  // Exception Handler for COM exceptions
138
139 extern void HandleException(COMException& e, HWND hwnd);
140
141
142  // We use a common IMalloc object for all shell memory allocations.
143
144 struct CommonShellMalloc
145 {
146         CommonShellMalloc()
147         {
148                 _p = 0;
149         }
150
151         void init()
152         {
153                 if (!_p)
154                         CheckError(SHGetMalloc(&_p));
155         }
156
157         ~CommonShellMalloc()
158         {
159                 if (_p)
160                         _p->Release();
161         }
162
163         operator IMalloc*()
164         {
165                 return _p;
166         }
167
168         IMalloc* _p;
169 };
170
171
172  // wrapper class for IMalloc with usage of common allocator
173
174 struct ShellMalloc
175 {
176         ShellMalloc()
177         {
178                  // initialize s_cmn_shell_malloc
179                 s_cmn_shell_malloc.init();
180         }
181
182         IMalloc* operator->()
183         {
184                 return s_cmn_shell_malloc;
185         }
186
187         static CommonShellMalloc s_cmn_shell_malloc;
188 };
189
190
191  // wrapper template class for pointers to shell objects managed by IMalloc
192
193 template<typename T> struct SShellPtr
194 {
195         ~SShellPtr()
196         {
197                 _malloc->Free(_p);
198         }
199
200         T* operator->()
201         {
202                 return _p;
203         }
204
205         T const* operator->() const
206         {
207                 return _p;
208         }
209
210         operator T const *() const
211         {
212                 return _p;
213         }
214
215         const T& operator*() const
216         {
217                 return *_p;
218         }
219
220         T& operator*()
221         {
222                 return *_p;
223         }
224
225 protected:
226         SShellPtr()
227          :      _p(0)
228         {
229         }
230
231         SShellPtr(T* p)
232          :      _p(p)
233         {
234         }
235
236         void Free()
237         {
238                 _malloc->Free(_p);
239                 _p = 0;
240         }
241
242         T* _p;
243         mutable ShellMalloc _malloc;    // IMalloc memory management object
244
245 private:
246          // disallow copying of SShellPtr objects
247         SShellPtr(const SShellPtr&) {}
248         void operator=(SShellPtr const&) {}
249 };
250
251
252  // wrapper class for COM interface pointers
253
254 template<typename T> struct SIfacePtr
255 {
256         SIfacePtr()
257          :      _p(0)
258         {
259         }
260
261         SIfacePtr(T* p)
262          :      _p(p)
263         {
264                 if (p)
265                         p->AddRef();
266         }
267
268         ~SIfacePtr()
269         {
270                 Free();
271         }
272
273         T* operator->()
274         {
275                 return _p;
276         }
277
278         const T* operator->() const
279         {
280                 return _p;
281         }
282
283 /* not GCC compatible
284         operator const T*() const
285         {
286                 return _p;
287         } */
288
289         operator T*()
290         {
291                 return _p;
292         }
293
294         T** operator&()
295         {
296                 return &_p;
297         }
298
299         bool empty() const      //NOTE: GCC seems not to work correctly when defining operator bool() AND operator T*()
300         {
301                 return !_p;
302         }
303
304         SIfacePtr& operator=(T* p)
305         {
306                 Free();
307                 p->AddRef();
308                 _p = p;
309
310                 return *this;
311         }
312
313         void operator=(SIfacePtr const& o)
314         {
315                 T* h = _p;
316
317                 if (o._p)
318                         o._p->AddRef();
319
320                 _p = o._p;
321
322                 if (h)
323                         h->Release();
324         }
325
326         void Free()
327         {
328                 T* h = _p;
329                 _p = 0;
330
331                 if (h)
332                         h->Release();
333         }
334
335 protected:
336         SIfacePtr(const SIfacePtr& o)
337          :      _p(o._p)
338         {
339                 if (_p)
340                         _p->AddRef();
341         }
342
343         T* _p;
344 };
345
346
347
348  // caching of desktop ShellFolder object
349
350 struct ShellFolder;
351
352 struct CommonDesktop
353 {
354         CommonDesktop()
355         {
356                 _desktop = 0;
357         }
358
359         ~CommonDesktop();
360
361         void init();
362
363         operator struct ShellFolder&()
364         {
365                 return *_desktop;
366         }
367
368 protected:
369         ShellFolder* _desktop;
370 };
371
372
373 #ifndef _NO_COMUTIL     // _com_ptr available?
374
375 struct ShellFolder : public IShellFolderPtr     // IShellFolderPtr uses intrinsic extensions of the VC++ compiler.
376 {
377         typedef IShellFolderPtr super;
378
379         ShellFolder();  // desktop folder
380         ShellFolder(IShellFolder* p);
381         ShellFolder(IShellFolder* parent, LPCITEMIDLIST pidl);
382         ShellFolder(LPCITEMIDLIST pidl);
383
384         void    attach(IShellFolder* parent, LPCITEMIDLIST pidl);
385         String  get_name(LPCITEMIDLIST pidl=NULL, SHGDNF flags=SHGDN_NORMAL) const;
386
387         bool    empty() const {return !operator bool();}        //NOTE: see SIfacePtr::empty()
388 };
389
390 #else // _com_ptr not available -> use SIfacePtr
391
392 struct ShellFolder : public SIfacePtr<IShellFolder>
393 {
394         typedef SIfacePtr<IShellFolder> super;
395
396         ShellFolder();
397         ShellFolder(IShellFolder* p);
398         ShellFolder(IShellFolder* parent, LPCITEMIDLIST pidl);
399         ShellFolder(LPCITEMIDLIST pidl);
400
401         void    attach(IShellFolder* parent, LPCITEMIDLIST pidl);
402         String  get_name(LPCITEMIDLIST pidl, SHGDNF flags=SHGDN_NORMAL) const;
403 };
404
405 #endif
406
407
408 extern ShellFolder& Desktop();
409
410
411 #ifdef UNICODE
412 #define path_from_pidl path_from_pidlW
413 #else
414 #define path_from_pidl path_from_pidlA
415 #endif
416
417 extern HRESULT path_from_pidlA(IShellFolder* folder, LPCITEMIDLIST pidl, LPSTR buffer, int len);
418 extern HRESULT path_from_pidlW(IShellFolder* folder, LPCITEMIDLIST pidl, LPWSTR buffer, int len);
419 extern HRESULT name_from_pidl(IShellFolder* folder, LPCITEMIDLIST pidl, LPTSTR buffer, int len, SHGDNF flags);
420
421
422  // wrapper class for item ID lists
423
424 struct ShellPath : public SShellPtr<ITEMIDLIST>
425 {
426         typedef SShellPtr<ITEMIDLIST> super;
427
428         ShellPath()
429         {
430         }
431
432         ShellPath(IShellFolder* folder, LPCWSTR path)
433         {
434                 ULONG l;
435                 CheckError(folder->ParseDisplayName(0, 0, (LPOLESTR)path, &l, &_p, 0));
436         }
437
438         ShellPath(LPCWSTR path)
439         {
440                 ULONG l;
441                 CheckError(Desktop()->ParseDisplayName(0, 0, (LPOLESTR)path, &l, &_p, 0));
442         }
443
444         ShellPath(IShellFolder* folder, LPCSTR path)
445         {
446                 ULONG l;
447                 WCHAR b[MAX_PATH];
448
449                 MultiByteToWideChar(CP_ACP, 0, path, -1, b, MAX_PATH);
450                 CheckError(folder->ParseDisplayName(0, 0, b, &l, &_p, 0));
451         }
452
453         ShellPath(LPCSTR path)
454         {
455                 ULONG l;
456                 WCHAR b[MAX_PATH];
457
458                 MultiByteToWideChar(CP_ACP, 0, path, -1, b, MAX_PATH);
459                 CheckError(Desktop()->ParseDisplayName(0, 0, b, &l, &_p, 0));
460         }
461
462         ShellPath(const ShellPath& o)
463          :      super(NULL)
464         {
465                 if (o._p) {
466                         int l = _malloc->GetSize(o._p);
467                         _p = (ITEMIDLIST*) _malloc->Alloc(l);
468                         memcpy(_p, o._p, l);
469                 }
470         }
471
472         ShellPath(ITEMIDLIST* p)
473          :      SShellPtr<ITEMIDLIST>(p)
474         {
475         }
476
477         void operator=(const ShellPath& o)
478         {
479                 ITEMIDLIST* h = _p;
480
481                 if (o._p) {
482                         int l = _malloc->GetSize(o._p);
483
484                         _p = (ITEMIDLIST*)_malloc->Alloc(l);
485                         memcpy(_p, o._p, l);
486                 }
487                 else
488                         _p = 0;
489
490                 _malloc->Free(h);
491         }
492
493         void operator=(ITEMIDLIST* p)
494         {
495                 ITEMIDLIST* h = _p;
496
497                 if (p) {
498                         int l = _malloc->GetSize(p);
499
500                         _p = (ITEMIDLIST*)_malloc->Alloc(l);
501                         memcpy(_p, p, l);
502                 }
503                 else
504                         _p = 0;
505
506                 _malloc->Free(h);
507         }
508
509         void operator=(const SHITEMID& o)
510         {
511                 ITEMIDLIST* h = _p;
512
513                 LPBYTE p = (LPBYTE)_malloc->Alloc(o.cb+2);
514                 *(PWORD)((LPBYTE)memcpy(p, &o, o.cb)+o.cb) = 0;
515                 _p = (ITEMIDLIST*)p;
516
517                 _malloc->Free(h);
518         }
519
520         void operator+=(const SHITEMID& o)
521         {
522                 int l0 = _malloc->GetSize(_p);
523                 LPBYTE p = (LPBYTE)_malloc->Alloc(l0+o.cb);
524                 int l = l0 - 2;
525
526                 memcpy(p, _p, l);
527                 *(PWORD)((LPBYTE)memcpy(p+l, &o, o.cb)+o.cb) = 0;
528
529                 _malloc->Free(_p);
530                 _p = (ITEMIDLIST*)p;
531         }
532
533         void assign(ITEMIDLIST* pidl, size_t size)
534         {
535                 ITEMIDLIST* h = _p;
536
537                 _p = (ITEMIDLIST*) _malloc->Alloc(size+sizeof(USHORT/*SHITEMID::cb*/));
538                 memcpy(_p, pidl, size);
539                 ((ITEMIDLIST*)((LPBYTE)_p+size))->mkid.cb = 0; // terminator
540
541                 _malloc->Free(h);
542         }
543
544         void assign(ITEMIDLIST* pidl)
545         {
546                 ITEMIDLIST* h = _p;
547
548                 if (pidl) {
549                         int l = _malloc->GetSize(pidl);
550                         _p = (ITEMIDLIST*)_malloc->Alloc(l);
551                         memcpy(_p, pidl, l);
552                 } else
553                         _p = 0; 
554
555                 _malloc->Free(h);
556         }
557
558         void split(ShellPath& parent, ShellPath& obj) const
559         {
560                 SHITEMID *piid, *piidLast;
561                 int size = 0;
562     
563                  // find last item-id and calculate total size of pidl
564                 for(piid=piidLast=&_p->mkid; piid->cb; ) {
565                         piidLast = piid;
566                         size += (piid->cb);
567                         piid = (SHITEMID*)((LPBYTE)piid + (piid->cb));
568                 }
569     
570                  // copy parent folder portion
571                 size -= piidLast->cb;  // don't count "object" item-id
572
573                 if (size > 0)
574                         parent.assign(_p, size);
575
576                  // copy "object" portion
577                 obj.assign((ITEMIDLIST*)piidLast, piidLast->cb);
578         }
579
580         void GetUIObjectOf(REFIID riid, LPVOID* ppvOut, HWND hWnd=0, ShellFolder& sf=Desktop())
581         {
582                 ShellPath parent, obj;
583
584                 split(parent, obj);
585
586                 LPCITEMIDLIST idl = obj;
587
588                 if (parent && parent->mkid.cb)
589                          // use the IShellFolder of the parent
590                         CheckError(ShellFolder((IShellFolder*)sf,parent)->GetUIObjectOf(hWnd, 1, &idl, riid, 0, ppvOut));
591                 else // else use desktop folder
592                         CheckError(sf->GetUIObjectOf(hWnd, 1, &idl, riid, 0, ppvOut));
593         }
594
595         ShellFolder get_folder()
596         {
597                 return ShellFolder(_p);
598         }
599
600         ShellFolder get_folder(IShellFolder* parent)
601         {
602                 return ShellFolder(parent, _p);
603         }
604
605
606          // convert an item id list from relative to absolute (=relative to the desktop) format
607         LPITEMIDLIST create_absolute_pidl(LPCITEMIDLIST parent_pidl, HWND hwnd) const
608         {
609                  // create a new item id list with _p append behind parent_pidl
610                 int l1 = _malloc->GetSize((void*)parent_pidl) - sizeof(USHORT/*SHITEMID::cb*/);
611                 int l2 = _malloc->GetSize(_p);
612
613                 LPITEMIDLIST p = (LPITEMIDLIST) _malloc->Alloc(l1+l2);
614
615                 memcpy(p, parent_pidl, l1);
616                 memcpy((LPBYTE)p+l1, _p, l2);
617
618                 return p;
619         }
620 };
621
622
623 #ifdef __GCC__  // Wine doesn't know of unnamed union members and uses some macros instead.
624 #define UNION_MEMBER(x) DUMMYUNIONNAME.##x
625 #else
626 #define UNION_MEMBER(x) x
627 #endif
628
629
630  // encapsulation of STRRET structure for easy string retrieval with conversion
631
632 #ifdef UNICODE
633 #define StrRet StrRetW
634 #define tcscpyn wcscpyn
635 #else
636 #define StrRet StrRetA
637 #define tcscpyn strcpyn
638 #endif
639
640 extern LPSTR strcpyn(LPSTR dest, LPCSTR source, size_t count);
641 extern LPWSTR wcscpyn(LPWSTR dest, LPCWSTR source, size_t count);
642
643 struct StrRetA : public STRRET
644 {
645         ~StrRetA()
646         {
647                 if (uType == STRRET_WSTR)
648                         ShellMalloc()->Free(pOleStr);
649         }
650
651         void GetString(const SHITEMID& shiid, LPSTR b, int l)
652         {
653                 switch(uType) {
654                   case STRRET_WSTR:
655                         WideCharToMultiByte(CP_ACP, 0, UNION_MEMBER(pOleStr), -1, b, l, NULL, NULL);
656                         break;
657
658                   case STRRET_OFFSET:
659                         strcpyn(b, (LPCSTR)&shiid+UNION_MEMBER(uOffset), l);
660                         break;
661
662                   case STRRET_CSTR:
663                         strcpyn(b, UNION_MEMBER(cStr), l);
664                 }
665         }
666 };
667
668 struct StrRetW : public STRRET
669 {
670         ~StrRetW()
671         {
672                 if (uType == STRRET_WSTR)
673                         ShellMalloc()->Free(pOleStr);
674         }
675
676         void GetString(const SHITEMID& shiid, LPWSTR b, int l)
677         {
678                 switch(uType) {
679                   case STRRET_WSTR:
680                         wcscpyn(b, UNION_MEMBER(pOleStr), l);
681                         break;
682
683                   case STRRET_OFFSET:
684                         MultiByteToWideChar(CP_ACP, 0, (LPCSTR)&shiid+UNION_MEMBER(uOffset), -1, b, l);
685                         break;
686
687                   case STRRET_CSTR:
688                         MultiByteToWideChar(CP_ACP, 0, UNION_MEMBER(cStr), -1, b, l);
689                 }
690         }
691 };
692
693
694 class FileSysShellPath : public ShellPath
695 {
696         TCHAR   _fullpath[MAX_PATH];
697
698 protected:
699         FileSysShellPath() {_fullpath[0] = '\0';}
700
701 public:
702         FileSysShellPath(const ShellPath& o) : ShellPath(o) {_fullpath[0] = '\0';}
703
704         operator LPCTSTR() {SHGetPathFromIDList(_p, _fullpath); return _fullpath;}
705 };
706
707
708 struct FolderBrowser : public FileSysShellPath
709 {
710         FolderBrowser(HWND owner, UINT flags, LPCTSTR title, LPCITEMIDLIST root=0)
711         {
712                 _displayname[0] = '\0';
713                 _browseinfo.hwndOwner = owner;
714                 _browseinfo.pidlRoot = root;
715                 _browseinfo.pszDisplayName = _displayname;
716                 _browseinfo.lpszTitle = title;
717                 _browseinfo.ulFlags = flags;
718                 _browseinfo.lpfn = 0;
719                 _browseinfo.lParam = 0;
720                 _browseinfo.iImage = 0;
721
722                 _p = SHBrowseForFolder(&_browseinfo);
723         }
724
725         LPCTSTR GetDisplayName()
726         {
727                 return _displayname;
728         }
729
730         bool IsOK()
731         {
732                 return _p != 0;
733         }
734
735 private:
736         BROWSEINFO _browseinfo;
737         TCHAR   _displayname[MAX_PATH];
738 };
739
740
741 struct SpecialFolderPath : public ShellPath
742 {
743         SpecialFolderPath(int folder, HWND hwnd)
744         {
745                 /*HRESULT hr = */SHGetSpecialFolderLocation(hwnd, folder, &_p);
746         }
747 };
748
749 struct DesktopFolder : public SpecialFolderPath
750 {
751         DesktopFolder()
752          :      SpecialFolderPath(CSIDL_DESKTOP, 0)
753         {
754         }
755 };
756
757 struct SpecialFolder : public ShellFolder
758 {
759         SpecialFolder(int folder, HWND hwnd)
760          :      ShellFolder(Desktop(), SpecialFolderPath(folder, hwnd))
761         {
762         }
763 };
764
765
766 #if _WIN32_IE>=0x400 // is SHGetSpecialFolderPath() available?
767
768  /// file system path of special folder
769 struct SpecialFolderFSPath
770 {
771         SpecialFolderFSPath(int folder/*e.g. CSIDL_DESKTOP*/, HWND hwnd)
772         {
773                 _fullpath[0] = '\0';
774
775                 SHGetSpecialFolderPath(hwnd, _fullpath, folder, TRUE);
776         }
777
778         operator LPCTSTR()
779         {
780                 return _fullpath;
781         }
782
783 protected:
784         TCHAR   _fullpath[MAX_PATH];
785 };
786
787 #else // _WIN32_IE<0x400 -> use SHGetSpecialFolderLocation()
788
789 struct SpecialFolderFSPath : public FileSysShellPath
790 {
791         SpecialFolderFSPath(int folder, HWND hwnd)
792         {
793                 HRESULT hr = SHGetSpecialFolderLocation(hwnd, folder, &_p);
794         }
795 };
796
797 #endif
798
799
800  // wrapper class for enumerating shell namespace objects
801
802 struct ShellItemEnumerator : public SIfacePtr<IEnumIDList>
803 {
804         ShellItemEnumerator(IShellFolder* folder, DWORD flags=SHCONTF_FOLDERS|SHCONTF_NONFOLDERS|SHCONTF_INCLUDEHIDDEN)
805         {
806                 CheckError(folder->EnumObjects(0, flags, &_p));
807         }
808 };