From 87efdf50848165fb8004b371970c68047d4911c3 Mon Sep 17 00:00:00 2001 From: xomx Date: Wed, 30 Aug 2023 16:34:43 +0200 Subject: [PATCH] Add Win10+ OS Restart-app feature It will allow for the Notepad++ to be a "restartable app", like some other SW can do today (eg Google Chrome, Mozilla Firefox or all the Microsoft UWP apps). This is to create a seamless experience wherein, if you have to reboot your PC, you can now pick back up from where you left off and resume being productive. The OS app-restart feature needs at least Windows 10 (20H1) and the user has to switch on the "Restart apps" in the system Settings (subsection Accounts > Sign-in options). Implemented as per previous discussion: https://github.com/notepad-plus-plus/notepad-plus-plus/issues/12541#issuecomment-1332662024 To disable this feature, add "noRestartAutomatically.xml" into "%APPDATA%\Notepad++\" or Notepad++ installation directory. Fix #9722, fix #11721, fix #11934, close #14074 --- PowerEditor/src/NppBigSwitch.cpp | 117 +++++++++++++++++++++++++++++-- PowerEditor/src/Parameters.cpp | 18 ++++- PowerEditor/src/Parameters.h | 4 ++ 3 files changed, 133 insertions(+), 6 deletions(-) diff --git a/PowerEditor/src/NppBigSwitch.cpp b/PowerEditor/src/NppBigSwitch.cpp index e7cb8f24e44..4aef685d277 100644 --- a/PowerEditor/src/NppBigSwitch.cpp +++ b/PowerEditor/src/NppBigSwitch.cpp @@ -18,6 +18,7 @@ #include #include #include // for EnableThemeDialogTexture +#include #include "Notepad_plus_Window.h" #include "TaskListDlg.h" #include "ImageListSet.h" @@ -57,6 +58,114 @@ struct SortTaskListPred final } }; +// app-restart feature needs Win10 20H1+ (builds 18963+), but it was quietly introduced earlier in the Fall Creators Update (b1709+) +bool SetOSAppRestart() +{ + NppParameters& nppParam = NppParameters::getInstance(); + if (nppParam.getWinVersion() < WV_WIN10) + return false; // unsupported + + bool bRet = false; + bool bUnregister = nppParam.isRegForOSAppRestartDisabled(); + + generic_string nppIssueLog; + if (nppParam.doNppLogNulContentCorruptionIssue()) + { + generic_string issueFn = nppLogNulContentCorruptionIssue; + issueFn += TEXT(".log"); + nppIssueLog = nppParam.getUserPath(); + pathAppend(nppIssueLog, issueFn); + } + + WCHAR wszCmdLine[RESTART_MAX_CMD_LINE] = { 0 }; + DWORD cchCmdLine = _countof(wszCmdLine); + DWORD dwPreviousFlags = 0; + HRESULT hr = ::GetApplicationRestartSettings(::GetCurrentProcess(), wszCmdLine, &cchCmdLine, &dwPreviousFlags); + if (bUnregister) + { + // unregistering (disabling) request + + if (hr == HRESULT_FROM_WIN32(ERROR_NOT_FOUND)) + { + // has not been registered before, nothing to do here + bRet = true; + } + else + { + if (hr == S_OK) + { + // has been already registered before, try to unregister + if (::UnregisterApplicationRestart() == S_OK) + { + bRet = true; + } + else + { + if (nppParam.doNppLogNulContentCorruptionIssue()) + { + std::string msg = "ERROR: UnregisterApplicationRestart WINAPI failed! (HRESULT: "; + msg += std::format("{:#010x}", hr); + msg += ")"; + writeLog(nppIssueLog.c_str(), msg.c_str()); + } + } + } + else + { + if (nppParam.doNppLogNulContentCorruptionIssue()) + { + std::string msg = "ERROR: GetApplicationRestartSettings WINAPI failed! (HRESULT: "; + msg += std::format("{:#010x}", hr); + msg += ")"; + writeLog(nppIssueLog.c_str(), msg.c_str()); + } + } + } + } + else + { + // registering request + + if (hr == S_OK) + ::UnregisterApplicationRestart(); // remove a previous registration 1st + + if (nppParam.getCmdLineString().length() >= RESTART_MAX_CMD_LINE) + { + if (nppParam.doNppLogNulContentCorruptionIssue()) + { + std::string msg = "WARNING: Skipping the RegisterApplicationRestart WINAPI call because of the cmdline length exceeds the RESTART_MAX_CMD_LINE! \n(current cmdline length: "; + msg += std::to_string(nppParam.getCmdLineString().length()); + msg += " chars)"; + writeLog(nppIssueLog.c_str(), msg.c_str()); + } + } + else + { + // do not restart the process: + // RESTART_NO_CRASH (1) ... for termination due to application crashes + // RESTART_NO_HANG (2) ... for termination due to application hangs + // RESTART_NO_PATCH (4) ... for termination due to patch installations + // RESTART_NO_REBOOT (8) ... when the system is rebooted + hr = ::RegisterApplicationRestart(nppParam.getCmdLineString().c_str(), RESTART_NO_CRASH | RESTART_NO_HANG | RESTART_NO_PATCH); + if (hr == S_OK) + { + bRet = true; + } + else + { + if (nppParam.doNppLogNulContentCorruptionIssue()) + { + std::string msg = "ERROR: RegisterApplicationRestart WINAPI failed! (HRESULT: "; + msg += std::format("{:#010x}", hr); + msg += ")"; + writeLog(nppIssueLog.c_str(), msg.c_str()); + } + } + } + } + + return bRet; +} LRESULT CALLBACK Notepad_plus_Window::Notepad_plus_Proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { @@ -115,6 +224,8 @@ LRESULT Notepad_plus_Window::runProc(HWND hwnd, UINT message, WPARAM wParam, LPA SWP_FRAMECHANGED); } + SetOSAppRestart(); + return lRet; } catch (std::exception& ex) @@ -2303,10 +2414,8 @@ LRESULT Notepad_plus::process(HWND hwnd, UINT message, WPARAM wParam, LPARAM lPa } } - // TODO: here is the last opportunity to call the following WINAPI in a possible future version of the Notepad++ - // - // flags RESTART_NO_PATCH and RESTART_NO_REBOOT are not set, so we should be restarted if terminated by an update or restart - //::RegisterApplicationRestart(restartCommandLine.c_str(), RESTART_NO_CRASH | RESTART_NO_HANG); + // NOTE: This should be the last possible place to eventually register Notepad++ for the app-restart OS feature, + // but unfortunately it doesn't work here. return TRUE; // nowadays, with the monstrous Win10+ Windows Update behind, is futile to try to interrupt the shutdown by returning FALSE here // (if one really needs so, there is the ShutdownBlockReasonCreate WINAPI for the rescue ...) diff --git a/PowerEditor/src/Parameters.cpp b/PowerEditor/src/Parameters.cpp index 1a995418f8c..c47dafe0b43 100644 --- a/PowerEditor/src/Parameters.cpp +++ b/PowerEditor/src/Parameters.cpp @@ -1604,8 +1604,22 @@ bool NppParameters::load() _doNppLogNulContentCorruptionIssue = (PathFileExists(filePath2.c_str()) == TRUE); } - - + //-------------------------------------------------------------// + // noRestartAutomatically.xml // + // This empty xml file is optional - user adds this empty file // + // manually in order to prevent Notepad++ registration // + // for the Win10+ OS app-restart feature. // + //-------------------------------------------------------------// + filePath = _nppPath; + std::wstring noRegForOSAppRestartTrigger = L"noRestartAutomatically.xml"; + pathAppend(filePath, noRegForOSAppRestartTrigger); + _isRegForOSAppRestartDisabled = (::PathFileExists(filePath.c_str()) == TRUE); + if (!_isRegForOSAppRestartDisabled) + { + filePath = _userPath; + pathAppend(filePath, noRegForOSAppRestartTrigger); + _isRegForOSAppRestartDisabled = (::PathFileExists(filePath.c_str()) == TRUE); + } return isAllLaoded; } diff --git a/PowerEditor/src/Parameters.h b/PowerEditor/src/Parameters.h index f0bdeafb249..466ee18fac8 100644 --- a/PowerEditor/src/Parameters.h +++ b/PowerEditor/src/Parameters.h @@ -283,6 +283,7 @@ struct CmdLineParamsDTO bool _isRecursive = false; bool _openFoldersAsWorkspace = false; bool _monitorFiles = false; + bool _isRestartedByOS = false; intptr_t _line2go = 0; intptr_t _column2go = 0; @@ -851,6 +852,7 @@ struct NppGUI final std::wstring _definedSessionExt; std::wstring _definedWorkspaceExt; + // items with no Notepad++ GUI to set std::wstring _commandLineInterpreter = CMD_INTERPRETER; struct AutoUpdateOptions @@ -1863,6 +1865,7 @@ class NppParameters final bool isAdmin() const { return _isAdminMode; } bool regexBackward4PowerUser() const { return _findHistory._regexBackward4PowerUser; } bool isSelectFgColorEnabled() const { return _isSelectFgColorEnabled; }; + bool isRegForOSAppRestartDisabled() const { return _isRegForOSAppRestartDisabled; }; private: bool _isAnyShortcutModified = false; @@ -1929,6 +1932,7 @@ class NppParameters final bool _isAdminMode = false; bool _isSelectFgColorEnabled = false; + bool _isRegForOSAppRestartDisabled = false; bool _doNppLogNetworkDriveIssue = false;