1 /* 2 * Copyright 2003, 2004, 2005 Martin Fuchs 3 * 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. 8 * 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. 13 * 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 17 */ 18 19 20 // 21 // Explorer clone 22 // 23 // shellclasses.cpp 24 // 25 // C++ wrapper classes for COM interfaces and shell objects 26 // 27 // Martin Fuchs, 20.07.2003 28 // 29 30 31 #include <precomp.h> 32 33 34 #ifdef _MS_VER 35 #pragma comment(lib, "shell32") // link to shell32.dll 36 #endif 37 38 39 // work around GCC's wide string constant bug 40 #ifdef __GNUC__ 41 const LPCTSTR sCFSTR_SHELLIDLIST = TEXT("Shell IDList Array"); 42 #endif 43 44 45 // helper functions for string copying 46 47 /*LPSTR strcpyn(LPSTR dest, LPCSTR source, size_t count) 48 { 49 LPCSTR s; 50 LPSTR d = dest; 51 52 for(s=source; count&&(*d++=*s++); ) 53 count--; 54 55 return dest; 56 } 57 58 LPWSTR wcscpyn(LPWSTR dest, LPCWSTR source, size_t count) 59 { 60 LPCWSTR s; 61 LPWSTR d = dest; 62 63 for(s=source; count&&(*d++=*s++); ) 64 count--; 65 66 return dest; 67 }*/ 68 69 70 String COMException::toString() const 71 { 72 TCHAR msg[4*BUFFER_LEN]; 73 #ifdef __STDC_WANT_SECURE_LIB__ 74 int l = 4*BUFFER_LEN; 75 #endif 76 LPTSTR p = msg; 77 78 int n = _stprintf_s2(p, l, TEXT("%s\nContext: %s"), super::ErrorMessage(), (LPCTSTR)_context.toString()); 79 p += n; 80 #ifdef __STDC_WANT_SECURE_LIB__ 81 l -= n; 82 #endif 83 84 if (_file) 85 p += _stprintf_s2(p, l, TEXT("\nLocation: %hs:%d"), _file, _line); 86 87 return msg; 88 } 89 90 91 /// Exception Handler for COM exceptions 92 93 void HandleException(COMException& e, HWND hwnd) 94 { 95 String msg = e.toString(); 96 97 SetLastError(0); 98 99 if (hwnd && !IsWindowVisible(hwnd)) 100 hwnd = 0; 101 102 MessageBox(hwnd, msg, TEXT("ShellClasses Exception"), MB_ICONHAND|MB_OK); 103 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); 107 } 108 109 110 // common IMalloc object 111 112 CommonShellMalloc ShellMalloc::s_cmn_shell_malloc; 113 114 115 // common desktop object 116 117 ShellFolder& GetDesktopFolder() 118 { 119 static CommonDesktop s_desktop; 120 121 // initialize s_desktop 122 s_desktop.init(); 123 124 return s_desktop; 125 } 126 127 128 void CommonDesktop::init() 129 { 130 CONTEXT("CommonDesktop::init()"); 131 132 if (!_desktop) 133 _desktop = new ShellFolder; 134 } 135 136 CommonDesktop::~CommonDesktop() 137 { 138 if (_desktop) 139 delete _desktop; 140 } 141 142 143 HRESULT path_from_pidlA(IShellFolder* folder, LPCITEMIDLIST pidl, LPSTR buffer, int len) 144 { 145 CONTEXT("path_from_pidlA()"); 146 147 StrRetA str; 148 149 HRESULT hr = folder->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &str); 150 151 if (SUCCEEDED(hr)) 152 str.GetString(pidl->mkid, buffer, len); 153 else 154 buffer[0] = '\0'; 155 156 return hr; 157 } 158 159 HRESULT path_from_pidlW(IShellFolder* folder, LPCITEMIDLIST pidl, LPWSTR buffer, int len) 160 { 161 CONTEXT("path_from_pidlW()"); 162 163 StrRetW str; 164 165 HRESULT hr = folder->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &str); 166 167 if (SUCCEEDED(hr)) 168 str.GetString(pidl->mkid, buffer, len); 169 else 170 buffer[0] = '\0'; 171 172 return hr; 173 } 174 175 HRESULT name_from_pidl(IShellFolder* folder, LPCITEMIDLIST pidl, LPTSTR buffer, int len, SHGDNF flags) 176 { 177 CONTEXT("name_from_pidl()"); 178 179 StrRet str; 180 181 HRESULT hr = folder->GetDisplayNameOf(pidl, flags, &str); 182 183 if (SUCCEEDED(hr)) 184 str.GetString(pidl->mkid, buffer, len); 185 else 186 buffer[0] = '\0'; 187 188 return hr; 189 } 190 191 192 #ifndef _NO_COMUTIL 193 194 ShellFolder::ShellFolder() 195 { 196 CONTEXT("ShellFolder::ShellFolder()"); 197 198 IShellFolder* desktop; 199 200 CHECKERROR(SHGetDesktopFolder(&desktop)); 201 202 super::Attach(desktop); 203 desktop->AddRef(); 204 } 205 206 ShellFolder::ShellFolder(IShellFolder* p) 207 : super(p) 208 { 209 CONTEXT("ShellFolder::ShellFolder(IShellFolder*)"); 210 211 p->AddRef(); 212 } 213 214 ShellFolder::ShellFolder(IShellFolder* parent, LPCITEMIDLIST pidl) 215 { 216 CONTEXT("ShellFolder::ShellFolder(IShellFolder*, LPCITEMIDLIST)"); 217 218 IShellFolder* ptr; 219 220 if (!pidl) 221 CHECKERROR(E_INVALIDARG); 222 223 if (pidl && pidl->mkid.cb) 224 CHECKERROR(parent->BindToObject(pidl, 0, IID_IShellFolder, (LPVOID*)&ptr)); 225 else 226 ptr = parent; 227 228 super::Attach(ptr); 229 ptr->AddRef(); 230 } 231 232 ShellFolder::ShellFolder(LPCITEMIDLIST pidl) 233 { 234 CONTEXT("ShellFolder::ShellFolder(LPCITEMIDLIST)"); 235 236 IShellFolder* ptr; 237 IShellFolder* parent = GetDesktopFolder(); 238 239 if (pidl && pidl->mkid.cb) 240 CHECKERROR(parent->BindToObject(pidl, 0, IID_IShellFolder, (LPVOID*)&ptr)); 241 else 242 ptr = parent; 243 244 super::Attach(ptr); 245 ptr->AddRef(); 246 } 247 248 void ShellFolder::attach(IShellFolder* parent, LPCITEMIDLIST pidl) 249 { 250 CONTEXT("ShellFolder::attach(IShellFolder*, LPCITEMIDLIST)"); 251 252 IShellFolder* ptr; 253 254 if (pidl && pidl->mkid.cb) 255 CHECKERROR(parent->BindToObject(pidl, 0, IID_IShellFolder, (LPVOID*)&ptr)); 256 else 257 ptr = parent; 258 259 super::Attach(ptr); 260 ptr->AddRef(); 261 } 262 263 #else // _com_ptr not available -> use SIfacePtr 264 265 ShellFolder::ShellFolder() 266 { 267 CONTEXT("ShellFolder::ShellFolder()"); 268 269 CHECKERROR(SHGetDesktopFolder(&_p)); 270 271 _p->AddRef(); 272 } 273 274 ShellFolder::ShellFolder(IShellFolder* p) 275 : super(p) 276 { 277 CONTEXT("ShellFolder::ShellFolder(IShellFolder*)"); 278 279 _p->AddRef(); 280 } 281 282 ShellFolder::ShellFolder(IShellFolder* parent, LPCITEMIDLIST pidl) 283 { 284 CONTEXT("ShellFolder::ShellFolder(IShellFolder*, LPCITEMIDLIST)"); 285 286 if (pidl && pidl->mkid.cb) 287 CHECKERROR(parent->BindToObject(pidl, 0, IID_IShellFolder, (LPVOID*)&_p)); 288 else 289 _p = GetDesktopFolder(); 290 291 _p->AddRef(); 292 } 293 294 ShellFolder::ShellFolder(LPCITEMIDLIST pidl) 295 { 296 CONTEXT("ShellFolder::ShellFolder(LPCITEMIDLIST)"); 297 298 if (pidl && pidl->mkid.cb) 299 CHECKERROR(GetDesktopFolder()->BindToObject(pidl, 0, IID_IShellFolder, (LPVOID*)&_p)); 300 else 301 _p = GetDesktopFolder(); 302 303 _p->AddRef(); 304 } 305 306 void ShellFolder::attach(IShellFolder* parent, LPCITEMIDLIST pidl) 307 { 308 CONTEXT("ShellFolder::ShellFolder(IShellFolder*, LPCITEMIDLIST)"); 309 310 IShellFolder* h = _p; 311 312 CHECKERROR(parent->BindToObject(pidl, 0, IID_IShellFolder, (LPVOID*)&_p)); 313 314 _p->AddRef(); 315 h->Release(); 316 } 317 318 #endif 319 320 String ShellFolder::get_name(LPCITEMIDLIST pidl, SHGDNF flags) const 321 { 322 CONTEXT("ShellFolder::get_name()"); 323 324 TCHAR buffer[MAX_PATH]; 325 StrRet strret; 326 327 HRESULT hr = ((IShellFolder*)*const_cast<ShellFolder*>(this))->GetDisplayNameOf(pidl, flags, &strret); 328 329 if (hr == S_OK) 330 strret.GetString(pidl->mkid, buffer, COUNTOF(buffer)); 331 else { 332 CHECKERROR(hr); 333 *buffer = TEXT('\0'); 334 } 335 336 return buffer; 337 } 338 339 340 void ShellPath::split(ShellPath& parent, ShellPath& obj) const 341 { 342 SHITEMID *piid, *piidLast; 343 int size = 0; 344 345 // find last item-id and calculate total size of pidl 346 for(piid=piidLast=&_p->mkid; piid->cb; ) { 347 piidLast = piid; 348 size += (piid->cb); 349 piid = (SHITEMID*)((LPBYTE)piid + (piid->cb)); 350 } 351 352 // copy parent folder portion 353 size -= piidLast->cb; // don't count "object" item-id 354 355 if (size > 0) 356 parent.assign(_p, size); 357 358 // copy "object" portion 359 obj.assign((ITEMIDLIST*)piidLast, piidLast->cb); 360 } 361 362 void ShellPath::GetUIObjectOf(REFIID riid, LPVOID* ppvOut, HWND hWnd, ShellFolder& sf) 363 { 364 CONTEXT("ShellPath::GetUIObjectOf()"); 365 366 ShellPath parent, obj; 367 368 split(parent, obj); 369 370 LPCITEMIDLIST idl = obj; 371 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)); 377 } 378 379 #if 0 // ILCombine() was missing in previous versions of MinGW and is not exported from shell32.dll on Windows 2000. 380 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 383 { 384 CONTEXT("ShellPath::create_absolute_pidl()"); 385 386 return ILCombine(parent_pidl, _p); 387 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); 392 393 LPITEMIDLIST p = (LPITEMIDLIST) _malloc->Alloc(l1+l2); 394 395 memcpy(p, parent_pidl, l1); 396 memcpy((LPBYTE)p+l1, _p, l2); 397 398 return p; 399 */ 400 } 401 402 #else 403 404 ShellPath ShellPath::create_absolute_pidl(LPCITEMIDLIST parent_pidl) const 405 { 406 CONTEXT("ShellPath::create_absolute_pidl()"); 407 408 static DynamicFct<LPITEMIDLIST(WINAPI*)(LPCITEMIDLIST, LPCITEMIDLIST)> ILCombine(TEXT("SHELL32"), 25); 409 410 if (ILCombine) 411 return (*ILCombine)(parent_pidl, _p); 412 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); 416 417 LPITEMIDLIST p = (LPITEMIDLIST) _malloc->Alloc(l1+l2); 418 419 memcpy(p, parent_pidl, l1); 420 memcpy((LPBYTE)p+l1, _p, l2); 421 422 return p; 423 } 424 425 #endif 426 427 // local implementation of ILGetSize() to replace missing export on Windows 2000 428 UINT ILGetSize_local(LPCITEMIDLIST pidl) 429 { 430 if (!pidl) 431 return 0; 432 433 int l = sizeof(USHORT/*SHITEMID::cb*/); 434 435 while(pidl->mkid.cb) { 436 l += pidl->mkid.cb; 437 pidl = LPCITEMIDLIST((LPBYTE)pidl+pidl->mkid.cb); 438 } 439 440 return l; 441 } 442 443 444 #ifndef _SHFOLDER_H_ 445 #define CSIDL_FLAG_CREATE 0x8000 446 #endif 447 448 /// file system path of special folder 449 SpecialFolderFSPath::SpecialFolderFSPath(int folder, HWND hwnd) 450 { 451 _fullpath[0] = '\0'; 452 453 #ifdef UNICODE 454 static DynamicFct<BOOL (__stdcall*)(HWND hwnd, LPTSTR pszPath, int csidl, BOOL fCreate)> s_pSHGetSpecialFolderPath(TEXT("shell32"), "SHGetSpecialFolderPathW"); 455 #else 456 static DynamicFct<BOOL (__stdcall*)(HWND hwnd, LPTSTR pszPath, int csidl, BOOL fCreate)> s_pSHGetSpecialFolderPath(TEXT("shell32"), "SHGetSpecialFolderPathA"); 457 #endif 458 if (*s_pSHGetSpecialFolderPath) 459 (*s_pSHGetSpecialFolderPath)(hwnd, _fullpath, folder, TRUE); 460 else { 461 // SHGetSpecialFolderPath() is not compatible to WIN95/NT4 462 #ifdef UNICODE 463 static DynamicFct<HRESULT (__stdcall*)(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath)> s_pSHGetFolderPath_shell32(TEXT("shell32"), "SHGetFolderPathW"); 464 #else 465 static DynamicFct<HRESULT (__stdcall*)(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath)> s_pSHGetFolderPath_shell32(TEXT("shell32"), "SHGetFolderPathA"); 466 #endif 467 if (*s_pSHGetFolderPath_shell32) 468 (*s_pSHGetFolderPath_shell32)(hwnd, folder|CSIDL_FLAG_CREATE, 0, 0, _fullpath); 469 else { 470 // SHGetFolderPath() is only present in shfolder.dll on some platforms. 471 #ifdef UNICODE 472 static DynamicLoadLibFct<HRESULT (__stdcall*)(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath)> s_pSHGetFolderPath_shfolder(TEXT("shfolder"), "SHGetFolderPathW"); 473 #else 474 static DynamicLoadLibFct<HRESULT (__stdcall*)(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath)> s_pSHGetFolderPath_shfolder(TEXT("shfolder"), "SHGetFolderPathA"); 475 #endif 476 if (*s_pSHGetFolderPath_shfolder) 477 (*s_pSHGetFolderPath_shfolder)(hwnd, folder|CSIDL_FLAG_CREATE, 0, 0, _fullpath); 478 } 479 } 480 } 481 482 483 void CtxMenuInterfaces::reset() 484 { 485 _pctxmenu2 = NULL; 486 _pctxmenu3 = NULL; 487 } 488 489 bool CtxMenuInterfaces::HandleMenuMsg(UINT nmsg, WPARAM wparam, LPARAM lparam) 490 { 491 if (_pctxmenu3) { 492 if (SUCCEEDED(_pctxmenu3->HandleMenuMsg(nmsg, wparam, lparam))) 493 return true; 494 } 495 496 if (_pctxmenu2) 497 if (SUCCEEDED(_pctxmenu2->HandleMenuMsg(nmsg, wparam, lparam))) 498 return true; 499 500 return false; 501 } 502 503 IContextMenu* CtxMenuInterfaces::query_interfaces(IContextMenu* pcm1) 504 { 505 IContextMenu* pcm = NULL; 506 507 reset(); 508 509 // Get the higher version context menu interfaces. 510 if (pcm1->QueryInterface(IID_IContextMenu3, (void**)&pcm) == NOERROR) 511 _pctxmenu3 = (LPCONTEXTMENU3)pcm; 512 else 513 if (pcm1->QueryInterface(IID_IContextMenu2, (void**)&pcm) == NOERROR) 514 _pctxmenu2 = (LPCONTEXTMENU2)pcm; 515 516 if (pcm) { 517 pcm1->Release(); 518 return pcm; 519 } else 520 return pcm1; 521 } 522 523 524 HRESULT ShellFolderContextMenu(IShellFolder* shell_folder, HWND hwndParent, int cidl, 525 LPCITEMIDLIST* apidl, int x, int y, CtxMenuInterfaces& cm_ifs) 526 { 527 IContextMenu* pcm; 528 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); 531 532 if (SUCCEEDED(hr)) { 533 pcm = cm_ifs.query_interfaces(pcm); 534 535 HMENU hmenu = CreatePopupMenu(); 536 537 if (hmenu) { 538 hr = pcm->QueryContextMenu(hmenu, 0, FCIDM_SHVIEWFIRST, FCIDM_SHVIEWLAST, CMF_NORMAL|CMF_EXPLORE); 539 540 if (SUCCEEDED(hr)) { 541 UINT idCmd = TrackPopupMenu(hmenu, TPM_LEFTALIGN|TPM_RETURNCMD|TPM_RIGHTBUTTON, x, y, 0, hwndParent, NULL); 542 543 cm_ifs.reset(); 544 545 if (idCmd) { 546 CMINVOKECOMMANDINFO cmi; 547 548 cmi.cbSize = sizeof(CMINVOKECOMMANDINFO); 549 cmi.fMask = 0; 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; 555 cmi.dwHotKey = 0; 556 cmi.hIcon = 0; 557 558 hr = pcm->InvokeCommand(&cmi); 559 } 560 } else 561 cm_ifs.reset(); 562 DestroyMenu(hmenu); 563 } 564 565 pcm->Release(); 566 } 567 568 return hr; 569 } 570