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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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
60 #define COMExceptionBase _com_error
64 /// COM ExceptionBase class as replacement for _com_error
65 struct COMExceptionBase
67 COMExceptionBase(HRESULT hr
)
77 LPCTSTR
ErrorMessage() const
82 if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
|FORMAT_MESSAGE_FROM_SYSTEM
,
83 0, _hr
, MAKELANGID(LANG_NEUTRAL
,SUBLANG_DEFAULT
), (LPTSTR
)&pBuf
, 0, NULL
)) {
88 _stprintf(buffer
, TEXT("unknown Exception: 0x%08lX"), _hr
);
104 /// Exception with context information
106 struct COMException
: public COMExceptionBase
108 typedef COMExceptionBase super
;
110 COMException(HRESULT hr
)
112 _context(CURRENT_CONTEXT
),
113 _file(NULL
), _line(0)
116 LOG(CURRENT_CONTEXT
.getStackTrace());
119 COMException(HRESULT hr
, const char* file
, int line
)
121 _context(CURRENT_CONTEXT
),
122 _file(file
), _line(line
)
125 LOG(CURRENT_CONTEXT
.getStackTrace());
128 COMException(HRESULT hr
, const String
& obj
)
130 _context(CURRENT_CONTEXT
),
131 _file(NULL
), _line(0)
134 LOG(CURRENT_CONTEXT
.getStackTrace());
137 COMException(HRESULT hr
, const String
& obj
, const char* file
, int line
)
139 _context(CURRENT_CONTEXT
),
140 _file(file
), _line(line
)
143 LOG(CURRENT_CONTEXT
.getStackTrace());
146 String
toString() const;
154 #define THROW_EXCEPTION(hr) throw COMException(hr, __FILE__, __LINE__)
155 #define CHECKERROR(hr) ((void)(FAILED(hr)? THROW_EXCEPTION(hr): 0))
160 inline void CheckError(HRESULT hr
)
163 throw COMException(hr
);
169 /// COM Initialisation
175 CHECKERROR(CoInitialize(0));
178 #if (_WIN32_WINNT>=0x0400) || defined(_WIN32_DCOM)
181 CHECKERROR(CoInitializeEx(0, flag
));
192 /// OLE initialisation for drag drop support
198 CHECKERROR(OleInitialize(0));
208 /// Exception Handler for COM exceptions
210 extern void HandleException(COMException
& e
, HWND hwnd
);
213 /// We use a common IMalloc object for all shell memory allocations.
215 struct CommonShellMalloc
225 CHECKERROR(SHGetMalloc(&_p
));
243 /// wrapper class for IMalloc with usage of common allocator
249 // initialize s_cmn_shell_malloc
250 s_cmn_shell_malloc
.init();
253 IMalloc
* operator->()
255 return s_cmn_shell_malloc
;
258 static CommonShellMalloc s_cmn_shell_malloc
;
262 /// wrapper template class for pointers to shell objects managed by IMalloc
264 template<typename T
> struct SShellPtr
276 T
const* operator->() const
281 operator T
const *() const
286 const T
& operator*() const
314 mutable ShellMalloc _malloc
; // IMalloc memory management object
317 // disallow copying of SShellPtr objects
318 SShellPtr(const SShellPtr
&) {}
319 void operator=(SShellPtr
const&) {}
323 /// wrapper class for COM interface pointers
325 template<typename T
> struct SIfacePtr
339 SIfacePtr(IUnknown
* unknown
, REFIID riid
)
341 CHECKERROR(unknown
->QueryInterface(riid
, (LPVOID
*)&_p
));
354 const T
* operator->() const
359 /* not GCC compatible
360 operator const T*() const
375 bool empty() const //NOTE: GCC seems not to work correctly when defining operator bool() AND operator T*() at one time
380 SIfacePtr
& operator=(T
* p
)
392 void operator=(SIfacePtr
const& o
)
405 HRESULT
CreateInstance(REFIID clsid
, REFIID riid
)
407 return CoCreateInstance(clsid
, NULL
, CLSCTX_INPROC_SERVER
, riid
, (LPVOID
*)&_p
);
410 template<typename I
> HRESULT
QueryInterface(REFIID riid
, I
* p
)
412 return _p
->QueryInterface(riid
, (LPVOID
*)p
);
430 SIfacePtr(const SIfacePtr
& o
)
445 /// caching of desktop ShellFolder object
458 operator ShellFolder
&()
464 ShellFolder
* _desktop
;
468 #ifndef _NO_COMUTIL // _com_ptr available?
470 /// IShellFolder smart pointer
471 struct ShellFolder
: public IShellFolderPtr
// IShellFolderPtr uses intrinsic extensions of the VC++ compiler.
473 typedef IShellFolderPtr super
;
475 ShellFolder(); // desktop folder
476 ShellFolder(IShellFolder
* p
);
477 ShellFolder(IShellFolder
* parent
, LPCITEMIDLIST pidl
);
478 ShellFolder(LPCITEMIDLIST pidl
);
480 void attach(IShellFolder
* parent
, LPCITEMIDLIST pidl
);
481 String
get_name(LPCITEMIDLIST pidl
=NULL
, SHGDNF flags
=SHGDN_NORMAL
) const;
483 bool empty() const {return !operator bool();} //NOTE: see SIfacePtr::empty()
487 #define IShellLinkPtr IShellLinkWPtr
489 #define IShellLinkPtr IShellLinkAPtr
492 /// IShellLink smart pointer
493 struct ShellLinkPtr
: public IShellLinkPtr
495 typedef IShellLinkPtr super
;
497 ShellLinkPtr(IShellLink
* p
)
503 bool empty() const {return !operator bool();} //NOTE: see SIfacePtr::empty()
506 #else // _com_ptr not available -> use SIfacePtr
508 /// IShellFolder smart pointer
509 struct ShellFolder
: public SIfacePtr
<IShellFolder
>
511 typedef SIfacePtr
<IShellFolder
> super
;
514 ShellFolder(IShellFolder
* p
);
515 ShellFolder(IShellFolder
* parent
, LPCITEMIDLIST pidl
);
516 ShellFolder(LPCITEMIDLIST pidl
);
518 void attach(IShellFolder
* parent
, LPCITEMIDLIST pidl
);
519 String
get_name(LPCITEMIDLIST pidl
, SHGDNF flags
=SHGDN_NORMAL
) const;
522 /// IShellLink smart pointer
523 struct ShellLinkPtr
: public SIfacePtr
<IShellLink
>
525 typedef SIfacePtr
<IShellLink
> super
;
527 ShellLinkPtr(IShellLink
* p
)
538 extern ShellFolder
& GetDesktopFolder();
542 #define path_from_pidl path_from_pidlW
544 #define path_from_pidl path_from_pidlA
547 extern HRESULT
path_from_pidlA(IShellFolder
* folder
, LPCITEMIDLIST pidl
, LPSTR buffer
, int len
);
548 extern HRESULT
path_from_pidlW(IShellFolder
* folder
, LPCITEMIDLIST pidl
, LPWSTR buffer
, int len
);
549 extern HRESULT
name_from_pidl(IShellFolder
* folder
, LPCITEMIDLIST pidl
, LPTSTR buffer
, int len
, SHGDNF flags
);
552 #ifdef __MINGW32__ // ILGetSize() is currently missing in MinGW.
553 extern "C" UINT STDCALL
ILGetSize(LPCITEMIDLIST pidl
);
555 #ifdef UNICODE // CFSTR_FILENAME is defined wrong in MinGW.
556 #define CFSTR_FILENAMEW TEXT("FileNameW")
557 #undef CFSTR_FILENAME
558 #define CFSTR_FILENAME CFSTR_FILENAMEW
564 /// wrapper class for item ID lists
566 struct ShellPath
: public SShellPtr
<ITEMIDLIST
>
568 typedef SShellPtr
<ITEMIDLIST
> super
;
574 ShellPath(IShellFolder
* folder
, LPCWSTR path
)
576 CONTEXT("ShellPath::ShellPath(IShellFolder*, LPCWSTR)");
579 CHECKERROR(folder
->ParseDisplayName(0, NULL
, (LPOLESTR
)path
, NULL
, &_p
, NULL
));
584 ShellPath(LPCWSTR path
)
586 OBJ_CONTEXT("ShellPath::ShellPath(LPCWSTR)", path
);
589 CHECKERROR(GetDesktopFolder()->ParseDisplayName(0, NULL
, (LPOLESTR
)path
, NULL
, &_p
, NULL
));
594 ShellPath(IShellFolder
* folder
, LPCSTR path
)
596 CONTEXT("ShellPath::ShellPath(IShellFolder*, LPCSTR)");
601 MultiByteToWideChar(CP_ACP
, 0, path
, -1, b
, MAX_PATH
);
602 CHECKERROR(folder
->ParseDisplayName(0, NULL
, b
, NULL
, &_p
, NULL
));
607 ShellPath(LPCSTR path
)
609 CONTEXT("ShellPath::ShellPath(LPCSTR)");
614 MultiByteToWideChar(CP_ACP
, 0, path
, -1, b
, MAX_PATH
);
615 CHECKERROR(GetDesktopFolder()->ParseDisplayName(0, NULL
, b
, NULL
, &_p
, NULL
));
620 ShellPath(const ShellPath
& o
)
623 //CONTEXT("ShellPath::ShellPath(const ShellPath&)");
626 int l
= ILGetSize(o
._p
);
627 _p
= (ITEMIDLIST
*) _malloc
->Alloc(l
);
628 if (_p
) memcpy(_p
, o
._p
, l
);
632 explicit ShellPath(LPITEMIDLIST p
)
637 ShellPath(LPCITEMIDLIST p
)
639 //CONTEXT("ShellPath::ShellPath(LPCITEMIDLIST)");
642 int l
= ILGetSize(p
);
643 _p
= (ITEMIDLIST
*) _malloc
->Alloc(l
);
644 if (_p
) memcpy(_p
, p
, l
);
648 void operator=(const ShellPath
& o
)
650 //CONTEXT("ShellPath::operator=(const ShellPath&)");
655 int l
= ILGetSize(o
._p
);
657 _p
= (ITEMIDLIST
*) _malloc
->Alloc(l
);
658 if (_p
) memcpy(_p
, o
._p
, l
);
666 void operator=(ITEMIDLIST
* p
)
668 //CONTEXT("ShellPath::operator=(ITEMIDLIST*)");
673 int l
= ILGetSize(p
);
674 _p
= (ITEMIDLIST
*) _malloc
->Alloc(l
);
675 if (_p
) memcpy(_p
, p
, l
);
683 void operator=(const SHITEMID
& o
)
687 LPBYTE p
= (LPBYTE
)_malloc
->Alloc(o
.cb
+2);
688 if (p
) *(PWORD
)((LPBYTE
)memcpy(p
, &o
, o
.cb
)+o
.cb
) = 0;
694 void operator+=(const SHITEMID
& o
)
696 int l0
= ILGetSize(_p
);
697 LPBYTE p
= (LPBYTE
)_malloc
->Alloc(l0
+o
.cb
);
702 *(PWORD
)((LPBYTE
)memcpy(p
+l
, &o
, o
.cb
)+o
.cb
) = 0;
709 void assign(LPCITEMIDLIST pidl
, size_t size
)
711 //CONTEXT("ShellPath::assign(LPCITEMIDLIST, size_t)");
715 _p
= (ITEMIDLIST
*) _malloc
->Alloc(size
+sizeof(USHORT
/*SHITEMID::cb*/));
718 memcpy(_p
, pidl
, size
);
719 ((ITEMIDLIST
*)((LPBYTE
)_p
+size
))->mkid
.cb
= 0; // terminator
725 void assign(LPCITEMIDLIST pidl
)
727 //CONTEXT("ShellPath::assign(LPCITEMIDLIST)");
732 int l
= ILGetSize(pidl
);
733 _p
= (ITEMIDLIST
*) _malloc
->Alloc(l
);
734 if (_p
) memcpy(_p
, pidl
, l
);
741 void split(ShellPath
& parent
, ShellPath
& obj
) const;
743 void GetUIObjectOf(REFIID riid
, LPVOID
* ppvOut
, HWND hWnd
=0, ShellFolder
& sf
=GetDesktopFolder());
745 ShellFolder
get_folder()
747 return ShellFolder(_p
);
750 ShellFolder
get_folder(IShellFolder
* parent
)
752 CONTEXT("ShellPath::get_folder()");
753 return ShellFolder(parent
, _p
);
756 // convert an item id list from relative to absolute (=relative to the desktop) format
757 ShellPath
create_absolute_pidl(LPCITEMIDLIST parent_pidl
) const;
761 #ifdef __WINE__ // Wine doesn't know of unnamed union members and uses some macros instead.
762 #define UNION_MEMBER(x) DUMMYUNIONNAME.##x
764 #define UNION_MEMBER(x) x
768 // encapsulation of STRRET structure for easy string retrieval with conversion
771 #define StrRet StrRetW
772 #define tcscpyn wcscpyn
774 #define StrRet StrRetA
775 #define tcscpyn strcpyn
778 extern LPSTR
strcpyn(LPSTR dest
, LPCSTR source
, size_t count
);
779 extern LPWSTR
wcscpyn(LPWSTR dest
, LPCWSTR source
, size_t count
);
781 /// easy retrieval of multi byte strings out of STRRET structures
782 struct StrRetA
: public STRRET
786 if (uType
== STRRET_WSTR
)
787 ShellMalloc()->Free(pOleStr
);
790 void GetString(const SHITEMID
& shiid
, LPSTR b
, int l
)
794 WideCharToMultiByte(CP_ACP
, 0, UNION_MEMBER(pOleStr
), -1, b
, l
, NULL
, NULL
);
798 strcpyn(b
, (LPCSTR
)&shiid
+UNION_MEMBER(uOffset
), l
);
802 strcpyn(b
, UNION_MEMBER(cStr
), l
);
807 /// easy retrieval of wide char strings out of STRRET structures
808 struct StrRetW
: public STRRET
812 if (uType
== STRRET_WSTR
)
813 ShellMalloc()->Free(pOleStr
);
816 void GetString(const SHITEMID
& shiid
, LPWSTR b
, int l
)
820 wcscpyn(b
, UNION_MEMBER(pOleStr
), l
);
824 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)&shiid
+UNION_MEMBER(uOffset
), -1, b
, l
);
828 MultiByteToWideChar(CP_ACP
, 0, UNION_MEMBER(cStr
), -1, b
, l
);
834 /// Retrieval of file system paths of ShellPath objects
835 class FileSysShellPath
: public ShellPath
837 TCHAR _fullpath
[MAX_PATH
];
840 FileSysShellPath() {_fullpath
[0] = '\0';}
843 FileSysShellPath(const ShellPath
& o
) : ShellPath(o
) {_fullpath
[0] = '\0';}
845 operator LPCTSTR() {if (!SHGetPathFromIDList(_p
, _fullpath
)) return NULL
; return _fullpath
;}
849 /// Browse dialog operating on shell namespace
850 struct FolderBrowser
: public FileSysShellPath
852 FolderBrowser(HWND owner
, UINT flags
, LPCTSTR title
, LPCITEMIDLIST root
=0)
854 _displayname
[0] = '\0';
855 _browseinfo
.hwndOwner
= owner
;
856 _browseinfo
.pidlRoot
= root
;
857 _browseinfo
.pszDisplayName
= _displayname
;
858 _browseinfo
.lpszTitle
= title
;
859 _browseinfo
.ulFlags
= flags
;
860 _browseinfo
.lpfn
= 0;
861 _browseinfo
.lParam
= 0;
862 _browseinfo
.iImage
= 0;
864 _p
= SHBrowseForFolder(&_browseinfo
);
867 LPCTSTR
GetDisplayName()
878 BROWSEINFO _browseinfo
;
879 TCHAR _displayname
[MAX_PATH
];
883 /// Retrieval of special shell folder paths
884 struct SpecialFolderPath
: public ShellPath
886 SpecialFolderPath(int folder
, HWND hwnd
)
888 HRESULT hr
= SHGetSpecialFolderLocation(hwnd
, folder
, &_p
);
893 /// Shell folder path of the desktop
894 struct DesktopFolderPath
: public SpecialFolderPath
897 : SpecialFolderPath(CSIDL_DESKTOP
, 0)
902 /// Retrieval of special shell folder
903 struct SpecialFolder
: public ShellFolder
905 SpecialFolder(int folder
, HWND hwnd
)
906 : ShellFolder(GetDesktopFolder(), SpecialFolderPath(folder
, hwnd
))
911 /// Shell folder of the desktop
912 struct DesktopFolder
: public ShellFolder
917 /// file system path of special folder
918 struct SpecialFolderFSPath
920 SpecialFolderFSPath(int folder
/*e.g. CSIDL_DESKTOP*/, HWND hwnd
);
928 TCHAR _fullpath
[MAX_PATH
];
932 /// file system path of special folder
933 struct SpecialFolderFSPath : public FileSysShellPath
935 SpecialFolderFSPath(int folder, HWND hwnd)
937 CONTEXT("SpecialFolderFSPath::SpecialFolderFSPath()");
939 HRESULT hr = SHGetSpecialFolderLocation(hwnd, folder, &_p);
946 /// wrapper class for enumerating shell namespace objects
948 struct ShellItemEnumerator
: public SIfacePtr
<IEnumIDList
>
950 ShellItemEnumerator(IShellFolder
* folder
, DWORD flags
=SHCONTF_FOLDERS
|SHCONTF_NONFOLDERS
|SHCONTF_INCLUDEHIDDEN
)
952 CONTEXT("ShellItemEnumerator::ShellItemEnumerator()");
954 CHECKERROR(folder
->EnumObjects(0, flags
, &_p
));
964 memset(&_stgm
, 0, sizeof(STGMEDIUM
));
970 GlobalUnlock(_stgm
.hGlobal
);
971 ReleaseStgMedium(&_stgm
);
975 HRESULT
GetData(IDataObject
* selection
)
977 static UINT CF_IDLIST
= RegisterClipboardFormat(CFSTR_SHELLIDLIST
);
980 fetc
.cfFormat
= CF_IDLIST
;
982 fetc
.dwAspect
= DVASPECT_CONTENT
;
984 fetc
.tymed
= TYMED_HGLOBAL
;
986 HRESULT hr
= selection
->QueryGetData(&fetc
);
990 hr
= selection
->GetData(&fetc
, &_stgm
);
994 _pIDList
= (LPIDA
)GlobalLock(_stgm
.hGlobal
);
999 operator LPIDA() {return _pIDList
;}
1007 struct CtxMenuInterfaces
1015 bool HandleMenuMsg(UINT nmsg
, WPARAM wparam
, LPARAM lparam
);
1016 IContextMenu
* query_interfaces(IContextMenu
* pcm1
);
1018 IContextMenu2
* _pctxmenu2
;
1020 #ifndef __MINGW32__ // IContextMenu3 missing in MinGW (as of 6.2.2005)
1021 IContextMenu3
* _pctxmenu3
;
1025 template<typename BASE
> struct ExtContextMenuHandlerT
1030 ExtContextMenuHandlerT(HWND hwnd
)
1035 template<typename PARA
> ExtContextMenuHandlerT(HWND hwnd
, const PARA
& info
)
1040 LRESULT
WndProc(UINT nmsg
, WPARAM wparam
, LPARAM lparam
)
1044 case WM_MEASUREITEM
:
1045 if (!wparam
) // Is the message menu-related?
1046 if (_cm_ifs
.HandleMenuMsg(nmsg
, wparam
, lparam
))
1051 case WM_INITMENUPOPUP
:
1052 if (_cm_ifs
.HandleMenuMsg(nmsg
, wparam
, lparam
))
1057 #ifndef __MINGW32__ // IContextMenu3 missing in MinGW (as of 6.2.2005)
1058 case WM_MENUCHAR
: // only supported by IContextMenu3
1059 if (_cm_ifs
._pctxmenu3
) {
1060 LRESULT lResult
= 0;
1062 _cm_ifs
._pctxmenu3
->HandleMenuMsg2(nmsg
, wparam
, lparam
, &lResult
);
1071 return super::WndProc(nmsg
, wparam
, lparam
);
1075 CtxMenuInterfaces _cm_ifs
;
1078 extern HRESULT
ShellFolderContextMenu(IShellFolder
* shell_folder
, HWND hwndParent
, int cidl
,
1079 LPCITEMIDLIST
* ppidl
, int x
, int y
, CtxMenuInterfaces
& cm_ifs
);