xref: /aosp_15_r20/external/lzma/CPP/Windows/Shell.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
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
14 extern 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 
Print_Number(UInt32 number,const char * s)27 static 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 
49 namespace NWindows {
50 namespace NShell {
51 
52 #ifndef UNDER_CE
53 
54 // SHGetMalloc is unsupported in Windows Mobile?
55 
Free()56 void 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 /*
80 CItemIDList::(LPCITEMIDLIST itemIDList): m_Object(NULL)
81   {  *this = itemIDList; }
82 CItemIDList::(const CItemIDList& itemIDList): m_Object(NULL)
83   {  *this = itemIDList; }
84 
85 CItemIDList& 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 
98 CItemIDList& 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 
ReadUnicodeStrings(const wchar_t * p,size_t size,UStringVector & names)113 static 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 
ReadAnsiStrings(const char * p,size_t size,UStringVector & names)150 static 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.Add_Char(c);
166   }
167   return E_INVALIDARG;
168 }
169 
170 
171 #define INIT_FORMATETC_HGLOBAL(type) { (type), NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }
172 
DataObject_GetData_HGLOBAL(IDataObject * dataObject,CLIPFORMAT cf,NCOM::CStgMedium & medium)173 static 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 
DataObject_GetData_HDROP_Names(IDataObject * dataObject,UStringVector & names)182 static 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 
229 typedef 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 
DataObject_GetData_IDLIST(IDataObject * dataObject,UStringVector & names)243 static 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 
DataObject_GetData_HDROP_or_IDLIST_Names(IDataObject * dataObject,UStringVector & paths)350 HRESULT 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)
368 typedef 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 
DataObject_GetData_FILE_ATTRS(IDataObject * dataObject,CFileAttribs & attribs)378 HRESULT 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 /*
460 void CDrop::Attach(HDROP object)
461 {
462   Free();
463   m_Object = object;
464   m_Assigned = true;
465 }
466 
467 void CDrop::Free()
468 {
469   if (m_MustBeFinished && m_Assigned)
470     Finish();
471   m_Assigned = false;
472 }
473 
474 UINT CDrop::QueryCountOfFiles()
475 {
476   return QueryFile(0xFFFFFFFF, (LPTSTR)NULL, 0);
477 }
478 
479 void 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 
522 void 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.
543 typedef int Z7_WIN_GPFIDL_FLAGS;
544 
545 extern "C" {
546 #ifndef _UNICODE
547 typedef BOOL (WINAPI * Func_SHGetPathFromIDListW)(LPCITEMIDLIST pidl, LPWSTR pszPath); // nt4
548 #endif
549 
550 #if !defined(Z7_WIN32_WINNT_MIN) || Z7_WIN32_WINNT_MIN < 0x0600  // Vista
551 #define Z7_USE_DYN_SHGetPathFromIDListEx
552 #endif
553 
554 #ifdef Z7_USE_DYN_SHGetPathFromIDListEx
555 Z7_DIAGNOSTIC_IGNORE_CAST_FUNCTION
556 typedef BOOL (WINAPI * Func_SHGetPathFromIDListEx)(LPCITEMIDLIST pidl, PWSTR pszPath, DWORD cchPath, Z7_WIN_GPFIDL_FLAGS uOpts); // vista
557 #endif
558 }
559 
560 #ifndef _UNICODE
561 
GetPathFromIDList(LPCITEMIDLIST itemIDList,AString & path)562 bool GetPathFromIDList(LPCITEMIDLIST itemIDList, AString &path)
563 {
564   path.Empty();
565   const unsigned len = MAX_PATH + 16;
566   const bool result = BOOLToBool(::SHGetPathFromIDList(itemIDList, path.GetBuf(len)));
567   path.ReleaseBuf_CalcLen(len);
568   return result;
569 }
570 
571 #endif
572 
GetPathFromIDList(LPCITEMIDLIST itemIDList,UString & path)573 bool GetPathFromIDList(LPCITEMIDLIST itemIDList, UString &path)
574 {
575   path.Empty();
576   unsigned len = MAX_PATH + 16;
577 
578 #ifdef _UNICODE
579   bool result = BOOLToBool(::SHGetPathFromIDList(itemIDList, path.GetBuf(len)));
580 #else
581   const
582   Func_SHGetPathFromIDListW
583        shGetPathFromIDListW = Z7_GET_PROC_ADDRESS(
584   Func_SHGetPathFromIDListW, ::GetModuleHandleW(L"shell32.dll"),
585       "SHGetPathFromIDListW");
586   if (!shGetPathFromIDListW)
587     return false;
588   bool result = BOOLToBool(shGetPathFromIDListW(itemIDList, path.GetBuf(len)));
589 #endif
590 
591   if (!result)
592   {
593     ODS("==== GetPathFromIDList() SHGetPathFromIDList() returned false")
594     /* for long path we need SHGetPathFromIDListEx().
595       win10: SHGetPathFromIDListEx() for long path returns path with
596              with super path prefix "\\\\?\\". */
597 #ifdef Z7_USE_DYN_SHGetPathFromIDListEx
598     const
599     Func_SHGetPathFromIDListEx
600     func_SHGetPathFromIDListEx = Z7_GET_PROC_ADDRESS(
601     Func_SHGetPathFromIDListEx, ::GetModuleHandleW(L"shell32.dll"),
602         "SHGetPathFromIDListEx");
603     if (func_SHGetPathFromIDListEx)
604 #endif
605     {
606       ODS("==== GetPathFromIDList() (SHGetPathFromIDListEx)")
607       do
608       {
609         len *= 4;
610         result = BOOLToBool(
611 #ifdef Z7_USE_DYN_SHGetPathFromIDListEx
612           func_SHGetPathFromIDListEx
613 #else
614           SHGetPathFromIDListEx
615 #endif
616           (itemIDList, path.GetBuf(len), len, 0));
617         if (result)
618           break;
619       }
620       while (len <= (1 << 16));
621     }
622   }
623 
624   path.ReleaseBuf_CalcLen(len);
625   return result;
626 }
627 
628 #endif
629 
630 #ifdef UNDER_CE
631 
BrowseForFolder(LPBROWSEINFO,CSysString)632 bool BrowseForFolder(LPBROWSEINFO, CSysString)
633 {
634   return false;
635 }
636 
BrowseForFolder(HWND,LPCTSTR,UINT,LPCTSTR,CSysString &)637 bool BrowseForFolder(HWND, LPCTSTR, UINT, LPCTSTR, CSysString &)
638 {
639   return false;
640 }
641 
BrowseForFolder(HWND,LPCTSTR,LPCTSTR,CSysString &)642 bool BrowseForFolder(HWND /* owner */, LPCTSTR /* title */,
643     LPCTSTR /* initialFolder */, CSysString & /* resultPath */)
644 {
645   /*
646   // SHBrowseForFolder doesn't work before CE 6.0 ?
647   if (GetProcAddress(LoadLibrary(L"ceshell.dll", L"SHBrowseForFolder") == 0)
648     MessageBoxW(0, L"no", L"", 0);
649   else
650     MessageBoxW(0, L"yes", L"", 0);
651   */
652   /*
653   UString s = "all files";
654   s += " (*.*)";
655   return MyGetOpenFileName(owner, title, initialFolder, s, resultPath, true);
656   */
657   return false;
658 }
659 
660 #else
661 
662 /* win10: SHBrowseForFolder() doesn't support long paths,
663    even if long path suppport is enabled in registry and in manifest.
664    and SHBrowseForFolder() doesn't support super path prefix "\\\\?\\". */
665 
BrowseForFolder(LPBROWSEINFO browseInfo,CSysString & resultPath)666 bool BrowseForFolder(LPBROWSEINFO browseInfo, CSysString &resultPath)
667 {
668   resultPath.Empty();
669   NWindows::NCOM::CComInitializer comInitializer;
670   LPITEMIDLIST itemIDList = ::SHBrowseForFolder(browseInfo);
671   if (!itemIDList)
672     return false;
673   CItemIDList itemIDListHolder;
674   itemIDListHolder.Attach(itemIDList);
675   return GetPathFromIDList(itemIDList, resultPath);
676 }
677 
678 
BrowseCallbackProc(HWND hwnd,UINT uMsg,LPARAM,LPARAM data)679 static int CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM /* lp */, LPARAM data)
680 {
681   #ifndef UNDER_CE
682   switch (uMsg)
683   {
684     case BFFM_INITIALIZED:
685     {
686       SendMessage(hwnd, BFFM_SETSELECTION, TRUE, data);
687       break;
688     }
689     /*
690     case BFFM_SELCHANGED:
691     {
692       TCHAR dir[MAX_PATH];
693       if (::SHGetPathFromIDList((LPITEMIDLIST) lp , dir))
694         SendMessage(hwnd, BFFM_SETSTATUSTEXT, 0, (LPARAM)dir);
695       else
696         SendMessage(hwnd, BFFM_SETSTATUSTEXT, 0, (LPARAM)TEXT(""));
697       break;
698     }
699     */
700     default:
701       break;
702   }
703   #endif
704   return 0;
705 }
706 
707 
BrowseForFolder(HWND owner,LPCTSTR title,UINT ulFlags,LPCTSTR initialFolder,CSysString & resultPath)708 static bool BrowseForFolder(HWND owner, LPCTSTR title, UINT ulFlags,
709     LPCTSTR initialFolder, CSysString &resultPath)
710 {
711   CSysString displayName;
712   BROWSEINFO browseInfo;
713   browseInfo.hwndOwner = owner;
714   browseInfo.pidlRoot = NULL;
715 
716   // there are Unicode/Astring problems in some WinCE SDK ?
717   /*
718   #ifdef UNDER_CE
719   browseInfo.pszDisplayName = (LPSTR)displayName.GetBuf(MAX_PATH);
720   browseInfo.lpszTitle = (LPCSTR)title;
721   #else
722   */
723   browseInfo.pszDisplayName = displayName.GetBuf(MAX_PATH);
724   browseInfo.lpszTitle = title;
725   // #endif
726   browseInfo.ulFlags = ulFlags;
727   browseInfo.lpfn = initialFolder ? BrowseCallbackProc : NULL;
728   browseInfo.lParam = (LPARAM)initialFolder;
729   return BrowseForFolder(&browseInfo, resultPath);
730 }
731 
732 #ifdef Z7_OLD_WIN_SDK
733 // ShlObj.h:
734 #ifndef BIF_NEWDIALOGSTYLE
735 #define BIF_NEWDIALOGSTYLE     0x0040
736 #endif
737 #endif
738 
BrowseForFolder(HWND owner,LPCTSTR title,LPCTSTR initialFolder,CSysString & resultPath)739 bool BrowseForFolder(HWND owner, LPCTSTR title,
740     LPCTSTR initialFolder, CSysString &resultPath)
741 {
742   return BrowseForFolder(owner, title,
743       #ifndef UNDER_CE
744       BIF_NEWDIALOGSTYLE |
745       #endif
746       BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT, initialFolder, resultPath);
747   // BIF_STATUSTEXT; BIF_USENEWUI   (Version 5.0)
748 }
749 
750 #ifndef _UNICODE
751 
752 extern "C" {
753 typedef LPITEMIDLIST (WINAPI * Func_SHBrowseForFolderW)(LPBROWSEINFOW lpbi);
754 }
755 
BrowseForFolder(LPBROWSEINFOW browseInfo,UString & resultPath)756 static bool BrowseForFolder(LPBROWSEINFOW browseInfo, UString &resultPath)
757 {
758   NWindows::NCOM::CComInitializer comInitializer;
759   const
760   Func_SHBrowseForFolderW
761      f_SHBrowseForFolderW = Z7_GET_PROC_ADDRESS(
762   Func_SHBrowseForFolderW, ::GetModuleHandleW(L"shell32.dll"),
763       "SHBrowseForFolderW");
764   if (!f_SHBrowseForFolderW)
765     return false;
766   LPITEMIDLIST itemIDList = f_SHBrowseForFolderW(browseInfo);
767   if (!itemIDList)
768     return false;
769   CItemIDList itemIDListHolder;
770   itemIDListHolder.Attach(itemIDList);
771   return GetPathFromIDList(itemIDList, resultPath);
772 }
773 
774 static
BrowseCallbackProc2(HWND hwnd,UINT uMsg,LPARAM,LPARAM data)775 int CALLBACK BrowseCallbackProc2(HWND hwnd, UINT uMsg, LPARAM /* lp */, LPARAM data)
776 {
777   switch (uMsg)
778   {
779     case BFFM_INITIALIZED:
780     {
781       SendMessageW(hwnd, BFFM_SETSELECTIONW, TRUE, data);
782       break;
783     }
784     /*
785     case BFFM_SELCHANGED:
786     {
787       wchar_t dir[MAX_PATH * 2];
788 
789       if (shGetPathFromIDListW((LPITEMIDLIST)lp , dir))
790         SendMessageW(hwnd, BFFM_SETSTATUSTEXTW, 0, (LPARAM)dir);
791       else
792         SendMessageW(hwnd, BFFM_SETSTATUSTEXTW, 0, (LPARAM)L"");
793       break;
794     }
795     */
796     default:
797       break;
798   }
799   return 0;
800 }
801 
802 
BrowseForFolder(HWND owner,LPCWSTR title,UINT ulFlags,LPCWSTR initialFolder,UString & resultPath)803 static bool BrowseForFolder(HWND owner, LPCWSTR title, UINT ulFlags,
804     LPCWSTR initialFolder, UString &resultPath)
805 {
806   UString displayName;
807   BROWSEINFOW browseInfo;
808   browseInfo.hwndOwner = owner;
809   browseInfo.pidlRoot = NULL;
810   browseInfo.pszDisplayName = displayName.GetBuf(MAX_PATH);
811   browseInfo.lpszTitle = title;
812   browseInfo.ulFlags = ulFlags;
813   browseInfo.lpfn = initialFolder ? BrowseCallbackProc2 : NULL;
814   browseInfo.lParam = (LPARAM)initialFolder;
815   return BrowseForFolder(&browseInfo, resultPath);
816 }
817 
BrowseForFolder(HWND owner,LPCWSTR title,LPCWSTR initialFolder,UString & resultPath)818 bool BrowseForFolder(HWND owner, LPCWSTR title, LPCWSTR initialFolder, UString &resultPath)
819 {
820   if (g_IsNT)
821     return BrowseForFolder(owner, title,
822       BIF_NEWDIALOGSTYLE | BIF_RETURNONLYFSDIRS
823       //  | BIF_STATUSTEXT // This flag is not supported when BIF_NEWDIALOGSTYLE is specified.
824       , initialFolder, resultPath);
825   // BIF_STATUSTEXT; BIF_USENEWUI   (Version 5.0)
826   CSysString s;
827   bool res = BrowseForFolder(owner, GetSystemString(title),
828       BIF_NEWDIALOGSTYLE | BIF_RETURNONLYFSDIRS
829       // | BIF_STATUSTEXT  // This flag is not supported when BIF_NEWDIALOGSTYLE is specified.
830       , GetSystemString(initialFolder), s);
831   resultPath = GetUnicodeString(s);
832   return res;
833 }
834 
835 #endif
836 
837 #endif
838 
839 }}
840