From 11dd30268932bf635b3dec40eeac2248fd316c61 Mon Sep 17 00:00:00 2001 From: ge0rdi Date: Tue, 20 Dec 2022 20:22:36 +0100 Subject: [PATCH] Properly handle Windows updates during shutdown Windows updates are applied on shutdown/reboot no matter of flags used in ExitWindowsEx (or InitiateShutdown). Windows start menu shutdown code handles updates in special way. If there are updates prepared and shutdown/reboot is selected it will use `UpdateSessionOrchestrator` object to dismiss updates and carry out the command. We will now do similar thing. Fixes #1250 --- Src/StartMenu/StartMenuDLL/MenuCommands.cpp | 156 ++++++++++++++++++++ 1 file changed, 156 insertions(+) diff --git a/Src/StartMenu/StartMenuDLL/MenuCommands.cpp b/Src/StartMenu/StartMenuDLL/MenuCommands.cpp index 3ce4fac..677c883 100644 --- a/Src/StartMenu/StartMenuDLL/MenuCommands.cpp +++ b/Src/StartMenu/StartMenuDLL/MenuCommands.cpp @@ -665,6 +665,159 @@ private: bool m_bArmed; }; +// Win10 +MIDL_INTERFACE("833EE9A0-2999-432C-8EF2-87A8EC2D748D") +IUxUpdateManager_Win10 : public IUnknown +{ + STDMETHOD(GetUxStateVariableBOOL)(enum UxUpdateStateVariable, int*, int*); + STDMETHOD(GetUxStateVariableDWORD)(UxUpdateStateVariable, DWORD*, int*); + STDMETHOD(GetUxStateVariableSYSTEMTIME)(UxUpdateStateVariable, SYSTEMTIME*, int*); + STDMETHOD(SetUxStateVariableBOOL)(UxUpdateStateVariable, int); + STDMETHOD(SetUxStateVariableDWORD)(UxUpdateStateVariable, DWORD); + STDMETHOD(SetUxStateVariableSYSTEMTIME)(UxUpdateStateVariable, SYSTEMTIME); + STDMETHOD(DeleteUxStateVariable)(UxUpdateStateVariable); + STDMETHOD(GetNextRebootTaskRunTime)(int*, SYSTEMTIME*); + STDMETHOD(CreateRebootTasks)(const wchar_t*, SYSTEMTIME); + STDMETHOD(CreateUpdateResultsTaskSchedule)(void); + STDMETHOD(CreateMigrationResultsTaskSchedule)(void); + STDMETHOD(CreateUpdateLogonNotificationTaskSchedule)(void); + STDMETHOD(CreateUpdateNotificationTaskSchedule)(SYSTEMTIME); + STDMETHOD(CreateLogonRebootTaskSchedule)(void); + STDMETHOD(DidUXRebootTaskWakeUpDevice)(int*); + STDMETHOD(RemoveUpdateResultsTaskSchedule)(void); + STDMETHOD(RemoveLogonRebootTaskSchedule)(void); + STDMETHOD(RemoveMigrationResultsTaskSchedule)(void); + STDMETHOD(EnableRebootTasks)(void); + STDMETHOD(DisableRebootTasks)(void); + STDMETHOD(ValidateAndRecoverRebootTasks)(void); + STDMETHOD(RebootToCompleteInstall)(DWORD, int, DWORD*, short, short, DWORD); + STDMETHOD(IsRestartAllowed)(DWORD, int, DWORD, int*); + STDMETHOD(GetIsWaaSOutOfDate)(DWORD, int, int, int*, DWORD*); + STDMETHOD(GetWaaSHoursOutOfDate)(int, int, DWORD*); + STDMETHOD(GetCachedPolicy)(DWORD, VARIANT*, DWORD*, DWORD*); + STDMETHOD(GetEnterpriseCachedPolicy)(DWORD, VARIANT*, DWORD*, DWORD*); + STDMETHOD(GetCachedSettingValue)(DWORD, short, VARIANT*); + STDMETHOD(GetOptInToMU)(int*); + STDMETHOD(SetOptInToMU)(int); + STDMETHOD(SetAndModifyShutdownFlags)(DWORD, DWORD*); + STDMETHOD(GetIsFlightingEnabled)(int*); + STDMETHOD(GetIsCTA)(int*); + STDMETHOD(NotifyStateVariableChange)(void); + STDMETHOD(GetAlwaysAllowMeteredNetwork)(int*); +}; + +// Win11 +MIDL_INTERFACE("B96BA95F-9479-4656-B7A1-6F3A69091910") +IUxUpdateManager_Win11 : public IUnknown +{ + STDMETHOD(GetUxStateVariableBOOL)(enum UxUpdateStateVariable, int*, int*); + STDMETHOD(GetUxStateVariableDWORD)(UxUpdateStateVariable, DWORD*, int*); + STDMETHOD(GetUxStateVariableSYSTEMTIME)(UxUpdateStateVariable, SYSTEMTIME*, int*); + STDMETHOD(SetUxStateVariableBOOL)(UxUpdateStateVariable, int); + STDMETHOD(SetUxStateVariableDWORD)(UxUpdateStateVariable, DWORD); + STDMETHOD(SetUxStateVariableSYSTEMTIME)(UxUpdateStateVariable, SYSTEMTIME); + STDMETHOD(DeleteUxStateVariable)(UxUpdateStateVariable); + STDMETHOD(GetNextScheduledRebootTaskRunTime)(SYSTEMTIME*); + STDMETHOD(GetIsRebootTaskScheduledToRun)(int*); + STDMETHOD(CreateRebootTasks)(const wchar_t*, SYSTEMTIME); + STDMETHOD(CreateUpdateResultsTaskSchedule)(void); + STDMETHOD(CreateMigrationResultsTaskSchedule)(void); + STDMETHOD(CreateUpdateLogonNotificationTaskSchedule)(void); + STDMETHOD(CreateUpdateNotificationTaskSchedule)(SYSTEMTIME); + STDMETHOD(CreateLogonRebootTaskSchedule)(void); + STDMETHOD(DidUXRebootTaskWakeUpDevice)(int*); + STDMETHOD(RemoveUpdateResultsTaskSchedule)(void); + STDMETHOD(RemoveLogonRebootTaskSchedule)(void); + STDMETHOD(RemoveMigrationResultsTaskSchedule)(void); + STDMETHOD(EnableRebootTasks)(void); + STDMETHOD(DisableRebootTasks)(void); + STDMETHOD(ValidateAndRecoverRebootTasks)(void); + STDMETHOD(RebootToCompleteInstall)(DWORD, int, DWORD*, int, int, double); + STDMETHOD(IsRestartAllowed)(DWORD, int, double, int*); + STDMETHOD(GetIsWaaSOutOfDate)(DWORD, int, int, int*, DWORD*); + STDMETHOD(GetWaaSHoursOutOfDate)(int, int, DWORD*); + STDMETHOD(GetDeviceEndOfServiceDate)(int, int*, FILETIME*); + STDMETHOD(GetCachedPolicy)(DWORD, VARIANT*, DWORD*, DWORD*); + STDMETHOD(GetEnterpriseCachedPolicy)(DWORD, VARIANT*, DWORD*, DWORD*); + STDMETHOD(GetOptInToMU)(int*); + STDMETHOD(SetOptInToMU)(int); + STDMETHOD(SetAndModifyShutdownFlags)(DWORD, DWORD*); + STDMETHOD(GetIsFlightingEnabled)(int*); + STDMETHOD(GetIsCTA)(int*); + STDMETHOD(NotifyStateVariableChange)(void); + STDMETHOD(GetAlwaysAllowMeteredNetwork)(int*); + STDMETHOD(SetInstallAtShutdown)(int); + STDMETHOD(GetUxStateVariableValueOrDefaultBOOL)(UxUpdateStateVariable, int, int*); + STDMETHOD(GetUxStateVariableValueOrDefaultDWORD)(UxUpdateStateVariable, DWORD, DWORD*); + STDMETHOD(GetUxStateVariableValueOrDefaultSYSTEMTIME)(UxUpdateStateVariable, SYSTEMTIME, SYSTEMTIME*); + STDMETHOD(GetSuggestedRebootTime)(int, SYSTEMTIME, SYSTEMTIME*, int*); + STDMETHOD(GetSuggestedActiveHours)(DWORD, DWORD*, DWORD*, int*); + STDMETHOD(GetIsIntervalAcceptableForActiveHours)(SYSTEMTIME, SYSTEMTIME, int*); + STDMETHOD(GetSmartScheduledPredictionsAccurate)(int*); + STDMETHOD(EvaluateAndStoreRebootDowntimePrediction)(void); + STDMETHOD(GetCachedRebootDowntimePrediction)(DWORD*); + STDMETHOD(GetAlwaysAllowCTADownload)(int*); +}; + +MIDL_INTERFACE("07F3AFAC-7C8A-4CE7-A5E0-3D24EE8A77E0") +IUpdateSessionOrchestrator : public IUnknown +{ + STDMETHOD(CreateUpdateSession)(enum UpdateSessionType, const GUID&, void**); + STDMETHOD(GetCurrentActiveUpdateSessions)(class IUsoSessionCollection**); + STDMETHOD(LogTaskRunning)(const wchar_t*); + STDMETHOD(CreateUxUpdateManager)(IUnknown**); +}; + +DWORD WindowsUpdateAdjustShutdwonFlags(DWORD flags) +{ + DWORD retval = flags; + + { + // "EnhancedShutdownEnabled" value must exist if Windows updates are prepared + // otherwise there is no need to do anything + + CRegKey key; + if (key.Open(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\WindowsUpdate\\Orchestrator", KEY_READ) != ERROR_SUCCESS) + return retval; + + DWORD value; + if (key.QueryDWORDValue(L"EnhancedShutdownEnabled", value) != ERROR_SUCCESS) + return retval; + } + + // this is what standard Windows shutdown handling does inside shutdownux!UsoCommitHelper::SetAndModifyShutdownFlags + + static const GUID CLSID_UpdateSessionOrchestrator = { 0xb91d5831,0xb1bd,0x4608,{0x81,0x98,0xd7,0x2e,0x15,0x50,0x20,0xf7} }; + + CComPtr updateSessionOrchestrator; + if (SUCCEEDED(updateSessionOrchestrator.CoCreateInstance(CLSID_UpdateSessionOrchestrator, nullptr, CLSCTX_LOCAL_SERVER))) + { + CComPtr mgr; + if (SUCCEEDED(updateSessionOrchestrator->CreateUxUpdateManager(&mgr))) + { + // call to IUxUpdateManager::SetAndModifyShutdownFlags will ensure that Windows updates will be dismissed if there is no `SHUTDOWN_INSTALL_UPDATES` flag provided + // it also provides recommended shutdown flags in some cases (so we will use them as advised) + // + // the method is implemented by `UxUpdateManager::SetAndModifyShutdownFlags` in `usosvc.dll` (Win10) / `usosvcimpl.dll` (Win11) + + if (CComPtr updateManager; SUCCEEDED(mgr.QueryInterface(&updateManager))) + { + DWORD newFlags; + if (SUCCEEDED(updateManager->SetAndModifyShutdownFlags(flags, &newFlags))) + retval = newFlags; + } + else if (CComPtr updateManager; SUCCEEDED(mgr.QueryInterface(&updateManager))) + { + DWORD newFlags; + if (SUCCEEDED(updateManager->SetAndModifyShutdownFlags(flags, &newFlags))) + retval = newFlags; + } + } + } + + return retval; +} + static TOKEN_ELEVATION_TYPE GetCurrentTokenElevationType() { TOKEN_ELEVATION_TYPE retval = TokenElevationTypeDefault; @@ -740,6 +893,7 @@ static bool ExecuteShutdownCommand(TMenuID menuCommand) { if (SetShutdownPrivileges()) { + flags = WindowsUpdateAdjustShutdwonFlags(flags); InitiateShutdown(NULL, NULL, 0, flags, SHTDN_REASON_FLAG_PLANNED); } else @@ -748,6 +902,8 @@ static bool ExecuteShutdownCommand(TMenuID menuCommand) // lets try silent elevate via SystemSettingsAdminFlows (for limited admin users only) if (GetCurrentTokenElevationType() == TokenElevationTypeLimited) { + flags = WindowsUpdateAdjustShutdwonFlags(flags); + wchar_t cmdLine[32]{}; Sprintf(cmdLine, _countof(cmdLine), L"Shutdown %d %d", flags, SHTDN_REASON_FLAG_PLANNED);