Fix opened network files hanging issue while the network disconnected

The issue is due to WinAPI's GetFileAttributes function can take a huge amount of time in various situations, like when the target server being offline (usalally).
The current solution is to wait at least 3 seconds for each GetFileAttributes function, then return the default value in case of the problem (or the returned value of the function, in case of normal situation - if it has been executed completely during the 3 seconds).
So there'll still be the hanging time (3 or 6 seconds) while the problem of network connection, but the hanging time (it could be 30 seconds more) is reduced considerablly.

"Wow64EnableWow64FsRedirection" call is also removed from x64 build (in which this call is unnecessary) in this commit to reduce the IO calls.

Fix #4306, fix #6178, fix #8055, fix #11388, fix #12553, fix #15540, close #15658
This commit is contained in:
Don Ho 2024-09-28 00:01:57 +02:00
parent fc051a1231
commit f884a39dd4
12 changed files with 344 additions and 138 deletions

View File

@ -1213,7 +1213,7 @@ bool isCertificateValidated(const wstring & fullFilePath, const wstring & subjec
bool isAssoCommandExisting(LPCTSTR FullPathName) bool isAssoCommandExisting(LPCTSTR FullPathName)
{ {
bool isAssoCommandExisting = false; bool isAssoCmdExist = false;
bool isFileExisting = doesFileExist(FullPathName); bool isFileExisting = doesFileExist(FullPathName);
@ -1228,11 +1228,11 @@ bool isAssoCommandExisting(LPCTSTR FullPathName)
// check if association exist // check if association exist
hres = AssocQueryString(ASSOCF_VERIFY|ASSOCF_INIT_IGNOREUNKNOWN, ASSOCSTR_COMMAND, ext, NULL, buffer, &bufferLen); hres = AssocQueryString(ASSOCF_VERIFY|ASSOCF_INIT_IGNOREUNKNOWN, ASSOCSTR_COMMAND, ext, NULL, buffer, &bufferLen);
isAssoCommandExisting = (hres == S_OK) // check if association exist and no error isAssoCmdExist = (hres == S_OK) // check if association exist and no error
&& (wcsstr(buffer, L"notepad++.exe")) == NULL; // check association with notepad++ && (wcsstr(buffer, L"notepad++.exe")) == NULL; // check association with notepad++
} }
return isAssoCommandExisting; return isAssoCmdExist;
} }
std::wstring s2ws(const std::string& str) std::wstring s2ws(const std::string& str)
@ -1764,20 +1764,183 @@ bool Version::isCompatibleTo(const Version& from, const Version& to) const
return false; 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)
{ {
DWORD dwAttrib = ::GetFileAttributesW(filePath); GetAttrParamResult* inAndOut = static_cast<GetAttrParamResult*>(data);
return (dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); inAndOut->_fileAttr = ::GetFileAttributesW(inAndOut->_filePath.c_str());
inAndOut->_isNetworkFailure = false;
return TRUE;
};
DWORD getFileAttrWaitSec(const wchar_t* filePath, DWORD milleSec2wait, bool* isNetWorkProblem)
{
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;
} }
bool doesDirectoryExist(const wchar_t* dirPath) // 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)
{ {
DWORD dwAttrib = ::GetFileAttributesW(dirPath); case WAIT_OBJECT_0: // Ok, the state of our worker thread is signaled, so it finished itself in the timeout given
return (dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); // - 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 doesPathExist(const wchar_t* path) bool doesDirectoryExist(const wchar_t* dirPath, DWORD milleSec2wait, bool* isNetWorkProblem)
{ {
DWORD dwAttrib = ::GetFileAttributesW(path); DWORD attr = getFileAttrWaitSec(dirPath, milleSec2wait, isNetWorkProblem);
return (dwAttrib != INVALID_FILE_ATTRIBUTES); return (attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY));
}
bool doesPathExist(const wchar_t* path, DWORD milleSec2wait, bool* isNetWorkProblem)
{
DWORD attr = getFileAttrWaitSec(path, milleSec2wait, isNetWorkProblem);
return (attr != INVALID_FILE_ATTRIBUTES);
}
//----------------------------------------------------
struct GetDiskFreeSpaceParamResult
{
std::wstring _dirPath;
ULARGE_INTEGER _freeBytesForUser {};
DWORD _result = FALSE;
bool _isNetworkFailure = true;
};
DWORD WINAPI getDiskFreeSpaceExWorker(void* data)
{
GetDiskFreeSpaceParamResult* inAndOut = static_cast<GetDiskFreeSpaceParamResult*>(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<GetAttrExParamResult*>(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;
} }

View File

@ -26,6 +26,7 @@
#include <tchar.h> #include <tchar.h>
#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; const bool dirUp = true;
@ -280,6 +281,9 @@ private:
unsigned long _build = 0; unsigned long _build = 0;
}; };
bool doesFileExist(const wchar_t* filePath); bool doesFileExist(const wchar_t* filePath, DWORD milleSec2wait = 0, bool* isNetWorkProblem = nullptr);
bool doesDirectoryExist(const wchar_t* dirPath); bool doesDirectoryExist(const wchar_t* dirPath, DWORD milleSec2wait = 0, bool* isNetWorkProblem = nullptr);
bool doesPathExist(const wchar_t* path); 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);

