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 'CPP/Windows/Shell.cpp')
| -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 | } |
