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
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
];
73 #ifdef __STDC_WANT_SECURE_LIB__
78 int n
= _stprintf_s2(p
, l
, TEXT("%s\nContext: %s"), super::ErrorMessage(), (LPCTSTR
)_context
.toString());
80 #ifdef __STDC_WANT_SECURE_LIB__
85 p
+= _stprintf_s2(p
, l
, TEXT("\nLocation: %hs:%d"), _file
, _line
);
91 /// Exception Handler for COM exceptions
93 void HandleException(COMException
& e
, HWND hwnd
)
95 String msg
= e
.toString();
99 if (hwnd
&& !IsWindowVisible(hwnd
))
102 MessageBox(hwnd
, msg
, TEXT("ShellClasses Exception"), MB_ICONHAND
|MB_OK
);
104 // If displaying the error message box _with_ parent was not successfull, display it now without a parent window.
105 if (GetLastError() == ERROR_INVALID_WINDOW_HANDLE
)
106 MessageBox(0, msg
, TEXT("ShellClasses Exception"), MB_ICONHAND
|MB_OK
);
110 // common IMalloc object
112 CommonShellMalloc
ShellMalloc::s_cmn_shell_malloc
;
115 // common desktop object
117 ShellFolder
& GetDesktopFolder()
119 static CommonDesktop s_desktop
;
121 // initialize s_desktop
128 void CommonDesktop::init()
130 CONTEXT("CommonDesktop::init()");
133 _desktop
= new ShellFolder
;
136 CommonDesktop::~CommonDesktop()
143 HRESULT
path_from_pidlA(IShellFolder
* folder
, LPCITEMIDLIST pidl
, LPSTR buffer
, int len
)
145 CONTEXT("path_from_pidlA()");
149 HRESULT hr
= folder
->GetDisplayNameOf(pidl
, SHGDN_FORPARSING
, &str
);
152 str
.GetString(pidl
->mkid
, buffer
, len
);
159 HRESULT
path_from_pidlW(IShellFolder
* folder
, LPCITEMIDLIST pidl
, LPWSTR buffer
, int len
)
161 CONTEXT("path_from_pidlW()");
165 HRESULT hr
= folder
->GetDisplayNameOf(pidl
, SHGDN_FORPARSING
, &str
);
168 str
.GetString(pidl
->mkid
, buffer
, len
);
175 HRESULT
name_from_pidl(IShellFolder
* folder
, LPCITEMIDLIST pidl
, LPTSTR buffer
, int len
, SHGDNF flags
)
177 CONTEXT("name_from_pidl()");
181 HRESULT hr
= folder
->GetDisplayNameOf(pidl
, flags
, &str
);
184 str
.GetString(pidl
->mkid
, buffer
, len
);
194 ShellFolder::ShellFolder()
196 CONTEXT("ShellFolder::ShellFolder()");
198 IShellFolder
* desktop
;
200 CHECKERROR(SHGetDesktopFolder(&desktop
));
202 super::Attach(desktop
);
206 ShellFolder::ShellFolder(IShellFolder
* p
)
209 CONTEXT("ShellFolder::ShellFolder(IShellFolder*)");
214 ShellFolder::ShellFolder(IShellFolder
* parent
, LPCITEMIDLIST pidl
)
216 CONTEXT("ShellFolder::ShellFolder(IShellFolder*, LPCITEMIDLIST)");
221 CHECKERROR(E_INVALIDARG
);
223 if (pidl
&& pidl
->mkid
.cb
)
224 CHECKERROR(parent
->BindToObject(pidl
, 0, IID_IShellFolder
, (LPVOID
*)&ptr
));
232 ShellFolder::ShellFolder(LPCITEMIDLIST pidl
)
234 CONTEXT("ShellFolder::ShellFolder(LPCITEMIDLIST)");
237 IShellFolder
* parent
= GetDesktopFolder();
239 if (pidl
&& pidl
->mkid
.cb
)
240 CHECKERROR(parent
->BindToObject(pidl
, 0, IID_IShellFolder
, (LPVOID
*)&ptr
));
248 void ShellFolder::attach(IShellFolder
* parent
, LPCITEMIDLIST pidl
)
250 CONTEXT("ShellFolder::attach(IShellFolder*, LPCITEMIDLIST)");
254 if (pidl
&& pidl
->mkid
.cb
)
255 CHECKERROR(parent
->BindToObject(pidl
, 0, IID_IShellFolder
, (LPVOID
*)&ptr
));
263 #else // _com_ptr not available -> use SIfacePtr
265 ShellFolder::ShellFolder()
267 CONTEXT("ShellFolder::ShellFolder()");
269 CHECKERROR(SHGetDesktopFolder(&_p
));
274 ShellFolder::ShellFolder(IShellFolder
* p
)
277 CONTEXT("ShellFolder::ShellFolder(IShellFolder*)");
282 ShellFolder::ShellFolder(IShellFolder
* parent
, LPCITEMIDLIST pidl
)
284 CONTEXT("ShellFolder::ShellFolder(IShellFolder*, LPCITEMIDLIST)");
286 if (pidl
&& pidl
->mkid
.cb
)
287 CHECKERROR(parent
->BindToObject(pidl
, 0, IID_IShellFolder
, (LPVOID
*)&_p
));
289 _p
= GetDesktopFolder();
294 ShellFolder::ShellFolder(LPCITEMIDLIST pidl
)
296 CONTEXT("ShellFolder::ShellFolder(LPCITEMIDLIST)");
298 if (pidl
&& pidl
->mkid
.cb
)
299 CHECKERROR(GetDesktopFolder()->BindToObject(pidl
, 0, IID_IShellFolder
, (LPVOID
*)&_p
));
301 _p
= GetDesktopFolder();
306 void ShellFolder::attach(IShellFolder
* parent
, LPCITEMIDLIST pidl
)
308 CONTEXT("ShellFolder::ShellFolder(IShellFolder*, LPCITEMIDLIST)");
310 IShellFolder
* h
= _p
;
312 CHECKERROR(parent
->BindToObject(pidl
, 0, IID_IShellFolder
, (LPVOID
*)&_p
));
320 String
ShellFolder::get_name(LPCITEMIDLIST pidl
, SHGDNF flags
) const
322 CONTEXT("ShellFolder::get_name()");
324 TCHAR buffer
[MAX_PATH
];
327 HRESULT hr
= ((IShellFolder
*)*const_cast<ShellFolder
*>(this))->GetDisplayNameOf(pidl
, flags
, &strret
);
330 strret
.GetString(pidl
->mkid
, buffer
, COUNTOF(buffer
));
333 *buffer
= TEXT('\0');
340 void ShellPath::split(ShellPath
& parent
, ShellPath
& obj
) const
342 SHITEMID
*piid
, *piidLast
;
345 // find last item-id and calculate total size of pidl
346 for(piid
=piidLast
=&_p
->mkid
; piid
->cb
; ) {
349 piid
= (SHITEMID
*)((LPBYTE
)piid
+ (piid
->cb
));
352 // copy parent folder portion
353 size
-= piidLast
->cb
; // don't count "object" item-id
356 parent
.assign(_p
, size
);
358 // copy "object" portion
359 obj
.assign((ITEMIDLIST
*)piidLast
, piidLast
->cb
);
362 void ShellPath::GetUIObjectOf(REFIID riid
, LPVOID
* ppvOut
, HWND hWnd
, ShellFolder
& sf
)
364 CONTEXT("ShellPath::GetUIObjectOf()");
366 ShellPath parent
, obj
;
370 LPCITEMIDLIST idl
= obj
;
372 if (parent
&& parent
->mkid
.cb
)
373 // use the IShellFolder of the parent
374 CHECKERROR(ShellFolder((IShellFolder
*)sf
,parent
)->GetUIObjectOf(hWnd
, 1, &idl
, riid
, 0, ppvOut
));
375 else // else use desktop folder
376 CHECKERROR(sf
->GetUIObjectOf(hWnd
, 1, &idl
, riid
, 0, ppvOut
));
379 #if 0 // ILCombine() was missing in previous versions of MinGW and is not exported from shell32.dll on Windows 2000.
381 // convert an item id list from relative to absolute (=relative to the desktop) format
382 ShellPath
ShellPath::create_absolute_pidl(LPCITEMIDLIST parent_pidl
) const
384 CONTEXT("ShellPath::create_absolute_pidl()");
386 return ILCombine(parent_pidl
, _p
);
388 /* seems to work only for NT upwards
389 // create a new item id list with _p append behind parent_pidl
390 int l1 = ILGetSize(parent_pidl) - sizeof(USHORT/ SHITEMID::cb /);
391 int l2 = ILGetSize(_p);
393 LPITEMIDLIST p = (LPITEMIDLIST) _malloc->Alloc(l1+l2);
395 memcpy(p, parent_pidl, l1);
396 memcpy((LPBYTE)p+l1, _p, l2);
404 ShellPath
ShellPath::create_absolute_pidl(LPCITEMIDLIST parent_pidl
) const
406 CONTEXT("ShellPath::create_absolute_pidl()");
408 static DynamicFct
<LPITEMIDLIST(WINAPI
*)(LPCITEMIDLIST
, LPCITEMIDLIST
)> ILCombine(TEXT("SHELL32"), 25);
411 return (*ILCombine
)(parent_pidl
, _p
);
413 // create a new item id list with _p append behind parent_pidl
414 int l1
= ILGetSize(parent_pidl
) - sizeof(USHORT
/*SHITEMID::cb*/);
415 int l2
= ILGetSize(_p
);
417 LPITEMIDLIST p
= (LPITEMIDLIST
) _malloc
->Alloc(l1
+l2
);
419 memcpy(p
, parent_pidl
, l1
);
420 memcpy((LPBYTE
)p
+l1
, _p
, l2
);
427 // local implementation of ILGetSize() to replace missing export on Windows 2000
428 UINT
ILGetSize_local(LPCITEMIDLIST pidl
)
433 int l
= sizeof(USHORT
/*SHITEMID::cb*/);
435 while(pidl
->mkid
.cb
) {
437 pidl
= LPCITEMIDLIST((LPBYTE
)pidl
+pidl
->mkid
.cb
);
445 #define CSIDL_FLAG_CREATE 0x8000
448 /// file system path of special folder
449 SpecialFolderFSPath::SpecialFolderFSPath(int folder
, HWND hwnd
)
454 static DynamicFct
<BOOL (__stdcall
*)(HWND hwnd
, LPTSTR pszPath
, int csidl
, BOOL fCreate
)> s_pSHGetSpecialFolderPath(TEXT("shell32"), "SHGetSpecialFolderPathW");
456 static DynamicFct
<BOOL (__stdcall
*)(HWND hwnd
, LPTSTR pszPath
, int csidl
, BOOL fCreate
)> s_pSHGetSpecialFolderPath(TEXT("shell32"), "SHGetSpecialFolderPathA");
458 if (*s_pSHGetSpecialFolderPath
)
459 (*s_pSHGetSpecialFolderPath
)(hwnd
, _fullpath
, folder
, TRUE
);
461 // SHGetSpecialFolderPath() is not compatible to WIN95/NT4
463 static DynamicFct
<HRESULT (__stdcall
*)(HWND hwnd
, int csidl
, HANDLE hToken
, DWORD dwFlags
, LPTSTR pszPath
)> s_pSHGetFolderPath_shell32(TEXT("shell32"), "SHGetFolderPathW");
465 static DynamicFct
<HRESULT (__stdcall
*)(HWND hwnd
, int csidl
, HANDLE hToken
, DWORD dwFlags
, LPTSTR pszPath
)> s_pSHGetFolderPath_shell32(TEXT("shell32"), "SHGetFolderPathA");
467 if (*s_pSHGetFolderPath_shell32
)
468 (*s_pSHGetFolderPath_shell32
)(hwnd
, folder
|CSIDL_FLAG_CREATE
, 0, 0, _fullpath
);
470 // SHGetFolderPath() is only present in shfolder.dll on some platforms.
472 static DynamicLoadLibFct
<HRESULT (__stdcall
*)(HWND hwnd
, int csidl
, HANDLE hToken
, DWORD dwFlags
, LPTSTR pszPath
)> s_pSHGetFolderPath_shfolder(TEXT("shfolder"), "SHGetFolderPathW");
474 static DynamicLoadLibFct
<HRESULT (__stdcall
*)(HWND hwnd
, int csidl
, HANDLE hToken
, DWORD dwFlags
, LPTSTR pszPath
)> s_pSHGetFolderPath_shfolder(TEXT("shfolder"), "SHGetFolderPathA");
476 if (*s_pSHGetFolderPath_shfolder
)
477 (*s_pSHGetFolderPath_shfolder
)(hwnd
, folder
|CSIDL_FLAG_CREATE
, 0, 0, _fullpath
);
483 void CtxMenuInterfaces::reset()
489 bool CtxMenuInterfaces::HandleMenuMsg(UINT nmsg
, WPARAM wparam
, LPARAM lparam
)
492 if (SUCCEEDED(_pctxmenu3
->HandleMenuMsg(nmsg
, wparam
, lparam
)))
497 if (SUCCEEDED(_pctxmenu2
->HandleMenuMsg(nmsg
, wparam
, lparam
)))
503 IContextMenu
* CtxMenuInterfaces::query_interfaces(IContextMenu
* pcm1
)
505 IContextMenu
* pcm
= NULL
;
509 // Get the higher version context menu interfaces.
510 if (pcm1
->QueryInterface(IID_IContextMenu3
, (void**)&pcm
) == NOERROR
)
511 _pctxmenu3
= (LPCONTEXTMENU3
)pcm
;
513 if (pcm1
->QueryInterface(IID_IContextMenu2
, (void**)&pcm
) == NOERROR
)
514 _pctxmenu2
= (LPCONTEXTMENU2
)pcm
;
524 HRESULT
ShellFolderContextMenu(IShellFolder
* shell_folder
, HWND hwndParent
, int cidl
,
525 LPCITEMIDLIST
* apidl
, int x
, int y
, CtxMenuInterfaces
& cm_ifs
)
529 HRESULT hr
= shell_folder
->GetUIObjectOf(hwndParent
, cidl
, apidl
, IID_IContextMenu
, NULL
, (LPVOID
*)&pcm
);
530 // HRESULT hr = CDefFolderMenu_Create2(dir?dir->_pidl:DesktopFolder(), hwndParent, 1, &pidl, shell_folder, NULL, 0, NULL, &pcm);
533 pcm
= cm_ifs
.query_interfaces(pcm
);
535 HMENU hmenu
= CreatePopupMenu();
538 hr
= pcm
->QueryContextMenu(hmenu
, 0, FCIDM_SHVIEWFIRST
, FCIDM_SHVIEWLAST
, CMF_NORMAL
|CMF_EXPLORE
);
541 UINT idCmd
= TrackPopupMenu(hmenu
, TPM_LEFTALIGN
|TPM_RETURNCMD
|TPM_RIGHTBUTTON
, x
, y
, 0, hwndParent
, NULL
);
546 CMINVOKECOMMANDINFO cmi
;
548 cmi
.cbSize
= sizeof(CMINVOKECOMMANDINFO
);
550 cmi
.hwnd
= hwndParent
;
551 cmi
.lpVerb
= (LPCSTR
)(INT_PTR
)(idCmd
- FCIDM_SHVIEWFIRST
);
552 cmi
.lpParameters
= NULL
;
553 cmi
.lpDirectory
= NULL
;
554 cmi
.nShow
= SW_SHOWNORMAL
;
558 hr
= pcm
->InvokeCommand(&cmi
);