diff --git a/PowerEditor/src/MISC/Common/Common.cpp b/PowerEditor/src/MISC/Common/Common.cpp index 679ce6293..77a50ca4e 100644 --- a/PowerEditor/src/MISC/Common/Common.cpp +++ b/PowerEditor/src/MISC/Common/Common.cpp @@ -1213,7 +1213,7 @@ bool isCertificateValidated(const wstring & fullFilePath, const wstring & subjec bool isAssoCommandExisting(LPCTSTR FullPathName) { - bool isAssoCommandExisting = false; + bool isAssoCmdExist = false; bool isFileExisting = doesFileExist(FullPathName); @@ -1228,11 +1228,11 @@ bool isAssoCommandExisting(LPCTSTR FullPathName) // check if association exist hres = AssocQueryString(ASSOCF_VERIFY|ASSOCF_INIT_IGNOREUNKNOWN, ASSOCSTR_COMMAND, ext, NULL, buffer, &bufferLen); - isAssoCommandExisting = (hres == S_OK) // check if association exist and no error - && (wcsstr(buffer, L"notepad++.exe")) == NULL; // check association with notepad++ + isAssoCmdExist = (hres == S_OK) // check if association exist and no error + && (wcsstr(buffer, L"notepad++.exe")) == NULL; // check association with notepad++ } - return isAssoCommandExisting; + return isAssoCmdExist; } std::wstring s2ws(const std::string& str) @@ -1764,20 +1764,183 @@ bool Version::isCompatibleTo(const Version& from, const Version& to) const return false; } -bool doesFileExist(const wchar_t* filePath) +//---------------------------------------------------- +struct GetAttrParamResult { + std::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 TRUE; +}; + +DWORD getFileAttrWaitSec(const wchar_t* filePath, DWORD milleSec2wait, bool* isNetWorkProblem) { - DWORD dwAttrib = ::GetFileAttributesW(filePath); - return (dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); + GetAttrParamResult data; + data._fileAttr = INVALID_FILE_ATTRIBUTES; + data._filePath = filePath; + data._isNetworkFailure = true; + + 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, milleSec2wait == 0 ? 1000 : milleSec2wait); + 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 milleSec2wait, bool* isNetWorkProblem) +{ + DWORD attr = getFileAttrWaitSec(filePath, milleSec2wait, isNetWorkProblem); + return (attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY)); +} + +bool doesDirectoryExist(const wchar_t* dirPath, DWORD milleSec2wait, bool* isNetWorkProblem) +{ + DWORD attr = getFileAttrWaitSec(dirPath, milleSec2wait, isNetWorkProblem); + return (attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY)); } -bool doesDirectoryExist(const wchar_t* dirPath) +bool doesPathExist(const wchar_t* path, DWORD milleSec2wait, bool* isNetWorkProblem) { - DWORD dwAttrib = ::GetFileAttributesW(dirPath); - return (dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); + DWORD attr = getFileAttrWaitSec(path, milleSec2wait, isNetWorkProblem); + return (attr != INVALID_FILE_ATTRIBUTES); } -bool doesPathExist(const wchar_t* path) +//---------------------------------------------------- + +struct GetDiskFreeSpaceParamResult +{ + std::wstring _dirPath; + ULARGE_INTEGER _freeBytesForUser {}; + DWORD _result = FALSE; + bool _isNetworkFailure = true; +}; + +DWORD WINAPI getDiskFreeSpaceExWorker(void* data) { - DWORD dwAttrib = ::GetFileAttributesW(path); - return (dwAttrib != INVALID_FILE_ATTRIBUTES); + GetDiskFreeSpaceParamResult* inAndOut = static_cast(data); + inAndOut->_result = ::GetDiskFreeSpaceExW(inAndOut->_dirPath.c_str(), &(inAndOut->_freeBytesForUser), nullptr, nullptr); + inAndOut->_isNetworkFailure = false; + return TRUE; +}; + +DWORD getDiskFreeSpaceWaitSec(const wchar_t* dirPath, ULARGE_INTEGER* freeBytesForUser, DWORD milleSec2wait, bool* isNetWorkProblem) +{ + GetDiskFreeSpaceParamResult data; + data._dirPath = dirPath; + data._freeBytesForUser = {}; + data._result = FALSE; + data._isNetworkFailure = true; + + HANDLE hThread = ::CreateThread(NULL, 0, getDiskFreeSpaceExWorker, &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, milleSec2wait == 0 ? 1000 : milleSec2wait); + 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); + + *freeBytesForUser = data._freeBytesForUser; + + if (isNetWorkProblem != nullptr) + *isNetWorkProblem = data._isNetworkFailure; + + return data._result; +} + + +//---------------------------------------------------- + +struct GetAttrExParamResult +{ + std::wstring _filePath; + WIN32_FILE_ATTRIBUTE_DATA _attributes{}; + DWORD _result = FALSE; + bool _isNetworkFailure = true; +}; + +DWORD WINAPI getFileAttributesExWorker(void* data) +{ + GetAttrExParamResult* inAndOut = static_cast(data); + inAndOut->_result = ::GetFileAttributesEx(inAndOut->_filePath.c_str(), GetFileExInfoStandard, &(inAndOut->_attributes)); + inAndOut->_isNetworkFailure = false; + return TRUE; +}; + +DWORD getFileAttributesExWaitSec(const wchar_t* filePath, WIN32_FILE_ATTRIBUTE_DATA* fileAttr, DWORD milleSec2wait, bool* isNetWorkProblem) +{ + GetAttrExParamResult data; + data._filePath = filePath; + data._attributes = {}; + data._result = FALSE; + data._isNetworkFailure = true; + + HANDLE hThread = ::CreateThread(NULL, 0, getFileAttributesExWorker, &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, milleSec2wait == 0 ? 1000 : milleSec2wait); + 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); + + *fileAttr = data._attributes; + + if (isNetWorkProblem != nullptr) + *isNetWorkProblem = data._isNetworkFailure; + + return data._result; } diff --git a/PowerEditor/src/MISC/Common/Common.h b/PowerEditor/src/MISC/Common/Common.h index 7717f80fd..d3318acff 100644 --- a/PowerEditor/src/MISC/Common/Common.h +++ b/PowerEditor/src/MISC/Common/Common.h @@ -25,7 +25,8 @@ #include #include -#pragma deprecated(PathFileExists) // Use doesFileExist, doesDirectoryExist or doesPathExist (for file or directory) instead. +#pragma deprecated(PathFileExists) // Use doesFileExist, doesDirectoryExist or doesPathExist (for file or directory) instead. +#pragma deprecated(PathIsDirectory) // Use doesDirectoryExist instead. const bool dirUp = true; @@ -280,6 +281,9 @@ private: unsigned long _build = 0; }; -bool doesFileExist(const wchar_t* filePath); -bool doesDirectoryExist(const wchar_t* dirPath); -bool doesPathExist(const wchar_t* path); \ No newline at end of file +bool doesFileExist(const wchar_t* filePath, DWORD milleSec2wait = 0, bool* isNetWorkProblem = nullptr); +bool doesDirectoryExist(const wchar_t* dirPath, DWORD milleSec2wait = 0, bool* isNetWorkProblem = nullptr); +bool doesPathExist(const wchar_t* path, DWORD milleSec2wait = 0, bool* isNetWorkProblem = nullptr); + +DWORD getDiskFreeSpaceWaitSec(const wchar_t* dirPath, ULARGE_INTEGER* freeBytesForUser, DWORD milleSec2wait = 0, bool* isNetWorkProblem = nullptr); +DWORD getFileAttributesExWaitSec(const wchar_t* filePath, WIN32_FILE_ATTRIBUTE_DATA* fileAttr, DWORD milleSec2wait = 0, bool* isNetWorkProblem = nullptr); diff --git a/PowerEditor/src/Notepad_plus.cpp b/PowerEditor/src/Notepad_plus.cpp index 77a8ee21a..a961e03c9 100644 --- a/PowerEditor/src/Notepad_plus.cpp +++ b/PowerEditor/src/Notepad_plus.cpp @@ -2579,7 +2579,8 @@ void Notepad_plus::checkDocState() bool isCurrentDirty = curBuf->isDirty(); bool isSeveralDirty = isCurrentDirty; - bool isFileExisting = doesFileExist(curBuf->getFullPathName()); + bool isNetworkProblem; + bool isFileExisting = doesFileExist(curBuf->getFullPathName(), 1000, &isNetworkProblem); if (!isCurrentDirty) { for (size_t i = 0; i < MainFileManager.getNbBuffers(); ++i) @@ -2613,7 +2614,7 @@ void Notepad_plus::checkDocState() enableCommand(IDM_FILE_RELOAD, isFileExisting, MENU); enableCommand(IDM_FILE_CONTAININGFOLDERASWORKSPACE, isFileExisting, MENU); - enableCommand(IDM_FILE_OPEN_DEFAULT_VIEWER, isAssoCommandExisting(curBuf->getFullPathName()), MENU); + enableCommand(IDM_FILE_OPEN_DEFAULT_VIEWER, isFileExisting ? isAssoCommandExisting(curBuf->getFullPathName()) : false, MENU); enableCommand(IDM_VIEW_IN_FIREFOX, isFileExisting, MENU); enableCommand(IDM_VIEW_IN_CHROME, isFileExisting, MENU); @@ -4435,7 +4436,7 @@ void Notepad_plus::dropFiles(HDROP hdrop) { wchar_t pathDropped[MAX_PATH]; ::DragQueryFile(hdrop, i, pathDropped, MAX_PATH); - if (::PathIsDirectory(pathDropped)) + if (doesDirectoryExist(pathDropped)) { size_t len = lstrlen(pathDropped); if (len > 0 && pathDropped[len - 1] != wchar_t('\\')) @@ -7040,7 +7041,7 @@ void Notepad_plus::setWorkingDir(const wchar_t *dir) { params.setWorkingDir(NULL); } - else if (dir && PathIsDirectory(dir)) + else if (dir && doesDirectoryExist(dir)) { params.setWorkingDir(dir); } @@ -7795,6 +7796,7 @@ static const QuoteParams quotes[] = {L"Don Ho #3", QuoteParams::rapid, true, SC_CP_UTF8, L_TEXT, L"The smartphone is the best invention of the 21st century for avoiding eye contact with people you know while crossing the street."}, {L"Don Ho #4", QuoteParams::rapid, false, SC_CP_UTF8, L_TEXT, L"Poor countries' museums vs. rich countries' museums:\nThe first show what they have left.\nThe second show what they have stolen."}, {L"Don Ho #5", QuoteParams::slow, false, SC_CP_UTF8, L_TEXT, L"With great refactoring comes great regressions."}, + {L"Don Ho #6", QuoteParams::rapid, false, SC_CP_UTF8, L_TEXT, L"Naming a variable always reminds me the effort I put into my existence,\nfor giving some sense to my meaningless life."}, {L"Anonymous #1", QuoteParams::slow, false, SC_CP_UTF8, L_TEXT, L"An opinion without 3.14 is just an onion."}, {L"Anonymous #2", QuoteParams::rapid, true, SC_CP_UTF8, L_TEXT, L"Before sex, you help each other get naked, after sex you only dress yourself.\nMoral of the story: in life no one helps you once you're fucked."}, {L"Anonymous #3", QuoteParams::rapid, false, SC_CP_UTF8, L_TEXT, L"I'm not totally useless. I can be used as a bad example."}, @@ -7817,7 +7819,7 @@ static const QuoteParams quotes[] = {L"Anonymous #20", QuoteParams::slow, false, SC_CP_UTF8, L_TEXT, L"Never make eye contact while eating a banana."}, {L"Anonymous #21", QuoteParams::rapid, true, SC_CP_UTF8, L_TEXT, L"I love my sixpack so much, I protect it with a layer of fat."}, {L"Anonymous #22", QuoteParams::rapid, true, SC_CP_UTF8, L_TEXT, L"\"It's impossible.\" said pride.\n\"It's risky.\" said experience.\n\"It's pointless.\" said reason.\n\"Give it a try.\" whispered the heart.\n...\n\"What the hell was that?!?!?!?!?!\" shouted the anus two minutes later."}, - {L"Anonymous #23", QuoteParams::rapid, false, SC_CP_UTF8, L_TEXT, L"A programmer is told to \"go to hell\".\nHe finds the worst part of that statement is the \"go to\"."}, + {L"Anonymous #23", QuoteParams::rapid, false, SC_CP_UTF8, L_TEXT, L"A C++ programmer is told to \"go to hell\".\nHe finds the most offensive part of that statement is the \"go to\"."}, {L"Anonymous #24", QuoteParams::slow, false, SC_CP_UTF8, L_TEXT, L"An Architect's dream is an Engineer's nightmare."}, {L"Anonymous #25", QuoteParams::rapid, true, SC_CP_UTF8, L_TEXT, L"In a way, I feel sorry for the kids of this generation.\nThey'll have parents who know how to check browser history."}, {L"Anonymous #26", QuoteParams::rapid, true, SC_CP_UTF8, L_TEXT, L"Q: What's the difference between git and github?\nA: It's the difference between porn and pornhub.\n"}, diff --git a/PowerEditor/src/NppCommands.cpp b/PowerEditor/src/NppCommands.cpp index 5edf7cd1c..51c12b407 100644 --- a/PowerEditor/src/NppCommands.cpp +++ b/PowerEditor/src/NppCommands.cpp @@ -595,7 +595,7 @@ void Notepad_plus::command(int id) fullFilePath += L"\""; if (id == IDM_EDIT_OPENINFOLDER || - (id == IDM_EDIT_OPENASFILE && !::PathIsDirectory(curentWord))) + (id == IDM_EDIT_OPENASFILE && !doesDirectoryExist(curentWord))) ::ShellExecute(hwnd, L"open", cmd2Exec, fullFilePath.c_str(), L".", SW_SHOW); } else // Relative file path - need concatenate with current full file path diff --git a/PowerEditor/src/NppIO.cpp b/PowerEditor/src/NppIO.cpp index 689b47615..68d075ca7 100644 --- a/PowerEditor/src/NppIO.cpp +++ b/PowerEditor/src/NppIO.cpp @@ -262,7 +262,8 @@ BufferID Notepad_plus::doOpen(const wstring& fileName, bool isRecursive, bool is } bool isSnapshotMode = backupFileName != NULL && doesFileExist(backupFileName); - if (isSnapshotMode && !doesFileExist(longFileName)) // UNTITLED + bool longFileNameExists = doesFileExist(longFileName); + if (isSnapshotMode && !longFileNameExists) // UNTITLED { wcscpy_s(longFileName, targetFileName.c_str()); } @@ -301,13 +302,13 @@ BufferID Notepad_plus::doOpen(const wstring& fileName, bool isRecursive, bool is return foundBufID; } - if (isFileSession(longFileName) && doesFileExist(longFileName)) + if (isFileSession(longFileName) && longFileNameExists) { fileLoadSession(longFileName); return BUFFER_INVALID; } - if (isFileWorkspace(longFileName) && doesFileExist(longFileName)) + if (isFileWorkspace(longFileName) && longFileNameExists) { nppParam.setWorkSpaceFilePath(0, longFileName); // This line switches to Project Panel 1 while starting up Npp @@ -316,12 +317,14 @@ BufferID Notepad_plus::doOpen(const wstring& fileName, bool isRecursive, bool is return BUFFER_INVALID; } +#ifndef _WIN64 bool isWow64Off = false; - if (!doesFileExist(longFileName)) + if (!longFileNameExists) { nppParam.safeWow64EnableWow64FsRedirection(FALSE); isWow64Off = true; } +#endif bool globbing; if (isRawFileName) @@ -388,11 +391,13 @@ BufferID Notepad_plus::doOpen(const wstring& fileName, bool isRecursive, bool is if (!isCreateFileSuccessful) { +#ifndef _WIN64 if (isWow64Off) { nppParam.safeWow64EnableWow64FsRedirection(TRUE); isWow64Off = false; } +#endif return BUFFER_INVALID; } } @@ -477,7 +482,7 @@ BufferID Notepad_plus::doOpen(const wstring& fileName, bool isRecursive, bool is } else { - if (globbing || ::PathIsDirectory(targetFileName.c_str())) + if (globbing || doesDirectoryExist(targetFileName.c_str())) { vector fileNames; vector patterns; @@ -542,12 +547,13 @@ BufferID Notepad_plus::doOpen(const wstring& fileName, bool isRecursive, bool is _pluginsManager.notify(&scnN); } } - +#ifndef _WIN64 if (isWow64Off) { nppParam.safeWow64EnableWow64FsRedirection(TRUE); //isWow64Off = false; } +#endif return buffer; } @@ -659,98 +665,109 @@ bool Notepad_plus::doSave(BufferID id, const wchar_t * filename, bool isCopy) } else if (res == SavingStatus::SaveOpenFailed) { - if (_isAdministrator) + Buffer* buf = MainFileManager.getBufferByID(id); + if (buf->isFromNetwork()) { - // Already in admin mode? File is probably locked. _nativeLangSpeaker.messageBox("FileLockedWarning", _pPublicInterface->getHSelf(), - L"Please check whether if this file is opened in another program", + L"Please check whether the network where the file is located is connected.", L"Save failed", MB_OK | MB_ICONWARNING); } else { - // try to open Notepad++ in admin mode - const NppGUI& nppGui = NppParameters::getInstance().getNppGUI(); - bool isSnapshotMode = nppGui.isSnapshotMode(); - bool isAlwaysInMultiInstMode = nppGui._multiInstSetting == multiInst; - if (isSnapshotMode && !isAlwaysInMultiInstMode) // if both rememberSession && backup mode are enabled and "Always In Multi-Instance Mode" option not activated: - { // Open the 2nd Notepad++ instance in Admin mode, then close the 1st instance. + if (_isAdministrator) + { + // Already in admin mode? File is probably locked. + _nativeLangSpeaker.messageBox("FileLockedWarning", + _pPublicInterface->getHSelf(), + L"Please check whether if this file is opened in another program.", + L"Save failed", + MB_OK | MB_ICONWARNING); + } + else + { + // try to open Notepad++ in admin mode + const NppGUI& nppGui = NppParameters::getInstance().getNppGUI(); + bool isSnapshotMode = nppGui.isSnapshotMode(); + bool isAlwaysInMultiInstMode = nppGui._multiInstSetting == multiInst; + if (isSnapshotMode && !isAlwaysInMultiInstMode) // if both rememberSession && backup mode are enabled and "Always In Multi-Instance Mode" option not activated: + { // Open the 2nd Notepad++ instance in Admin mode, then close the 1st instance. + + int openInAdminModeRes = _nativeLangSpeaker.messageBox("OpenInAdminMode", + _pPublicInterface->getHSelf(), + L"This file cannot be saved and it may be protected.\rDo you want to launch Notepad++ in Administrator mode?", + L"Save failed", + MB_YESNO); - int openInAdminModeRes = _nativeLangSpeaker.messageBox("OpenInAdminMode", - _pPublicInterface->getHSelf(), - L"This file cannot be saved and it may be protected.\rDo you want to launch Notepad++ in Administrator mode?", - L"Save failed", - MB_YESNO); + if (openInAdminModeRes == IDYES) + { + wchar_t nppFullPath[MAX_PATH]{}; + ::GetModuleFileName(NULL, nppFullPath, MAX_PATH); - if (openInAdminModeRes == IDYES) - { - wchar_t nppFullPath[MAX_PATH]{}; - ::GetModuleFileName(NULL, nppFullPath, MAX_PATH); + wstring args = L"-multiInst"; + size_t shellExecRes = (size_t)::ShellExecute(_pPublicInterface->getHSelf(), L"runas", nppFullPath, args.c_str(), L".", SW_SHOW); - wstring args = L"-multiInst"; - size_t shellExecRes = (size_t)::ShellExecute(_pPublicInterface->getHSelf(), L"runas", nppFullPath, args.c_str(), L".", SW_SHOW); + // If the function succeeds, it returns a value greater than 32. If the function fails, + // it returns an error value that indicates the cause of the failure. + // https://msdn.microsoft.com/en-us/library/windows/desktop/bb762153%28v=vs.85%29.aspx - // If the function succeeds, it returns a value greater than 32. If the function fails, - // it returns an error value that indicates the cause of the failure. - // https://msdn.microsoft.com/en-us/library/windows/desktop/bb762153%28v=vs.85%29.aspx + if (shellExecRes <= 32) + { + _nativeLangSpeaker.messageBox("OpenInAdminModeFailed", + _pPublicInterface->getHSelf(), + L"Notepad++ cannot be opened in Administrator mode.", + L"Open in Administrator mode failed", + MB_OK); + } + else + { + ::SendMessage(_pPublicInterface->getHSelf(), WM_CLOSE, 0, 0); + } - if (shellExecRes <= 32) - { - _nativeLangSpeaker.messageBox("OpenInAdminModeFailed", - _pPublicInterface->getHSelf(), - L"Notepad++ cannot be opened in Administrator mode.", - L"Open in Administrator mode failed", - MB_OK); } - else - { - ::SendMessage(_pPublicInterface->getHSelf(), WM_CLOSE, 0, 0); - } - } - } - else // rememberSession && backup mode are not both enabled, or "Always In Multi-Instance Mode" option is ON: - { // Open only the file to save in Notepad++ of Administrator mode by keeping the current instance. + else // rememberSession && backup mode are not both enabled, or "Always In Multi-Instance Mode" option is ON: + { // Open only the file to save in Notepad++ of Administrator mode by keeping the current instance. - int openInAdminModeRes = _nativeLangSpeaker.messageBox("OpenInAdminModeWithoutCloseCurrent", - _pPublicInterface->getHSelf(), - L"The file cannot be saved and it may be protected.\rDo you want to launch Notepad++ in Administrator mode?", - L"Save failed", - MB_YESNO); + int openInAdminModeRes = _nativeLangSpeaker.messageBox("OpenInAdminModeWithoutCloseCurrent", + _pPublicInterface->getHSelf(), + L"The file cannot be saved and it may be protected.\rDo you want to launch Notepad++ in Administrator mode?", + L"Save failed", + MB_YESNO); - if (openInAdminModeRes == IDYES) - { - wchar_t nppFullPath[MAX_PATH]{}; - ::GetModuleFileName(NULL, nppFullPath, MAX_PATH); + if (openInAdminModeRes == IDYES) + { + wchar_t nppFullPath[MAX_PATH]{}; + ::GetModuleFileName(NULL, nppFullPath, MAX_PATH); - BufferID bufferID = _pEditView->getCurrentBufferID(); - Buffer * buf = MainFileManager.getBufferByID(bufferID); + Buffer* buf = MainFileManager.getBufferByID(id); - //process the fileNamePath into LRF - wstring fileNamePath = buf->getFullPathName(); + //process the fileNamePath into LRF + wstring fileNamePath = buf->getFullPathName(); - wstring args = L"-multiInst -nosession "; - args += L"\""; - args += fileNamePath; - args += L"\""; - size_t shellExecRes = (size_t)::ShellExecute(_pPublicInterface->getHSelf(), L"runas", nppFullPath, args.c_str(), L".", SW_SHOW); + wstring args = L"-multiInst -nosession "; + args += L"\""; + args += fileNamePath; + args += L"\""; + size_t shellExecRes = (size_t)::ShellExecute(_pPublicInterface->getHSelf(), L"runas", nppFullPath, args.c_str(), L".", SW_SHOW); - // If the function succeeds, it returns a value greater than 32. If the function fails, - // it returns an error value that indicates the cause of the failure. - // https://msdn.microsoft.com/en-us/library/windows/desktop/bb762153%28v=vs.85%29.aspx + // If the function succeeds, it returns a value greater than 32. If the function fails, + // it returns an error value that indicates the cause of the failure. + // https://msdn.microsoft.com/en-us/library/windows/desktop/bb762153%28v=vs.85%29.aspx - if (shellExecRes <= 32) - { - _nativeLangSpeaker.messageBox("OpenInAdminModeFailed", - _pPublicInterface->getHSelf(), - L"Notepad++ cannot be opened in Administrator mode.", - L"Open in Administrator mode failed", - MB_OK); + if (shellExecRes <= 32) + { + _nativeLangSpeaker.messageBox("OpenInAdminModeFailed", + _pPublicInterface->getHSelf(), + L"Notepad++ cannot be opened in Administrator mode.", + L"Open in Administrator mode failed", + MB_OK); + } } } - } + } } } @@ -791,26 +808,33 @@ void Notepad_plus::doClose(BufferID id, int whichOne, bool doDeleteBackup) wstring fileFullPath; if (!buf->isUntitled()) { - // if the file doesn't exist, it could be redirected + const wchar_t *fn = buf->getFullPathName(); + bool fileExists = doesFileExist(fn); + +#ifndef _WIN64 + // For Notepad++ 32 bits, if the file doesn't exist, it could be redirected // So we turn Wow64 off - bool isWow64Off = false; NppParameters& nppParam = NppParameters::getInstance(); - const wchar_t *fn = buf->getFullPathName(); - if (!doesFileExist(fn)) + bool isWow64Off = false; + if (!fileExists) { nppParam.safeWow64EnableWow64FsRedirection(FALSE); isWow64Off = true; + fileExists = doesFileExist(fn); } +#endif - if (doesFileExist(buf->getFullPathName())) - fileFullPath = buf->getFullPathName(); + if (fileExists) + fileFullPath = fn; +#ifndef _WIN64 // We enable Wow64 system, if it was disabled if (isWow64Off) { nppParam.safeWow64EnableWow64FsRedirection(TRUE); //isWow64Off = false; } +#endif } size_t nbDocs = whichOne==MAIN_VIEW?(_mainDocTab.nbItem()):(_subDocTab.nbItem()); @@ -2307,14 +2331,14 @@ bool Notepad_plus::loadSession(Session & session, bool isSnapshotMode, const wch session._mainViewFiles.erase(posIt); continue; //skip session files, not supporting recursive sessions or embedded workspace files } - +#ifndef _WIN64 bool isWow64Off = false; if (!doesFileExist(pFn)) { nppParam.safeWow64EnableWow64FsRedirection(FALSE); isWow64Off = true; } - +#endif if (doesFileExist(pFn)) { if (isSnapshotMode && !session._mainViewFiles[i]._backupFilePath.empty()) @@ -2332,12 +2356,13 @@ bool Notepad_plus::loadSession(Session & session, bool isSnapshotMode, const wch if (foundBufID == BUFFER_INVALID) lastOpened = nppGUI._keepSessionAbsentFileEntries ? MainFileManager.newPlaceholderDocument(pFn, MAIN_VIEW, userCreatedSessionName) : BUFFER_INVALID; } +#ifndef _WIN64 if (isWow64Off) { nppParam.safeWow64EnableWow64FsRedirection(TRUE); isWow64Off = false; } - +#endif if (lastOpened != BUFFER_INVALID) { showView(MAIN_VIEW); @@ -2437,14 +2462,14 @@ bool Notepad_plus::loadSession(Session & session, bool isSnapshotMode, const wch session._subViewFiles.erase(posIt); continue; //skip session files, not supporting recursive sessions or embedded workspace files } - +#ifndef _WIN64 bool isWow64Off = false; if (!doesFileExist(pFn)) { nppParam.safeWow64EnableWow64FsRedirection(FALSE); isWow64Off = true; } - +#endif if (doesFileExist(pFn)) { //check if already open in main. If so, clone @@ -2472,12 +2497,13 @@ bool Notepad_plus::loadSession(Session & session, bool isSnapshotMode, const wch if (foundBufID == BUFFER_INVALID) lastOpened = nppGUI._keepSessionAbsentFileEntries ? MainFileManager.newPlaceholderDocument(pFn, SUB_VIEW, userCreatedSessionName) : BUFFER_INVALID; } - +#ifndef _WIN64 if (isWow64Off) { nppParam.safeWow64EnableWow64FsRedirection(TRUE); isWow64Off = false; } +#endif if (lastOpened != BUFFER_INVALID) { diff --git a/PowerEditor/src/Parameters.cpp b/PowerEditor/src/Parameters.cpp index 0431418f9..bd9be36a4 100644 --- a/PowerEditor/src/Parameters.cpp +++ b/PowerEditor/src/Parameters.cpp @@ -1244,7 +1244,7 @@ bool NppParameters::load() // if (!_cmdSettingsDir.empty()) { - if (!::PathIsDirectory(_cmdSettingsDir.c_str())) + if (!doesDirectoryExist(_cmdSettingsDir.c_str())) { // The following text is not translatable. // _pNativeLangSpeaker is initialized AFTER _userPath being dterminated because nativeLang.xml is from from _userPath. @@ -8653,6 +8653,7 @@ void NppParameters::addScintillaModifiedIndex(int index) } } +#ifndef _WIN64 void NppParameters::safeWow64EnableWow64FsRedirection(BOOL Wow64FsEnableRedirection) { HMODULE kernel = GetModuleHandle(L"kernel32"); @@ -8678,6 +8679,7 @@ void NppParameters::safeWow64EnableWow64FsRedirection(BOOL Wow64FsEnableRedirect } } } +#endif void NppParameters::setUdlXmlDirtyFromIndex(size_t i) { diff --git a/PowerEditor/src/Parameters.h b/PowerEditor/src/Parameters.h index c659a0f13..16f44da93 100644 --- a/PowerEditor/src/Parameters.h +++ b/PowerEditor/src/Parameters.h @@ -1759,7 +1759,9 @@ public: std::wstring getWinVerBitStr() const; FindHistory & getFindHistory() {return _findHistory;}; bool _isFindReplacing = false; // an on the fly variable for find/replace functions +#ifndef _WIN64 void safeWow64EnableWow64FsRedirection(BOOL Wow64FsEnableRedirection); +#endif LocalizationSwitcher & getLocalizationSwitcher() { return _localizationSwitcher; diff --git a/PowerEditor/src/ScintillaComponent/Buffer.cpp b/PowerEditor/src/ScintillaComponent/Buffer.cpp index 447414301..7b4253f5e 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 (GetFileAttributesEx(_fullPathName.c_str(), GetFileExInfoStandard, &attributes) != 0) + if (getFileAttributesExWaitSec(_fullPathName.c_str(), &attributes) != FALSE) { timeStampLive = attributes.ftLastWriteTime; } @@ -194,6 +194,7 @@ void Buffer::setFileName(const wchar_t *fn) _fullPathName = fn; _fileName = PathFindFileName(_fullPathName.c_str()); + _isFromNetwork = PathIsNetworkPath(fn); // for _lang LangType determinatedLang = L_TEXT; @@ -259,15 +260,19 @@ bool Buffer::checkFileState() // returns true if the status has been changed (it return false; WIN32_FILE_ATTRIBUTE_DATA attributes{}; - bool isWow64Off = false; NppParameters& nppParam = NppParameters::getInstance(); - bool fileExists = doesFileExist(_fullPathName.c_str()); + +#ifndef _WIN64 + bool isWow64Off = false; if (!fileExists) { nppParam.safeWow64EnableWow64FsRedirection(FALSE); isWow64Off = true; + + fileExists = doesFileExist(_fullPathName.c_str()); } +#endif bool isOK = false; if (_currentStatus == DOC_INACCESSIBLE && !fileExists) //document is absent on its first load - we set readonly and not dirty, and make it be as document which has been deleted @@ -289,9 +294,9 @@ bool Buffer::checkFileState() // returns true if the status has been changed (it doNotify(BufferChangeStatus | BufferChangeReadonly | BufferChangeTimestamp); isOK = true; } - else if (_currentStatus == DOC_DELETED && fileExists) - { //document has returned from its grave - if (GetFileAttributesEx(_fullPathName.c_str(), GetFileExInfoStandard, &attributes) != 0) + else if (_currentStatus == DOC_DELETED && fileExists) //document has returned from its grave + { + if (GetFileAttributesEx(_fullPathName.c_str(), GetFileExInfoStandard, &attributes) != 0) // fileExists so it's safe to call GetFileAttributesEx directly { _isFileReadOnly = attributes.dwFileAttributes & FILE_ATTRIBUTE_READONLY; @@ -306,7 +311,7 @@ bool Buffer::checkFileState() // returns true if the status has been changed (it isOK = true; } } - else if (GetFileAttributesEx(_fullPathName.c_str(), GetFileExInfoStandard, &attributes) != 0) + else if (getFileAttributesExWaitSec(_fullPathName.c_str(), &attributes) != FALSE) { int mask = 0; //status always 'changes', even if from modified to modified bool isFileReadOnly = attributes.dwFileAttributes & FILE_ATTRIBUTE_READONLY; @@ -364,10 +369,12 @@ bool Buffer::checkFileState() // returns true if the status has been changed (it return false; } +#ifndef _WIN64 if (isWow64Off) { nppParam.safeWow64EnableWow64FsRedirection(TRUE); } +#endif return isOK; } @@ -420,8 +427,6 @@ wstring Buffer::getTimeString(FILETIME rawtime) const wstring Buffer::getFileTime(fileTimeType ftt) const { - wstring filePath; - WIN32_FILE_ATTRIBUTE_DATA attributes{}; if (GetFileAttributesEx(_currentStatus == DOC_UNNAMED ? _backupFileName.c_str() : _fullPathName.c_str(), GetFileExInfoStandard, &attributes) != 0) { @@ -1177,8 +1182,8 @@ SavingStatus FileManager::saveBuffer(BufferID id, const wchar_t* filename, bool const wchar_t* currentBufFilePath = buffer->getFullPathName(); ULARGE_INTEGER freeBytesForUser; - BOOL getFreeSpaceRes = ::GetDiskFreeSpaceExW(dirDest, &freeBytesForUser, nullptr, nullptr); - if (getFreeSpaceRes != FALSE) + BOOL getFreeSpaceSuccessful = getDiskFreeSpaceWaitSec(dirDest, &freeBytesForUser); + if (getFreeSpaceSuccessful) { int64_t fileSize = buffer->getFileLength(); if (fileSize >= 0 && lstrcmp(fullpath, currentBufFilePath) == 0) // if file to save does exist, and it's an operation "Save" but not "Save As" diff --git a/PowerEditor/src/ScintillaComponent/Buffer.h b/PowerEditor/src/ScintillaComponent/Buffer.h index 331c702b4..cbb122f9b 100644 --- a/PowerEditor/src/ScintillaComponent/Buffer.h +++ b/PowerEditor/src/ScintillaComponent/Buffer.h @@ -186,6 +186,8 @@ public: bool isUntitled() const { return ((_currentStatus & DOC_UNNAMED) == DOC_UNNAMED); } + bool isFromNetwork() const { return _isFromNetwork; } + bool isInaccessible() const { return _isInaccessible; } void setInaccessibility(bool val) { _isInaccessible = val; } @@ -279,7 +281,7 @@ public: std::wstring tabCreatedTimeString() const { return _tabCreatedTimeString; } void setTabCreatedTimeStringFromBakFile() { - if (_currentStatus == DOC_UNNAMED) + if (!_isFromNetwork && _currentStatus == DOC_UNNAMED) _tabCreatedTimeString = getFileTime(Buffer::ft_created); // while DOC_UNNAMED, getFileTime will retrieve time from backup file } void setTabCreatedTimeStringWithCurrentTime() { @@ -389,6 +391,7 @@ private: UniMode _unicodeMode = uniUTF8; int _encoding = -1; bool _isUserReadOnly = false; + bool _isFromNetwork = false; bool _needLexer = false; // new buffers do not need lexing, Scintilla takes care of that //these properties have to be duplicated because of multiple references diff --git a/PowerEditor/src/ScintillaComponent/FindReplaceDlg.cpp b/PowerEditor/src/ScintillaComponent/FindReplaceDlg.cpp index 1daf275ae..5e28823aa 100644 --- a/PowerEditor/src/ScintillaComponent/FindReplaceDlg.cpp +++ b/PowerEditor/src/ScintillaComponent/FindReplaceDlg.cpp @@ -2648,7 +2648,7 @@ intptr_t CALLBACK FindReplaceDlg::run_dlgProc(UINT message, WPARAM wParam, LPARA currPath = buf->getFullPathName(); PathRemoveFileSpec(currPath); } - if (currPath.empty() || !PathIsDirectory(currPath.c_str())) + if (currPath.empty() || !doesDirectoryExist(currPath.c_str())) currPath = NppParameters::getInstance().getWorkingDir(); ::SetDlgItemText(_hSelf, IDD_FINDINFILES_DIR_COMBO, currPath.c_str()); } diff --git a/PowerEditor/src/WinControls/FileBrowser/fileBrowser.cpp b/PowerEditor/src/WinControls/FileBrowser/fileBrowser.cpp index 1a15ba8b9..2deaf8c95 100644 --- a/PowerEditor/src/WinControls/FileBrowser/fileBrowser.cpp +++ b/PowerEditor/src/WinControls/FileBrowser/fileBrowser.cpp @@ -1235,7 +1235,7 @@ bool FileBrowser::addToTree(FilesToChange & group, HTREEITEM node) // Not found, good - Action for (auto & file : group._files) { - if (::PathIsDirectory((group._commonPath + file).c_str())) + if (doesDirectoryExist((group._commonPath + file).c_str())) { SortingData4lParam* customData = new SortingData4lParam(L"", file, true); sortingDataArray.push_back(customData); @@ -1468,7 +1468,7 @@ bool FolderInfo::addToStructure(wstring & fullpath, std::vector linarPa { fullpath += L"\\"; fullpath += linarPathArray[0]; - if (PathIsDirectory(fullpath.c_str())) + if (doesDirectoryExist(fullpath.c_str())) { // search in folders, if found - no good for (const auto& folder : _subFolders) diff --git a/PowerEditor/src/WinControls/OpenSaveFileDialog/CustomFileDialog.cpp b/PowerEditor/src/WinControls/OpenSaveFileDialog/CustomFileDialog.cpp index 867dc94ce..b8ad0bb25 100644 --- a/PowerEditor/src/WinControls/OpenSaveFileDialog/CustomFileDialog.cpp +++ b/PowerEditor/src/WinControls/OpenSaveFileDialog/CustomFileDialog.cpp @@ -16,7 +16,6 @@ #include -#include // PathIsDirectory #ifdef __MINGW32__ #include #endif @@ -142,7 +141,7 @@ namespace // anonymous HRESULT hr = SHCreateItemFromParsingName(path, nullptr, IID_PPV_ARGS(&shellItem)); - if (SUCCEEDED(hr) && shellItem && !::PathIsDirectory(path)) + if (SUCCEEDED(hr) && shellItem && !::doesDirectoryExist(path)) { com_ptr parentItem; hr = shellItem->GetParent(&parentItem); @@ -506,7 +505,7 @@ private: expandEnv(fileName); bool nameChanged = transformPath(fileName); // Update the controls. - if (!::PathIsDirectory(getAbsPath(fileName).c_str())) + if (doesDirectoryExist(getAbsPath(fileName).c_str())) { // Name is a file path. // Add file extension if missing.