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.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  // work around GCC's wide string constant bug when compiling inline functions
49 #ifdef __GNUC__
50 extern const LPCTSTR sCFSTR_SHELLIDLIST;
51 #undef CFSTR_SHELLIDLIST
52 #define	CFSTR_SHELLIDLIST sCFSTR_SHELLIDLIST
53 #endif
54 
55 #ifdef _MSC_VER
56 #define	NOVTABLE __declspec(novtable)
57 #else
58 #define	NOVTABLE
59 #endif
60 #define	ANSUNC
61 
62 
63  // Exception Handling
64 
65 #ifndef _NO_COMUTIL
66 
67 #define	COMExceptionBase _com_error
68 
69 #else
70 
71  /// COM ExceptionBase class as replacement for _com_error
72 struct COMExceptionBase
73 {
COMExceptionBaseCOMExceptionBase74 	COMExceptionBase(HRESULT hr)
75 	 :	_hr(hr)
76 	{
77 	}
78 
ErrorCOMExceptionBase79 	HRESULT Error() const
80 	{
81 		return _hr;
82 	}
83 
ErrorMessageCOMExceptionBase84 	LPCTSTR ErrorMessage() const
85 	{
86 		if (_msg.empty()) {
87 			LPTSTR pBuf;
88 
89 			if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
90 				0, _hr, MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), (LPTSTR)&pBuf, 0, NULL)) {
91 				_msg = pBuf;
92 				LocalFree(pBuf);
93 			 } else {
94 				TCHAR buffer[128];
95 				_sntprintf(buffer, COUNTOF(buffer), TEXT("unknown Exception: 0x%08lX"), _hr);
96 				_msg = buffer;
97 			 }
98 		}
99 
100 		return _msg;
101 	}
102 
103 protected:
104 	HRESULT	_hr;
105 	mutable String _msg;
106 };
107 
108 #endif
109 
110 
111  /// Exception with context information
112 
113 struct COMException : public COMExceptionBase
114 {
115 	typedef COMExceptionBase super;
116 
COMExceptionCOMException117 	COMException(HRESULT hr)
118 	 :	super(hr),
119 		_context(CURRENT_CONTEXT),
120 		_file(NULL), _line(0)
121 	{
122 		LOG(toString());
123 		LOG(CURRENT_CONTEXT.getStackTrace());
124 	}
125 
COMExceptionCOMException126 	COMException(HRESULT hr, const char* file, int line)
127 	 :	super(hr),
128 		_context(CURRENT_CONTEXT),
129 		_file(file), _line(line)
130 	{
131 		LOG(toString());
132 		LOG(CURRENT_CONTEXT.getStackTrace());
133 	}
134 
COMExceptionCOMException135 	COMException(HRESULT hr, const String& obj)
136 	 :	super(hr),
137 		_context(CURRENT_CONTEXT),
138 		_file(NULL), _line(0)
139 	{
140 		LOG(toString());
141 		LOG(CURRENT_CONTEXT.getStackTrace());
142 	}
143 
COMExceptionCOMException144 	COMException(HRESULT hr, const String& obj, const char* file, int line)
145 	 :	super(hr),
146 		_context(CURRENT_CONTEXT),
147 		_file(file), _line(line)
148 	{
149 		LOG(toString());
150 		LOG(CURRENT_CONTEXT.getStackTrace());
151 	}
152 
153 	String toString() const;
154 
155 	Context _context;
156 
157 	const char* _file;
158 	int _line;
159 };
160 
161 #define	THROW_EXCEPTION(hr) throw COMException(hr, __FILE__, __LINE__)
162 #define	CHECKERROR(hr) ((void)(FAILED(hr)? THROW_EXCEPTION(hr): 0))
163 
164 
165 #ifdef _NO_COMUTIL
166 
CheckError(HRESULT hr)167 inline void CheckError(HRESULT hr)
168 {
169 	if (FAILED(hr))
170 		throw COMException(hr);
171 }
172 
173 #endif
174 
175 
176  /// COM Initialisation
177 
178 struct ComInit
179 {
ComInitComInit180 	ComInit()
181 	{
182 		CHECKERROR(CoInitialize(0));
183 	}
184 
185 #if (_WIN32_WINNT>=0x0400) || defined(_WIN32_DCOM)
ComInitComInit186 	ComInit(DWORD flag)
187 	{
188 		CHECKERROR(CoInitializeEx(0, flag));
189 	}
190 #endif
191 
~ComInitComInit192 	~ComInit()
193 	{
194 		CoUninitialize();
195 	}
196 };
197 
198 
199  /// OLE initialisation for drag drop support
200 
201 struct OleInit
202 {
OleInitOleInit203 	OleInit()
204 	{
205 		CHECKERROR(OleInitialize(0));
206 	}
207 
~OleInitOleInit208 	~OleInit()
209 	{
210 		OleUninitialize();
211 	}
212 };
213 
214 
215  /// Exception Handler for COM exceptions
216 
217 extern void HandleException(COMException& e, HWND hwnd);
218 
219 
220  /// We use a common IMalloc object for all shell memory allocations.
221 
222 struct CommonShellMalloc
223 {
CommonShellMallocCommonShellMalloc224 	CommonShellMalloc()
225 	{
226 		_p = NULL;
227 	}
228 
initCommonShellMalloc229 	void init()
230 	{
231 		if (!_p)
232 			CHECKERROR(SHGetMalloc(&_p));
233 	}
234 
~CommonShellMallocCommonShellMalloc235 	~CommonShellMalloc()
236 	{
237 		if (_p)
238 			_p->Release();
239 	}
240 
241 	operator IMalloc*()
242 	{
243 		return _p;
244 	}
245 
246 	IMalloc* _p;
247 };
248 
249 
250  /// wrapper class for IMalloc with usage of common allocator
251 
252 struct ShellMalloc
253 {
ShellMallocShellMalloc254 	ShellMalloc()
255 	{
256 		 // initialize s_cmn_shell_malloc
257 		s_cmn_shell_malloc.init();
258 	}
259 
260 	IMalloc* operator->()
261 	{
262 		return s_cmn_shell_malloc;
263 	}
264 
265 	static CommonShellMalloc s_cmn_shell_malloc;
266 };
267 
268 
269  /// wrapper template class for pointers to shell objects managed by IMalloc
270 
271 template<typename T> struct SShellPtr
272 {
~SShellPtrSShellPtr273 	~SShellPtr()
274 	{
275 		_malloc->Free(_p);
276 	}
277 
278 	T* operator->()
279 	{
280 		return _p;
281 	}
282 
283 	T const* operator->() const
284 	{
285 		return _p;
286 	}
287 
288 	operator T const *() const
289 	{
290 		return _p;
291 	}
292 
293 	const T& operator*() const
294 	{
295 		return *_p;
296 	}
297 
298 	T& operator*()
299 	{
300 		return *_p;
301 	}
302 
303 protected:
SShellPtrSShellPtr304 	SShellPtr()
305 	 :	_p(0)
306 	{
307 	}
308 
SShellPtrSShellPtr309 	SShellPtr(T* p)
310 	 :	_p(p)
311 	{
312 	}
313 
FreeSShellPtr314 	void Free()
315 	{
316 		_malloc->Free(_p);
317 		_p = NULL;
318 	}
319 
320 	T* _p;
321 	mutable ShellMalloc _malloc;	// IMalloc memory management object
322 
323 private:
324 	 // disallow copying of SShellPtr objects
SShellPtrSShellPtr325 	SShellPtr(const SShellPtr&) {}
326 	void operator=(SShellPtr const&) {}
327 };
328 
329 
330  /// wrapper class for COM interface pointers
331 
332 template<typename T> struct SIfacePtr
333 {
SIfacePtrSIfacePtr334 	SIfacePtr()
335 	 :	_p(0)
336 	{
337 	}
338 
SIfacePtrSIfacePtr339 	SIfacePtr(T* p)
340 	 :	_p(p)
341 	{
342 		if (p)
343 			p->AddRef();
344 	}
345 
SIfacePtrSIfacePtr346 	SIfacePtr(IUnknown* unknown, REFIID riid)
347 	{
348 		CHECKERROR(unknown->QueryInterface(riid, (LPVOID*)&_p));
349 	}
350 
~SIfacePtrSIfacePtr351 	~SIfacePtr()
352 	{
353 		Free();
354 	}
355 
356 	T* operator->()
357 	{
358 		return _p;
359 	}
360 
361 	const T* operator->() const
362 	{
363 		return _p;
364 	}
365 
366 /* not GCC compatible
367 	operator const T*() const
368 	{
369 		return _p;
370 	} */
371 
372 	operator T*()
373 	{
374 		return _p;
375 	}
376 
377 	T** operator&()
378 	{
379 		return &_p;
380 	}
381 
emptySIfacePtr382 	bool empty() const	//NOTE: GCC seems not to work correctly when defining operator bool() AND operator T*() at one time
383 	{
384 		return !_p;
385 	}
386 
387 	SIfacePtr& operator=(T* p)
388 	{
389 		Free();
390 
391 		if (p) {
392 			p->AddRef();
393 			_p = p;
394 		}
395 
396 		return *this;
397 	}
398 
399 	void operator=(SIfacePtr const& o)
400 	{
401 		T* h = _p;
402 
403 		if (o._p)
404 			o._p->AddRef();
405 
406 		_p = o._p;
407 
408 		if (h)
409 			h->Release();
410 	}
411 
CreateInstanceSIfacePtr412 	HRESULT CreateInstance(REFIID clsid, REFIID riid)
413 	{
414 		return CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, riid, (LPVOID*)&_p);
415 	}
416 
QueryInterfaceSIfacePtr417 	template<typename I> HRESULT QueryInterface(REFIID riid, I* p)
418 	{
419 		return _p->QueryInterface(riid, (LPVOID*)p);
420 	}
421 
getSIfacePtr422 	T* get()
423 	{
424 		return _p;
425 	}
426 
FreeSIfacePtr427 	void Free()
428 	{
429 		T* h = _p;
430 		_p = NULL;
431 
432 		if (h)
433 			h->Release();
434 	}
435 
436 protected:
SIfacePtrSIfacePtr437 	SIfacePtr(const SIfacePtr& o)
438 	 :	_p(o._p)
439 	{
440 		if (_p)
441 			_p->AddRef();
442 	}
443 
444 	T* _p;
445 };
446 
447 
448 struct NOVTABLE ComSrvObject	// NOVTABLE erlaubt, da protected Destruktor
449 {
450 protected:
ComSrvObjectComSrvObject451 	ComSrvObject() : _ref(1) {}
~ComSrvObjectComSrvObject452 	virtual ~ComSrvObject() {}
453 
454 	ULONG	_ref;
455 };
456 
457 struct SimpleComObject : public ComSrvObject
458 {
IncRefSimpleComObject459 	ULONG IncRef() {return ++_ref;}
DecRefSimpleComObject460 	ULONG DecRef() {ULONG ref=--_ref; if (!ref) {_ref++; delete this;} return ref;}
461 };
462 
463 
464  // server object interfaces
465 
466 template<typename BASE> struct IComSrvQI : public BASE
467 {
IComSrvQIIComSrvQI468 	IComSrvQI(REFIID uuid_base)
469 	 :	_uuid_base(uuid_base)
470 	{
471 	}
472 
QueryInterfaceIComSrvQI473 	STDMETHODIMP QueryInterface(REFIID riid, LPVOID* ppv)
474 	{
475 		*ppv = NULL;
476 
477 		if (IsEqualIID(riid, _uuid_base) || IsEqualIID(riid, IID_IUnknown))
478 			{*ppv=static_cast<BASE*>(this); this->AddRef(); return S_OK;}
479 
480 		return E_NOINTERFACE;
481 	}
482 
483 protected:
IComSrvQIIComSrvQI484 	IComSrvQI() {}
~IComSrvQIIComSrvQI485 	virtual ~IComSrvQI() {}
486 
487 	REFIID	_uuid_base;
488 };
489 
490 template<> struct IComSrvQI<IUnknown> : public IUnknown
491 {
492 	STDMETHODIMP QueryInterface(REFIID riid, LPVOID* ppv)
493 	{
494 		*ppv = NULL;
495 
496 		if (IsEqualIID(riid, IID_IUnknown))
497 			{*ppv=this; AddRef(); return S_OK;}
498 
499 		return E_NOINTERFACE;
500 	}
501 
502 protected:
503 	IComSrvQI<IUnknown>() {}
504 	virtual ~IComSrvQI<IUnknown>() {}
505 };
506 
507 
508 template<typename BASE, typename OBJ>
509 	class IComSrvBase : public IComSrvQI<BASE>
510 {
511 	typedef IComSrvQI<BASE> super;
512 
513 protected:
514 	IComSrvBase(REFIID uuid_base)
515 	 :	super(uuid_base)
516 	{
517 	}
518 
519 public:
520 	STDMETHODIMP_(ULONG) AddRef() {return static_cast<OBJ*>(this)->IncRef();}
521 	STDMETHODIMP_(ULONG) Release() {return static_cast<OBJ*>(this)->DecRef();}
522 };
523 
524 
525 
526 struct ShellFolder;
527 
528 
529  /// caching of desktop ShellFolder object
530 
531 struct CommonDesktop
532 {
533 	CommonDesktop()
534 	{
535 		_desktop = 0;
536 	}
537 
538 	~CommonDesktop();
539 
540 	void init();
541 
542 	operator ShellFolder&()
543 	{
544 		return *_desktop;
545 	}
546 
547 protected:
548 	ShellFolder* _desktop;
549 };
550 
551 
552 #ifndef _NO_COMUTIL	// _com_ptr available?
553 
554  /// IShellFolder smart pointer
555 struct ShellFolder : public IShellFolderPtr	// IShellFolderPtr uses intrinsic extensions of the VC++ compiler.
556 {
557 	typedef IShellFolderPtr super;
558 
559 	ShellFolder();	// desktop folder
560 	ShellFolder(IShellFolder* p);
561 	ShellFolder(IShellFolder* parent, LPCITEMIDLIST pidl);
562 	ShellFolder(LPCITEMIDLIST pidl);
563 
564 	void	attach(IShellFolder* parent, LPCITEMIDLIST pidl);
565 	String	get_name(LPCITEMIDLIST pidl=NULL, SHGDNF flags=SHGDN_NORMAL) const;
566 
567 	bool	empty() const {return !operator bool();}	//NOTE: see SIfacePtr::empty()
568 };
569 
570 #ifdef UNICODE
571 #define	IShellLinkPtr IShellLinkWPtr
572 #else
573 #define	IShellLinkPtr IShellLinkAPtr
574 #endif
575 
576  /// IShellLink smart pointer
577 struct ShellLinkPtr : public IShellLinkPtr
578 {
579 	typedef IShellLinkPtr super;
580 
581 	ShellLinkPtr(IShellLink* p)
582 	 :	super(p)
583 	{
584 		p->AddRef();
585 	}
586 
587 	bool	empty() const {return !operator bool();}	//NOTE: see SIfacePtr::empty()
588 };
589 
590 #else // _com_ptr not available -> use SIfacePtr
591 
592  /// IShellFolder smart pointer
593 struct ShellFolder : public SIfacePtr<IShellFolder>
594 {
595 	typedef SIfacePtr<IShellFolder> super;
596 
597 	ShellFolder();
598 	ShellFolder(IShellFolder* p);
599 	ShellFolder(IShellFolder* parent, LPCITEMIDLIST pidl);
600 	ShellFolder(LPCITEMIDLIST pidl);
601 
602 	void	attach(IShellFolder* parent, LPCITEMIDLIST pidl);
603 	String	get_name(LPCITEMIDLIST pidl, SHGDNF flags=SHGDN_NORMAL) const;
604 };
605 
606  /// IShellLink smart pointer
607 struct ShellLinkPtr : public SIfacePtr<IShellLink>
608 {
609 	typedef SIfacePtr<IShellLink> super;
610 
611 	ShellLinkPtr(IShellLink* p)
612 	 :	super(p)
613 	{
614 		_p->AddRef();
615 	}
616 
617 };
618 
619 #endif
620 
621 
622 extern ShellFolder& GetDesktopFolder();
623 
624 
625 #ifdef UNICODE
626 #define	path_from_pidl path_from_pidlW
627 #else
628 #define	path_from_pidl path_from_pidlA
629 #endif
630 
631 extern HRESULT path_from_pidlA(IShellFolder* folder, LPCITEMIDLIST pidl, LPSTR buffer, int len);
632 extern HRESULT path_from_pidlW(IShellFolder* folder, LPCITEMIDLIST pidl, LPWSTR buffer, int len);
633 extern HRESULT name_from_pidl(IShellFolder* folder, LPCITEMIDLIST pidl, LPTSTR buffer, int len, SHGDNF flags);
634 
635 
636  // ILGetSize() was missing in previous versions of MinGW and is not exported from shell32.dll on Windows 2000.
637 extern "C" UINT ILGetSize_local(LPCITEMIDLIST pidl);
638 #define ILGetSize ILGetSize_local
639 
640 #if 0
641 #ifdef UNICODE		// CFSTR_FILENAME was defined wrong in previous versions of MinGW.
642 #define CFSTR_FILENAMEW TEXT("FileNameW")
643 #undef CFSTR_FILENAME
644 #define CFSTR_FILENAME CFSTR_FILENAMEW
645 #endif
646 #endif
647 
648 
649  /// wrapper class for item ID lists
650 
651 struct ShellPath : public SShellPtr<ITEMIDLIST>
652 {
653 	typedef SShellPtr<ITEMIDLIST> super;
654 
655 	ShellPath()
656 	{
657 	}
658 
659 	ShellPath(IShellFolder* folder, LPCWSTR path)
660 	{
661 		CONTEXT("ShellPath::ShellPath(IShellFolder*, LPCWSTR)");
662 
663 		if (path)
664 			CHECKERROR(folder->ParseDisplayName(0, NULL, (LPOLESTR)path, NULL, &_p, NULL));
665 		else
666 			_p = NULL;
667 	}
668 
669 	ShellPath(LPCWSTR path)
670 	{
671 		OBJ_CONTEXT("ShellPath::ShellPath(LPCWSTR)", path);
672 
673 		if (path)
674 			CHECKERROR(GetDesktopFolder()->ParseDisplayName(0, NULL, (LPOLESTR)path, NULL, &_p, NULL));
675 		else
676 			_p = NULL;
677 	}
678 
679 	ShellPath(IShellFolder* folder, LPCSTR path)
680 	{
681 		CONTEXT("ShellPath::ShellPath(IShellFolder*, LPCSTR)");
682 
683 		WCHAR b[MAX_PATH];
684 
685 		if (path) {
686 			MultiByteToWideChar(CP_ACP, 0, path, -1, b, COUNTOF(b));
687 			CHECKERROR(folder->ParseDisplayName(0, NULL, b, NULL, &_p, NULL));
688 		} else
689 			_p = NULL;
690 	}
691 
692 	ShellPath(LPCSTR path)
693 	{
694 		CONTEXT("ShellPath::ShellPath(LPCSTR)");
695 
696 		WCHAR b[MAX_PATH];
697 
698 		if (path) {
699 			MultiByteToWideChar(CP_ACP, 0, path, -1, b, COUNTOF(b));
700 			CHECKERROR(GetDesktopFolder()->ParseDisplayName(0, NULL, b, NULL, &_p, NULL));
701 		} else
702 			_p = NULL;
703 	}
704 
705 	ShellPath(const ShellPath& o)
706 	 :	super(NULL)
707 	{
708 		//CONTEXT("ShellPath::ShellPath(const ShellPath&)");
709 
710 		if (o._p) {
711 			int l = ILGetSize(o._p);
712 			_p = (ITEMIDLIST*) _malloc->Alloc(l);
713 			if (_p) memcpy(_p, o._p, l);
714 		}
715 	}
716 
717 	explicit ShellPath(LPITEMIDLIST p)
718 	 :	super(p)
719 	{
720 	}
721 
722 	ShellPath(LPCITEMIDLIST p)
723 	{
724 		//CONTEXT("ShellPath::ShellPath(LPCITEMIDLIST)");
725 
726 		if (p) {
727 			int l = ILGetSize(p);
728 			_p = (ITEMIDLIST*) _malloc->Alloc(l);
729 			if (_p) memcpy(_p, p, l);
730 		}
731 	}
732 
733 	void operator=(const ShellPath& o)
734 	{
735 		//CONTEXT("ShellPath::operator=(const ShellPath&)");
736 
737 		ITEMIDLIST* h = _p;
738 
739 		if (o._p) {
740 			int l = ILGetSize(o._p);
741 
742 			_p = (ITEMIDLIST*) _malloc->Alloc(l);
743 			if (_p) memcpy(_p, o._p, l);
744 		}
745 		else
746 			_p = NULL;
747 
748 		_malloc->Free(h);
749 	}
750 
751 	void operator=(ITEMIDLIST* p)
752 	{
753 		//CONTEXT("ShellPath::operator=(ITEMIDLIST*)");
754 
755 		ITEMIDLIST* h = _p;
756 
757 		if (p) {
758 			int l = ILGetSize(p);
759 			_p = (ITEMIDLIST*) _malloc->Alloc(l);
760 			if (_p) memcpy(_p, p, l);
761 		}
762 		else
763 			_p = NULL;
764 
765 		_malloc->Free(h);
766 	}
767 
768 	void operator=(const SHITEMID& o)
769 	{
770 		ITEMIDLIST* h = _p;
771 
772 		LPBYTE p = (LPBYTE)_malloc->Alloc(o.cb+2);
773 		if (p) *(PWORD)((LPBYTE)memcpy(p, &o, o.cb)+o.cb) = 0;
774 		_p = (ITEMIDLIST*)p;
775 
776 		_malloc->Free(h);
777 	}
778 
779 	void operator+=(const SHITEMID& o)
780 	{
781 		int l0 = ILGetSize(_p);
782 		LPBYTE p = (LPBYTE)_malloc->Alloc(l0+o.cb);
783 		int l = l0 - 2;
784 
785 		if (p) {
786 			memcpy(p, _p, l);
787 			*(PWORD)((LPBYTE)memcpy(p+l, &o, o.cb)+o.cb) = 0;
788 		}
789 
790 		_malloc->Free(_p);
791 		_p = (ITEMIDLIST*)p;
792 	}
793 
794 	friend bool operator<(const ShellPath& a, const ShellPath& b)
795 	{
796 		int la = ILGetSize(a._p);
797 		int lb = ILGetSize(b._p);
798 
799 		int r = memcmp(a._p, b._p, min(la, lb));
800 		if (r)
801 			return r < 0;
802 		else
803 			return la < lb;
804 	}
805 
806 	void assign(LPCITEMIDLIST pidl, size_t size)
807 	{
808 		//CONTEXT("ShellPath::assign(LPCITEMIDLIST, size_t)");
809 
810 		ITEMIDLIST* h = _p;
811 
812 		_p = (ITEMIDLIST*) _malloc->Alloc(size+sizeof(USHORT/*SHITEMID::cb*/));
813 
814 		if (_p) {
815 			memcpy(_p, pidl, size);
816 			((ITEMIDLIST*)((LPBYTE)_p+size))->mkid.cb = 0; // terminator
817 		}
818 
819 		_malloc->Free(h);
820 	}
821 
822 	void assign(LPCITEMIDLIST pidl)
823 	{
824 		//CONTEXT("ShellPath::assign(LPCITEMIDLIST)");
825 
826 		ITEMIDLIST* h = _p;
827 
828 		if (pidl) {
829 			int l = ILGetSize(pidl);
830 			_p = (ITEMIDLIST*) _malloc->Alloc(l);
831 			if (_p) memcpy(_p, pidl, l);
832 		} else
833 			_p = NULL;
834 
835 		_malloc->Free(h);
836 	}
837 
838 	void split(ShellPath& parent, ShellPath& obj) const;
839 
840 	void GetUIObjectOf(REFIID riid, LPVOID* ppvOut, HWND hWnd=0, ShellFolder& sf=GetDesktopFolder());
841 
842 	ShellFolder get_folder()
843 	{
844 		return ShellFolder(_p);
845 	}
846 
847 	ShellFolder get_folder(IShellFolder* parent)
848 	{
849 		CONTEXT("ShellPath::get_folder()");
850 		return ShellFolder(parent, _p);
851 	}
852 
853 	 // convert an item id list from relative to absolute (=relative to the desktop) format
854 	ShellPath create_absolute_pidl(LPCITEMIDLIST parent_pidl) const;
855 };
856 
857 
858 #if defined(__WINE__) && defined(NONAMELESSUNION)	// Wine doesn't know of unnamed union members and uses some macros instead.
859 #define	UNION_MEMBER(x) DUMMYUNIONNAME.##x
860 #else
861 #define	UNION_MEMBER(x) x
862 #endif
863 
864 
865  // encapsulation of STRRET structure for easy string retrieval with conversion
866 
867 #ifdef UNICODE
868 #define	StrRet StrRetW
869 //#define	tcscpyn wcscpyn
870 #else
871 #define	StrRet StrRetA
872 //#define	tcscpyn strcpyn
873 #endif
874 
875 //extern LPSTR strcpyn(LPSTR dest, LPCSTR source, size_t count);
876 //extern LPWSTR wcscpyn(LPWSTR dest, LPCWSTR source, size_t count);
877 
878  /// easy retrieval of multi byte strings out of STRRET structures
879 struct StrRetA : public STRRET
880 {
881 	~StrRetA()
882 	{
883 		if (uType == STRRET_WSTR)
884 			ShellMalloc()->Free(pOleStr);
885 	}
886 
887 	void GetString(const SHITEMID& shiid, LPSTR b, int l)
888 	{
889 		switch(uType) {
890 		  case STRRET_WSTR:
891 			WideCharToMultiByte(CP_ACP, 0, UNION_MEMBER(pOleStr), -1, b, l, NULL, NULL);
892 			break;
893 
894 		  case STRRET_OFFSET:
895 			lstrcpynA(b, (LPCSTR)&shiid+UNION_MEMBER(uOffset), l);
896 			break;
897 
898 		  case STRRET_CSTR:
899 			lstrcpynA(b, UNION_MEMBER(cStr), l);
900 		}
901 	}
902 };
903 
904  /// easy retrieval of wide char strings out of STRRET structures
905 struct StrRetW : public STRRET
906 {
907 	~StrRetW()
908 	{
909 		if (uType == STRRET_WSTR)
910 			ShellMalloc()->Free(pOleStr);
911 	}
912 
913 	void GetString(const SHITEMID& shiid, LPWSTR b, int l)
914 	{
915 		switch(uType) {
916 		  case STRRET_WSTR:
917 			lstrcpynW(b, UNION_MEMBER(pOleStr), l);
918 			break;
919 
920 		  case STRRET_OFFSET:
921 			MultiByteToWideChar(CP_ACP, 0, (LPCSTR)&shiid+UNION_MEMBER(uOffset), -1, b, l);
922 			break;
923 
924 		  case STRRET_CSTR:
925 			MultiByteToWideChar(CP_ACP, 0, UNION_MEMBER(cStr), -1, b, l);
926 		}
927 	}
928 };
929 
930 
931  /// Retrieval of file system paths of ShellPath objects
932 class FileSysShellPath : public ShellPath
933 {
934 	TCHAR	_fullpath[MAX_PATH];
935 
936 protected:
937 	FileSysShellPath() {_fullpath[0] = '\0';}
938 
939 public:
940 	FileSysShellPath(const ShellPath& o) : ShellPath(o) {_fullpath[0] = '\0';}
941 
942 	operator LPCTSTR() {if (!SHGetPathFromIDList(_p, _fullpath)) return NULL; return _fullpath;}
943 };
944 
945 
946  /// Browse dialog operating on shell namespace
947 struct FolderBrowser : public FileSysShellPath
948 {
949 	FolderBrowser(HWND owner, UINT flags, LPCTSTR title, LPCITEMIDLIST root=0)
950 	{
951 		_displayname[0] = '\0';
952 		_browseinfo.hwndOwner = owner;
953 		_browseinfo.pidlRoot = root;
954 		_browseinfo.pszDisplayName = _displayname;
955 		_browseinfo.lpszTitle = title;
956 		_browseinfo.ulFlags = flags;
957 		_browseinfo.lpfn = 0;
958 		_browseinfo.lParam = 0;
959 		_browseinfo.iImage = 0;
960 
961 		_p = SHBrowseForFolder(&_browseinfo);
962 	}
963 
964 	LPCTSTR GetDisplayName()
965 	{
966 		return _displayname;
967 	}
968 
969 	bool IsOK()
970 	{
971 		return _p != 0;
972 	}
973 
974 private:
975 	BROWSEINFO _browseinfo;
976 	TCHAR	_displayname[MAX_PATH];
977 };
978 
979 
980  /// Retrieval of special shell folder paths
981 struct SpecialFolderPath : public ShellPath
982 {
983 	SpecialFolderPath(int folder, HWND hwnd)
984 	{
985 		HRESULT hr = SHGetSpecialFolderLocation(hwnd, folder, &_p);
986 		CHECKERROR(hr);
987 	}
988 };
989 
990  /// Shell folder path of the desktop
991 struct DesktopFolderPath : public SpecialFolderPath
992 {
993 	DesktopFolderPath()
994 	 :	SpecialFolderPath(CSIDL_DESKTOP, 0)
995 	{
996 	}
997 };
998 
999  /// Retrieval of special shell folder
1000 struct SpecialFolder : public ShellFolder
1001 {
1002 	SpecialFolder(int folder, HWND hwnd)
1003 	 :	ShellFolder(GetDesktopFolder(), SpecialFolderPath(folder, hwnd))
1004 	{
1005 	}
1006 };
1007 
1008  /// Shell folder of the desktop
1009 struct DesktopFolder : public ShellFolder
1010 {
1011 };
1012 
1013 
1014  /// file system path of special folder
1015 struct SpecialFolderFSPath
1016 {
1017 	SpecialFolderFSPath(int folder/*e.g. CSIDL_DESKTOP*/, HWND hwnd);
1018 
1019 	operator LPCTSTR()
1020 	{
1021 		return _fullpath;
1022 	}
1023 
1024 protected:
1025 	TCHAR	_fullpath[MAX_PATH];
1026 };
1027 
1028 /*
1029  /// file system path of special folder
1030 struct SpecialFolderFSPath : public FileSysShellPath
1031 {
1032 	SpecialFolderFSPath(int folder, HWND hwnd)
1033 	{
1034 		CONTEXT("SpecialFolderFSPath::SpecialFolderFSPath()");
1035 
1036 		HRESULT hr = SHGetSpecialFolderLocation(hwnd, folder, &_p);
1037 		CHECKERROR(hr);
1038 	}
1039 };
1040 */
1041 
1042 
1043  /// wrapper class for enumerating shell namespace objects
1044 
1045 struct ShellItemEnumerator : public SIfacePtr<IEnumIDList>
1046 {
1047 	ShellItemEnumerator(IShellFolder* folder, DWORD flags=SHCONTF_FOLDERS|SHCONTF_NONFOLDERS|SHCONTF_INCLUDEHIDDEN)
1048 	{
1049 		CONTEXT("ShellItemEnumerator::ShellItemEnumerator()");
1050 
1051 		CHECKERROR(folder->EnumObjects(0, flags, &_p));
1052 	}
1053 };
1054 
1055 
1056  /// list of PIDLs
1057 struct PIDList
1058 {
1059 	PIDList()
1060 	{
1061 		memset(&_stgm, 0, sizeof(STGMEDIUM));
1062 	}
1063 
1064 	~PIDList()
1065 	{
1066 		if (_stgm.hGlobal) {
1067 			GlobalUnlock(_stgm.hGlobal);
1068 			ReleaseStgMedium(&_stgm);
1069 		}
1070 	}
1071 
1072 	HRESULT GetData(IDataObject* selection)
1073 	{
1074 		static UINT CF_IDLIST = RegisterClipboardFormat(CFSTR_SHELLIDLIST);
1075 
1076 		FORMATETC fetc;
1077 		fetc.cfFormat = CF_IDLIST;
1078 		fetc.ptd = NULL;
1079 		fetc.dwAspect = DVASPECT_CONTENT;
1080 		fetc.lindex = -1;
1081 		fetc.tymed = TYMED_HGLOBAL;
1082 
1083 		HRESULT hr = selection->QueryGetData(&fetc);
1084 		if (FAILED(hr))
1085 			return hr;
1086 
1087 		hr = selection->GetData(&fetc, &_stgm);
1088 		if (FAILED(hr))
1089 			return hr;
1090 
1091 		_pIDList = (LPIDA)GlobalLock(_stgm.hGlobal);
1092 
1093 		return hr;
1094 	}
1095 
1096 	operator LPIDA() {return _pIDList;}
1097 
1098 protected:
1099 	STGMEDIUM _stgm;
1100 	LPIDA _pIDList;
1101 };
1102 
1103 
1104 struct CtxMenuInterfaces
1105 {
1106 	CtxMenuInterfaces()
1107 	{
1108 		reset();
1109 	}
1110 
1111 	void	reset();
1112 	bool	HandleMenuMsg(UINT nmsg, WPARAM wparam, LPARAM lparam);
1113 	IContextMenu* query_interfaces(IContextMenu* pcm1);
1114 
1115 	IContextMenu2*	_pctxmenu2;
1116 
1117 	IContextMenu3*	_pctxmenu3;
1118 };
1119 
1120 template<typename BASE> struct ExtContextMenuHandlerT
1121  : public BASE
1122 {
1123 	typedef BASE super;
1124 
1125 	ExtContextMenuHandlerT(HWND hwnd)
1126 	 :	super(hwnd)
1127 	{
1128 	}
1129 
1130 	template<typename PARA> ExtContextMenuHandlerT(HWND hwnd, const PARA& info)
1131 	 :	super(hwnd, info)
1132 	{
1133 	}
1134 
1135 	LRESULT WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
1136 	{
1137 		switch(nmsg) {
1138 		  case WM_DRAWITEM:
1139 		  case WM_MEASUREITEM:
1140 			if (!wparam)	// Is the message menu-related?
1141 				if (_cm_ifs.HandleMenuMsg(nmsg, wparam, lparam))
1142 					return TRUE;
1143 
1144 			break;
1145 
1146 		  case WM_INITMENUPOPUP:
1147 			if (_cm_ifs.HandleMenuMsg(nmsg, wparam, lparam))
1148 				return 0;
1149 
1150 			break;
1151 
1152 		  case WM_MENUCHAR:	// only supported by IContextMenu3
1153 		   if (_cm_ifs._pctxmenu3) {
1154 			   LRESULT lResult = 0;
1155 
1156 			   _cm_ifs._pctxmenu3->HandleMenuMsg2(nmsg, wparam, lparam, &lResult);
1157 
1158 			   return lResult;
1159 		   }
1160 
1161 		   return 0;
1162 		}
1163 
1164 		return super::WndProc(nmsg, wparam, lparam);
1165 	}
1166 
1167 protected:
1168 	CtxMenuInterfaces _cm_ifs;
1169 };
1170 
1171 
1172 extern HRESULT ShellFolderContextMenu(IShellFolder* shell_folder, HWND hwndParent, int cidl,
1173 										LPCITEMIDLIST* ppidl, int x, int y, CtxMenuInterfaces& cm_ifs);
1174