View File

@ -2579,7 +2579,8 @@ void Notepad_plus::checkDocState()
bool isCurrentDirty = curBuf->isDirty(); bool isCurrentDirty = curBuf->isDirty();
bool isSeveralDirty = isCurrentDirty; bool isSeveralDirty = isCurrentDirty;
bool isFileExisting = doesFileExist(curBuf->getFullPathName()); bool isNetworkProblem;
bool isFileExisting = doesFileExist(curBuf->getFullPathName(), 1000, &isNetworkProblem);
if (!isCurrentDirty) if (!isCurrentDirty)
{ {
for (size_t i = 0; i < MainFileManager.getNbBuffers(); ++i) 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_RELOAD, isFileExisting, MENU);
enableCommand(IDM_FILE_CONTAININGFOLDERASWORKSPACE, 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_FIREFOX, isFileExisting, MENU);
enableCommand(IDM_VIEW_IN_CHROME, isFileExisting, MENU); enableCommand(IDM_VIEW_IN_CHROME, isFileExisting, MENU);
@ -4435,7 +4436,7 @@ void Notepad_plus::dropFiles(HDROP hdrop)
{ {
wchar_t pathDropped[MAX_PATH]; wchar_t pathDropped[MAX_PATH];
::DragQueryFile(hdrop, i, pathDropped, MAX_PATH); ::DragQueryFile(hdrop, i, pathDropped, MAX_PATH);
if (::PathIsDirectory(pathDropped)) if (doesDirectoryExist(pathDropped))
{ {
size_t len = lstrlen(pathDropped); size_t len = lstrlen(pathDropped);
if (len > 0 && pathDropped[len - 1] != wchar_t('\\')) if (len > 0 && pathDropped[len - 1] != wchar_t('\\'))
@ -7040,7 +7041,7 @@ void Notepad_plus::setWorkingDir(const wchar_t *dir)
{ {
params.setWorkingDir(NULL); params.setWorkingDir(NULL);
} }
else if (dir && PathIsDirectory(dir)) else if (dir && doesDirectoryExist(dir))
{ {
params.setWorkingDir(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 #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 #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 #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 #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 #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."}, {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 #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 #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 #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 #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 #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"}, {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"},

View File

@ -595,7 +595,7 @@ void Notepad_plus::command(int id)
fullFilePath += L"\""; fullFilePath += L"\"";
if (id == IDM_EDIT_OPENINFOLDER || 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); ::ShellExecute(hwnd, L"open", cmd2Exec, fullFilePath.c_str(), L".", SW_SHOW);
} }
else // Relative file path - need concatenate with current full file path else // Relative file path - need concatenate with current full file path

View File

@ -262,7 +262,8 @@ BufferID Notepad_plus::doOpen(const wstring& fileName, bool isRecursive, bool is
} }
bool isSnapshotMode = backupFileName != NULL && doesFileExist(backupFileName); 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()); wcscpy_s(longFileName, targetFileName.c_str());
} }
@ -301,13 +302,13 @@ BufferID Notepad_plus::doOpen(const wstring& fileName, bool isRecursive, bool is
return foundBufID; return foundBufID;
} }
if (isFileSession(longFileName) && doesFileExist(longFileName)) if (isFileSession(longFileName) && longFileNameExists)
{ {
fileLoadSession(longFileName); fileLoadSession(longFileName);
return BUFFER_INVALID; return BUFFER_INVALID;
} }
if (isFileWorkspace(longFileName) && doesFileExist(longFileName)) if (isFileWorkspace(longFileName) && longFileNameExists)
{ {
nppParam.setWorkSpaceFilePath(0, longFileName); nppParam.setWorkSpaceFilePath(0, longFileName);
// This line switches to Project Panel 1 while starting up Npp // 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; return BUFFER_INVALID;
} }
#ifndef _WIN64
bool isWow64Off = false; bool isWow64Off = false;
if (!doesFileExist(longFileName)) if (!longFileNameExists)
{ {
nppParam.safeWow64EnableWow64FsRedirection(FALSE); nppParam.safeWow64EnableWow64FsRedirection(FALSE);
isWow64Off = true; isWow64Off = true;
} }
#endif
bool globbing; bool globbing;
if (isRawFileName) if (isRawFileName)
@ -388,11 +391,13 @@ BufferID Notepad_plus::doOpen(const wstring& fileName, bool isRecursive, bool is
if (!isCreateFileSuccessful) if (!isCreateFileSuccessful)
{ {
#ifndef _WIN64
if (isWow64Off) if (isWow64Off)
{ {
nppParam.safeWow64EnableWow64FsRedirection(TRUE); nppParam.safeWow64EnableWow64FsRedirection(TRUE);
isWow64Off = false; isWow64Off = false;
} }
#endif
return BUFFER_INVALID; return BUFFER_INVALID;
} }
} }
@ -477,7 +482,7 @@ BufferID Notepad_plus::doOpen(const wstring& fileName, bool isRecursive, bool is
} }
else else
{ {
if (globbing || ::PathIsDirectory(targetFileName.c_str())) if (globbing || doesDirectoryExist(targetFileName.c_str()))
{ {
vector<wstring> fileNames; vector<wstring> fileNames;
vector<wstring> patterns; vector<wstring> patterns;
@ -542,12 +547,13 @@ BufferID Notepad_plus::doOpen(const wstring& fileName, bool isRecursive, bool is
_pluginsManager.notify(&scnN); _pluginsManager.notify(&scnN);
} }
} }
#ifndef _WIN64
if (isWow64Off) if (isWow64Off)
{ {
nppParam.safeWow64EnableWow64FsRedirection(TRUE); nppParam.safeWow64EnableWow64FsRedirection(TRUE);
//isWow64Off = false; //isWow64Off = false;
} }
#endif
return buffer; return buffer;
} }
@ -658,13 +664,24 @@ bool Notepad_plus::doSave(BufferID id, const wchar_t * filename, bool isCopy)
::MessageBox(_pPublicInterface->getHSelf(), errorMessage.c_str(), L"Save failed", MB_OK | MB_ICONWARNING); ::MessageBox(_pPublicInterface->getHSelf(), errorMessage.c_str(), L"Save failed", MB_OK | MB_ICONWARNING);
} }
else if (res == SavingStatus::SaveOpenFailed) else if (res == SavingStatus::SaveOpenFailed)
{
Buffer* buf = MainFileManager.getBufferByID(id);
if (buf->isFromNetwork())
{
_nativeLangSpeaker.messageBox("FileLockedWarning",
_pPublicInterface->getHSelf(),
L"Please check whether the network where the file is located is connected.",
L"Save failed",
MB_OK | MB_ICONWARNING);
}
else
{ {
if (_isAdministrator) if (_isAdministrator)
{ {
// Already in admin mode? File is probably locked. // Already in admin mode? File is probably locked.
_nativeLangSpeaker.messageBox("FileLockedWarning", _nativeLangSpeaker.messageBox("FileLockedWarning",
_pPublicInterface->getHSelf(), _pPublicInterface->getHSelf(),
L"Please check whether if this file is opened in another program", L"Please check whether if this file is opened in another program.",
L"Save failed", L"Save failed",
MB_OK | MB_ICONWARNING); MB_OK | MB_ICONWARNING);
} }
@ -724,8 +741,7 @@ bool Notepad_plus::doSave(BufferID id, const wchar_t * filename, bool isCopy)
wchar_t nppFullPath[MAX_PATH]{}; wchar_t nppFullPath[MAX_PATH]{};
::GetModuleFileName(NULL, nppFullPath, MAX_PATH); ::GetModuleFileName(NULL, nppFullPath, MAX_PATH);
BufferID bufferID = _pEditView->getCurrentBufferID(); Buffer* buf = MainFileManager.getBufferByID(id);
Buffer * buf = MainFileManager.getBufferByID(bufferID);
//process the fileNamePath into LRF //process the fileNamePath into LRF
wstring fileNamePath = buf->getFullPathName(); wstring fileNamePath = buf->getFullPathName();
@ -753,6 +769,7 @@ bool Notepad_plus::doSave(BufferID id, const wchar_t * filename, bool isCopy)
} }
} }
}
if (res == SavingStatus::SaveOK && _pFuncList && (!_pFuncList->isClosed()) && _pFuncList->isVisible()) if (res == SavingStatus::SaveOK && _pFuncList && (!_pFuncList->isClosed()) && _pFuncList->isVisible())
{ {
@ -791,26 +808,33 @@ void Notepad_plus::doClose(BufferID id, int whichOne, bool doDeleteBackup)
wstring fileFullPath; wstring fileFullPath;
if (!buf->isUntitled()) if (!buf->isUntitled())
{ {
// 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(); const wchar_t *fn = buf->getFullPathName();
if (!doesFileExist(fn)) 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
NppParameters& nppParam = NppParameters::getInstance();
bool isWow64Off = false;
if (!fileExists)
{ {
nppParam.safeWow64EnableWow64FsRedirection(FALSE); nppParam.safeWow64EnableWow64FsRedirection(FALSE);
isWow64Off = true; isWow64Off = true;
fileExists = doesFileExist(fn);
} }
#endif
if (doesFileExist(buf->getFullPathName())) if (fileExists)
fileFullPath = buf->getFullPathName(); fileFullPath = fn;
#ifndef _WIN64
// We enable Wow64 system, if it was disabled // We enable Wow64 system, if it was disabled
if (isWow64Off) if (isWow64Off)
{ {
nppParam.safeWow64EnableWow64FsRedirection(TRUE); nppParam.safeWow64EnableWow64FsRedirection(TRUE);
//isWow64Off = false; //isWow64Off = false;
} }
#endif
} }
size_t nbDocs = whichOne==MAIN_VIEW?(_mainDocTab.nbItem()):(_subDocTab.nbItem()); 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); session._mainViewFiles.erase(posIt);
continue; //skip session files, not supporting recursive sessions or embedded workspace files continue; //skip session files, not supporting recursive sessions or embedded workspace files
} }
#ifndef _WIN64
bool isWow64Off = false; bool isWow64Off = false;
if (!doesFileExist(pFn)) if (!doesFileExist(pFn))
{ {
nppParam.safeWow64EnableWow64FsRedirection(FALSE); nppParam.safeWow64EnableWow64FsRedirection(FALSE);
isWow64Off = true; isWow64Off = true;
} }
#endif
if (doesFileExist(pFn)) if (doesFileExist(pFn))
{ {
if (isSnapshotMode && !session._mainViewFiles[i]._backupFilePath.empty()) 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) if (foundBufID == BUFFER_INVALID)
lastOpened = nppGUI._keepSessionAbsentFileEntries ? MainFileManager.newPlaceholderDocument(pFn, MAIN_VIEW, userCreatedSessionName) : BUFFER_INVALID; lastOpened = nppGUI._keepSessionAbsentFileEntries ? MainFileManager.newPlaceholderDocument(pFn, MAIN_VIEW, userCreatedSessionName) : BUFFER_INVALID;
} }
#ifndef _WIN64
if (isWow64Off) if (isWow64Off)
{ {
nppParam.safeWow64EnableWow64FsRedirection(TRUE); nppParam.safeWow64EnableWow64FsRedirection(TRUE);
isWow64Off = false; isWow64Off = false;
} }
#endif
if (lastOpened != BUFFER_INVALID) if (lastOpened != BUFFER_INVALID)
{ {
showView(MAIN_VIEW); showView(MAIN_VIEW);
@ -2437,14 +2462,14 @@ bool Notepad_plus::loadSession(Session & session, bool isSnapshotMode, const wch
session._subViewFiles.erase(posIt); session._subViewFiles.erase(posIt);
continue; //skip session files, not supporting recursive sessions or embedded workspace files continue; //skip session files, not supporting recursive sessions or embedded workspace files
} }
#ifndef _WIN64
bool isWow64Off = false; bool isWow64Off = false;
if (!doesFileExist(pFn)) if (!doesFileExist(pFn))
{ {
nppParam.safeWow64EnableWow64FsRedirection(FALSE); nppParam.safeWow64EnableWow64FsRedirection(FALSE);
isWow64Off = true; isWow64Off = true;
} }
#endif
if (doesFileExist(pFn)) if (doesFileExist(pFn))
{ {
//check if already open in main. If so, clone //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) if (foundBufID == BUFFER_INVALID)
lastOpened = nppGUI._keepSessionAbsentFileEntries ? MainFileManager.newPlaceholderDocument(pFn, SUB_VIEW, userCreatedSessionName) : BUFFER_INVALID; lastOpened = nppGUI._keepSessionAbsentFileEntries ? MainFileManager.newPlaceholderDocument(pFn, SUB_VIEW, userCreatedSessionName) : BUFFER_INVALID;
} }
#ifndef _WIN64
if (isWow64Off) if (isWow64Off)
{ {
nppParam.safeWow64EnableWow64FsRedirection(TRUE); nppParam.safeWow64EnableWow64FsRedirection(TRUE);
isWow64Off = false; isWow64Off = false;
} }
#endif
if (lastOpened != BUFFER_INVALID) if (lastOpened != BUFFER_INVALID)
{ {

View File

@ -1244,7 +1244,7 @@ bool NppParameters::load()
// //
if (!_cmdSettingsDir.empty()) if (!_cmdSettingsDir.empty())
{ {
if (!::PathIsDirectory(_cmdSettingsDir.c_str())) if (!doesDirectoryExist(_cmdSettingsDir.c_str()))
{ {
// The following text is not translatable. // The following text is not translatable.
// _pNativeLangSpeaker is initialized AFTER _userPath being dterminated because nativeLang.xml is from from _userPath. // _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) void NppParameters::safeWow64EnableWow64FsRedirection(BOOL Wow64FsEnableRedirection)
{ {
HMODULE kernel = GetModuleHandle(L"kernel32"); HMODULE kernel = GetModuleHandle(L"kernel32");
@ -8678,6 +8679,7 @@ void NppParameters::safeWow64EnableWow64FsRedirection(BOOL Wow64FsEnableRedirect
} }
} }
} }
#endif
void NppParameters::setUdlXmlDirtyFromIndex(size_t i) void NppParameters::setUdlXmlDirtyFromIndex(size_t i)
{ {

View File

@ -1759,7 +1759,9 @@ public:
std::wstring getWinVerBitStr() const; std::wstring getWinVerBitStr() const;
FindHistory & getFindHistory() {return _findHistory;}; FindHistory & getFindHistory() {return _findHistory;};
bool _isFindReplacing = false; // an on the fly variable for find/replace functions bool _isFindReplacing = false; // an on the fly variable for find/replace functions
#ifndef _WIN64
void safeWow64EnableWow64FsRedirection(BOOL Wow64FsEnableRedirection); void safeWow64EnableWow64FsRedirection(BOOL Wow64FsEnableRedirection);
#endif
LocalizationSwitcher & getLocalizationSwitcher() { LocalizationSwitcher & getLocalizationSwitcher() {
return _localizationSwitcher; return _localizationSwitcher;

View File

@ -142,7 +142,7 @@ void Buffer::updateTimeStamp()
{ {
FILETIME timeStampLive {}; FILETIME timeStampLive {};
WIN32_FILE_ATTRIBUTE_DATA attributes{}; WIN32_FILE_ATTRIBUTE_DATA attributes{};
if (GetFileAttributesEx(_fullPathName.c_str(), GetFileExInfoStandard, &attributes) != 0) if (getFileAttributesExWaitSec(_fullPathName.c_str(), &attributes) != FALSE)
{ {
timeStampLive = attributes.ftLastWriteTime; timeStampLive = attributes.ftLastWriteTime;
} }
@ -194,6 +194,7 @@ void Buffer::setFileName(const wchar_t *fn)
_fullPathName = fn; _fullPathName = fn;
_fileName = PathFindFileName(_fullPathName.c_str()); _fileName = PathFindFileName(_fullPathName.c_str());
_isFromNetwork = PathIsNetworkPath(fn);
// for _lang // for _lang
LangType determinatedLang = L_TEXT; LangType determinatedLang = L_TEXT;
@ -259,15 +260,19 @@ bool Buffer::checkFileState() // returns true if the status has been changed (it
return false; return false;
WIN32_FILE_ATTRIBUTE_DATA attributes{}; WIN32_FILE_ATTRIBUTE_DATA attributes{};
bool isWow64Off = false;
NppParameters& nppParam = NppParameters::getInstance(); NppParameters& nppParam = NppParameters::getInstance();
bool fileExists = doesFileExist(_fullPathName.c_str()); bool fileExists = doesFileExist(_fullPathName.c_str());
#ifndef _WIN64
bool isWow64Off = false;
if (!fileExists) if (!fileExists)
{ {
nppParam.safeWow64EnableWow64FsRedirection(FALSE); nppParam.safeWow64EnableWow64FsRedirection(FALSE);
isWow64Off = true; isWow64Off = true;
fileExists = doesFileExist(_fullPathName.c_str());
} }
#endif
bool isOK = false; 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 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); doNotify(BufferChangeStatus | BufferChangeReadonly | BufferChangeTimestamp);
isOK = true; isOK = true;
} }
else if (_currentStatus == DOC_DELETED && fileExists) else if (_currentStatus == DOC_DELETED && fileExists) //document has returned from its grave
{ //document has returned from its grave {
if (GetFileAttributesEx(_fullPathName.c_str(), GetFileExInfoStandard, &attributes) != 0) if (GetFileAttributesEx(_fullPathName.c_str(), GetFileExInfoStandard, &attributes) != 0) // fileExists so it's safe to call GetFileAttributesEx directly
{ {
_isFileReadOnly = attributes.dwFileAttributes & FILE_ATTRIBUTE_READONLY; _isFileReadOnly = attributes.dwFileAttributes & FILE_ATTRIBUTE_READONLY;
@ -306,7 +311,7 @@ bool Buffer::checkFileState() // returns true if the status has been changed (it
isOK = true; 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 int mask = 0; //status always 'changes', even if from modified to modified
bool isFileReadOnly = attributes.dwFileAttributes & FILE_ATTRIBUTE_READONLY; 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; return false;
} }
#ifndef _WIN64
if (isWow64Off) if (isWow64Off)
{ {
nppParam.safeWow64EnableWow64FsRedirection(TRUE); nppParam.safeWow64EnableWow64FsRedirection(TRUE);
} }
#endif
return isOK; return isOK;
} }
@ -420,8 +427,6 @@ wstring Buffer::getTimeString(FILETIME rawtime) const
wstring Buffer::getFileTime(fileTimeType ftt) const wstring Buffer::getFileTime(fileTimeType ftt) const
{ {
wstring filePath;
WIN32_FILE_ATTRIBUTE_DATA attributes{}; WIN32_FILE_ATTRIBUTE_DATA attributes{};
if (GetFileAttributesEx(_currentStatus == DOC_UNNAMED ? _backupFileName.c_str() : _fullPathName.c_str(), GetFileExInfoStandard, &attributes) != 0) 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(); const wchar_t* currentBufFilePath = buffer->getFullPathName();
ULARGE_INTEGER freeBytesForUser; ULARGE_INTEGER freeBytesForUser;
BOOL getFreeSpaceRes = ::GetDiskFreeSpaceExW(dirDest, &freeBytesForUser, nullptr, nullptr); BOOL getFreeSpaceSuccessful = getDiskFreeSpaceWaitSec(dirDest, &freeBytesForUser);
if (getFreeSpaceRes != FALSE) if (getFreeSpaceSuccessful)
{ {
int64_t fileSize = buffer->getFileLength(); 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" if (fileSize >= 0 && lstrcmp(fullpath, currentBufFilePath) == 0) // if file to save does exist, and it's an operation "Save" but not "Save As"

View File

@ -186,6 +186,8 @@ public:
bool isUntitled() const { return ((_currentStatus & DOC_UNNAMED) == DOC_UNNAMED); } bool isUntitled() const { return ((_currentStatus & DOC_UNNAMED) == DOC_UNNAMED); }
bool isFromNetwork() const { return _isFromNetwork; }
bool isInaccessible() const { return _isInaccessible; } bool isInaccessible() const { return _isInaccessible; }
void setInaccessibility(bool val) { _isInaccessible = val; } void setInaccessibility(bool val) { _isInaccessible = val; }
@ -279,7 +281,7 @@ public:
std::wstring tabCreatedTimeString() const { return _tabCreatedTimeString; } std::wstring tabCreatedTimeString() const { return _tabCreatedTimeString; }
void setTabCreatedTimeStringFromBakFile() { 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 _tabCreatedTimeString = getFileTime(Buffer::ft_created); // while DOC_UNNAMED, getFileTime will retrieve time from backup file
} }
void setTabCreatedTimeStringWithCurrentTime() { void setTabCreatedTimeStringWithCurrentTime() {
@ -389,6 +391,7 @@ private:
UniMode _unicodeMode = uniUTF8; UniMode _unicodeMode = uniUTF8;
int _encoding = -1; int _encoding = -1;
bool _isUserReadOnly = false; bool _isUserReadOnly = false;
bool _isFromNetwork = false;
bool _needLexer = false; // new buffers do not need lexing, Scintilla takes care of that bool _needLexer = false; // new buffers do not need lexing, Scintilla takes care of that
//these properties have to be duplicated because of multiple references //these properties have to be duplicated because of multiple references

View File

@ -2648,7 +2648,7 @@ intptr_t CALLBACK FindReplaceDlg::run_dlgProc(UINT message, WPARAM wParam, LPARA
currPath = buf->getFullPathName(); currPath = buf->getFullPathName();
PathRemoveFileSpec(currPath); PathRemoveFileSpec(currPath);
} }
if (currPath.empty() || !PathIsDirectory(currPath.c_str())) if (currPath.empty() || !doesDirectoryExist(currPath.c_str()))
currPath = NppParameters::getInstance().getWorkingDir(); currPath = NppParameters::getInstance().getWorkingDir();
::SetDlgItemText(_hSelf, IDD_FINDINFILES_DIR_COMBO, currPath.c_str()); ::SetDlgItemText(_hSelf, IDD_FINDINFILES_DIR_COMBO, currPath.c_str());
} }

View File

@ -1235,7 +1235,7 @@ bool FileBrowser::addToTree(FilesToChange & group, HTREEITEM node)
// Not found, good - Action // Not found, good - Action
for (auto & file : group._files) { 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); SortingData4lParam* customData = new SortingData4lParam(L"", file, true);
sortingDataArray.push_back(customData); sortingDataArray.push_back(customData);
@ -1468,7 +1468,7 @@ bool FolderInfo::addToStructure(wstring & fullpath, std::vector<wstring> linarPa
{ {
fullpath += L"\\"; fullpath += L"\\";
fullpath += linarPathArray[0]; fullpath += linarPathArray[0];
if (PathIsDirectory(fullpath.c_str())) if (doesDirectoryExist(fullpath.c_str()))
{ {
// search in folders, if found - no good // search in folders, if found - no good
for (const auto& folder : _subFolders) for (const auto& folder : _subFolders)

View File

@ -16,7 +16,6 @@
#include <shobjidl.h> #include <shobjidl.h>
#include <shlwapi.h> // PathIsDirectory
#ifdef __MINGW32__ #ifdef __MINGW32__
#include <cwchar> #include <cwchar>
#endif #endif
@ -142,7 +141,7 @@ namespace // anonymous
HRESULT hr = SHCreateItemFromParsingName(path, HRESULT hr = SHCreateItemFromParsingName(path,
nullptr, nullptr,
IID_PPV_ARGS(&shellItem)); IID_PPV_ARGS(&shellItem));
if (SUCCEEDED(hr) && shellItem && !::PathIsDirectory(path)) if (SUCCEEDED(hr) && shellItem && !::doesDirectoryExist(path))
{ {
com_ptr<IShellItem> parentItem; com_ptr<IShellItem> parentItem;
hr = shellItem->GetParent(&parentItem); hr = shellItem->GetParent(&parentItem);
@ -506,7 +505,7 @@ private:
expandEnv(fileName); expandEnv(fileName);
bool nameChanged = transformPath(fileName); bool nameChanged = transformPath(fileName);
// Update the controls. // Update the controls.
if (!::PathIsDirectory(getAbsPath(fileName).c_str())) if (doesDirectoryExist(getAbsPath(fileName).c_str()))
{ {
// Name is a file path. // Name is a file path.
// Add file extension if missing. // Add file extension if missing.