/* * Copyright 2003, 2004, 2005 Martin Fuchs * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ // // Explorer clone // // shellclasses.cpp // // C++ wrapper classes for COM interfaces and shell objects // // Martin Fuchs, 20.07.2003 // #include #ifdef _MS_VER #pragma comment(lib, "shell32") // link to shell32.dll #endif // work around GCC's wide string constant bug #ifdef __GNUC__ const LPCTSTR sCFSTR_SHELLIDLIST = TEXT("Shell IDList Array"); #endif // helper functions for string copying /*LPSTR strcpyn(LPSTR dest, LPCSTR source, size_t count) { LPCSTR s; LPSTR d = dest; for(s=source; count&&(*d++=*s++); ) count--; return dest; } LPWSTR wcscpyn(LPWSTR dest, LPCWSTR source, size_t count) { LPCWSTR s; LPWSTR d = dest; for(s=source; count&&(*d++=*s++); ) count--; return dest; }*/ String COMException::toString() const { TCHAR msg[4*BUFFER_LEN]; #ifdef __STDC_WANT_SECURE_LIB__ int l = 4*BUFFER_LEN; #endif LPTSTR p = msg; int n = _stprintf_s2(p, l, TEXT("%s\nContext: %s"), super::ErrorMessage(), (LPCTSTR)_context.toString()); p += n; #ifdef __STDC_WANT_SECURE_LIB__ l -= n; #endif if (_file) p += _stprintf_s2(p, l, TEXT("\nLocation: %hs:%d"), _file, _line); return msg; } /// Exception Handler for COM exceptions void HandleException(COMException& e, HWND hwnd) { String msg = e.toString(); SetLastError(0); if (hwnd && !IsWindowVisible(hwnd)) hwnd = 0; MessageBox(hwnd, msg, TEXT("ShellClasses Exception"), MB_ICONHAND|MB_OK); // If displaying the error message box _with_ parent was not successfull, display it now without a parent window. if (GetLastError() == ERROR_INVALID_WINDOW_HANDLE) MessageBox(0, msg, TEXT("ShellClasses Exception"), MB_ICONHAND|MB_OK); } // common IMalloc object CommonShellMalloc ShellMalloc::s_cmn_shell_malloc; // common desktop object ShellFolder& GetDesktopFolder() { static CommonDesktop s_desktop; // initialize s_desktop s_desktop.init(); return s_desktop; } void CommonDesktop::init() { CONTEXT("CommonDesktop::init()"); if (!_desktop) _desktop = new ShellFolder; } CommonDesktop::~CommonDesktop() { if (_desktop) delete _desktop; } HRESULT path_from_pidlA(IShellFolder* folder, LPCITEMIDLIST pidl, LPSTR buffer, int len) { CONTEXT("path_from_pidlA()"); StrRetA str; HRESULT hr = folder->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &str); if (SUCCEEDED(hr)) str.GetString(pidl->mkid, buffer, len); else buffer[0] = '\0'; return hr; } HRESULT path_from_pidlW(IShellFolder* folder, LPCITEMIDLIST pidl, LPWSTR buffer, int len) { CONTEXT("path_from_pidlW()"); StrRetW str; HRESULT hr = folder->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &str); if (SUCCEEDED(hr)) str.GetString(pidl->mkid, buffer, len); else buffer[0] = '\0'; return hr; } HRESULT name_from_pidl(IShellFolder* folder, LPCITEMIDLIST pidl, LPTSTR buffer, int len, SHGDNF flags) { CONTEXT("name_from_pidl()"); StrRet str; HRESULT hr = folder->GetDisplayNameOf(pidl, flags, &str); if (SUCCEEDED(hr)) str.GetString(pidl->mkid, buffer, len); else buffer[0] = '\0'; return hr; } #ifndef _NO_COMUTIL ShellFolder::ShellFolder() { CONTEXT("ShellFolder::ShellFolder()"); IShellFolder* desktop; CHECKERROR(SHGetDesktopFolder(&desktop)); super::Attach(desktop); desktop->AddRef(); } ShellFolder::ShellFolder(IShellFolder* p) : super(p) { CONTEXT("ShellFolder::ShellFolder(IShellFolder*)"); p->AddRef(); } ShellFolder::ShellFolder(IShellFolder* parent, LPCITEMIDLIST pidl) { CONTEXT("ShellFolder::ShellFolder(IShellFolder*, LPCITEMIDLIST)"); IShellFolder* ptr; if (!pidl) CHECKERROR(E_INVALIDARG); if (pidl && pidl->mkid.cb) CHECKERROR(parent->BindToObject(pidl, 0, IID_IShellFolder, (LPVOID*)&ptr)); else ptr = parent; super::Attach(ptr); ptr->AddRef(); } ShellFolder::ShellFolder(LPCITEMIDLIST pidl) { CONTEXT("ShellFolder::ShellFolder(LPCITEMIDLIST)"); IShellFolder* ptr; IShellFolder* parent = GetDesktopFolder(); if (pidl && pidl->mkid.cb) CHECKERROR(parent->BindToObject(pidl, 0, IID_IShellFolder, (LPVOID*)&ptr)); else ptr = parent; super::Attach(ptr); ptr->AddRef(); } void ShellFolder::attach(IShellFolder* parent, LPCITEMIDLIST pidl) { CONTEXT("ShellFolder::attach(IShellFolder*, LPCITEMIDLIST)"); IShellFolder* ptr; if (pidl && pidl->mkid.cb) CHECKERROR(parent->BindToObject(pidl, 0, IID_IShellFolder, (LPVOID*)&ptr)); else ptr = parent; super::Attach(ptr); ptr->AddRef(); } #else // _com_ptr not available -> use SIfacePtr ShellFolder::ShellFolder() { CONTEXT("ShellFolder::ShellFolder()"); CHECKERROR(SHGetDesktopFolder(&_p)); _p->AddRef(); } ShellFolder::ShellFolder(IShellFolder* p) : super(p) { CONTEXT("ShellFolder::ShellFolder(IShellFolder*)"); _p->AddRef(); } ShellFolder::ShellFolder(IShellFolder* parent, LPCITEMIDLIST pidl) { CONTEXT("ShellFolder::ShellFolder(IShellFolder*, LPCITEMIDLIST)"); if (pidl && pidl->mkid.cb) CHECKERROR(parent->BindToObject(pidl, 0, IID_IShellFolder, (LPVOID*)&_p)); else _p = GetDesktopFolder(); _p->AddRef(); } ShellFolder::ShellFolder(LPCITEMIDLIST pidl) { CONTEXT("ShellFolder::ShellFolder(LPCITEMIDLIST)"); if (pidl && pidl->mkid.cb) CHECKERROR(GetDesktopFolder()->BindToObject(pidl, 0, IID_IShellFolder, (LPVOID*)&_p)); else _p = GetDesktopFolder(); _p->AddRef(); } void ShellFolder::attach(IShellFolder* parent, LPCITEMIDLIST pidl) { CONTEXT("ShellFolder::ShellFolder(IShellFolder*, LPCITEMIDLIST)"); IShellFolder* h = _p; CHECKERROR(parent->BindToObject(pidl, 0, IID_IShellFolder, (LPVOID*)&_p)); _p->AddRef(); h->Release(); } #endif String ShellFolder::get_name(LPCITEMIDLIST pidl, SHGDNF flags) const { CONTEXT("ShellFolder::get_name()"); TCHAR buffer[MAX_PATH]; StrRet strret; HRESULT hr = ((IShellFolder*)*const_cast(this))->GetDisplayNameOf(pidl, flags, &strret); if (hr == S_OK) strret.GetString(pidl->mkid, buffer, COUNTOF(buffer)); else { CHECKERROR(hr); *buffer = TEXT('\0'); } return buffer; } void ShellPath::split(ShellPath& parent, ShellPath& obj) const { SHITEMID *piid, *piidLast; int size = 0; // find last item-id and calculate total size of pidl for(piid=piidLast=&_p->mkid; piid->cb; ) { piidLast = piid; size += (piid->cb); piid = (SHITEMID*)((LPBYTE)piid + (piid->cb)); } // copy parent folder portion size -= piidLast->cb; // don't count "object" item-id if (size > 0) parent.assign(_p, size); // copy "object" portion obj.assign((ITEMIDLIST*)piidLast, piidLast->cb); } void ShellPath::GetUIObjectOf(REFIID riid, LPVOID* ppvOut, HWND hWnd, ShellFolder& sf) { CONTEXT("ShellPath::GetUIObjectOf()"); ShellPath parent, obj; split(parent, obj); LPCITEMIDLIST idl = obj; if (parent && parent->mkid.cb) // use the IShellFolder of the parent CHECKERROR(ShellFolder((IShellFolder*)sf,parent)->GetUIObjectOf(hWnd, 1, &idl, riid, 0, ppvOut)); else // else use desktop folder CHECKERROR(sf->GetUIObjectOf(hWnd, 1, &idl, riid, 0, ppvOut)); } #if 0 // ILCombine() was missing in previous versions of MinGW and is not exported from shell32.dll on Windows 2000. // convert an item id list from relative to absolute (=relative to the desktop) format ShellPath ShellPath::create_absolute_pidl(LPCITEMIDLIST parent_pidl) const { CONTEXT("ShellPath::create_absolute_pidl()"); return ILCombine(parent_pidl, _p); /* seems to work only for NT upwards // create a new item id list with _p append behind parent_pidl int l1 = ILGetSize(parent_pidl) - sizeof(USHORT/ SHITEMID::cb /); int l2 = ILGetSize(_p); LPITEMIDLIST p = (LPITEMIDLIST) _malloc->Alloc(l1+l2); memcpy(p, parent_pidl, l1); memcpy((LPBYTE)p+l1, _p, l2); return p; */ } #else ShellPath ShellPath::create_absolute_pidl(LPCITEMIDLIST parent_pidl) const { CONTEXT("ShellPath::create_absolute_pidl()"); static DynamicFct ILCombine(TEXT("SHELL32"), 25); if (ILCombine) return (*ILCombine)(parent_pidl, _p); // create a new item id list with _p append behind parent_pidl int l1 = ILGetSize(parent_pidl) - sizeof(USHORT/*SHITEMID::cb*/); int l2 = ILGetSize(_p); LPITEMIDLIST p = (LPITEMIDLIST) _malloc->Alloc(l1+l2); memcpy(p, parent_pidl, l1); memcpy((LPBYTE)p+l1, _p, l2); return p; } #endif // local implementation of ILGetSize() to replace missing export on Windows 2000 UINT ILGetSize_local(LPCITEMIDLIST pidl) { if (!pidl) return 0; int l = sizeof(USHORT/*SHITEMID::cb*/); while(pidl->mkid.cb) { l += pidl->mkid.cb; pidl = LPCITEMIDLIST((LPBYTE)pidl+pidl->mkid.cb); } return l; } #ifndef _SHFOLDER_H_ #define CSIDL_FLAG_CREATE 0x8000 #endif /// file system path of special folder SpecialFolderFSPath::SpecialFolderFSPath(int folder, HWND hwnd) { _fullpath[0] = '\0'; #ifdef UNICODE static DynamicFct s_pSHGetSpecialFolderPath(TEXT("shell32"), "SHGetSpecialFolderPathW"); #else static DynamicFct s_pSHGetSpecialFolderPath(TEXT("shell32"), "SHGetSpecialFolderPathA"); #endif if (*s_pSHGetSpecialFolderPath) (*s_pSHGetSpecialFolderPath)(hwnd, _fullpath, folder, TRUE); else { // SHGetSpecialFolderPath() is not compatible to WIN95/NT4 #ifdef UNICODE static DynamicFct s_pSHGetFolderPath_shell32(TEXT("shell32"), "SHGetFolderPathW"); #else static DynamicFct s_pSHGetFolderPath_shell32(TEXT("shell32"), "SHGetFolderPathA"); #endif if (*s_pSHGetFolderPath_shell32) (*s_pSHGetFolderPath_shell32)(hwnd, folder|CSIDL_FLAG_CREATE, 0, 0, _fullpath); else { // SHGetFolderPath() is only present in shfolder.dll on some platforms. #ifdef UNICODE static DynamicLoadLibFct s_pSHGetFolderPath_shfolder(TEXT("shfolder"), "SHGetFolderPathW"); #else static DynamicLoadLibFct s_pSHGetFolderPath_shfolder(TEXT("shfolder"), "SHGetFolderPathA"); #endif if (*s_pSHGetFolderPath_shfolder) (*s_pSHGetFolderPath_shfolder)(hwnd, folder|CSIDL_FLAG_CREATE, 0, 0, _fullpath); } } } void CtxMenuInterfaces::reset() { _pctxmenu2 = NULL; #ifndef __MINGW32__ // IContextMenu3 missing in MinGW (as of 6.2.2005) _pctxmenu3 = NULL; #endif } bool CtxMenuInterfaces::HandleMenuMsg(UINT nmsg, WPARAM wparam, LPARAM lparam) { #ifndef __MINGW32__ // IContextMenu3 missing in MinGW (as of 6.2.2005) if (_pctxmenu3) { if (SUCCEEDED(_pctxmenu3->HandleMenuMsg(nmsg, wparam, lparam))) return true; } #endif if (_pctxmenu2) if (SUCCEEDED(_pctxmenu2->HandleMenuMsg(nmsg, wparam, lparam))) return true; return false; } IContextMenu* CtxMenuInterfaces::query_interfaces(IContextMenu* pcm1) { IContextMenu* pcm = NULL; reset(); // Get the higher version context menu interfaces. #ifndef __MINGW32__ // IContextMenu3 missing in MinGW (as of 6.2.2005) if (pcm1->QueryInterface(IID_IContextMenu3, (void**)&pcm) == NOERROR) _pctxmenu3 = (LPCONTEXTMENU3)pcm; else #endif if (pcm1->QueryInterface(IID_IContextMenu2, (void**)&pcm) == NOERROR) _pctxmenu2 = (LPCONTEXTMENU2)pcm; if (pcm) { pcm1->Release(); return pcm; } else return pcm1; } HRESULT ShellFolderContextMenu(IShellFolder* shell_folder, HWND hwndParent, int cidl, LPCITEMIDLIST* apidl, int x, int y, CtxMenuInterfaces& cm_ifs) { IContextMenu* pcm; HRESULT hr = shell_folder->GetUIObjectOf(hwndParent, cidl, apidl, IID_IContextMenu, NULL, (LPVOID*)&pcm); // HRESULT hr = CDefFolderMenu_Create2(dir?dir->_pidl:DesktopFolder(), hwndParent, 1, &pidl, shell_folder, NULL, 0, NULL, &pcm); if (SUCCEEDED(hr)) { pcm = cm_ifs.query_interfaces(pcm); HMENU hmenu = CreatePopupMenu(); if (hmenu) { hr = pcm->QueryContextMenu(hmenu, 0, FCIDM_SHVIEWFIRST, FCIDM_SHVIEWLAST, CMF_NORMAL|CMF_EXPLORE); if (SUCCEEDED(hr)) { UINT idCmd = TrackPopupMenu(hmenu, TPM_LEFTALIGN|TPM_RETURNCMD|TPM_RIGHTBUTTON, x, y, 0, hwndParent, NULL); cm_ifs.reset(); if (idCmd) { CMINVOKECOMMANDINFO cmi; cmi.cbSize = sizeof(CMINVOKECOMMANDINFO); cmi.fMask = 0; cmi.hwnd = hwndParent; cmi.lpVerb = (LPCSTR)(INT_PTR)(idCmd - FCIDM_SHVIEWFIRST); cmi.lpParameters = NULL; cmi.lpDirectory = NULL; cmi.nShow = SW_SHOWNORMAL; cmi.dwHotKey = 0; cmi.hIcon = 0; hr = pcm->InvokeCommand(&cmi); } } else cm_ifs.reset(); DestroyMenu(hmenu); } pcm->Release(); } return hr; }