xref: /third_party/lzma/CPP/Windows/Shell.cpp (revision 370b324c)
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