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 += 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 typedef BOOL (WINAPI * Func_SHGetPathFromIDListW)(LPCITEMIDLIST pidl, LPWSTR pszPath);
547 typedef BOOL (WINAPI * Func_SHGetPathFromIDListEx)(LPCITEMIDLIST pidl, PWSTR pszPath, DWORD cchPath, Z7_WIN_GPFIDL_FLAGS uOpts);
548 }
549
550 #ifndef _UNICODE
551
GetPathFromIDList(LPCITEMIDLIST itemIDList, AString &path)552 bool 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
GetPathFromIDList(LPCITEMIDLIST itemIDList, UString &path)563 bool 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
BrowseForFolder(LPBROWSEINFO, CSysString)614 bool BrowseForFolder(LPBROWSEINFO, CSysString)
615 {
616 return false;
617 }
618
BrowseForFolder(HWND, LPCTSTR, UINT, LPCTSTR, CSysString &)619 bool BrowseForFolder(HWND, LPCTSTR, UINT, LPCTSTR, CSysString &)
620 {
621 return false;
622 }
623
BrowseForFolder(HWND , LPCTSTR , LPCTSTR , CSysString & )624 bool 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
BrowseForFolder(LPBROWSEINFO browseInfo, CSysString &resultPath)648 bool 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
BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM , LPARAM data)661 static 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
BrowseForFolder(HWND owner, LPCTSTR title, UINT ulFlags, LPCTSTR initialFolder, CSysString &resultPath)690 static 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
BrowseForFolder(HWND owner, LPCTSTR title, LPCTSTR initialFolder, CSysString &resultPath)721 bool 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
734 extern "C" {
735 typedef LPITEMIDLIST (WINAPI * Func_SHBrowseForFolderW)(LPBROWSEINFOW lpbi);
736 }
737
BrowseForFolder(LPBROWSEINFOW browseInfo, UString &resultPath)738 static 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
756 static
BrowseCallbackProc2(HWND hwnd, UINT uMsg, LPARAM , LPARAM data)757 int 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
BrowseForFolder(HWND owner, LPCWSTR title, UINT ulFlags, LPCWSTR initialFolder, UString &resultPath)785 static 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
BrowseForFolder(HWND owner, LPCWSTR title, LPCWSTR initialFolder, UString &resultPath)800 bool 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