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