diff --git a/PowerEditor/src/MISC/Common/Common.cpp b/PowerEditor/src/MISC/Common/Common.cpp index 40c90bce8..dfcc46d0f 100644 --- a/PowerEditor/src/MISC/Common/Common.cpp +++ b/PowerEditor/src/MISC/Common/Common.cpp @@ -1771,70 +1771,6 @@ bool Version::isCompatibleTo(const Version& from, const Version& to) const #define DEFAULT_MILLISEC 1000 -//---------------------------------------------------- -struct GetAttrParamResult { - wstring _filePath; - DWORD _fileAttr = INVALID_FILE_ATTRIBUTES; - bool _isNetworkFailure = true; -}; - -DWORD WINAPI getFileAttributesWorker(void* data) -{ - GetAttrParamResult* inAndOut = static_cast(data); - inAndOut->_fileAttr = ::GetFileAttributesW(inAndOut->_filePath.c_str()); - inAndOut->_isNetworkFailure = false; - return ERROR_SUCCESS; -}; - -DWORD getFileAttrWaitSec(const wchar_t* filePath, DWORD milliSec2wait, bool* isNetWorkProblem) -{ - GetAttrParamResult data(filePath); - - HANDLE hThread = ::CreateThread(NULL, 0, getFileAttributesWorker, &data, 0, NULL); - if (!hThread) - { - return INVALID_FILE_ATTRIBUTES; - } - - // wait for our worker thread to complete or terminate it when the required timeout has elapsed - DWORD dwWaitStatus = ::WaitForSingleObject(hThread, milliSec2wait == 0 ? DEFAULT_MILLISEC : milliSec2wait); - switch (dwWaitStatus) - { - case WAIT_OBJECT_0: // Ok, the state of our worker thread is signaled, so it finished itself in the timeout given - // - nothing else to do here, except the thread handle closing later - break; - - case WAIT_TIMEOUT: // the timeout interval elapsed, but the worker's state is still non-signaled - default: // any other dwWaitStatus is a BAD one here - // WAIT_FAILED or WAIT_ABANDONED - ::TerminateThread(hThread, dwWaitStatus); - break; - } - CloseHandle(hThread); - - if (isNetWorkProblem != nullptr) - *isNetWorkProblem = data._isNetworkFailure; - - return data._fileAttr; -}; - -bool doesFileExist(const wchar_t* filePath, DWORD milliSec2wait, bool* isNetWorkProblem) -{ - DWORD attr = getFileAttrWaitSec(filePath, milliSec2wait, isNetWorkProblem); - return (attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY)); -} - -bool doesDirectoryExist(const wchar_t* dirPath, DWORD milliSec2wait, bool* isNetWorkProblem) -{ - DWORD attr = getFileAttrWaitSec(dirPath, milliSec2wait, isNetWorkProblem); - return (attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY)); -} - -bool doesPathExist(const wchar_t* path, DWORD milliSec2wait, bool* isNetWorkProblem) -{ - DWORD attr = getFileAttrWaitSec(path, milliSec2wait, isNetWorkProblem); - return (attr != INVALID_FILE_ATTRIBUTES); -} //---------------------------------------------------- @@ -1855,7 +1791,7 @@ DWORD WINAPI getDiskFreeSpaceExWorker(void* data) return ERROR_SUCCESS; }; -DWORD getDiskFreeSpaceWaitSec(const wchar_t* dirPath, ULARGE_INTEGER* freeBytesForUser, DWORD milliSec2wait, bool* isNetWorkProblem) +DWORD getDiskFreeSpaceWithTimeout(const wchar_t* dirPath, ULARGE_INTEGER* freeBytesForUser, DWORD milliSec2wait, bool* isNetWorkProblem) { GetDiskFreeSpaceParamResult data(dirPath); @@ -1911,7 +1847,7 @@ DWORD WINAPI getFileAttributesExWorker(void* data) return ERROR_SUCCESS; }; -DWORD getFileAttributesExWaitSec(const wchar_t* filePath, WIN32_FILE_ATTRIBUTE_DATA* fileAttr, DWORD milliSec2wait, bool* isNetWorkProblem) +DWORD getFileAttributesExWithTimeout(const wchar_t* filePath, WIN32_FILE_ATTRIBUTE_DATA* fileAttr, DWORD milliSec2wait, bool* isNetWorkProblem) { GetAttrExParamResult data(filePath); @@ -1945,61 +1881,23 @@ DWORD getFileAttributesExWaitSec(const wchar_t* filePath, WIN32_FILE_ATTRIBUTE_D return data._result; } - -//---------------------------------------------------- - -struct CreateFileParamResult +bool doesFileExist(const wchar_t* filePath, DWORD milliSec2wait, bool* isNetWorkProblem) { - wstring _filePath; - HANDLE _hFile = INVALID_HANDLE_VALUE; - DWORD _accessParam = GENERIC_READ | GENERIC_WRITE; - DWORD _shareParam = FILE_SHARE_READ | FILE_SHARE_WRITE; - DWORD _dispParam = CREATE_ALWAYS; - DWORD _attribParam = FILE_ATTRIBUTE_NORMAL; - bool _isNetworkFailure = true; - CreateFileParamResult(wstring filePath, DWORD accessParam, DWORD shareParam, DWORD dispParam, DWORD attribParam) : - _filePath(filePath), _accessParam(accessParam), _shareParam(shareParam), _dispParam(dispParam), _attribParam(attribParam) {}; -}; - -DWORD WINAPI createFileWorker(void* data) -{ - CreateFileParamResult* inAndOut = static_cast(data); - inAndOut->_hFile = ::CreateFileW(inAndOut->_filePath.c_str(), inAndOut->_accessParam, inAndOut->_shareParam, NULL, inAndOut->_dispParam, inAndOut->_attribParam, NULL); - inAndOut->_isNetworkFailure = false; - return ERROR_SUCCESS; -}; - -HANDLE createFileWaitSec(const wchar_t* filePath, DWORD accessParam, DWORD shareParam, DWORD dispParam, DWORD attribParam, DWORD milliSec2wait, bool* isNetWorkProblem) -{ - CreateFileParamResult data(filePath, accessParam, shareParam, dispParam, attribParam); - - HANDLE hThread = ::CreateThread(NULL, 0, createFileWorker, &data, 0, NULL); - if (!hThread) - { - return INVALID_HANDLE_VALUE; - } - - // wait for our worker thread to complete or terminate it when the required timeout has elapsed - DWORD dwWaitStatus = ::WaitForSingleObject(hThread, milliSec2wait == 0 ? DEFAULT_MILLISEC : milliSec2wait); - switch (dwWaitStatus) - { - case WAIT_OBJECT_0: // Ok, the state of our worker thread is signaled, so it finished itself in the timeout given - // - nothing else to do here, except the thread handle closing later - break; - - case WAIT_TIMEOUT: // the timeout interval elapsed, but the worker's state is still non-signaled - default: // Timeout reached, or WAIT_FAILED or WAIT_ABANDONED - // attempt to cancel the operation - ::CancelIoEx(data._hFile, NULL); - ::TerminateThread(hThread, dwWaitStatus); - break; - } - CloseHandle(hThread); - - if (isNetWorkProblem != nullptr) - *isNetWorkProblem = data._isNetworkFailure; - - return data._hFile; + WIN32_FILE_ATTRIBUTE_DATA attributes{}; + getFileAttributesExWithTimeout(filePath, &attributes, milliSec2wait, isNetWorkProblem); + return (attributes.dwFileAttributes != INVALID_FILE_ATTRIBUTES && !(attributes.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)); } +bool doesDirectoryExist(const wchar_t* dirPath, DWORD milliSec2wait, bool* isNetWorkProblem) +{ + WIN32_FILE_ATTRIBUTE_DATA attributes{}; + getFileAttributesExWithTimeout(dirPath, &attributes, milliSec2wait, isNetWorkProblem); + return (attributes.dwFileAttributes != INVALID_FILE_ATTRIBUTES && (attributes.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)); +} +bool doesPathExist(const wchar_t* path, DWORD milliSec2wait, bool* isNetWorkProblem) +{ + WIN32_FILE_ATTRIBUTE_DATA attributes{}; + getFileAttributesExWithTimeout(path, &attributes, milliSec2wait, isNetWorkProblem); + return (attributes.dwFileAttributes != INVALID_FILE_ATTRIBUTES); +} diff --git a/PowerEditor/src/MISC/Common/Common.h b/PowerEditor/src/MISC/Common/Common.h index 4639f9dd3..7a0233689 100644 --- a/PowerEditor/src/MISC/Common/Common.h +++ b/PowerEditor/src/MISC/Common/Common.h @@ -282,11 +282,10 @@ private: unsigned long _build = 0; }; -DWORD getFileAttrWaitSec(const wchar_t* filePath, DWORD milliSec2wait = 0, bool* isNetWorkProblem = nullptr); + +DWORD getDiskFreeSpaceWithTimeout(const wchar_t* dirPath, ULARGE_INTEGER* freeBytesForUser, DWORD milliSec2wait = 0, bool* isNetWorkProblem = nullptr); +DWORD getFileAttributesExWithTimeout(const wchar_t* filePath, WIN32_FILE_ATTRIBUTE_DATA* fileAttr, DWORD milliSec2wait = 0, bool* isNetWorkProblem = nullptr); + bool doesFileExist(const wchar_t* filePath, DWORD milliSec2wait = 0, bool* isNetWorkProblem = nullptr); bool doesDirectoryExist(const wchar_t* dirPath, DWORD milliSec2wait = 0, bool* isNetWorkProblem = nullptr); -bool doesPathExist(const wchar_t* path, DWORD milliSec2wait = 0, bool* isNetWorkProblem = nullptr); - -DWORD getDiskFreeSpaceWaitSec(const wchar_t* dirPath, ULARGE_INTEGER* freeBytesForUser, DWORD milliSec2wait = 0, bool* isNetWorkProblem = nullptr); -DWORD getFileAttributesExWaitSec(const wchar_t* filePath, WIN32_FILE_ATTRIBUTE_DATA* fileAttr, DWORD milliSec2wait = 0, bool* isNetWorkProblem = nullptr); -HANDLE createFileWaitSec(const wchar_t* filePath, DWORD accessParam, DWORD shareParam, DWORD dispParam, DWORD attribParam, DWORD milliSec2wait = 0, bool* isNetWorkProblem = nullptr); +bool doesPathExist(const wchar_t* path, DWORD milliSec2wait = 0, bool* isNetWorkProblem = nullptr); \ No newline at end of file diff --git a/PowerEditor/src/MISC/Common/FileInterface.cpp b/PowerEditor/src/MISC/Common/FileInterface.cpp index 73930be72..2a5d3082e 100644 --- a/PowerEditor/src/MISC/Common/FileInterface.cpp +++ b/PowerEditor/src/MISC/Common/FileInterface.cpp @@ -33,9 +33,9 @@ Win32_IO_File::Win32_IO_File(const wchar_t *fname) WIN32_FILE_ATTRIBUTE_DATA attributes_original{}; DWORD dispParam = CREATE_ALWAYS; bool fileExists = false; - + bool hasNetworkProblem = false; // Store the file creation date & attributes for a possible use later... - if (getFileAttributesExWaitSec(fname, &attributes_original)) + if (getFileAttributesExWithTimeout(fname, &attributes_original, 0, &hasNetworkProblem)) { fileExists = (attributes_original.dwFileAttributes != INVALID_FILE_ATTRIBUTES); } @@ -51,16 +51,22 @@ Win32_IO_File::Win32_IO_File(const wchar_t *fname) FindClose(hFind); } } + else + { + bool isFromNetwork = PathIsNetworkPath(fname); + if (isFromNetwork && hasNetworkProblem) // The file doesn't exist, and the file is a network file, plus the network problem has been detected due to timeout + return; // In this case, we don't call createFile to prevent hanging + } - _hFile = ::createFileWaitSec(fname, _accessParam, _shareParam, dispParam, _attribParam); + _hFile = ::CreateFileW(fname, _accessParam, _shareParam, NULL, dispParam, _attribParam, NULL); // Race condition management: - // If file didn't exist while calling PathFileExistsW, but before calling CreateFileW, file is created: use CREATE_ALWAYS is OK - // If file did exist while calling PathFileExistsW, but before calling CreateFileW, file is deleted: use TRUNCATE_EXISTING will cause the error + // If file didn't exist while calling getFileAttributesExWithTimeout, but before calling CreateFileW, file is created: use CREATE_ALWAYS is OK + // If file did exist while calling getFileAttributesExWithTimeout, but before calling CreateFileW, file is deleted: use TRUNCATE_EXISTING will cause the error if (dispParam == TRUNCATE_EXISTING && _hFile == INVALID_HANDLE_VALUE && ::GetLastError() == ERROR_FILE_NOT_FOUND) { dispParam = CREATE_ALWAYS; - _hFile = ::createFileWaitSec(fname, _accessParam, _shareParam, dispParam, _attribParam); + _hFile = ::CreateFileW(fname, _accessParam, _shareParam, NULL, dispParam, _attribParam, NULL); } if (fileExists && (dispParam == CREATE_ALWAYS) && (_hFile != INVALID_HANDLE_VALUE)) diff --git a/PowerEditor/src/ScintillaComponent/Buffer.cpp b/PowerEditor/src/ScintillaComponent/Buffer.cpp index e37a53573..47fa544c2 100644 --- a/PowerEditor/src/ScintillaComponent/Buffer.cpp +++ b/PowerEditor/src/ScintillaComponent/Buffer.cpp @@ -142,7 +142,7 @@ void Buffer::updateTimeStamp() { FILETIME timeStampLive {}; WIN32_FILE_ATTRIBUTE_DATA attributes{}; - if (getFileAttributesExWaitSec(_fullPathName.c_str(), &attributes) != FALSE) + if (getFileAttributesExWithTimeout(_fullPathName.c_str(), &attributes) != FALSE) { timeStampLive = attributes.ftLastWriteTime; } @@ -311,7 +311,7 @@ bool Buffer::checkFileState() // returns true if the status has been changed (it isOK = true; } } - else if (getFileAttributesExWaitSec(_fullPathName.c_str(), &attributes) != FALSE) + else if (getFileAttributesExWithTimeout(_fullPathName.c_str(), &attributes) != FALSE) { int mask = 0; //status always 'changes', even if from modified to modified bool isFileReadOnly = attributes.dwFileAttributes & FILE_ATTRIBUTE_READONLY; @@ -710,7 +710,7 @@ BufferID FileManager::loadFile(const wchar_t* filename, Document doc, int encodi if (pPath) { WIN32_FILE_ATTRIBUTE_DATA attributes{}; - if (getFileAttributesExWaitSec(pPath, &attributes) != FALSE) + if (getFileAttributesExWithTimeout(pPath, &attributes) != FALSE) { LARGE_INTEGER size{}; size.LowPart = attributes.nFileSizeLow; @@ -845,7 +845,7 @@ bool FileManager::reloadBuffer(BufferID id) //Get file size int64_t fileSize = 0; WIN32_FILE_ATTRIBUTE_DATA attributes{}; - getFileAttributesExWaitSec(buf->getFullPathName(), &attributes); + getFileAttributesExWithTimeout(buf->getFullPathName(), &attributes); if (attributes.dwFileAttributes == INVALID_FILE_ATTRIBUTES) { return false; @@ -1193,7 +1193,7 @@ SavingStatus FileManager::saveBuffer(BufferID id, const wchar_t* filename, bool const wchar_t* currentBufFilePath = buffer->getFullPathName(); ULARGE_INTEGER freeBytesForUser; - BOOL getFreeSpaceSuccessful = getDiskFreeSpaceWaitSec(dirDest, &freeBytesForUser); + BOOL getFreeSpaceSuccessful = getDiskFreeSpaceWithTimeout(dirDest, &freeBytesForUser); if (getFreeSpaceSuccessful) { int64_t fileSize = buffer->getFileLength(); @@ -1208,12 +1208,13 @@ SavingStatus FileManager::saveBuffer(BufferID id, const wchar_t* filename, bool return SavingStatus::NotEnoughRoom; } - DWORD attrib = getFileAttrWaitSec(fullpath); - if (attrib != INVALID_FILE_ATTRIBUTES) + WIN32_FILE_ATTRIBUTE_DATA attributes{}; + getFileAttributesExWithTimeout(fullpath, &attributes); + if (attributes.dwFileAttributes != INVALID_FILE_ATTRIBUTES) { - isHiddenOrSys = (attrib & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) != 0; + isHiddenOrSys = (attributes.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) != 0; if (isHiddenOrSys) - ::SetFileAttributes(filename, attrib & ~(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)); + ::SetFileAttributes(filename, attributes.dwFileAttributes & ~(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)); } UniMode mode = buffer->getUnicodeMode(); @@ -1274,7 +1275,7 @@ SavingStatus FileManager::saveBuffer(BufferID id, const wchar_t* filename, bool } if (isHiddenOrSys) - ::SetFileAttributes(fullpath, attrib); + ::SetFileAttributes(fullpath, attributes.dwFileAttributes); if (isCopy) // "Save a Copy As..." command {