2 * Copyright 2003, 2004, 2005 Martin Fuchs
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.
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.
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
25 // C++ wrapper classes for COM interfaces and shell objects
27 // Martin Fuchs, 20.07.2003
31 // windows shell headers
36 #if _MSC_VER>=1300 // VS.Net
38 using namespace _com_util;
42 #ifndef _INC_COMUTIL // is comutil.h of MS headers not available?
48 // work around GCC's wide string constant bug when compiling inline functions
50 extern const LPCTSTR sCFSTR_SHELLIDLIST
;
51 #undef CFSTR_SHELLIDLIST
52 #define CFSTR_SHELLIDLIST sCFSTR_SHELLIDLIST
56 #define NOVTABLE __declspec(novtable)
67 #define COMExceptionBase _com_error
71 /// COM ExceptionBase class as replacement for _com_error
72 struct COMExceptionBase
74 COMExceptionBase(HRESULT hr
)
84 LPCTSTR
ErrorMessage() const
89 if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
|FORMAT_MESSAGE_FROM_SYSTEM
,
90 0, _hr
, MAKELANGID(LANG_NEUTRAL
,SUBLANG_DEFAULT
), (LPTSTR
)&pBuf
, 0, NULL
)) {
95 _sntprintf(buffer
, COUNTOF(buffer
), TEXT("unknown Exception: 0x%08lX"), _hr
);
111 /// Exception with context information
113 struct COMException
: public COMExceptionBase
115 typedef COMExceptionBase super
;
117 COMException(HRESULT hr
)
119 _context(CURRENT_CONTEXT
),
120 _file(NULL
), _line(0)
123 LOG(CURRENT_CONTEXT
.getStackTrace());
126 COMException(HRESULT hr
, const char* file
, int line
)
128 _context(CURRENT_CONTEXT
),
129 _file(file
), _line(line
)
132 LOG(CURRENT_CONTEXT
.getStackTrace());
135 COMException(HRESULT hr
, const String
& obj
)
137 _context(CURRENT_CONTEXT
),
138 _file(NULL
), _line(0)
141 LOG(CURRENT_CONTEXT
.getStackTrace());
144 COMException(HRESULT hr
, const String
& obj
, const char* file
, int line
)
146 _context(CURRENT_CONTEXT
),
147 _file(file
), _line(line
)
150 LOG(CURRENT_CONTEXT
.getStackTrace());
153 String
toString() const;
161 #define THROW_EXCEPTION(hr) throw COMException(hr, __FILE__, __LINE__)
162 #define CHECKERROR(hr) ((void)(FAILED(hr)? THROW_EXCEPTION(hr): 0))
167 inline void CheckError(HRESULT hr
)
170 throw COMException(hr
);
176 /// COM Initialisation
182 CHECKERROR(CoInitialize(0));
185 #if (_WIN32_WINNT>=0x0400) || defined(_WIN32_DCOM)
188 CHECKERROR(CoInitializeEx(0, flag
));
199 /// OLE initialisation for drag drop support
205 CHECKERROR(OleInitialize(0));
215 /// Exception Handler for COM exceptions
217 extern void HandleException(COMException
& e
, HWND hwnd
);
220 /// We use a common IMalloc object for all shell memory allocations.
222 struct CommonShellMalloc
232 CHECKERROR(SHGetMalloc(&_p
));
250 /// wrapper class for IMalloc with usage of common allocator
256 // initialize s_cmn_shell_malloc
257 s_cmn_shell_malloc
.init();
260 IMalloc
* operator->()
262 return s_cmn_shell_malloc
;
265 static CommonShellMalloc s_cmn_shell_malloc
;
269 /// wrapper template class for pointers to shell objects managed by IMalloc
271 template<typename T
> struct SShellPtr
283 T
const* operator->() const
288 operator T
const *() const
293 const T
& operator*() const
321 mutable ShellMalloc _malloc
; // IMalloc memory management object
324 // disallow copying of SShellPtr objects
325 SShellPtr(const SShellPtr
&) {}
326 void operator=(SShellPtr
const&) {}
330 /// wrapper class for COM interface pointers
332 template<typename T
> struct SIfacePtr
346 SIfacePtr(IUnknown
* unknown
, REFIID riid
)
348 CHECKERROR(unknown
->QueryInterface(riid
, (LPVOID
*)&_p
));
361 const T
* operator->() const
366 /* not GCC compatible
367 operator const T*() const
382 bool empty() const //NOTE: GCC seems not to work correctly when defining operator bool() AND operator T*() at one time
387 SIfacePtr
& operator=(T
* p
)
399 void operator=(SIfacePtr
const& o
)
412 HRESULT
CreateInstance(REFIID clsid
, REFIID riid
)
414 return CoCreateInstance(clsid
, NULL
, CLSCTX_INPROC_SERVER
, riid
, (LPVOID
*)&_p
);
417 template<typename I
> HRESULT
QueryInterface(REFIID riid
, I
* p
)
419 return _p
->QueryInterface(riid
, (LPVOID
*)p
);
437 SIfacePtr(const SIfacePtr
& o
)
448 struct NOVTABLE ComSrvObject
// NOVTABLE erlaubt, da protected Destruktor
451 ComSrvObject() : _ref(1) {}
452 virtual ~ComSrvObject() {}
457 struct SimpleComObject
: public ComSrvObject
459 ULONG
IncRef() {return ++_ref
;}
460 ULONG
DecRef() {ULONG ref
=--_ref
; if (!ref
) {_ref
++; delete this;} return ref
;}
464 // server object interfaces
466 template<typename BASE
> struct IComSrvQI
: public BASE
468 IComSrvQI(REFIID uuid_base
)
469 : _uuid_base(uuid_base
)
473 STDMETHODIMP
QueryInterface(REFIID riid
, LPVOID
* ppv
)
477 if (IsEqualIID(riid
, _uuid_base
) || IsEqualIID(riid
, IID_IUnknown
))
478 {*ppv
=static_cast<BASE
*>(this); this->AddRef(); return S_OK
;}
480 return E_NOINTERFACE
;
485 virtual ~IComSrvQI() {}
490 template<> struct IComSrvQI
<IUnknown
> : public IUnknown
492 STDMETHODIMP
QueryInterface(REFIID riid
, LPVOID
* ppv
)
496 if (IsEqualIID(riid
, IID_IUnknown
))
497 {*ppv
=this; AddRef(); return S_OK
;}
499 return E_NOINTERFACE
;
503 IComSrvQI
<IUnknown
>() {}
504 virtual ~IComSrvQI
<IUnknown
>() {}
508 template<typename BASE
, typename OBJ
>
509 class IComSrvBase
: public IComSrvQI
<BASE
>
511 typedef IComSrvQI
<BASE
> super
;
514 IComSrvBase(REFIID uuid_base
)
520 STDMETHODIMP_(ULONG
) AddRef() {return static_cast<OBJ
*>(this)->IncRef();}
521 STDMETHODIMP_(ULONG
) Release() {return static_cast<OBJ
*>(this)->DecRef();}
529 /// caching of desktop ShellFolder object
542 operator ShellFolder
&()
548 ShellFolder
* _desktop
;
552 #ifndef _NO_COMUTIL // _com_ptr available?
554 /// IShellFolder smart pointer
555 struct ShellFolder
: public IShellFolderPtr
// IShellFolderPtr uses intrinsic extensions of the VC++ compiler.
557 typedef IShellFolderPtr super
;
559 ShellFolder(); // desktop folder
560 ShellFolder(IShellFolder
* p
);
561 ShellFolder(IShellFolder
* parent
, LPCITEMIDLIST pidl
);
562 ShellFolder(LPCITEMIDLIST pidl
);
564 void attach(IShellFolder
* parent
, LPCITEMIDLIST pidl
);
565 String
get_name(LPCITEMIDLIST pidl
=NULL
, SHGDNF flags
=SHGDN_NORMAL
) const;
567 bool empty() const {return !operator bool();} //NOTE: see SIfacePtr::empty()
571 #define IShellLinkPtr IShellLinkWPtr
573 #define IShellLinkPtr IShellLinkAPtr
576 /// IShellLink smart pointer
577 struct ShellLinkPtr
: public IShellLinkPtr
579 typedef IShellLinkPtr super
;
581 ShellLinkPtr(IShellLink
* p
)
587 bool empty() const {return !operator bool();} //NOTE: see SIfacePtr::empty()
590 #else // _com_ptr not available -> use SIfacePtr
592 /// IShellFolder smart pointer
593 struct ShellFolder
: public SIfacePtr
<IShellFolder
>
595 typedef SIfacePtr
<IShellFolder
> super
;
598 ShellFolder(IShellFolder
* p
);
599 ShellFolder(IShellFolder
* parent
, LPCITEMIDLIST pidl
);
600 ShellFolder(LPCITEMIDLIST pidl
);
602 void attach(IShellFolder
* parent
, LPCITEMIDLIST pidl
);
603 String
get_name(LPCITEMIDLIST pidl
, SHGDNF flags
=SHGDN_NORMAL
) const;
606 /// IShellLink smart pointer
607 struct ShellLinkPtr
: public SIfacePtr
<IShellLink
>
609 typedef SIfacePtr
<IShellLink
> super
;
611 ShellLinkPtr(IShellLink
* p
)
622 extern ShellFolder
& GetDesktopFolder();
626 #define path_from_pidl path_from_pidlW
628 #define path_from_pidl path_from_pidlA
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
);
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
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
649 /// wrapper class for item ID lists
651 struct ShellPath
: public SShellPtr
<ITEMIDLIST
>
653 typedef SShellPtr
<ITEMIDLIST
> super
;
659 ShellPath(IShellFolder
* folder
, LPCWSTR path
)
661 CONTEXT("ShellPath::ShellPath(IShellFolder*, LPCWSTR)");
664 CHECKERROR(folder
->ParseDisplayName(0, NULL
, (LPOLESTR
)path
, NULL
, &_p
, NULL
));
669 ShellPath(LPCWSTR path
)
671 OBJ_CONTEXT("ShellPath::ShellPath(LPCWSTR)", path
);
674 CHECKERROR(GetDesktopFolder()->ParseDisplayName(0, NULL
, (LPOLESTR
)path
, NULL
, &_p
, NULL
));
679 ShellPath(IShellFolder
* folder
, LPCSTR path
)
681 CONTEXT("ShellPath::ShellPath(IShellFolder*, LPCSTR)");
686 MultiByteToWideChar(CP_ACP
, 0, path
, -1, b
, COUNTOF(b
));
687 CHECKERROR(folder
->ParseDisplayName(0, NULL
, b
, NULL
, &_p
, NULL
));
692 ShellPath(LPCSTR path
)
694 CONTEXT("ShellPath::ShellPath(LPCSTR)");
699 MultiByteToWideChar(CP_ACP
, 0, path
, -1, b
, COUNTOF(b
));
700 CHECKERROR(GetDesktopFolder()->ParseDisplayName(0, NULL
, b
, NULL
, &_p
, NULL
));
705 ShellPath(const ShellPath
& o
)
708 //CONTEXT("ShellPath::ShellPath(const ShellPath&)");
711 int l
= ILGetSize(o
._p
);
712 _p
= (ITEMIDLIST
*) _malloc
->Alloc(l
);
713 if (_p
) memcpy(_p
, o
._p
, l
);
717 explicit ShellPath(LPITEMIDLIST p
)
722 ShellPath(LPCITEMIDLIST p
)
724 //CONTEXT("ShellPath::ShellPath(LPCITEMIDLIST)");
727 int l
= ILGetSize(p
);
728 _p
= (ITEMIDLIST
*) _malloc
->Alloc(l
);
729 if (_p
) memcpy(_p
, p
, l
);
733 void operator=(const ShellPath
& o
)
735 //CONTEXT("ShellPath::operator=(const ShellPath&)");
740 int l
= ILGetSize(o
._p
);
742 _p
= (ITEMIDLIST
*) _malloc
->Alloc(l
);
743 if (_p
) memcpy(_p
, o
._p
, l
);
751 void operator=(ITEMIDLIST
* p
)
753 //CONTEXT("ShellPath::operator=(ITEMIDLIST*)");
758 int l
= ILGetSize(p
);
759 _p
= (ITEMIDLIST
*) _malloc
->Alloc(l
);
760 if (_p
) memcpy(_p
, p
, l
);
768 void operator=(const SHITEMID
& o
)
772 LPBYTE p
= (LPBYTE
)_malloc
->Alloc(o
.cb
+2);
773 if (p
) *(PWORD
)((LPBYTE
)memcpy(p
, &o
, o
.cb
)+o
.cb
) = 0;
779 void operator+=(const SHITEMID
& o
)
781 int l0
= ILGetSize(_p
);
782 LPBYTE p
= (LPBYTE
)_malloc
->Alloc(l0
+o
.cb
);
787 *(PWORD
)((LPBYTE
)memcpy(p
+l
, &o
, o
.cb
)+o
.cb
) = 0;
794 friend bool operator<(const ShellPath
& a
, const ShellPath
& b
)
796 int la
= ILGetSize(a
._p
);
797 int lb
= ILGetSize(b
._p
);
799 int r
= memcmp(a
._p
, b
._p
, min(la
, lb
));
806 void assign(LPCITEMIDLIST pidl
, size_t size
)
808 //CONTEXT("ShellPath::assign(LPCITEMIDLIST, size_t)");
812 _p
= (ITEMIDLIST
*) _malloc
->Alloc(size
+sizeof(USHORT
/*SHITEMID::cb*/));
815 memcpy(_p
, pidl
, size
);
816 ((ITEMIDLIST
*)((LPBYTE
)_p
+size
))->mkid
.cb
= 0; // terminator
822 void assign(LPCITEMIDLIST pidl
)
824 //CONTEXT("ShellPath::assign(LPCITEMIDLIST)");
829 int l
= ILGetSize(pidl
);
830 _p
= (ITEMIDLIST
*) _malloc
->Alloc(l
);
831 if (_p
) memcpy(_p
, pidl
, l
);
838 void split(ShellPath
& parent
, ShellPath
& obj
) const;
840 void GetUIObjectOf(REFIID riid
, LPVOID
* ppvOut
, HWND hWnd
=0, ShellFolder
& sf
=GetDesktopFolder());
842 ShellFolder
get_folder()
844 return ShellFolder(_p
);
847 ShellFolder
get_folder(IShellFolder
* parent
)
849 CONTEXT("ShellPath::get_folder()");
850 return ShellFolder(parent
, _p
);
853 // convert an item id list from relative to absolute (=relative to the desktop) format
854 ShellPath
create_absolute_pidl(LPCITEMIDLIST parent_pidl
) const;
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
861 #define UNION_MEMBER(x) x
865 // encapsulation of STRRET structure for easy string retrieval with conversion
868 #define StrRet StrRetW
869 //#define tcscpyn wcscpyn
871 #define StrRet StrRetA
872 //#define tcscpyn strcpyn
875 //extern LPSTR strcpyn(LPSTR dest, LPCSTR source, size_t count);
876 //extern LPWSTR wcscpyn(LPWSTR dest, LPCWSTR source, size_t count);
878 /// easy retrieval of multi byte strings out of STRRET structures
879 struct StrRetA
: public STRRET
883 if (uType
== STRRET_WSTR
)
884 ShellMalloc()->Free(pOleStr
);
887 void GetString(const SHITEMID
& shiid
, LPSTR b
, int l
)
891 WideCharToMultiByte(CP_ACP
, 0, UNION_MEMBER(pOleStr
), -1, b
, l
, NULL
, NULL
);
895 lstrcpynA(b
, (LPCSTR
)&shiid
+UNION_MEMBER(uOffset
), l
);
899 lstrcpynA(b
, UNION_MEMBER(cStr
), l
);
904 /// easy retrieval of wide char strings out of STRRET structures
905 struct StrRetW
: public STRRET
909 if (uType
== STRRET_WSTR
)
910 ShellMalloc()->Free(pOleStr
);
913 void GetString(const SHITEMID
& shiid
, LPWSTR b
, int l
)
917 lstrcpynW(b
, UNION_MEMBER(pOleStr
), l
);
921 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)&shiid
+UNION_MEMBER(uOffset
), -1, b
, l
);
925 MultiByteToWideChar(CP_ACP
, 0, UNION_MEMBER(cStr
), -1, b
, l
);
931 /// Retrieval of file system paths of ShellPath objects
932 class FileSysShellPath
: public ShellPath
934 TCHAR _fullpath
[MAX_PATH
];
937 FileSysShellPath() {_fullpath
[0] = '\0';}
940 FileSysShellPath(const ShellPath
& o
) : ShellPath(o
) {_fullpath
[0] = '\0';}
942 operator LPCTSTR() {if (!SHGetPathFromIDList(_p
, _fullpath
)) return NULL
; return _fullpath
;}
946 /// Browse dialog operating on shell namespace
947 struct FolderBrowser
: public FileSysShellPath
949 FolderBrowser(HWND owner
, UINT flags
, LPCTSTR title
, LPCITEMIDLIST root
=0)
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;
961 _p
= SHBrowseForFolder(&_browseinfo
);
964 LPCTSTR
GetDisplayName()
975 BROWSEINFO _browseinfo
;
976 TCHAR _displayname
[MAX_PATH
];
980 /// Retrieval of special shell folder paths
981 struct SpecialFolderPath
: public ShellPath
983 SpecialFolderPath(int folder
, HWND hwnd
)
985 HRESULT hr
= SHGetSpecialFolderLocation(hwnd
, folder
, &_p
);
990 /// Shell folder path of the desktop
991 struct DesktopFolderPath
: public SpecialFolderPath
994 : SpecialFolderPath(CSIDL_DESKTOP
, 0)
999 /// Retrieval of special shell folder
1000 struct SpecialFolder
: public ShellFolder
1002 SpecialFolder(int folder
, HWND hwnd
)
1003 : ShellFolder(GetDesktopFolder(), SpecialFolderPath(folder
, hwnd
))
1008 /// Shell folder of the desktop
1009 struct DesktopFolder
: public ShellFolder
1014 /// file system path of special folder
1015 struct SpecialFolderFSPath
1017 SpecialFolderFSPath(int folder
/*e.g. CSIDL_DESKTOP*/, HWND hwnd
);
1025 TCHAR _fullpath
[MAX_PATH
];
1029 /// file system path of special folder
1030 struct SpecialFolderFSPath : public FileSysShellPath
1032 SpecialFolderFSPath(int folder, HWND hwnd)
1034 CONTEXT("SpecialFolderFSPath::SpecialFolderFSPath()");
1036 HRESULT hr = SHGetSpecialFolderLocation(hwnd, folder, &_p);
1043 /// wrapper class for enumerating shell namespace objects
1045 struct ShellItemEnumerator
: public SIfacePtr
<IEnumIDList
>
1047 ShellItemEnumerator(IShellFolder
* folder
, DWORD flags
=SHCONTF_FOLDERS
|SHCONTF_NONFOLDERS
|SHCONTF_INCLUDEHIDDEN
)
1049 CONTEXT("ShellItemEnumerator::ShellItemEnumerator()");
1051 CHECKERROR(folder
->EnumObjects(0, flags
, &_p
));
1061 memset(&_stgm
, 0, sizeof(STGMEDIUM
));
1066 if (_stgm
.hGlobal
) {
1067 GlobalUnlock(_stgm
.hGlobal
);
1068 ReleaseStgMedium(&_stgm
);
1072 HRESULT
GetData(IDataObject
* selection
)
1074 static UINT CF_IDLIST
= RegisterClipboardFormat(CFSTR_SHELLIDLIST
);
1077 fetc
.cfFormat
= CF_IDLIST
;
1079 fetc
.dwAspect
= DVASPECT_CONTENT
;
1081 fetc
.tymed
= TYMED_HGLOBAL
;
1083 HRESULT hr
= selection
->QueryGetData(&fetc
);
1087 hr
= selection
->GetData(&fetc
, &_stgm
);
1091 _pIDList
= (LPIDA
)GlobalLock(_stgm
.hGlobal
);
1096 operator LPIDA() {return _pIDList
;}
1104 struct CtxMenuInterfaces
1112 bool HandleMenuMsg(UINT nmsg
, WPARAM wparam
, LPARAM lparam
);
1113 IContextMenu
* query_interfaces(IContextMenu
* pcm1
);
1115 IContextMenu2
* _pctxmenu2
;
1117 #ifndef __MINGW32__ // IContextMenu3 missing in MinGW (as of 6.2.2005)
1118 IContextMenu3
* _pctxmenu3
;
1122 template<typename BASE
> struct ExtContextMenuHandlerT
1127 ExtContextMenuHandlerT(HWND hwnd
)
1132 template<typename PARA
> ExtContextMenuHandlerT(HWND hwnd
, const PARA
& info
)
1137 LRESULT
WndProc(UINT nmsg
, WPARAM wparam
, LPARAM lparam
)
1141 case WM_MEASUREITEM
:
1142 if (!wparam
) // Is the message menu-related?
1143 if (_cm_ifs
.HandleMenuMsg(nmsg
, wparam
, lparam
))
1148 case WM_INITMENUPOPUP
:
1149 if (_cm_ifs
.HandleMenuMsg(nmsg
, wparam
, lparam
))
1154 #ifndef __MINGW32__ // IContextMenu3 missing in MinGW (as of 6.2.2005)
1155 case WM_MENUCHAR
: // only supported by IContextMenu3
1156 if (_cm_ifs
._pctxmenu3
) {
1157 LRESULT lResult
= 0;
1159 _cm_ifs
._pctxmenu3
->HandleMenuMsg2(nmsg
, wparam
, lparam
, &lResult
);
1168 return super::WndProc(nmsg
, wparam
, lparam
);
1172 CtxMenuInterfaces _cm_ifs
;
1176 extern HRESULT
ShellFolderContextMenu(IShellFolder
* shell_folder
, HWND hwndParent
, int cidl
,
1177 LPCITEMIDLIST
* ppidl
, int x
, int y
, CtxMenuInterfaces
& cm_ifs
);