Skip to content

Commit

Permalink
Add Win10+ OS Restart-app feature
Browse files Browse the repository at this point in the history
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: #12541 (comment)

To disable this feature, add "noRestartAutomatically.xml" into "%APPDATA%\Notepad++\" or Notepad++ installation directory.

Fix #9722, fix #11721, fix #11934, close #14074
  • Loading branch information
xomx authored and donho committed Sep 21, 2023
1 parent 39001d7 commit 87efdf5
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 6 deletions.
117 changes: 113 additions & 4 deletions PowerEditor/src/NppBigSwitch.cpp
Expand Up @@ -18,6 +18,7 @@
#include <algorithm>
#include <shlwapi.h>
#include <uxtheme.h> // for EnableThemeDialogTexture
#include <format>
#include "Notepad_plus_Window.h"
#include "TaskListDlg.h"
#include "ImageListSet.h"
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 ...)
Expand Down
18 changes: 16 additions & 2 deletions PowerEditor/src/Parameters.cpp
Expand Up @@ -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;
}
Expand Down
4 changes: 4 additions & 0 deletions PowerEditor/src/Parameters.h
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -1929,6 +1932,7 @@ class NppParameters final
bool _isAdminMode = false;

bool _isSelectFgColorEnabled = false;
bool _isRegForOSAppRestartDisabled = false;

bool _doNppLogNetworkDriveIssue = false;

Expand Down

0 comments on commit 87efdf5

Please sign in to comment.