diff --git a/libs/clipboard/src/windows/wf_cliprdr.c b/libs/clipboard/src/windows/wf_cliprdr.c index c32a20259..3baf83a1d 100644 --- a/libs/clipboard/src/windows/wf_cliprdr.c +++ b/libs/clipboard/src/windows/wf_cliprdr.c @@ -2443,9 +2443,6 @@ static FILEDESCRIPTORW *wf_cliprdr_get_file_descriptor(WCHAR *file_name, size_t return NULL; } - // to-do: use `fd->dwFlags = FD_ATTRIBUTES | FD_FILESIZE | FD_WRITESTIME | FD_PROGRESSUI`. - // We keep `fd->dwFlags = FD_ATTRIBUTES | FD_WRITESTIME | FD_PROGRESSUI` for compatibility. - // fd->dwFlags = FD_ATTRIBUTES | FD_FILESIZE | FD_WRITESTIME | FD_PROGRESSUI; fd->dwFlags = FD_ATTRIBUTES | FD_WRITESTIME | FD_PROGRESSUI; fd->dwFileAttributes = GetFileAttributesW(file_name); if (fd->dwFileAttributes == INVALID_FILE_ATTRIBUTES) @@ -2458,7 +2455,34 @@ static FILEDESCRIPTORW *wf_cliprdr_get_file_descriptor(WCHAR *file_name, size_t fd->dwFlags &= ~FD_WRITESTIME; } - fd->nFileSizeLow = GetFileSize(hFile, &fd->nFileSizeHigh); + // Announce the size in the file list. Without FD_FILESIZE the receiving side cannot + // trust the size fields, so CliprdrStream_New() asks for each file's size with its own + // FILECONTENTS_SIZE request and blocks on the reply. Those requests are made for every + // entry up front, while the shell is inside IDataObject::GetData(), so the cost grows + // with the number of files and not with their size. + // + // GetFileSize() reports failure as INVALID_FILE_SIZE, which cannot be told apart from a + // genuine 4GB-1 file without GetLastError(), and it fails outright on the directory + // handles FILE_FLAG_BACKUP_SEMANTICS lets us open above. A directory gets no size. A + // file whose size cannot be read is rejected rather than sent with the flag off: the + // Unix receiver reads the size fields whether or not the flag is set, for compatibility + // with older Windows senders, and would take the zero for an empty file. + if ((fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) + { + LARGE_INTEGER file_size = {0}; + + if (!GetFileSizeEx(hFile, &file_size) || file_size.QuadPart < 0) + { + CloseHandle(hFile); + free(fd); + return NULL; + } + + fd->nFileSizeLow = file_size.LowPart; + fd->nFileSizeHigh = (DWORD)file_size.HighPart; + fd->dwFlags |= FD_FILESIZE; + } + if ((wcslen(file_name + pathLen) + 1) > sizeof(fd->cFileName) / sizeof(fd->cFileName[0])) { // The file name is too long, which is not a normal case.