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
35 #pragma comment(lib, "shell32") // link to shell32.dll
39 // work around GCC's wide string constant bug
41 const LPCTSTR sCFSTR_SHELLIDLIST
= TEXT("Shell IDList Array");
45 // helper functions for string copying
47 LPSTR
strcpyn(LPSTR dest
, LPCSTR source
, size_t count
)
52 for(s
=source
; count
&&(*d
++=*s
++); )
58 LPWSTR
wcscpyn(LPWSTR dest
, LPCWSTR source
, size_t count
)
63 for(s
=source
; count
&&(*d
++=*s
++); )
70 String
COMException::toString() const
72 TCHAR msg
[4*BUFFER_LEN
];
75 p
+= _stprintf(p
, TEXT("%s\nContext: %s"), super::ErrorMessage(), (LPCTSTR
)_context
.toString());
78 p
+= _stprintf(p
, TEXT("\nLocation: %hs:%d"), _file
, _line
);
84 /// Exception Handler for COM exceptions
86 void HandleException(COMException
& e
, HWND hwnd
)
88 String msg
= e
.toString();
92 if (hwnd
&& !IsWindowVisible(hwnd
))
95 MessageBox(hwnd
, msg
, TEXT("ShellClasses Exception"), MB_ICONHAND
|MB_OK
);
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
);
103 // common IMalloc object
105 CommonShellMalloc
ShellMalloc::s_cmn_shell_malloc
;
108 // common desktop object
110 ShellFolder
& GetDesktopFolder()
112 static CommonDesktop s_desktop
;
114 // initialize s_desktop
121 void CommonDesktop::init()
123 CONTEXT("CommonDesktop::init()");
126 _desktop
= new ShellFolder
;
129 CommonDesktop::~CommonDesktop()
136 HRESULT
path_from_pidlA(IShellFolder
* folder
, LPCITEMIDLIST pidl
, LPSTR buffer
, int len
)
138 CONTEXT("path_from_pidlA()");
142 HRESULT hr
= folder
->GetDisplayNameOf(pidl
, SHGDN_FORPARSING
, &str
);
145 str
.GetString(pidl
->mkid
, buffer
, len
);
152 HRESULT
path_from_pidlW(IShellFolder
* folder
, LPCITEMIDLIST pidl
, LPWSTR buffer
, int len
)
154 CONTEXT("path_from_pidlW()");
158 HRESULT hr
= folder
->GetDisplayNameOf(pidl
, SHGDN_FORPARSING
, &str
);
161 str
.GetString(pidl
->mkid
, buffer
, len
);
168 HRESULT
name_from_pidl(IShellFolder
* folder
, LPCITEMIDLIST pidl
, LPTSTR buffer
, int len
, SHGDNF flags
)
170 CONTEXT("name_from_pidl()");
174 HRESULT hr
= folder
->GetDisplayNameOf(pidl
, flags
, &str
);
177 str
.GetString(pidl
->mkid
, buffer
, len
);
187 ShellFolder::ShellFolder()
189 CONTEXT("ShellFolder::ShellFolder()");
191 IShellFolder
* desktop
;
193 CHECKERROR(SHGetDesktopFolder(&desktop
));
195 super::Attach(desktop
);
199 ShellFolder::ShellFolder(IShellFolder
* p
)
202 CONTEXT("ShellFolder::ShellFolder(IShellFolder*)");
207 ShellFolder::ShellFolder(IShellFolder
* parent
, LPCITEMIDLIST pidl
)
209 CONTEXT("ShellFolder::ShellFolder(IShellFolder*, LPCITEMIDLIST)");
214 CHECKERROR(E_INVALIDARG
);
216 if (pidl
&& pidl
->mkid
.cb
)
217 CHECKERROR(parent
->BindToObject(pidl
, 0, IID_IShellFolder
, (LPVOID
*)&ptr
));
225 ShellFolder::ShellFolder(LPCITEMIDLIST pidl
)
227 CONTEXT("ShellFolder::ShellFolder(LPCITEMIDLIST)");
230 IShellFolder
* parent
= GetDesktopFolder();
232 if (pidl
&& pidl
->mkid
.cb
)
233 CHECKERROR(parent
->BindToObject(pidl
, 0, IID_IShellFolder
, (LPVOID
*)&ptr
));
241 void ShellFolder::attach(IShellFolder
* parent
, LPCITEMIDLIST pidl
)
243 CONTEXT("ShellFolder::attach(IShellFolder*, LPCITEMIDLIST)");
247 if (pidl
&& pidl
->mkid
.cb
)
248 CHECKERROR(parent
->BindToObject(pidl
, 0, IID_IShellFolder
, (LPVOID
*)&ptr
));
256 #else // _com_ptr not available -> use SIfacePtr
258 ShellFolder::ShellFolder()
260 CONTEXT("ShellFolder::ShellFolder()");
262 CHECKERROR(SHGetDesktopFolder(&_p
));
267 ShellFolder::ShellFolder(IShellFolder
* p
)
270 CONTEXT("ShellFolder::ShellFolder(IShellFolder*)");
275 ShellFolder::ShellFolder(IShellFolder
* parent
, LPCITEMIDLIST pidl
)
277 CONTEXT("ShellFolder::ShellFolder(IShellFolder*, LPCITEMIDLIST)");
279 if (pidl
&& pidl
->mkid
.cb
)
280 CHECKERROR(parent
->BindToObject(pidl
, 0, IID_IShellFolder
, (LPVOID
*)&_p
));
282 _p
= GetDesktopFolder();
287 ShellFolder::ShellFolder(LPCITEMIDLIST pidl
)
289 CONTEXT("ShellFolder::ShellFolder(LPCITEMIDLIST)");
291 if (pidl
&& pidl
->mkid
.cb
)
292 CHECKERROR(GetDesktopFolder()->BindToObject(pidl
, 0, IID_IShellFolder
, (LPVOID
*)&_p
));
294 _p
= GetDesktopFolder();
299 void ShellFolder::attach(IShellFolder
* parent
, LPCITEMIDLIST pidl
)
301 CONTEXT("ShellFolder::ShellFolder(IShellFolder*, LPCITEMIDLIST)");
303 IShellFolder
* h
= _p
;
305 CHECKERROR(parent
->BindToObject(pidl
, 0, IID_IShellFolder
, (LPVOID
*)&_p
));
313 String
ShellFolder::get_name(LPCITEMIDLIST pidl
, SHGDNF flags
) const
315 CONTEXT("ShellFolder::get_name()");
317 TCHAR buffer
[MAX_PATH
];
320 HRESULT hr
= ((IShellFolder
*)*const_cast<ShellFolder
*>(this))->GetDisplayNameOf(pidl
, flags
, &strret
);
323 strret
.GetString(pidl
->mkid
, buffer
, MAX_PATH
);
326 *buffer
= TEXT('\0');
333 void ShellPath::split(ShellPath
& parent
, ShellPath
& obj
) const
335 SHITEMID
*piid
, *piidLast
;
338 // find last item-id and calculate total size of pidl
339 for(piid
=piidLast
=&_p
->mkid
; piid
->cb
; ) {
342 piid
= (SHITEMID
*)((LPBYTE
)piid
+ (piid
->cb
));
345 // copy parent folder portion
346 size
-= piidLast
->cb
; // don't count "object" item-id
349 parent
.assign(_p
, size
);
351 // copy "object" portion
352 obj
.assign((ITEMIDLIST
*)piidLast
, piidLast
->cb
);
355 void ShellPath::GetUIObjectOf(REFIID riid
, LPVOID
* ppvOut
, HWND hWnd
, ShellFolder
& sf
)
357 CONTEXT("ShellPath::GetUIObjectOf()");
359 ShellPath parent
, obj
;
363 LPCITEMIDLIST idl
= obj
;
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
));
372 #ifndef __MINGW32__ // ILCombine() is currently missing in MinGW.
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
377 CONTEXT("ShellPath::create_absolute_pidl()");
379 return ILCombine(parent_pidl
, _p
);
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);
386 LPITEMIDLIST p = (LPITEMIDLIST) _malloc->Alloc(l1+l2);
388 memcpy(p, parent_pidl, l1);
389 memcpy((LPBYTE)p+l1, _p, l2);
397 ShellPath
ShellPath::create_absolute_pidl(LPCITEMIDLIST parent_pidl
) const
399 CONTEXT("ShellPath::create_absolute_pidl()");
401 static DynamicFct
<LPITEMIDLIST(WINAPI
*)(LPCITEMIDLIST
, LPCITEMIDLIST
)> ILCombine(TEXT("SHELL32"), 25);
404 return (*ILCombine
)(parent_pidl
, _p
);
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
);
410 LPITEMIDLIST p
= (LPITEMIDLIST
) _malloc
->Alloc(l1
+l2
);
412 memcpy(p
, parent_pidl
, l1
);
413 memcpy((LPBYTE
)p
+l1
, _p
, l2
);
418 UINT
ILGetSize(LPCITEMIDLIST pidl
)
423 int l
= sizeof(USHORT
/*SHITEMID::cb*/);
425 while(pidl
->mkid
.cb
) {
427 pidl
= LPCITEMIDLIST((LPBYTE
)pidl
+pidl
->mkid
.cb
);
437 #define CSIDL_FLAG_CREATE 0x8000
440 /// file system path of special folder
441 SpecialFolderFSPath::SpecialFolderFSPath(int folder
, HWND hwnd
)
446 static DynamicFct
<BOOL (__stdcall
*)(HWND hwnd
, LPTSTR pszPath
, int csidl
, BOOL fCreate
)> s_pSHGetSpecialFolderPath(TEXT("shell32"), "SHGetSpecialFolderPathW");
448 static DynamicFct
<BOOL (__stdcall
*)(HWND hwnd
, LPTSTR pszPath
, int csidl
, BOOL fCreate
)> s_pSHGetSpecialFolderPath(TEXT("shell32"), "SHGetSpecialFolderPathA");
450 if (*s_pSHGetSpecialFolderPath
)
451 (*s_pSHGetSpecialFolderPath
)(hwnd
, _fullpath
, folder
, TRUE
);
453 // SHGetSpecialFolderPath() is not compatible to WIN95/NT4
455 static DynamicFct
<HRESULT (__stdcall
*)(HWND hwnd
, int csidl
, HANDLE hToken
, DWORD dwFlags
, LPTSTR pszPath
)> s_pSHGetFolderPath_shell32(TEXT("shell32"), "SHGetFolderPathW");
457 static DynamicFct
<HRESULT (__stdcall
*)(HWND hwnd
, int csidl
, HANDLE hToken
, DWORD dwFlags
, LPTSTR pszPath
)> s_pSHGetFolderPath_shell32(TEXT("shell32"), "SHGetFolderPathA");
459 if (*s_pSHGetFolderPath_shell32
)
460 (*s_pSHGetFolderPath_shell32
)(hwnd
, folder
|CSIDL_FLAG_CREATE
, 0, 0, _fullpath
);
462 // SHGetFolderPath() is only present in shfolder.dll on some platforms.
464 static DynamicLoadLibFct
<HRESULT (__stdcall
*)(HWND hwnd
, int csidl
, HANDLE hToken
, DWORD dwFlags
, LPTSTR pszPath
)> s_pSHGetFolderPath_shfolder(TEXT("shfolder"), "SHGetFolderPathW");
466 static DynamicLoadLibFct
<HRESULT (__stdcall
*)(HWND hwnd
, int csidl
, HANDLE hToken
, DWORD dwFlags
, LPTSTR pszPath
)> s_pSHGetFolderPath_shfolder(TEXT("shfolder"), "SHGetFolderPathA");
468 if (*s_pSHGetFolderPath_shfolder
)
469 (*s_pSHGetFolderPath_shfolder
)(hwnd
, folder
|CSIDL_FLAG_CREATE
, 0, 0, _fullpath
);
475 void CtxMenuInterfaces::reset()
479 #ifndef __MINGW32__ // IContextMenu3 missing in MinGW (as of 6.2.2005)
484 bool CtxMenuInterfaces::HandleMenuMsg(UINT nmsg
, WPARAM wparam
, LPARAM lparam
)
486 #ifndef __MINGW32__ // IContextMenu3 missing in MinGW (as of 6.2.2005)
488 if (SUCCEEDED(_pctxmenu3
->HandleMenuMsg(nmsg
, wparam
, lparam
)))
494 if (SUCCEEDED(_pctxmenu2
->HandleMenuMsg(nmsg
, wparam
, lparam
)))
500 IContextMenu
* CtxMenuInterfaces::query_interfaces(IContextMenu
* pcm1
)
502 IContextMenu
* pcm
= NULL
;
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
;
512 if (pcm1
->QueryInterface (IID_IContextMenu2
, (void**)&pcm
) == NOERROR
)
513 _pctxmenu2
= (LPCONTEXTMENU2
)pcm
;
523 HRESULT
ShellFolderContextMenu(IShellFolder
* shell_folder
, HWND hwndParent
, int cidl
,
524 LPCITEMIDLIST
* apidl
, int x
, int y
, CtxMenuInterfaces
& cm_ifs
)
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);
532 pcm
= cm_ifs
.query_interfaces(pcm
);
534 HMENU hmenu
= CreatePopupMenu();
537 hr
= pcm
->QueryContextMenu(hmenu
, 0, FCIDM_SHVIEWFIRST
, FCIDM_SHVIEWLAST
, CMF_NORMAL
|CMF_EXPLORE
);
540 UINT idCmd
= TrackPopupMenu(hmenu
, TPM_LEFTALIGN
|TPM_RETURNCMD
|TPM_RIGHTBUTTON
, x
, y
, 0, hwndParent
, NULL
);
545 CMINVOKECOMMANDINFO cmi
;
547 cmi
.cbSize
= sizeof(CMINVOKECOMMANDINFO
);
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
;
557 hr
= pcm
->InvokeCommand(&cmi
);