Browse Source

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
pull/15670/head
Don Ho 2 months ago
parent
commit
f884a39dd4
  1. 189
      PowerEditor/src/MISC/Common/Common.cpp
  2. 12
      PowerEditor/src/MISC/Common/Common.h
  3. 12
      PowerEditor/src/Notepad_plus.cpp
  4. 2
      PowerEditor/src/NppCommands.cpp
  5. 196
      PowerEditor/src/NppIO.cpp
  6. 4
      PowerEditor/src/Parameters.cpp
  7. 2
      PowerEditor/src/Parameters.h
  8. 27
      PowerEditor/src/ScintillaComponent/Buffer.cpp
  9. 5
      PowerEditor/src/ScintillaComponent/Buffer.h
  10. 2
      PowerEditor/src/ScintillaComponent/FindReplaceDlg.cpp
  11. 4
      PowerEditor/src/WinControls/FileBrowser/fileBrowser.cpp
  12. 5
      PowerEditor/src/WinControls/OpenSaveFileDialog/CustomFileDialog.cpp

189
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<GetAttrParamResult*>(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<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;
}

12
PowerEditor/src/MISC/Common/Common.h

@ -25,7 +25,8 @@
#include <algorithm>
#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;
@ -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);
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);

12
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"},

2
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

196
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<wstring> fileNames;
vector<wstring> 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)
{

4
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)
{

2
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;

27
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"

5
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

2
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());
}

4
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<wstring> 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)

5
PowerEditor/src/WinControls/OpenSaveFileDialog/CustomFileDialog.cpp

@ -16,7 +16,6 @@
#include <shobjidl.h>
#include <shlwapi.h> // PathIsDirectory
#ifdef __MINGW32__
#include <cwchar>
#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<IShellItem> 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.

Loading…
Cancel
Save