shellclasses.cpp

Go to the documentation of this file.
00001 /*
00002  * Copyright 2003, 2004, 2005 Martin Fuchs
00003  *
00004  * This library is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU Lesser General Public
00006  * License as published by the Free Software Foundation; either
00007  * version 2.1 of the License, or (at your option) any later version.
00008  *
00009  * This library is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * Lesser General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU Lesser General Public
00015  * License along with this library; if not, write to the Free Software
00016  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00017  */
00018 
00019 
00020  //
00021  // Explorer clone
00022  //
00023  // shellclasses.cpp
00024  //
00025  // C++ wrapper classes for COM interfaces and shell objects
00026  //
00027  // Martin Fuchs, 20.07.2003
00028  //
00029 
00030 
00031 #include <precomp.h>
00032 
00033 
00034 #ifdef _MS_VER
00035 #pragma comment(lib, "shell32") // link to shell32.dll
00036 #endif
00037 
00038 
00039  // work around GCC's wide string constant bug
00040 #ifdef __GNUC__
00041 const LPCTSTR sCFSTR_SHELLIDLIST = TEXT("Shell IDList Array");
00042 #endif
00043 
00044 
00045  // helper functions for string copying
00046 
00047 /*LPSTR strcpyn(LPSTR dest, LPCSTR source, size_t count)
00048 {
00049     LPCSTR s;
00050     LPSTR d = dest;
00051 
00052     for(s=source; count&&(*d++=*s++); )
00053         count--;
00054 
00055     return dest;
00056 }
00057 
00058 LPWSTR wcscpyn(LPWSTR dest, LPCWSTR source, size_t count)
00059 {
00060     LPCWSTR s;
00061     LPWSTR d = dest;
00062 
00063     for(s=source; count&&(*d++=*s++); )
00064         count--;
00065 
00066     return dest;
00067 }*/
00068 
00069 
00070 String COMException::toString() const
00071 {
00072     TCHAR msg[4*BUFFER_LEN];
00073 #ifdef __STDC_WANT_SECURE_LIB__
00074     int l = 4*BUFFER_LEN;
00075 #endif
00076     LPTSTR p = msg;
00077 
00078     int n = _stprintf_s2(p, l, TEXT("%s\nContext: %s"), super::ErrorMessage(), (LPCTSTR)_context.toString());
00079     p += n;
00080 #ifdef __STDC_WANT_SECURE_LIB__
00081     l -= n;
00082 #endif
00083 
00084     if (_file)
00085         p += _stprintf_s2(p, l, TEXT("\nLocation: %hs:%d"), _file, _line);
00086 
00087     return msg;
00088 }
00089 
00090 
00091  /// Exception Handler for COM exceptions
00092 
00093 void HandleException(COMException& e, HWND hwnd)
00094 {
00095     String msg = e.toString();
00096 
00097     SetLastError(0);
00098 
00099     if (hwnd && !IsWindowVisible(hwnd))
00100         hwnd = 0;
00101 
00102     MessageBox(hwnd, msg, TEXT("ShellClasses Exception"), MB_ICONHAND|MB_OK);
00103 
00104      // If displaying the error message box _with_ parent was not successfull, display it now without a parent window.
00105     if (GetLastError() == ERROR_INVALID_WINDOW_HANDLE)
00106         MessageBox(0, msg, TEXT("ShellClasses Exception"), MB_ICONHAND|MB_OK);
00107 }
00108 
00109 
00110  // common IMalloc object
00111 
00112 CommonShellMalloc ShellMalloc::s_cmn_shell_malloc;
00113 
00114 
00115  // common desktop object
00116 
00117 ShellFolder& GetDesktopFolder()
00118 {
00119     static CommonDesktop s_desktop;
00120 
00121      // initialize s_desktop
00122     s_desktop.init();
00123 
00124     return s_desktop;
00125 }
00126 
00127 
00128 void CommonDesktop::init()
00129 {
00130     CONTEXT("CommonDesktop::init()");
00131 
00132     if (!_desktop)
00133         _desktop = new ShellFolder;
00134 }
00135 
00136 CommonDesktop::~CommonDesktop()
00137 {
00138     if (_desktop)
00139         delete _desktop;
00140 }
00141 
00142 
00143 HRESULT path_from_pidlA(IShellFolder* folder, LPCITEMIDLIST pidl, LPSTR buffer, int len)
00144 {
00145     CONTEXT("path_from_pidlA()");
00146 
00147     StrRetA str;
00148 
00149     HRESULT hr = folder->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &str);
00150 
00151     if (SUCCEEDED(hr))
00152         str.GetString(pidl->mkid, buffer, len);
00153     else
00154         buffer[0] = '\0';
00155 
00156     return hr;
00157 }
00158 
00159 HRESULT path_from_pidlW(IShellFolder* folder, LPCITEMIDLIST pidl, LPWSTR buffer, int len)
00160 {
00161     CONTEXT("path_from_pidlW()");
00162 
00163     StrRetW str;
00164 
00165     HRESULT hr = folder->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &str);
00166 
00167     if (SUCCEEDED(hr))
00168         str.GetString(pidl->mkid, buffer, len);
00169     else
00170         buffer[0] = '\0';
00171 
00172     return hr;
00173 }
00174 
00175 HRESULT name_from_pidl(IShellFolder* folder, LPCITEMIDLIST pidl, LPTSTR buffer, int len, SHGDNF flags)
00176 {
00177     CONTEXT("name_from_pidl()");
00178 
00179     StrRet str;
00180 
00181     HRESULT hr = folder->GetDisplayNameOf(pidl, flags, &str);
00182 
00183     if (SUCCEEDED(hr))
00184         str.GetString(pidl->mkid, buffer, len);
00185     else
00186         buffer[0] = '\0';
00187 
00188     return hr;
00189 }
00190 
00191 
00192 #ifndef _NO_COMUTIL
00193 
00194 ShellFolder::ShellFolder()
00195 {
00196     CONTEXT("ShellFolder::ShellFolder()");
00197 
00198     IShellFolder* desktop;
00199 
00200     CHECKERROR(SHGetDesktopFolder(&desktop));
00201 
00202     super::Attach(desktop);
00203     desktop->AddRef();
00204 }
00205 
00206 ShellFolder::ShellFolder(IShellFolder* p)
00207  :  super(p)
00208 {
00209     CONTEXT("ShellFolder::ShellFolder(IShellFolder*)");
00210 
00211     p->AddRef();
00212 }
00213 
00214 ShellFolder::ShellFolder(IShellFolder* parent, LPCITEMIDLIST pidl)
00215 {
00216     CONTEXT("ShellFolder::ShellFolder(IShellFolder*, LPCITEMIDLIST)");
00217 
00218     IShellFolder* ptr;
00219 
00220     if (!pidl)
00221         CHECKERROR(E_INVALIDARG);
00222 
00223     if (pidl && pidl->mkid.cb)
00224         CHECKERROR(parent->BindToObject(pidl, 0, IID_IShellFolder, (LPVOID*)&ptr));
00225     else
00226         ptr = parent;
00227 
00228     super::Attach(ptr);
00229     ptr->AddRef();
00230 }
00231 
00232 ShellFolder::ShellFolder(LPCITEMIDLIST pidl)
00233 {
00234     CONTEXT("ShellFolder::ShellFolder(LPCITEMIDLIST)");
00235 
00236     IShellFolder* ptr;
00237     IShellFolder* parent = GetDesktopFolder();
00238 
00239     if (pidl && pidl->mkid.cb)
00240         CHECKERROR(parent->BindToObject(pidl, 0, IID_IShellFolder, (LPVOID*)&ptr));
00241     else
00242         ptr = parent;
00243 
00244     super::Attach(ptr);
00245     ptr->AddRef();
00246 }
00247 
00248 void ShellFolder::attach(IShellFolder* parent, LPCITEMIDLIST pidl)
00249 {
00250     CONTEXT("ShellFolder::attach(IShellFolder*, LPCITEMIDLIST)");
00251 
00252     IShellFolder* ptr;
00253 
00254     if (pidl && pidl->mkid.cb)
00255         CHECKERROR(parent->BindToObject(pidl, 0, IID_IShellFolder, (LPVOID*)&ptr));
00256     else
00257         ptr = parent;
00258 
00259     super::Attach(ptr);
00260     ptr->AddRef();
00261 }
00262 
00263 #else // _com_ptr not available -> use SIfacePtr
00264 
00265 ShellFolder::ShellFolder()
00266 {
00267     CONTEXT("ShellFolder::ShellFolder()");
00268 
00269     CHECKERROR(SHGetDesktopFolder(&_p));
00270 
00271     _p->AddRef();
00272 }
00273 
00274 ShellFolder::ShellFolder(IShellFolder* p)
00275  :  super(p)
00276 {
00277     CONTEXT("ShellFolder::ShellFolder(IShellFolder*)");
00278 
00279     _p->AddRef();
00280 }
00281 
00282 ShellFolder::ShellFolder(IShellFolder* parent, LPCITEMIDLIST pidl)
00283 {
00284     CONTEXT("ShellFolder::ShellFolder(IShellFolder*, LPCITEMIDLIST)");
00285 
00286     if (pidl && pidl->mkid.cb)
00287         CHECKERROR(parent->BindToObject(pidl, 0, IID_IShellFolder, (LPVOID*)&_p));
00288     else
00289         _p = GetDesktopFolder();
00290 
00291     _p->AddRef();
00292 }
00293 
00294 ShellFolder::ShellFolder(LPCITEMIDLIST pidl)
00295 {
00296     CONTEXT("ShellFolder::ShellFolder(LPCITEMIDLIST)");
00297 
00298     if (pidl && pidl->mkid.cb)
00299         CHECKERROR(GetDesktopFolder()->BindToObject(pidl, 0, IID_IShellFolder, (LPVOID*)&_p));
00300     else
00301         _p = GetDesktopFolder();
00302 
00303     _p->AddRef();
00304 }
00305 
00306 void ShellFolder::attach(IShellFolder* parent, LPCITEMIDLIST pidl)
00307 {
00308     CONTEXT("ShellFolder::ShellFolder(IShellFolder*, LPCITEMIDLIST)");
00309 
00310     IShellFolder* h = _p;
00311 
00312     CHECKERROR(parent->BindToObject(pidl, 0, IID_IShellFolder, (LPVOID*)&_p));
00313 
00314     _p->AddRef();
00315     h->Release();
00316 }
00317 
00318 #endif
00319 
00320 String ShellFolder::get_name(LPCITEMIDLIST pidl, SHGDNF flags) const
00321 {
00322     CONTEXT("ShellFolder::get_name()");
00323 
00324     TCHAR buffer[MAX_PATH];
00325     StrRet strret;
00326 
00327     HRESULT hr = ((IShellFolder*)*const_cast<ShellFolder*>(this))->GetDisplayNameOf(pidl, flags, &strret);
00328 
00329     if (hr == S_OK)
00330         strret.GetString(pidl->mkid, buffer, COUNTOF(buffer));
00331     else {
00332         CHECKERROR(hr);
00333         *buffer = TEXT('\0');
00334     }
00335 
00336     return buffer;
00337 }
00338 
00339 
00340 void ShellPath::split(ShellPath& parent, ShellPath& obj) const
00341 {
00342     SHITEMID *piid, *piidLast;
00343     int size = 0;
00344 
00345      // find last item-id and calculate total size of pidl
00346     for(piid=piidLast=&_p->mkid; piid->cb; ) {
00347         piidLast = piid;
00348         size += (piid->cb);
00349         piid = (SHITEMID*)((LPBYTE)piid + (piid->cb));
00350     }
00351 
00352      // copy parent folder portion
00353     size -= piidLast->cb;  // don't count "object" item-id
00354 
00355     if (size > 0)
00356         parent.assign(_p, size);
00357 
00358      // copy "object" portion
00359     obj.assign((ITEMIDLIST*)piidLast, piidLast->cb);
00360 }
00361 
00362 void ShellPath::GetUIObjectOf(REFIID riid, LPVOID* ppvOut, HWND hWnd, ShellFolder& sf)
00363 {
00364     CONTEXT("ShellPath::GetUIObjectOf()");
00365 
00366     ShellPath parent, obj;
00367 
00368     split(parent, obj);
00369 
00370     LPCITEMIDLIST idl = obj;
00371 
00372     if (parent && parent->mkid.cb)
00373          // use the IShellFolder of the parent
00374         CHECKERROR(ShellFolder((IShellFolder*)sf,parent)->GetUIObjectOf(hWnd, 1, &idl, riid, 0, ppvOut));
00375     else // else use desktop folder
00376         CHECKERROR(sf->GetUIObjectOf(hWnd, 1, &idl, riid, 0, ppvOut));
00377 }
00378 
00379 #if 0   // ILCombine() was missing in previous versions of MinGW and is not exported from shell32.dll on Windows 2000.
00380 
00381  // convert an item id list from relative to absolute (=relative to the desktop) format
00382 ShellPath ShellPath::create_absolute_pidl(LPCITEMIDLIST parent_pidl) const
00383 {
00384     CONTEXT("ShellPath::create_absolute_pidl()");
00385 
00386     return ILCombine(parent_pidl, _p);
00387 
00388 /* seems to work only for NT upwards
00389      // create a new item id list with _p append behind parent_pidl
00390     int l1 = ILGetSize(parent_pidl) - sizeof(USHORT/ SHITEMID::cb /);
00391     int l2 = ILGetSize(_p);
00392 
00393     LPITEMIDLIST p = (LPITEMIDLIST) _malloc->Alloc(l1+l2);
00394 
00395     memcpy(p, parent_pidl, l1);
00396     memcpy((LPBYTE)p+l1, _p, l2);
00397 
00398     return p;
00399 */
00400 }
00401 
00402 #else
00403 
00404 ShellPath ShellPath::create_absolute_pidl(LPCITEMIDLIST parent_pidl) const
00405 {
00406     CONTEXT("ShellPath::create_absolute_pidl()");
00407 
00408     static DynamicFct<LPITEMIDLIST(WINAPI*)(LPCITEMIDLIST, LPCITEMIDLIST)> ILCombine(TEXT("SHELL32"), 25);
00409 
00410     if (ILCombine)
00411         return (*ILCombine)(parent_pidl, _p);
00412 
00413      // create a new item id list with _p append behind parent_pidl
00414     int l1 = ILGetSize(parent_pidl) - sizeof(USHORT/*SHITEMID::cb*/);
00415     int l2 = ILGetSize(_p);
00416 
00417     LPITEMIDLIST p = (LPITEMIDLIST) _malloc->Alloc(l1+l2);
00418 
00419     memcpy(p, parent_pidl, l1);
00420     memcpy((LPBYTE)p+l1, _p, l2);
00421 
00422     return p;
00423 }
00424 
00425 #endif
00426 
00427  // local implementation of ILGetSize() to replace missing export on Windows 2000
00428 UINT ILGetSize_local(LPCITEMIDLIST pidl)
00429 {
00430     if (!pidl)
00431         return 0;
00432 
00433     int l = sizeof(USHORT/*SHITEMID::cb*/);
00434 
00435     while(pidl->mkid.cb) {
00436         l += pidl->mkid.cb;
00437         pidl = LPCITEMIDLIST((LPBYTE)pidl+pidl->mkid.cb);
00438     }
00439 
00440     return l;
00441 }
00442 
00443 
00444 #ifndef _SHFOLDER_H_
00445 #define CSIDL_FLAG_CREATE   0x8000
00446 #endif
00447 
00448  /// file system path of special folder
00449 SpecialFolderFSPath::SpecialFolderFSPath(int folder, HWND hwnd)
00450 {
00451     _fullpath[0] = '\0';
00452 
00453 #ifdef UNICODE
00454     static DynamicFct<BOOL (__stdcall*)(HWND hwnd, LPTSTR pszPath, int csidl, BOOL fCreate)> s_pSHGetSpecialFolderPath(TEXT("shell32"), "SHGetSpecialFolderPathW");
00455 #else
00456     static DynamicFct<BOOL (__stdcall*)(HWND hwnd, LPTSTR pszPath, int csidl, BOOL fCreate)> s_pSHGetSpecialFolderPath(TEXT("shell32"), "SHGetSpecialFolderPathA");
00457 #endif
00458     if (*s_pSHGetSpecialFolderPath)
00459         (*s_pSHGetSpecialFolderPath)(hwnd, _fullpath, folder, TRUE);
00460     else {
00461          // SHGetSpecialFolderPath() is not compatible to WIN95/NT4
00462 #ifdef UNICODE
00463         static DynamicFct<HRESULT (__stdcall*)(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath)> s_pSHGetFolderPath_shell32(TEXT("shell32"), "SHGetFolderPathW");
00464 #else
00465         static DynamicFct<HRESULT (__stdcall*)(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath)> s_pSHGetFolderPath_shell32(TEXT("shell32"), "SHGetFolderPathA");
00466 #endif
00467         if (*s_pSHGetFolderPath_shell32)
00468             (*s_pSHGetFolderPath_shell32)(hwnd, folder|CSIDL_FLAG_CREATE, 0, 0, _fullpath);
00469         else {
00470              // SHGetFolderPath() is only present in shfolder.dll on some platforms.
00471 #ifdef UNICODE
00472             static DynamicLoadLibFct<HRESULT (__stdcall*)(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath)> s_pSHGetFolderPath_shfolder(TEXT("shfolder"), "SHGetFolderPathW");
00473 #else
00474             static DynamicLoadLibFct<HRESULT (__stdcall*)(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath)> s_pSHGetFolderPath_shfolder(TEXT("shfolder"), "SHGetFolderPathA");
00475 #endif
00476             if (*s_pSHGetFolderPath_shfolder)
00477                 (*s_pSHGetFolderPath_shfolder)(hwnd, folder|CSIDL_FLAG_CREATE, 0, 0, _fullpath);
00478         }
00479     }
00480 }
00481 
00482 
00483 void CtxMenuInterfaces::reset()
00484 {
00485     _pctxmenu2 = NULL;
00486 
00487 #ifndef __MINGW32__ // IContextMenu3 missing in MinGW (as of 6.2.2005)
00488     _pctxmenu3 = NULL;
00489 #endif
00490 }
00491 
00492 bool CtxMenuInterfaces::HandleMenuMsg(UINT nmsg, WPARAM wparam, LPARAM lparam)
00493 {
00494 #ifndef __MINGW32__ // IContextMenu3 missing in MinGW (as of 6.2.2005)
00495     if (_pctxmenu3) {
00496         if (SUCCEEDED(_pctxmenu3->HandleMenuMsg(nmsg, wparam, lparam)))
00497             return true;
00498     }
00499 #endif
00500 
00501     if (_pctxmenu2)
00502         if (SUCCEEDED(_pctxmenu2->HandleMenuMsg(nmsg, wparam, lparam)))
00503             return true;
00504 
00505     return false;
00506 }
00507 
00508 IContextMenu* CtxMenuInterfaces::query_interfaces(IContextMenu* pcm1)
00509 {
00510     IContextMenu* pcm = NULL;
00511 
00512     reset();
00513 
00514      // Get the higher version context menu interfaces.
00515 #ifndef __MINGW32__ // IContextMenu3 missing in MinGW (as of 6.2.2005)
00516     if (pcm1->QueryInterface(IID_IContextMenu3, (void**)&pcm) == NOERROR)
00517         _pctxmenu3 = (LPCONTEXTMENU3)pcm;
00518     else
00519 #endif
00520     if (pcm1->QueryInterface(IID_IContextMenu2, (void**)&pcm) == NOERROR)
00521         _pctxmenu2 = (LPCONTEXTMENU2)pcm;
00522 
00523     if (pcm) {
00524         pcm1->Release();
00525         return pcm;
00526     } else
00527         return pcm1;
00528 }
00529 
00530 
00531 HRESULT ShellFolderContextMenu(IShellFolder* shell_folder, HWND hwndParent, int cidl,
00532                                 LPCITEMIDLIST* apidl, int x, int y, CtxMenuInterfaces& cm_ifs)
00533 {
00534     IContextMenu* pcm;
00535 
00536     HRESULT hr = shell_folder->GetUIObjectOf(hwndParent, cidl, apidl, IID_IContextMenu, NULL, (LPVOID*)&pcm);
00537 //  HRESULT hr = CDefFolderMenu_Create2(dir?dir->_pidl:DesktopFolder(), hwndParent, 1, &pidl, shell_folder, NULL, 0, NULL, &pcm);
00538 
00539     if (SUCCEEDED(hr)) {
00540         pcm = cm_ifs.query_interfaces(pcm);
00541 
00542         HMENU hmenu = CreatePopupMenu();
00543 
00544         if (hmenu) {
00545             hr = pcm->QueryContextMenu(hmenu, 0, FCIDM_SHVIEWFIRST, FCIDM_SHVIEWLAST, CMF_NORMAL|CMF_EXPLORE);
00546 
00547             if (SUCCEEDED(hr)) {
00548                 UINT idCmd = TrackPopupMenu(hmenu, TPM_LEFTALIGN|TPM_RETURNCMD|TPM_RIGHTBUTTON, x, y, 0, hwndParent, NULL);
00549 
00550                 cm_ifs.reset();
00551 
00552                 if (idCmd) {
00553                   CMINVOKECOMMANDINFO cmi;
00554 
00555                   cmi.cbSize = sizeof(CMINVOKECOMMANDINFO);
00556                   cmi.fMask = 0;
00557                   cmi.hwnd = hwndParent;
00558                   cmi.lpVerb = (LPCSTR)(INT_PTR)(idCmd - FCIDM_SHVIEWFIRST);
00559                   cmi.lpParameters = NULL;
00560                   cmi.lpDirectory = NULL;
00561                   cmi.nShow = SW_SHOWNORMAL;
00562                   cmi.dwHotKey = 0;
00563                   cmi.hIcon = 0;
00564 
00565                   hr = pcm->InvokeCommand(&cmi);
00566                 }
00567             } else
00568                 cm_ifs.reset();
00569             DestroyMenu(hmenu);
00570         }
00571 
00572         pcm->Release();
00573     }
00574 
00575     return hr;
00576 }

ROS Explorer Source Code Documentation
generated on 02.01.2013 by doxygen

ROS Explorer Homepage