aboutsummaryrefslogtreecommitdiff
path: root/CPP/Windows/Shell.cpp
diff options
context:
space:
mode:
authorIgor Pavlov <87184205+ip7z@users.noreply.github.com>2023-06-21 00:00:00 +0000
committerIgor Pavlov <87184205+ip7z@users.noreply.github.com>2023-12-17 14:59:19 +0500
commit5b39dc76f1bc82f941d5c800ab9f34407a06b53a (patch)
treefe5e17420300b715021a76328444088d32047963 /CPP/Windows/Shell.cpp
parent93be7d4abfd4233228f58ee1fbbcd76d91be66a4 (diff)
download7zip-5b39dc76f1bc82f941d5c800ab9f34407a06b53a.tar.gz
7zip-5b39dc76f1bc82f941d5c800ab9f34407a06b53a.tar.bz2
7zip-5b39dc76f1bc82f941d5c800ab9f34407a06b53a.zip
23.0123.01
Diffstat (limited to '')
-rw-r--r--CPP/Windows/Shell.cpp574
1 files changed, 515 insertions, 59 deletions
diff --git a/CPP/Windows/Shell.cpp b/CPP/Windows/Shell.cpp
index 071833c..b2a3489 100644
--- a/CPP/Windows/Shell.cpp
+++ b/CPP/Windows/Shell.cpp
@@ -2,23 +2,50 @@
2 2
3#include "StdAfx.h" 3#include "StdAfx.h"
4 4
5/*
6#include <stdio.h>
7#include <string.h>
8*/
9
10#include "../Common/MyCom.h" 5#include "../Common/MyCom.h"
11#ifndef _UNICODE
12#include "../Common/StringConvert.h" 6#include "../Common/StringConvert.h"
13#endif
14 7
15#include "COM.h" 8#include "COM.h"
9#include "FileName.h"
10#include "MemoryGlobal.h"
16#include "Shell.h" 11#include "Shell.h"
17 12
18#ifndef _UNICODE 13#ifndef _UNICODE
19extern bool g_IsNT; 14extern bool g_IsNT;
20#endif 15#endif
21 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
22namespace NWindows { 49namespace NWindows {
23namespace NShell { 50namespace NShell {
24 51
@@ -28,12 +55,24 @@ namespace NShell {
28 55
29void CItemIDList::Free() 56void CItemIDList::Free()
30{ 57{
31 if (m_Object == NULL) 58 if (!m_Object)
32 return; 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 /*
33 CMyComPtr<IMalloc> shellMalloc; 70 CMyComPtr<IMalloc> shellMalloc;
34 if (::SHGetMalloc(&shellMalloc) != NOERROR) 71 if (::SHGetMalloc(&shellMalloc) != NOERROR)
35 throw 41099; 72 throw 41099;
36 shellMalloc->Free(m_Object); 73 shellMalloc->Free(m_Object);
74 */
75 CoTaskMemFree(m_Object);
37 m_Object = NULL; 76 m_Object = NULL;
38} 77}
39 78
@@ -70,9 +109,354 @@ CItemIDList& CItemIDList::operator=(const CItemIDList &object)
70} 109}
71*/ 110*/
72 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
73///////////////////////////// 437/////////////////////////////
74// CDrop 438// CDrop
75 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/*
76void CDrop::Attach(HDROP object) 460void CDrop::Attach(HDROP object)
77{ 461{
78 Free(); 462 Free();
@@ -92,56 +476,133 @@ UINT CDrop::QueryCountOfFiles()
92 return QueryFile(0xFFFFFFFF, (LPTSTR)NULL, 0); 476 return QueryFile(0xFFFFFFFF, (LPTSTR)NULL, 0);
93} 477}
94 478
95UString CDrop::QueryFileName(UINT fileIndex) 479void CDrop::QueryFileName(UINT fileIndex, UString &fileName)
96{ 480{
97 UString fileName;
98 #ifndef _UNICODE 481 #ifndef _UNICODE
99 if (!g_IsNT) 482 if (!g_IsNT)
100 { 483 {
101 AString fileNameA; 484 AString fileNameA;
102 UINT bufferSize = QueryFile(fileIndex, (LPTSTR)NULL, 0); 485 const UINT len = QueryFile(fileIndex, (LPTSTR)NULL, 0);
103 const unsigned len = bufferSize + 2; 486 const UINT numCopied = QueryFile(fileIndex, fileNameA.GetBuf(len + 2), len + 2);
104 QueryFile(fileIndex, fileNameA.GetBuf(len), bufferSize + 1);
105 fileNameA.ReleaseBuf_CalcLen(len); 487 fileNameA.ReleaseBuf_CalcLen(len);
488 if (numCopied != len)
489 throw 20221223;
106 fileName = GetUnicodeString(fileNameA); 490 fileName = GetUnicodeString(fileNameA);
107 } 491 }
108 else 492 else
109 #endif 493 #endif
110 { 494 {
111 UINT bufferSize = QueryFile(fileIndex, (LPWSTR)NULL, 0); 495 // kReserve must be >= 3 for additional buffer size
112 const unsigned len = bufferSize + 2; 496 // safety and for optimal performance
113 QueryFile(fileIndex, fileName.GetBuf(len), bufferSize + 1); 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);
114 fileName.ReleaseBuf_CalcLen(len); 515 fileName.ReleaseBuf_CalcLen(len);
516 if (numCopied != len)
517 throw 20221223;
115 } 518 }
116 return fileName;
117} 519}
118 520
521
119void CDrop::QueryFileNames(UStringVector &fileNames) 522void CDrop::QueryFileNames(UStringVector &fileNames)
120{ 523{
121 UINT numFiles = QueryCountOfFiles(); 524 UINT numFiles = QueryCountOfFiles();
122 /* 525
123 char s[100]; 526 Print_Number(numFiles, "\n====== CDrop::QueryFileNames START ===== \n");
124 sprintf(s, "QueryFileNames: %d files", numFiles); 527
125 OutputDebugStringA(s);
126 */
127 fileNames.ClearAndReserve(numFiles); 528 fileNames.ClearAndReserve(numFiles);
529 UString s;
128 for (UINT i = 0; i < numFiles; i++) 530 for (UINT i = 0; i < numFiles; i++)
129 { 531 {
130 const UString s2 = QueryFileName(i); 532 QueryFileName(i, s);
131 if (!s2.IsEmpty()) 533 if (!s.IsEmpty())
132 fileNames.AddInReserved(s2); 534 fileNames.AddInReserved(s);
133 /*
134 OutputDebugStringW(L"file ---");
135 OutputDebugStringW(s2);
136 */
137 } 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);
138} 548}
139 549
550#ifndef _UNICODE
140 551
141bool GetPathFromIDList(LPCITEMIDLIST itemIDList, CSysString &path) 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)
142{ 564{
143 const unsigned len = MAX_PATH * 2; 565 path.Empty();
566 unsigned len = MAX_PATH + 16;
567
568#ifdef _UNICODE
144 bool result = BOOLToBool(::SHGetPathFromIDList(itemIDList, path.GetBuf(len))); 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
145 path.ReleaseBuf_CalcLen(len); 606 path.ReleaseBuf_CalcLen(len);
146 return result; 607 return result;
147} 608}
@@ -180,11 +641,16 @@ bool BrowseForFolder(HWND /* owner */, LPCTSTR /* title */,
180 641
181#else 642#else
182 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
183bool BrowseForFolder(LPBROWSEINFO browseInfo, CSysString &resultPath) 648bool BrowseForFolder(LPBROWSEINFO browseInfo, CSysString &resultPath)
184{ 649{
650 resultPath.Empty();
185 NWindows::NCOM::CComInitializer comInitializer; 651 NWindows::NCOM::CComInitializer comInitializer;
186 LPITEMIDLIST itemIDList = ::SHBrowseForFolder(browseInfo); 652 LPITEMIDLIST itemIDList = ::SHBrowseForFolder(browseInfo);
187 if (itemIDList == NULL) 653 if (!itemIDList)
188 return false; 654 return false;
189 CItemIDList itemIDListHolder; 655 CItemIDList itemIDListHolder;
190 itemIDListHolder.Attach(itemIDList); 656 itemIDListHolder.Attach(itemIDList);
@@ -240,11 +706,18 @@ static bool BrowseForFolder(HWND owner, LPCTSTR title, UINT ulFlags,
240 browseInfo.lpszTitle = title; 706 browseInfo.lpszTitle = title;
241 // #endif 707 // #endif
242 browseInfo.ulFlags = ulFlags; 708 browseInfo.ulFlags = ulFlags;
243 browseInfo.lpfn = (initialFolder != NULL) ? BrowseCallbackProc : NULL; 709 browseInfo.lpfn = initialFolder ? BrowseCallbackProc : NULL;
244 browseInfo.lParam = (LPARAM)initialFolder; 710 browseInfo.lParam = (LPARAM)initialFolder;
245 return BrowseForFolder(&browseInfo, resultPath); 711 return BrowseForFolder(&browseInfo, resultPath);
246} 712}
247 713
714#ifdef Z7_OLD_WIN_SDK
715// ShlObj.h:
716#ifndef BIF_NEWDIALOGSTYLE
717#define BIF_NEWDIALOGSTYLE 0x0040
718#endif
719#endif
720
248bool BrowseForFolder(HWND owner, LPCTSTR title, 721bool BrowseForFolder(HWND owner, LPCTSTR title,
249 LPCTSTR initialFolder, CSysString &resultPath) 722 LPCTSTR initialFolder, CSysString &resultPath)
250{ 723{
@@ -259,38 +732,21 @@ bool BrowseForFolder(HWND owner, LPCTSTR title,
259#ifndef _UNICODE 732#ifndef _UNICODE
260 733
261extern "C" { 734extern "C" {
262typedef BOOL (WINAPI * Func_SHGetPathFromIDListW)(LPCITEMIDLIST pidl, LPWSTR pszPath);
263typedef LPITEMIDLIST (WINAPI * Func_SHBrowseForFolderW)(LPBROWSEINFOW lpbi); 735typedef LPITEMIDLIST (WINAPI * Func_SHBrowseForFolderW)(LPBROWSEINFOW lpbi);
264} 736}
265 737
266#define MY_CAST_FUNC (void(*)())
267// #define MY_CAST_FUNC
268
269bool GetPathFromIDList(LPCITEMIDLIST itemIDList, UString &path)
270{
271 path.Empty();
272 Func_SHGetPathFromIDListW shGetPathFromIDListW = (Func_SHGetPathFromIDListW)
273 MY_CAST_FUNC
274 ::GetProcAddress(::GetModuleHandleW(L"shell32.dll"), "SHGetPathFromIDListW");
275 if (!shGetPathFromIDListW)
276 return false;
277 const unsigned len = MAX_PATH * 2;
278 bool result = BOOLToBool(shGetPathFromIDListW(itemIDList, path.GetBuf(len)));
279 path.ReleaseBuf_CalcLen(len);
280 return result;
281}
282
283
284static bool BrowseForFolder(LPBROWSEINFOW browseInfo, UString &resultPath) 738static bool BrowseForFolder(LPBROWSEINFOW browseInfo, UString &resultPath)
285{ 739{
286 NWindows::NCOM::CComInitializer comInitializer; 740 NWindows::NCOM::CComInitializer comInitializer;
287 Func_SHBrowseForFolderW shBrowseForFolderW = (Func_SHBrowseForFolderW) 741 const
288 MY_CAST_FUNC 742 Func_SHBrowseForFolderW
289 ::GetProcAddress(::GetModuleHandleW(L"shell32.dll"), "SHBrowseForFolderW"); 743 f_SHBrowseForFolderW = Z7_GET_PROC_ADDRESS(
290 if (!shBrowseForFolderW) 744 Func_SHBrowseForFolderW, ::GetModuleHandleW(L"shell32.dll"),
745 "SHBrowseForFolderW");
746 if (!f_SHBrowseForFolderW)
291 return false; 747 return false;
292 LPITEMIDLIST itemIDList = shBrowseForFolderW(browseInfo); 748 LPITEMIDLIST itemIDList = f_SHBrowseForFolderW(browseInfo);
293 if (itemIDList == NULL) 749 if (!itemIDList)
294 return false; 750 return false;
295 CItemIDList itemIDListHolder; 751 CItemIDList itemIDListHolder;
296 itemIDListHolder.Attach(itemIDList); 752 itemIDListHolder.Attach(itemIDList);
@@ -336,7 +792,7 @@ static bool BrowseForFolder(HWND owner, LPCWSTR title, UINT ulFlags,
336 browseInfo.pszDisplayName = displayName.GetBuf(MAX_PATH); 792 browseInfo.pszDisplayName = displayName.GetBuf(MAX_PATH);
337 browseInfo.lpszTitle = title; 793 browseInfo.lpszTitle = title;
338 browseInfo.ulFlags = ulFlags; 794 browseInfo.ulFlags = ulFlags;
339 browseInfo.lpfn = (initialFolder != NULL) ? BrowseCallbackProc2 : NULL; 795 browseInfo.lpfn = initialFolder ? BrowseCallbackProc2 : NULL;
340 browseInfo.lParam = (LPARAM)initialFolder; 796 browseInfo.lParam = (LPARAM)initialFolder;
341 return BrowseForFolder(&browseInfo, resultPath); 797 return BrowseForFolder(&browseInfo, resultPath);
342} 798}