1// Windows/Shell.cpp 2 3#include "StdAfx.h" 4 5#include "../Common/MyCom.h" 6#include "../Common/StringConvert.h" 7 8#include "COM.h" 9#include "FileName.h" 10#include "MemoryGlobal.h" 11#include "Shell.h" 12 13#ifndef _UNICODE 14extern bool g_IsNT; 15#endif 16 17// MSVC6 and old SDK don't support this function: 18// #define LWSTDAPI EXTERN_C DECLSPEC_IMPORT HRESULT STDAPICALLTYPE 19// LWSTDAPI StrRetToStrW(STRRET *pstr, LPCITEMIDLIST pidl, LPWSTR *ppsz); 20 21// #define SHOW_DEBUG_SHELL 22 23#ifdef SHOW_DEBUG_SHELL 24 25#include "../Common/IntToString.h" 26 27static void Print_Number(UInt32 number, const char *s) 28{ 29 AString s2; 30 s2.Add_UInt32(number); 31 s2.Add_Space(); 32 s2 += s; 33 OutputDebugStringA(s2); 34} 35 36#define ODS(sz) { OutputDebugStringA(sz); } 37#define ODS_U(s) { OutputDebugStringW(s); } 38#define ODS_(op) { op; } 39 40#else 41 42#define ODS(sz) 43#define ODS_U(s) 44#define ODS_(op) 45 46#endif 47 48 49namespace NWindows { 50namespace NShell { 51 52#ifndef UNDER_CE 53 54// SHGetMalloc is unsupported in Windows Mobile? 55 56void CItemIDList::Free() 57{ 58 if (!m_Object) 59 return; 60 /* DOCs: 61 SHGetMalloc was introduced in Windows 95 and Microsoft Windows NT 4.0, 62 but as of Windows 2000 it is no longer necessary. 63 In its place, programs can call the equivalent (and easier to use) CoTaskMemAlloc and CoTaskMemFree. 64 Description from oldnewthings: 65 shell functions could work without COM (if OLE32.DLL is not loaded), 66 but now if OLE32.DLL is loaded, then shell functions and com functions do same things. 67 22.02: so we use OLE32.DLL function to free memory: 68 */ 69 /* 70 CMyComPtr<IMalloc> shellMalloc; 71 if (::SHGetMalloc(&shellMalloc) != NOERROR) 72 throw 41099; 73 shellMalloc->Free(m_Object); 74 */ 75 CoTaskMemFree(m_Object); 76 m_Object = NULL; 77} 78 79/* 80CItemIDList::(LPCITEMIDLIST itemIDList): m_Object(NULL) 81 { *this = itemIDList; } 82CItemIDList::(const CItemIDList& itemIDList): m_Object(NULL) 83 { *this = itemIDList; } 84 85CItemIDList& CItemIDList::operator=(LPCITEMIDLIST object) 86{ 87 Free(); 88 if (object != 0) 89 { 90 UINT32 size = GetSize(object); 91 m_Object = (LPITEMIDLIST)CoTaskMemAlloc(size); 92 if (m_Object != NULL) 93 MoveMemory(m_Object, object, size); 94 } 95 return *this; 96} 97 98CItemIDList& CItemIDList::operator=(const CItemIDList &object) 99{ 100 Free(); 101 if (object.m_Object != NULL) 102 { 103 UINT32 size = GetSize(object.m_Object); 104 m_Object = (LPITEMIDLIST)CoTaskMemAlloc(size); 105 if (m_Object != NULL) 106 MoveMemory(m_Object, object.m_Object, size); 107 } 108 return *this; 109} 110*/ 111 112 113static HRESULT ReadUnicodeStrings(const wchar_t *p, size_t size, UStringVector &names) 114{ 115 names.Clear(); 116 const wchar_t *lim = p + size; 117 UString s; 118 /* 119 if (size == 0 || p[size - 1] != 0) 120 return E_INVALIDARG; 121 if (size == 1) 122 return S_OK; 123 if (p[size - 2] != 0) 124 return E_INVALIDARG; 125 */ 126 for (;;) 127 { 128 const wchar_t *start = p; 129 for (;;) 130 { 131 if (p == lim) return E_INVALIDARG; // S_FALSE 132 if (*p++ == 0) 133 break; 134 } 135 const size_t num = (size_t)(p - start); 136 if (num == 1) 137 { 138 if (p != lim) return E_INVALIDARG; // S_FALSE 139 return S_OK; 140 } 141 s.SetFrom(start, (unsigned)(num - 1)); 142 ODS_U(s) 143 names.Add(s); 144 // names.ReserveOnePosition(); 145 // names.AddInReserved_Ptr_of_new(new UString((unsigned)num - 1, start)); 146 } 147} 148 149 150static HRESULT ReadAnsiStrings(const char *p, size_t size, UStringVector &names) 151{ 152 names.Clear(); 153 AString name; 154 for (; size != 0; size--) 155 { 156 const char c = *p++; 157 if (c == 0) 158 { 159 if (name.IsEmpty()) 160 return S_OK; 161 names.Add(GetUnicodeString(name)); 162 name.Empty(); 163 } 164 else 165 name += c; 166 } 167 return E_INVALIDARG; 168} 169 170 171#define INIT_FORMATETC_HGLOBAL(type) { (type), NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL } 172 173static HRESULT DataObject_GetData_HGLOBAL(IDataObject *dataObject, CLIPFORMAT cf, NCOM::CStgMedium &medium) 174{ 175 FORMATETC etc = INIT_FORMATETC_HGLOBAL(cf); 176 RINOK(dataObject->GetData(&etc, &medium)) 177 if (medium.tymed != TYMED_HGLOBAL) 178 return E_INVALIDARG; 179 return S_OK; 180} 181 182static HRESULT DataObject_GetData_HDROP_Names(IDataObject *dataObject, UStringVector &names) 183{ 184 names.Clear(); 185 NCOM::CStgMedium medium; 186 187 /* Win10 : if (dataObject) is from IContextMenu::Initialize() and 188 if (len_of_path >= MAX_PATH (260) for some file in data object) 189 { 190 GetData() returns HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) 191 "The data area passed to a system call is too small", 192 Is there a way to fix this code for long paths? 193 } */ 194 195 RINOK(DataObject_GetData_HGLOBAL(dataObject, CF_HDROP, medium)) 196 const size_t blockSize = GlobalSize(medium.hGlobal); 197 if (blockSize < sizeof(DROPFILES)) 198 return E_INVALIDARG; 199 NMemory::CGlobalLock dropLock(medium.hGlobal); 200 const DROPFILES *dropFiles = (const DROPFILES *)dropLock.GetPointer(); 201 if (!dropFiles) 202 return E_INVALIDARG; 203 if (blockSize < dropFiles->pFiles 204 || dropFiles->pFiles < sizeof(DROPFILES) 205 // || dropFiles->pFiles != sizeof(DROPFILES) 206 ) 207 return E_INVALIDARG; 208 const size_t size = blockSize - dropFiles->pFiles; 209 const void *namesData = (const Byte *)(const void *)dropFiles + dropFiles->pFiles; 210 HRESULT hres; 211 if (dropFiles->fWide) 212 { 213 if (size % sizeof(wchar_t) != 0) 214 return E_INVALIDARG; 215 hres = ReadUnicodeStrings((const wchar_t *)namesData, size / sizeof(wchar_t), names); 216 } 217 else 218 hres = ReadAnsiStrings((const char *)namesData, size, names); 219 220 ODS_(Print_Number(names.Size(), "DataObject_GetData_HDROP_Names")) 221 return hres; 222} 223 224 225 226// CF_IDLIST: 227#define MYWIN_CFSTR_SHELLIDLIST TEXT("Shell IDList Array") 228 229typedef struct 230{ 231 UINT cidl; 232 UINT aoffset[1]; 233} MYWIN_CIDA; 234/* 235 cidl : number of PIDLs that are being transferred, not including the parent folder. 236 aoffset : An array of offsets, relative to the beginning of this structure. 237 aoffset[0] - fully qualified PIDL of a parent folder. 238 If this PIDL is empty, the parent folder is the desktop. 239 aoffset[1] ... aoffset[cidl] : offset to one of the PIDLs to be transferred. 240 All of these PIDLs are relative to the PIDL of the parent folder. 241*/ 242 243static HRESULT DataObject_GetData_IDLIST(IDataObject *dataObject, UStringVector &names) 244{ 245 names.Clear(); 246 NCOM::CStgMedium medium; 247 RINOK(DataObject_GetData_HGLOBAL(dataObject, (CLIPFORMAT) 248 RegisterClipboardFormat(MYWIN_CFSTR_SHELLIDLIST), medium)) 249 const size_t blockSize = GlobalSize(medium.hGlobal); 250 if (blockSize < sizeof(MYWIN_CIDA) || blockSize >= (UInt32)((UInt32)0 - 1)) 251 return E_INVALIDARG; 252 NMemory::CGlobalLock dropLock(medium.hGlobal); 253 const MYWIN_CIDA *cida = (const MYWIN_CIDA *)dropLock.GetPointer(); 254 if (!cida) 255 return E_INVALIDARG; 256 if (cida->cidl == 0) 257 { 258 // is it posssible to have no selected items? 259 // it's unexpected case. 260 return E_INVALIDARG; 261 } 262 if (cida->cidl >= (blockSize - (UInt32)sizeof(MYWIN_CIDA)) / sizeof(UINT)) 263 return E_INVALIDARG; 264 const UInt32 start = cida->cidl * (UInt32)sizeof(UINT) + (UInt32)sizeof(MYWIN_CIDA); 265 266 STRRET strret; 267 CMyComPtr<IShellFolder> parentFolder; 268 { 269 const UINT offset = cida->aoffset[0]; 270 if (offset < start || offset >= blockSize 271 // || offset != start 272 ) 273 return E_INVALIDARG; 274 275 CMyComPtr<IShellFolder> desktopFolder; 276 RINOK(::SHGetDesktopFolder(&desktopFolder)) 277 if (!desktopFolder) 278 return E_FAIL; 279 280 LPCITEMIDLIST const lpcItem = (LPCITEMIDLIST)(const void *)((const Byte *)cida + offset); 281 282 #ifdef SHOW_DEBUG_SHELL 283 { 284 const HRESULT res = desktopFolder->GetDisplayNameOf( 285 lpcItem, SHGDN_FORPARSING, &strret); 286 if (res == S_OK && strret.uType == STRRET_WSTR) 287 { 288 ODS_U(strret.pOleStr) 289 /* if lpcItem is empty, the path will be 290 "C:\Users\user_name\Desktop" 291 if lpcItem is "My Computer" folder, the path will be 292 "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}" */ 293 CoTaskMemFree(strret.pOleStr); 294 } 295 } 296 #endif 297 298 RINOK(desktopFolder->BindToObject(lpcItem, 299 NULL, IID_IShellFolder, (void **)&parentFolder)) 300 if (!parentFolder) 301 return E_FAIL; 302 } 303 304 names.ClearAndReserve(cida->cidl); 305 UString path; 306 307 // for (int y = 0; y < 1; y++) // for debug 308 for (unsigned i = 1; i <= cida->cidl; i++) 309 { 310 const UINT offset = cida->aoffset[i]; 311 if (offset < start || offset >= blockSize) 312 return E_INVALIDARG; 313 const void *p = (const Byte *)(const void *)cida + offset; 314 /* ITEMIDLIST of file can contain more than one SHITEMID item. 315 In win10 only SHGDN_FORPARSING returns path that contains 316 all path parts related to parts of ITEMIDLIST. 317 So we can use only SHGDN_FORPARSING here. 318 Don't use (SHGDN_INFOLDER) 319 Don't use (SHGDN_INFOLDER | SHGDN_FORPARSING) 320 */ 321 RINOK(parentFolder->GetDisplayNameOf((LPCITEMIDLIST)p, SHGDN_FORPARSING, &strret)) 322 323 /* 324 // MSVC6 and old SDK do not support StrRetToStrW(). 325 LPWSTR lpstr; 326 RINOK (StrRetToStrW(&strret, NULL, &lpstr)) 327 ODS_U(lpstr) 328 path = lpstr; 329 CoTaskMemFree(lpstr); 330 */ 331 if (strret.uType != STRRET_WSTR) 332 return E_INVALIDARG; 333 ODS_U(strret.pOleStr) 334 path = strret.pOleStr; 335 // the path could have super path prefix "\\\\?\\" 336 // we can remove super path prefix here, if we don't need that prefix 337 #ifdef Z7_LONG_PATH 338 // we remove super prefix, if we can work without that prefix 339 NFile::NName::If_IsSuperPath_RemoveSuperPrefix(path); 340 #endif 341 names.AddInReserved(path); 342 CoTaskMemFree(strret.pOleStr); 343 } 344 345 ODS_(Print_Number(cida->cidl, "CFSTR_SHELLIDLIST END")) 346 return S_OK; 347} 348 349 350HRESULT DataObject_GetData_HDROP_or_IDLIST_Names(IDataObject *dataObject, UStringVector &paths) 351{ 352 ODS("-- DataObject_GetData_HDROP_or_IDLIST_Names START") 353 HRESULT hres = NShell::DataObject_GetData_HDROP_Names(dataObject, paths); 354 // if (hres == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) 355 if (hres != S_OK) 356 { 357 ODS("-- DataObject_GetData_IDLIST START") 358 // for (int y = 0; y < 10000; y++) // for debug 359 hres = NShell::DataObject_GetData_IDLIST(dataObject, paths); 360 } 361 ODS("-- DataObject_GetData_HDROP_or_IDLIST_Names END") 362 return hres; 363} 364 365 366 367// #if (NTDDI_VERSION >= NTDDI_VISTA) 368typedef struct 369{ 370 UINT cItems; // number of items in rgdwFileAttributes array 371 DWORD dwSumFileAttributes; // all of the attributes ORed together 372 DWORD dwProductFileAttributes; // all of the attributes ANDed together 373 DWORD rgdwFileAttributes[1]; // array 374} MYWIN_FILE_ATTRIBUTES_ARRAY; 375 376#define MYWIN_CFSTR_FILE_ATTRIBUTES_ARRAY TEXT("File Attributes Array") 377 378HRESULT DataObject_GetData_FILE_ATTRS(IDataObject *dataObject, CFileAttribs &attribs) 379{ 380 attribs.Clear(); 381 NCOM::CStgMedium medium; 382 RINOK(DataObject_GetData_HGLOBAL(dataObject, (CLIPFORMAT) 383 RegisterClipboardFormat(MYWIN_CFSTR_FILE_ATTRIBUTES_ARRAY), medium)) 384 const size_t blockSize = GlobalSize(medium.hGlobal); 385 if (blockSize < sizeof(MYWIN_FILE_ATTRIBUTES_ARRAY)) 386 return E_INVALIDARG; 387 NMemory::CGlobalLock dropLock(medium.hGlobal); 388 const MYWIN_FILE_ATTRIBUTES_ARRAY *faa = (const MYWIN_FILE_ATTRIBUTES_ARRAY *)dropLock.GetPointer(); 389 if (!faa) 390 return E_INVALIDARG; 391 const unsigned numFiles = faa->cItems; 392 if (numFiles == 0) 393 { 394 // is it posssible to have empty array here? 395 return E_INVALIDARG; 396 } 397 if ((blockSize - (sizeof(MYWIN_FILE_ATTRIBUTES_ARRAY) - sizeof(DWORD))) 398 / sizeof(DWORD) != numFiles) 399 return E_INVALIDARG; 400 // attribs.Sum = faa->dwSumFileAttributes; 401 // attribs.Product = faa->dwProductFileAttributes; 402 // attribs.Vals.SetFromArray(faa->rgdwFileAttributes, numFiles); 403 // attribs.IsDirVector.ClearAndSetSize(numFiles); 404 405 if ((faa->dwSumFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) 406 { 407 /* in win10: if selected items are volumes (c:\, d:\ ..) in My Compter, 408 all items have FILE_ATTRIBUTE_DIRECTORY attribute 409 ntfs volume also have FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM 410 udf volume: FILE_ATTRIBUTE_READONLY 411 dvd-rom device: (-1) : all bits are set 412 */ 413 const DWORD *attr = faa->rgdwFileAttributes; 414 // DWORD product = (UInt32)0 - 1, sum = 0; 415 for (unsigned i = 0; i < numFiles; i++) 416 { 417 if (attr[i] & FILE_ATTRIBUTE_DIRECTORY) 418 { 419 // attribs.ThereAreDirs = true; 420 attribs.FirstDirIndex = (int)i; 421 break; 422 } 423 // attribs.IsDirVector[i] = (attr[i] & FILE_ATTRIBUTE_DIRECTORY) != 0; 424 // product &= v; 425 // sum |= v; 426 } 427 // ODS_(Print_Number(product, "Product calc FILE_ATTRIBUTES_ARRAY ==== DataObject_GetData_HDROP_Names")) 428 // ODS_(Print_Number(sum, "Sum calc FILE_ATTRIBUTES_ARRAY ==== DataObject_GetData_HDROP_Names")) 429 } 430 // ODS_(Print_Number(attribs.Product, "Product FILE_ATTRIBUTES_ARRAY ==== DataObject_GetData_HDROP_Names")) 431 // ODS_(Print_Number(attribs.Sum, "Sum FILE_ATTRIBUTES_ARRAY ==== DataObject_GetData_HDROP_Names")) 432 ODS_(Print_Number(numFiles, "FILE_ATTRIBUTES_ARRAY ==== DataObject_GetData_HDROP_Names")) 433 return S_OK; 434} 435 436 437///////////////////////////// 438// CDrop 439 440/* 441 win10: 442 DragQueryFile() implementation code is not effective because 443 there is no pointer inside DROP internal file list, so 444 DragQueryFile(fileIndex) runs all names in range [0, fileIndex]. 445 DragQueryFile(,, buf, bufSize) 446 if (buf == NULL) by spec 447 { 448 returns value is the required size 449 in characters, of the buffer, not including the terminating null character 450 tests show that if (bufSize == 0), then it also returns required size. 451 } 452 if (bufSize != NULL) 453 { 454 returns: the count of the characters copied, not including null character. 455 win10: null character is also copied at position buf[ret_count]; 456 } 457*/ 458 459/* 460void CDrop::Attach(HDROP object) 461{ 462 Free(); 463 m_Object = object; 464 m_Assigned = true; 465} 466 467void CDrop::Free() 468{ 469 if (m_MustBeFinished && m_Assigned) 470 Finish(); 471 m_Assigned = false; 472} 473 474UINT CDrop::QueryCountOfFiles() 475{ 476 return QueryFile(0xFFFFFFFF, (LPTSTR)NULL, 0); 477} 478 479void CDrop::QueryFileName(UINT fileIndex, UString &fileName) 480{ 481 #ifndef _UNICODE 482 if (!g_IsNT) 483 { 484 AString fileNameA; 485 const UINT len = QueryFile(fileIndex, (LPTSTR)NULL, 0); 486 const UINT numCopied = QueryFile(fileIndex, fileNameA.GetBuf(len + 2), len + 2); 487 fileNameA.ReleaseBuf_CalcLen(len); 488 if (numCopied != len) 489 throw 20221223; 490 fileName = GetUnicodeString(fileNameA); 491 } 492 else 493 #endif 494 { 495 // kReserve must be >= 3 for additional buffer size 496 // safety and for optimal performance 497 const unsigned kReserve = 3; 498 { 499 unsigned len = 0; 500 wchar_t *buf = fileName.GetBuf_GetMaxAvail(len); 501 if (len >= kReserve) 502 { 503 const UINT numCopied = QueryFile(fileIndex, buf, len); 504 if (numCopied < len - 1) 505 { 506 // (numCopied < len - 1) case means that it have copied full string. 507 fileName.ReleaseBuf_CalcLen(numCopied); 508 return; 509 } 510 } 511 } 512 const UINT len = QueryFile(fileIndex, (LPWSTR)NULL, 0); 513 const UINT numCopied = QueryFile(fileIndex, 514 fileName.GetBuf(len + kReserve), len + kReserve); 515 fileName.ReleaseBuf_CalcLen(len); 516 if (numCopied != len) 517 throw 20221223; 518 } 519} 520 521 522void CDrop::QueryFileNames(UStringVector &fileNames) 523{ 524 UINT numFiles = QueryCountOfFiles(); 525 526 Print_Number(numFiles, "\n====== CDrop::QueryFileNames START ===== \n"); 527 528 fileNames.ClearAndReserve(numFiles); 529 UString s; 530 for (UINT i = 0; i < numFiles; i++) 531 { 532 QueryFileName(i, s); 533 if (!s.IsEmpty()) 534 fileNames.AddInReserved(s); 535 } 536 Print_Number(numFiles, "\n====== CDrop::QueryFileNames END ===== \n"); 537} 538*/ 539 540 541// #if (NTDDI_VERSION >= NTDDI_VISTA) 542// SHGetPathFromIDListEx returns a win32 file system path for the item in the name space. 543typedef int Z7_WIN_GPFIDL_FLAGS; 544 545extern "C" { 546typedef BOOL (WINAPI * Func_SHGetPathFromIDListW)(LPCITEMIDLIST pidl, LPWSTR pszPath); 547typedef BOOL (WINAPI * Func_SHGetPathFromIDListEx)(LPCITEMIDLIST pidl, PWSTR pszPath, DWORD cchPath, Z7_WIN_GPFIDL_FLAGS uOpts); 548} 549 550#ifndef _UNICODE 551 552bool GetPathFromIDList(LPCITEMIDLIST itemIDList, AString &path) 553{ 554 path.Empty(); 555 const unsigned len = MAX_PATH + 16; 556 const bool result = BOOLToBool(::SHGetPathFromIDList(itemIDList, path.GetBuf(len))); 557 path.ReleaseBuf_CalcLen(len); 558 return result; 559} 560 561#endif 562 563bool GetPathFromIDList(LPCITEMIDLIST itemIDList, UString &path) 564{ 565 path.Empty(); 566 unsigned len = MAX_PATH + 16; 567 568#ifdef _UNICODE 569 bool result = BOOLToBool(::SHGetPathFromIDList(itemIDList, path.GetBuf(len))); 570#else 571 const 572 Func_SHGetPathFromIDListW 573 shGetPathFromIDListW = Z7_GET_PROC_ADDRESS( 574 Func_SHGetPathFromIDListW, ::GetModuleHandleW(L"shell32.dll"), 575 "SHGetPathFromIDListW"); 576 if (!shGetPathFromIDListW) 577 return false; 578 bool result = BOOLToBool(shGetPathFromIDListW(itemIDList, path.GetBuf(len))); 579#endif 580 581 if (!result) 582 { 583 ODS("==== GetPathFromIDList() SHGetPathFromIDList() returned false") 584 /* for long path we need SHGetPathFromIDListEx(). 585 win10: SHGetPathFromIDListEx() for long path returns path with 586 with super path prefix "\\\\?\\". */ 587 const 588 Func_SHGetPathFromIDListEx 589 func_SHGetPathFromIDListEx = Z7_GET_PROC_ADDRESS( 590 Func_SHGetPathFromIDListEx, ::GetModuleHandleW(L"shell32.dll"), 591 "SHGetPathFromIDListEx"); 592 if (func_SHGetPathFromIDListEx) 593 { 594 ODS("==== GetPathFromIDList() (SHGetPathFromIDListEx)") 595 do 596 { 597 len *= 4; 598 result = BOOLToBool(func_SHGetPathFromIDListEx(itemIDList, path.GetBuf(len), len, 0)); 599 if (result) 600 break; 601 } 602 while (len <= (1 << 16)); 603 } 604 } 605 606 path.ReleaseBuf_CalcLen(len); 607 return result; 608} 609 610#endif 611 612#ifdef UNDER_CE 613 614bool BrowseForFolder(LPBROWSEINFO, CSysString) 615{ 616 return false; 617} 618 619bool BrowseForFolder(HWND, LPCTSTR, UINT, LPCTSTR, CSysString &) 620{ 621 return false; 622} 623 624bool BrowseForFolder(HWND /* owner */, LPCTSTR /* title */, 625 LPCTSTR /* initialFolder */, CSysString & /* resultPath */) 626{ 627 /* 628 // SHBrowseForFolder doesn't work before CE 6.0 ? 629 if (GetProcAddress(LoadLibrary(L"ceshell.dll", L"SHBrowseForFolder") == 0) 630 MessageBoxW(0, L"no", L"", 0); 631 else 632 MessageBoxW(0, L"yes", L"", 0); 633 */ 634 /* 635 UString s = "all files"; 636 s += " (*.*)"; 637 return MyGetOpenFileName(owner, title, initialFolder, s, resultPath, true); 638 */ 639 return false; 640} 641 642#else 643 644/* win10: SHBrowseForFolder() doesn't support long paths, 645 even if long path suppport is enabled in registry and in manifest. 646 and SHBrowseForFolder() doesn't support super path prefix "\\\\?\\". */ 647 648bool BrowseForFolder(LPBROWSEINFO browseInfo, CSysString &resultPath) 649{ 650 resultPath.Empty(); 651 NWindows::NCOM::CComInitializer comInitializer; 652 LPITEMIDLIST itemIDList = ::SHBrowseForFolder(browseInfo); 653 if (!itemIDList) 654 return false; 655 CItemIDList itemIDListHolder; 656 itemIDListHolder.Attach(itemIDList); 657 return GetPathFromIDList(itemIDList, resultPath); 658} 659 660 661static int CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM /* lp */, LPARAM data) 662{ 663 #ifndef UNDER_CE 664 switch (uMsg) 665 { 666 case BFFM_INITIALIZED: 667 { 668 SendMessage(hwnd, BFFM_SETSELECTION, TRUE, data); 669 break; 670 } 671 /* 672 case BFFM_SELCHANGED: 673 { 674 TCHAR dir[MAX_PATH]; 675 if (::SHGetPathFromIDList((LPITEMIDLIST) lp , dir)) 676 SendMessage(hwnd, BFFM_SETSTATUSTEXT, 0, (LPARAM)dir); 677 else 678 SendMessage(hwnd, BFFM_SETSTATUSTEXT, 0, (LPARAM)TEXT("")); 679 break; 680 } 681 */ 682 default: 683 break; 684 } 685 #endif 686 return 0; 687} 688 689 690static bool BrowseForFolder(HWND owner, LPCTSTR title, UINT ulFlags, 691 LPCTSTR initialFolder, CSysString &resultPath) 692{ 693 CSysString displayName; 694 BROWSEINFO browseInfo; 695 browseInfo.hwndOwner = owner; 696 browseInfo.pidlRoot = NULL; 697 698 // there are Unicode/Astring problems in some WinCE SDK ? 699 /* 700 #ifdef UNDER_CE 701 browseInfo.pszDisplayName = (LPSTR)displayName.GetBuf(MAX_PATH); 702 browseInfo.lpszTitle = (LPCSTR)title; 703 #else 704 */ 705 browseInfo.pszDisplayName = displayName.GetBuf(MAX_PATH); 706 browseInfo.lpszTitle = title; 707 // #endif 708 browseInfo.ulFlags = ulFlags; 709 browseInfo.lpfn = initialFolder ? BrowseCallbackProc : NULL; 710 browseInfo.lParam = (LPARAM)initialFolder; 711 return BrowseForFolder(&browseInfo, resultPath); 712} 713 714#ifdef Z7_OLD_WIN_SDK 715// ShlObj.h: 716#ifndef BIF_NEWDIALOGSTYLE 717#define BIF_NEWDIALOGSTYLE 0x0040 718#endif 719#endif 720 721bool BrowseForFolder(HWND owner, LPCTSTR title, 722 LPCTSTR initialFolder, CSysString &resultPath) 723{ 724 return BrowseForFolder(owner, title, 725 #ifndef UNDER_CE 726 BIF_NEWDIALOGSTYLE | 727 #endif 728 BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT, initialFolder, resultPath); 729 // BIF_STATUSTEXT; BIF_USENEWUI (Version 5.0) 730} 731 732#ifndef _UNICODE 733 734extern "C" { 735typedef LPITEMIDLIST (WINAPI * Func_SHBrowseForFolderW)(LPBROWSEINFOW lpbi); 736} 737 738static bool BrowseForFolder(LPBROWSEINFOW browseInfo, UString &resultPath) 739{ 740 NWindows::NCOM::CComInitializer comInitializer; 741 const 742 Func_SHBrowseForFolderW 743 f_SHBrowseForFolderW = Z7_GET_PROC_ADDRESS( 744 Func_SHBrowseForFolderW, ::GetModuleHandleW(L"shell32.dll"), 745 "SHBrowseForFolderW"); 746 if (!f_SHBrowseForFolderW) 747 return false; 748 LPITEMIDLIST itemIDList = f_SHBrowseForFolderW(browseInfo); 749 if (!itemIDList) 750 return false; 751 CItemIDList itemIDListHolder; 752 itemIDListHolder.Attach(itemIDList); 753 return GetPathFromIDList(itemIDList, resultPath); 754} 755 756static 757int CALLBACK BrowseCallbackProc2(HWND hwnd, UINT uMsg, LPARAM /* lp */, LPARAM data) 758{ 759 switch (uMsg) 760 { 761 case BFFM_INITIALIZED: 762 { 763 SendMessageW(hwnd, BFFM_SETSELECTIONW, TRUE, data); 764 break; 765 } 766 /* 767 case BFFM_SELCHANGED: 768 { 769 wchar_t dir[MAX_PATH * 2]; 770 771 if (shGetPathFromIDListW((LPITEMIDLIST)lp , dir)) 772 SendMessageW(hwnd, BFFM_SETSTATUSTEXTW, 0, (LPARAM)dir); 773 else 774 SendMessageW(hwnd, BFFM_SETSTATUSTEXTW, 0, (LPARAM)L""); 775 break; 776 } 777 */ 778 default: 779 break; 780 } 781 return 0; 782} 783 784 785static bool BrowseForFolder(HWND owner, LPCWSTR title, UINT ulFlags, 786 LPCWSTR initialFolder, UString &resultPath) 787{ 788 UString displayName; 789 BROWSEINFOW browseInfo; 790 browseInfo.hwndOwner = owner; 791 browseInfo.pidlRoot = NULL; 792 browseInfo.pszDisplayName = displayName.GetBuf(MAX_PATH); 793 browseInfo.lpszTitle = title; 794 browseInfo.ulFlags = ulFlags; 795 browseInfo.lpfn = initialFolder ? BrowseCallbackProc2 : NULL; 796 browseInfo.lParam = (LPARAM)initialFolder; 797 return BrowseForFolder(&browseInfo, resultPath); 798} 799 800bool BrowseForFolder(HWND owner, LPCWSTR title, LPCWSTR initialFolder, UString &resultPath) 801{ 802 if (g_IsNT) 803 return BrowseForFolder(owner, title, 804 BIF_NEWDIALOGSTYLE | BIF_RETURNONLYFSDIRS 805 // | BIF_STATUSTEXT // This flag is not supported when BIF_NEWDIALOGSTYLE is specified. 806 , initialFolder, resultPath); 807 // BIF_STATUSTEXT; BIF_USENEWUI (Version 5.0) 808 CSysString s; 809 bool res = BrowseForFolder(owner, GetSystemString(title), 810 BIF_NEWDIALOGSTYLE | BIF_RETURNONLYFSDIRS 811 // | BIF_STATUSTEXT // This flag is not supported when BIF_NEWDIALOGSTYLE is specified. 812 , GetSystemString(initialFolder), s); 813 resultPath = GetUnicodeString(s); 814 return res; 815} 816 817#endif 818 819#endif 820 821}} 822