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
];
76 int n
= _stprintf_s2(p
, l
, TEXT("%s\nContext: %s"), super::ErrorMessage(), (LPCTSTR
)_context
.toString());
81 p
+= _stprintf_s2(p
, l
, TEXT("\nLocation: %hs:%d"), _file
, _line
);
87 /// Exception Handler for COM exceptions
89 void HandleException(COMException
& e
, HWND hwnd
)
91 String msg
= e
.toString();
95 if (hwnd
&& !IsWindowVisible(hwnd
))
98 MessageBox(hwnd
, msg
, TEXT("ShellClasses Exception"), MB_ICONHAND
|MB_OK
);
100 // If displaying the error message box _with_ parent was not successfull, display it now without a parent window.
101 if (GetLastError() == ERROR_INVALID_WINDOW_HANDLE
)
102 MessageBox(0, msg
, TEXT("ShellClasses Exception"), MB_ICONHAND
|MB_OK
);
106 // common IMalloc object
108 CommonShellMalloc
ShellMalloc::s_cmn_shell_malloc
;
111 // common desktop object
113 ShellFolder
& GetDesktopFolder()
115 static CommonDesktop s_desktop
;
117 // initialize s_desktop
124 void CommonDesktop::init()
126 CONTEXT("CommonDesktop::init()");
129 _desktop
= new ShellFolder
;
132 CommonDesktop::~CommonDesktop()
139 HRESULT
path_from_pidlA(IShellFolder
* folder
, LPCITEMIDLIST pidl
, LPSTR buffer
, int len
)
141 CONTEXT("path_from_pidlA()");
145 HRESULT hr
= folder
->GetDisplayNameOf(pidl
, SHGDN_FORPARSING
, &str
);
148 str
.GetString(pidl
->mkid
, buffer
, len
);
155 HRESULT
path_from_pidlW(IShellFolder
* folder
, LPCITEMIDLIST pidl
, LPWSTR buffer
, int len
)
157 CONTEXT("path_from_pidlW()");
161 HRESULT hr
= folder
->GetDisplayNameOf(pidl
, SHGDN_FORPARSING
, &str
);
164 str
.GetString(pidl
->mkid
, buffer
, len
);
171 HRESULT
name_from_pidl(IShellFolder
* folder
, LPCITEMIDLIST pidl
, LPTSTR buffer
, int len
, SHGDNF flags
)
173 CONTEXT("name_from_pidl()");
177 HRESULT hr
= folder
->GetDisplayNameOf(pidl
, flags
, &str
);
180 str
.GetString(pidl
->mkid
, buffer
, len
);
190 ShellFolder::ShellFolder()
192 CONTEXT("ShellFolder::ShellFolder()");
194 IShellFolder
* desktop
;
196 CHECKERROR(SHGetDesktopFolder(&desktop
));
198 super::Attach(desktop
);
202 ShellFolder::ShellFolder(IShellFolder
* p
)
205 CONTEXT("ShellFolder::ShellFolder(IShellFolder*)");
210 ShellFolder::ShellFolder(IShellFolder
* parent
, LPCITEMIDLIST pidl
)
212 CONTEXT("ShellFolder::ShellFolder(IShellFolder*, LPCITEMIDLIST)");
217 CHECKERROR(E_INVALIDARG
);
219 if (pidl
&& pidl
->mkid
.cb
)
220 CHECKERROR(parent
->BindToObject(pidl
, 0, IID_IShellFolder
, (LPVOID
*)&ptr
));
228 ShellFolder::ShellFolder(LPCITEMIDLIST pidl
)
230 CONTEXT("ShellFolder::ShellFolder(LPCITEMIDLIST)");
233 IShellFolder
* parent
= GetDesktopFolder();
235 if (pidl
&& pidl
->mkid
.cb
)
236 CHECKERROR(parent
->BindToObject(pidl
, 0, IID_IShellFolder
, (LPVOID
*)&ptr
));
244 void ShellFolder::attach(IShellFolder
* parent
, LPCITEMIDLIST pidl
)
246 CONTEXT("ShellFolder::attach(IShellFolder*, LPCITEMIDLIST)");
250 if (pidl
&& pidl
->mkid
.cb
)
251 CHECKERROR(parent
->BindToObject(pidl
, 0, IID_IShellFolder
, (LPVOID
*)&ptr
));
259 #else // _com_ptr not available -> use SIfacePtr
261 ShellFolder::ShellFolder()
263 CONTEXT("ShellFolder::ShellFolder()");
265 CHECKERROR(SHGetDesktopFolder(&_p
));
270 ShellFolder::ShellFolder(IShellFolder
* p
)
273 CONTEXT("ShellFolder::ShellFolder(IShellFolder*)");
278 ShellFolder::ShellFolder(IShellFolder
* parent
, LPCITEMIDLIST pidl
)
280 CONTEXT("ShellFolder::ShellFolder(IShellFolder*, LPCITEMIDLIST)");
282 if (pidl
&& pidl
->mkid
.cb
)
283 CHECKERROR(parent
->BindToObject(pidl
, 0, IID_IShellFolder
, (LPVOID
*)&_p
));
285 _p
= GetDesktopFolder();
290 ShellFolder::ShellFolder(LPCITEMIDLIST pidl
)
292 CONTEXT("ShellFolder::ShellFolder(LPCITEMIDLIST)");
294 if (pidl
&& pidl
->mkid
.cb
)
295 CHECKERROR(GetDesktopFolder()->BindToObject(pidl
, 0, IID_IShellFolder
, (LPVOID
*)&_p
));
297 _p
= GetDesktopFolder();
302 void ShellFolder::attach(IShellFolder
* parent
, LPCITEMIDLIST pidl
)
304 CONTEXT("ShellFolder::ShellFolder(IShellFolder*, LPCITEMIDLIST)");
306 IShellFolder
* h
= _p
;
308 CHECKERROR(parent
->BindToObject(pidl
, 0, IID_IShellFolder
, (LPVOID
*)&_p
));
316 String
ShellFolder::get_name(LPCITEMIDLIST pidl
, SHGDNF flags
) const
318 CONTEXT("ShellFolder::get_name()");
320 TCHAR buffer
[MAX_PATH
];
323 HRESULT hr
= ((IShellFolder
*)*const_cast<ShellFolder
*>(this))->GetDisplayNameOf(pidl
, flags
, &strret
);
326 strret
.GetString(pidl
->mkid
, buffer
, COUNTOF(buffer
));
329 *buffer
= TEXT('\0');
336 void ShellPath::split(ShellPath
& parent
, ShellPath
& obj
) const
338 SHITEMID
*piid
, *piidLast
;
341 // find last item-id and calculate total size of pidl
342 for(piid
=piidLast
=&_p
->mkid
; piid
->cb
; ) {
345 piid
= (SHITEMID
*)((LPBYTE
)piid
+ (piid
->cb
));
348 // copy parent folder portion
349 size
-= piidLast
->cb
; // don't count "object" item-id
352 parent
.assign(_p
, size
);
354 // copy "object" portion
355 obj
.assign((ITEMIDLIST
*)piidLast
, piidLast
->cb
);
358 void ShellPath::GetUIObjectOf(REFIID riid
, LPVOID
* ppvOut
, HWND hWnd
, ShellFolder
& sf
)
360 CONTEXT("ShellPath::GetUIObjectOf()");
362 ShellPath parent
, obj
;
366 LPCITEMIDLIST idl
= obj
;
368 if (parent
&& parent
->mkid
.cb
)
369 // use the IShellFolder of the parent
370 CHECKERROR(ShellFolder((IShellFolder
*)sf
,parent
)->GetUIObjectOf(hWnd
, 1, &idl
, riid
, 0, ppvOut
));
371 else // else use desktop folder
372 CHECKERROR(sf
->GetUIObjectOf(hWnd
, 1, &idl
, riid
, 0, ppvOut
));
375 #if 0 // ILCombine() was missing in previous versions of MinGW and is not exported from shell32.dll on Windows 2000.
377 // convert an item id list from relative to absolute (=relative to the desktop) format
378 ShellPath
ShellPath::create_absolute_pidl(LPCITEMIDLIST parent_pidl
) const
380 CONTEXT("ShellPath::create_absolute_pidl()");
382 return ILCombine(parent_pidl
, _p
);
384 /* seems to work only for NT upwards
385 // create a new item id list with _p append behind parent_pidl
386 int l1 = ILGetSize(parent_pidl) - sizeof(USHORT/ SHITEMID::cb /);
387 int l2 = ILGetSize(_p);
389 LPITEMIDLIST p = (LPITEMIDLIST) _malloc->Alloc(l1+l2);
391 memcpy(p, parent_pidl, l1);
392 memcpy((LPBYTE)p+l1, _p, l2);
400 ShellPath
ShellPath::create_absolute_pidl(LPCITEMIDLIST parent_pidl
) const
402 CONTEXT("ShellPath::create_absolute_pidl()");
404 static DynamicFct
<LPITEMIDLIST(WINAPI
*)(LPCITEMIDLIST
, LPCITEMIDLIST
)> ILCombine(TEXT("SHELL32"), 25);
407 return (*ILCombine
)(parent_pidl
, _p
);
409 // create a new item id list with _p append behind parent_pidl
410 int l1
= ILGetSize(parent_pidl
) - sizeof(USHORT
/*SHITEMID::cb*/);
411 int l2
= ILGetSize(_p
);
413 LPITEMIDLIST p
= (LPITEMIDLIST
) _malloc
->Alloc(l1
+l2
);
415 memcpy(p
, parent_pidl
, l1
);
416 memcpy((LPBYTE
)p
+l1
, _p
, l2
);
423 // local implementation of ILGetSize() to replace missing export on Windows 2000
424 UINT
ILGetSize_local(LPCITEMIDLIST pidl
)
429 int l
= sizeof(USHORT
/*SHITEMID::cb*/);
431 while(pidl
->mkid
.cb
) {
433 pidl
= LPCITEMIDLIST((LPBYTE
)pidl
+pidl
->mkid
.cb
);
441 #define CSIDL_FLAG_CREATE 0x8000
444 /// file system path of special folder
445 SpecialFolderFSPath::SpecialFolderFSPath(int folder
, HWND hwnd
)
450 static DynamicFct
<BOOL (__stdcall
*)(HWND hwnd
, LPTSTR pszPath
, int csidl
, BOOL fCreate
)> s_pSHGetSpecialFolderPath(TEXT("shell32"), "SHGetSpecialFolderPathW");
452 static DynamicFct
<BOOL (__stdcall
*)(HWND hwnd
, LPTSTR pszPath
, int csidl
, BOOL fCreate
)> s_pSHGetSpecialFolderPath(TEXT("shell32"), "SHGetSpecialFolderPathA");
454 if (*s_pSHGetSpecialFolderPath
)
455 (*s_pSHGetSpecialFolderPath
)(hwnd
, _fullpath
, folder
, TRUE
);
457 // SHGetSpecialFolderPath() is not compatible to WIN95/NT4
459 static DynamicFct
<HRESULT (__stdcall
*)(HWND hwnd
, int csidl
, HANDLE hToken
, DWORD dwFlags
, LPTSTR pszPath
)> s_pSHGetFolderPath_shell32(TEXT("shell32"), "SHGetFolderPathW");
461 static DynamicFct
<HRESULT (__stdcall
*)(HWND hwnd
, int csidl
, HANDLE hToken
, DWORD dwFlags
, LPTSTR pszPath
)> s_pSHGetFolderPath_shell32(TEXT("shell32"), "SHGetFolderPathA");
463 if (*s_pSHGetFolderPath_shell32
)
464 (*s_pSHGetFolderPath_shell32
)(hwnd
, folder
|CSIDL_FLAG_CREATE
, 0, 0, _fullpath
);
466 // SHGetFolderPath() is only present in shfolder.dll on some platforms.
468 static DynamicLoadLibFct
<HRESULT (__stdcall
*)(HWND hwnd
, int csidl
, HANDLE hToken
, DWORD dwFlags
, LPTSTR pszPath
)> s_pSHGetFolderPath_shfolder(TEXT("shfolder"), "SHGetFolderPathW");
470 static DynamicLoadLibFct
<HRESULT (__stdcall
*)(HWND hwnd
, int csidl
, HANDLE hToken
, DWORD dwFlags
, LPTSTR pszPath
)> s_pSHGetFolderPath_shfolder(TEXT("shfolder"), "SHGetFolderPathA");
472 if (*s_pSHGetFolderPath_shfolder
)
473 (*s_pSHGetFolderPath_shfolder
)(hwnd
, folder
|CSIDL_FLAG_CREATE
, 0, 0, _fullpath
);
479 void CtxMenuInterfaces::reset()
483 #ifndef __MINGW32__ // IContextMenu3 missing in MinGW (as of 6.2.2005)
488 bool CtxMenuInterfaces::HandleMenuMsg(UINT nmsg
, WPARAM wparam
, LPARAM lparam
)
490 #ifndef __MINGW32__ // IContextMenu3 missing in MinGW (as of 6.2.2005)
492 if (SUCCEEDED(_pctxmenu3
->HandleMenuMsg(nmsg
, wparam
, lparam
)))
498 if (SUCCEEDED(_pctxmenu2
->HandleMenuMsg(nmsg
, wparam
, lparam
)))
504 IContextMenu
* CtxMenuInterfaces::query_interfaces(IContextMenu
* pcm1
)
506 IContextMenu
* pcm
= NULL
;
510 // Get the higher version context menu interfaces.
511 #ifndef __MINGW32__ // IContextMenu3 missing in MinGW (as of 6.2.2005)
512 if (pcm1
->QueryInterface(IID_IContextMenu3
, (void**)&pcm
) == NOERROR
)
513 _pctxmenu3
= (LPCONTEXTMENU3
)pcm
;
516 if (pcm1
->QueryInterface(IID_IContextMenu2
, (void**)&pcm
) == NOERROR
)
517 _pctxmenu2
= (LPCONTEXTMENU2
)pcm
;
527 HRESULT
ShellFolderContextMenu(IShellFolder
* shell_folder
, HWND hwndParent
, int cidl
,
528 LPCITEMIDLIST
* apidl
, int x
, int y
, CtxMenuInterfaces
& cm_ifs
)
532 HRESULT hr
= shell_folder
->GetUIObjectOf(hwndParent
, cidl
, apidl
, IID_IContextMenu
, NULL
, (LPVOID
*)&pcm
);
533 // HRESULT hr = CDefFolderMenu_Create2(dir?dir->_pidl:DesktopFolder(), hwndParent, 1, &pidl, shell_folder, NULL, 0, NULL, &pcm);
536 pcm
= cm_ifs
.query_interfaces(pcm
);
538 HMENU hmenu
= CreatePopupMenu();
541 hr
= pcm
->QueryContextMenu(hmenu
, 0, FCIDM_SHVIEWFIRST
, FCIDM_SHVIEWLAST
, CMF_NORMAL
|CMF_EXPLORE
);
544 UINT idCmd
= TrackPopupMenu(hmenu
, TPM_LEFTALIGN
|TPM_RETURNCMD
|TPM_RIGHTBUTTON
, x
, y
, 0, hwndParent
, NULL
);
549 CMINVOKECOMMANDINFO cmi
;
551 cmi
.cbSize
= sizeof(CMINVOKECOMMANDINFO
);
553 cmi
.hwnd
= hwndParent
;
554 cmi
.lpVerb
= (LPCSTR
)(INT_PTR
)(idCmd
- FCIDM_SHVIEWFIRST
);
555 cmi
.lpParameters
= NULL
;
556 cmi
.lpDirectory
= NULL
;
557 cmi
.nShow
= SW_SHOWNORMAL
;
561 hr
= pcm
->InvokeCommand(&cmi
);