diff options
author | Igor Pavlov <87184205+ip7z@users.noreply.github.com> | 2023-06-21 00:00:00 +0000 |
---|---|---|
committer | Igor Pavlov <87184205+ip7z@users.noreply.github.com> | 2023-12-17 14:59:19 +0500 |
commit | 5b39dc76f1bc82f941d5c800ab9f34407a06b53a (patch) | |
tree | fe5e17420300b715021a76328444088d32047963 /CPP/Windows/Shell.cpp | |
parent | 93be7d4abfd4233228f58ee1fbbcd76d91be66a4 (diff) | |
download | 7zip-5b39dc76f1bc82f941d5c800ab9f34407a06b53a.tar.gz 7zip-5b39dc76f1bc82f941d5c800ab9f34407a06b53a.tar.bz2 7zip-5b39dc76f1bc82f941d5c800ab9f34407a06b53a.zip |
23.0123.01
Diffstat (limited to '')
-rw-r--r-- | CPP/Windows/Shell.cpp | 574 |
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 |
19 | extern bool g_IsNT; | 14 | extern 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 | |||
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 | |||
22 | namespace NWindows { | 49 | namespace NWindows { |
23 | namespace NShell { | 50 | namespace NShell { |
24 | 51 | ||
@@ -28,12 +55,24 @@ namespace NShell { | |||
28 | 55 | ||
29 | void CItemIDList::Free() | 56 | void 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 | |||
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 | |||
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 | |||
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 | |||
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 | |||
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 | |||
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 | |||
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 | |||
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 | /* | ||
76 | void CDrop::Attach(HDROP object) | 460 | void 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 | ||
95 | UString CDrop::QueryFileName(UINT fileIndex) | 479 | void 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 | |||
119 | void CDrop::QueryFileNames(UStringVector &fileNames) | 522 | void 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. | ||
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); | ||
138 | } | 548 | } |
139 | 549 | ||
550 | #ifndef _UNICODE | ||
140 | 551 | ||
141 | bool GetPathFromIDList(LPCITEMIDLIST itemIDList, CSysString &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 | |||
563 | bool 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 | |||
183 | bool BrowseForFolder(LPBROWSEINFO browseInfo, CSysString &resultPath) | 648 | bool 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 | |||
248 | bool BrowseForFolder(HWND owner, LPCTSTR title, | 721 | bool 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 | ||
261 | extern "C" { | 734 | extern "C" { |
262 | typedef BOOL (WINAPI * Func_SHGetPathFromIDListW)(LPCITEMIDLIST pidl, LPWSTR pszPath); | ||
263 | typedef LPITEMIDLIST (WINAPI * Func_SHBrowseForFolderW)(LPBROWSEINFOW lpbi); | 735 | typedef LPITEMIDLIST (WINAPI * Func_SHBrowseForFolderW)(LPBROWSEINFOW lpbi); |
264 | } | 736 | } |
265 | 737 | ||
266 | #define MY_CAST_FUNC (void(*)()) | ||
267 | // #define MY_CAST_FUNC | ||
268 | |||
269 | bool 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 | |||
284 | static bool BrowseForFolder(LPBROWSEINFOW browseInfo, UString &resultPath) | 738 | static 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 | } |