diff --git a/PowerEditor/src/MISC/Common/Common.cpp b/PowerEditor/src/MISC/Common/Common.cpp index 47154552b39b..8650e1fe06c9 100644 --- a/PowerEditor/src/MISC/Common/Common.cpp +++ b/PowerEditor/src/MISC/Common/Common.cpp @@ -1837,6 +1837,58 @@ bool doesPathExist(const wchar_t* path, DWORD milliSec2wait, bool* isNetWorkProb return (attr != INVALID_FILE_ATTRIBUTES); } + +//---------------------------------------------------- + +struct wfopenParamResult +{ + std::wstring _filePath; + FILE* _pFile = nullptr; + bool _isNetworkFailure = true; + wfopenParamResult(wstring filePath) : _filePath(filePath) {}; +}; + +DWORD WINAPI wfopenWorker(void* data) +{ + wfopenParamResult* inAndOut = static_cast(data); + inAndOut->_pFile = _wfopen(inAndOut->_filePath.c_str(), L"rb"); + inAndOut->_isNetworkFailure = false; + return ERROR_SUCCESS; +}; + +FILE* wfopenWaitSec(const wchar_t* filePath, DWORD milliSec2wait, bool* isNetWorkProblem) +{ + wfopenParamResult data(filePath); + + HANDLE hThread = ::CreateThread(NULL, 0, wfopenWorker, &data, 0, NULL); + if (!hThread) + { + return FALSE; + } + + // 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._pFile; +} + + //---------------------------------------------------- struct GetDiskFreeSpaceParamResult diff --git a/PowerEditor/src/MISC/Common/Common.h b/PowerEditor/src/MISC/Common/Common.h index b4a8e0b62167..2b7e79a3bdd5 100644 --- a/PowerEditor/src/MISC/Common/Common.h +++ b/PowerEditor/src/MISC/Common/Common.h @@ -287,5 +287,6 @@ bool doesFileExist(const wchar_t* filePath, DWORD milliSec2wait = 0, bool* isNet bool doesDirectoryExist(const wchar_t* dirPath, DWORD milliSec2wait = 0, bool* isNetWorkProblem = nullptr); bool doesPathExist(const wchar_t* path, DWORD milliSec2wait = 0, bool* isNetWorkProblem = nullptr); +FILE* wfopenWaitSec(const wchar_t* filePath, 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); diff --git a/PowerEditor/src/ScintillaComponent/Buffer.cpp b/PowerEditor/src/ScintillaComponent/Buffer.cpp index 4effd22ee8ef..291a48321eb2 100644 --- a/PowerEditor/src/ScintillaComponent/Buffer.cpp +++ b/PowerEditor/src/ScintillaComponent/Buffer.cpp @@ -702,22 +702,42 @@ BufferID FileManager::loadFile(const wchar_t* filename, Document doc, int encodi //Get file size int64_t fileSize = -1; const wchar_t* pPath = filename; + std::wstring msg = pPath; + msg += L"\t BEFORE doesFileExist"; + writeLog(L"c:\\tmp\\QQQ", msg.c_str()); if (!doesFileExist(pPath)) { pPath = backupFileName; } + msg = pPath; + msg += L"\t AFTER doesFileExist"; + writeLog(L"c:\\tmp\\QQQ", msg.c_str()); + + if (pPath) { + msg = pPath; + msg += L"\t BEFORE getFileAttributesExWaitSec"; + writeLog(L"c:\\tmp\\QQQ", msg.c_str()); + WIN32_FILE_ATTRIBUTE_DATA attributes{}; if (getFileAttributesExWaitSec(pPath, &attributes) != FALSE) { + msg = pPath; + msg += L"\t getFileAttributesExWaitSec OK - let's retrieve file length"; + writeLog(L"c:\\tmp\\QQQ", msg.c_str()); + LARGE_INTEGER size{}; size.LowPart = attributes.nFileSizeLow; size.HighPart = attributes.nFileSizeHigh; fileSize = size.QuadPart; } + + msg = pPath; + msg += L"\t AFTER getFileAttributesExWaitSec"; + writeLog(L"c:\\tmp\\QQQ", msg.c_str()); } // * the auto-completion feature will be disabled for large files @@ -777,8 +797,16 @@ BufferID FileManager::loadFile(const wchar_t* filename, Document doc, int encodi loadedFileFormat._eolFormat = EolType::unknown; loadedFileFormat._language = L_TEXT; + msg = pPath; + msg += L"\t BEFORE loadFileData"; + writeLog(L"c:\\tmp\\QQQ", msg.c_str()); + bool loadRes = loadFileData(doc, fileSize, backupFileName ? backupFileName : fullpath, data, &UnicodeConvertor, loadedFileFormat); + msg = pPath; + msg += L"\t AFTER loadFileData"; + writeLog(L"c:\\tmp\\QQQ", msg.c_str()); + delete[] data; if (loadRes) @@ -843,12 +871,22 @@ bool FileManager::reloadBuffer(BufferID id) // Set _isLoadedDirty false before calling "_pscratchTilla->execute(SCI_CLEARALL);" in loadFileData() to avoid setDirty in SCN_SAVEPOINTREACHED / SCN_SAVEPOINTLEFT //Get file size - FILE* fp = _wfopen(buf->getFullPathName(), L"rb"); - if (!fp) + int64_t fileSize = 0; + WIN32_FILE_ATTRIBUTE_DATA attributes{}; + getFileAttributesExWaitSec(buf->getFullPathName(), &attributes); + if (attributes.dwFileAttributes == INVALID_FILE_ATTRIBUTES) + { return false; - _fseeki64(fp, 0, SEEK_END); - int64_t fileSize = _ftelli64(fp); - fclose(fp); + } + else + { + LARGE_INTEGER size{}; + size.LowPart = attributes.nFileSizeLow; + size.HighPart = attributes.nFileSizeHigh; + + fileSize = size.QuadPart; + } + char* data = new char[blockSize + 8]; // +8 for incomplete multibyte char @@ -1561,10 +1599,7 @@ LangType FileManager::detectLanguageFromTextBegining(const unsigned char *data, bool FileManager::loadFileData(Document doc, int64_t fileSize, const wchar_t * filename, char* data, Utf8_16_Read * unicodeConvertor, LoadedFileFormat& fileFormat) { - FILE *fp = _wfopen(filename, L"rb"); - if (!fp) - return false; - + // Check file size firstly // size/6 is the normal room Scintilla keeps for editing, but here we limit it to 1MiB when loading (maybe we want to load big files without editing them too much) int64_t bufferSizeRequested = fileSize + std::min(1LL << 20, fileSize / 6); @@ -1582,7 +1617,6 @@ bool FileManager::loadFileData(Document doc, int64_t fileSize, const wchar_t * f L"File size problem", MB_OK | MB_APPLMODAL); - fclose(fp); return false; } else // x64 @@ -1602,13 +1636,25 @@ bool FileManager::loadFileData(Document doc, int64_t fileSize, const wchar_t * f } else { - fclose(fp); return false; } } } } + wstring msg = filename; + msg += L"\t BEFORE _wfopen"; + writeLog(L"c:\\tmp\\QQQ", msg.c_str()); + + FILE* fp = wfopenWaitSec(filename, 5000); + + msg = filename; + msg += L"\t AFTER _wfopen"; + writeLog(L"c:\\tmp\\QQQ", msg.c_str()); + + if (!fp) + return false; + //Setup scratchtilla for new filedata _pscratchTilla->execute(SCI_SETSTATUS, SC_STATUS_OK); // reset error status _pscratchTilla->execute(SCI_SETDOCPOINTER, 0, doc); @@ -1641,8 +1687,8 @@ bool FileManager::loadFileData(Document doc, int64_t fileSize, const wchar_t * f bool success = true; EolType format = EolType::unknown; int sciStatus = SC_STATUS_OK; - wchar_t szException[64] = { '\0' }; - __try + /*wchar_t szException[64] = {'\0'}; + __try*/ { // First allocate enough memory for the whole file (this will reduce memory copy during loading) _pscratchTilla->execute(SCI_ALLOCATE, WPARAM(bufferSizeRequested)); @@ -1730,7 +1776,7 @@ bool FileManager::loadFileData(Document doc, int64_t fileSize, const wchar_t * f } while (lenFile > 0); } - __except(EXCEPTION_EXECUTE_HANDLER) + /*__except (EXCEPTION_EXECUTE_HANDLER) { switch (sciStatus) { @@ -1769,8 +1815,8 @@ bool FileManager::loadFileData(Document doc, int64_t fileSize, const wchar_t * f szException); } success = false; - } - + }*/ + fclose(fp); // broadcast the format