Skip to content

Commit 42d8e12

Browse files
committed
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
1 parent 23b183d commit 42d8e12

File tree

1 file changed

+156
-0
lines changed

1 file changed

+156
-0
lines changed

Src/StartMenu/StartMenuDLL/MenuCommands.cpp

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,159 @@ class ExitGuard
665665
bool m_bArmed;
666666
};
667667

668+
// Win10
669+
MIDL_INTERFACE("833EE9A0-2999-432C-8EF2-87A8EC2D748D")
670+
IUxUpdateManager_Win10 : public IUnknown
671+
{
672+
STDMETHOD(GetUxStateVariableBOOL)(enum UxUpdateStateVariable, int*, int*);
673+
STDMETHOD(GetUxStateVariableDWORD)(UxUpdateStateVariable, DWORD*, int*);
674+
STDMETHOD(GetUxStateVariableSYSTEMTIME)(UxUpdateStateVariable, SYSTEMTIME*, int*);
675+
STDMETHOD(SetUxStateVariableBOOL)(UxUpdateStateVariable, int);
676+
STDMETHOD(SetUxStateVariableDWORD)(UxUpdateStateVariable, DWORD);
677+
STDMETHOD(SetUxStateVariableSYSTEMTIME)(UxUpdateStateVariable, SYSTEMTIME);
678+
STDMETHOD(DeleteUxStateVariable)(UxUpdateStateVariable);
679+
STDMETHOD(GetNextRebootTaskRunTime)(int*, SYSTEMTIME*);
680+
STDMETHOD(CreateRebootTasks)(const wchar_t*, SYSTEMTIME);
681+
STDMETHOD(CreateUpdateResultsTaskSchedule)(void);
682+
STDMETHOD(CreateMigrationResultsTaskSchedule)(void);
683+
STDMETHOD(CreateUpdateLogonNotificationTaskSchedule)(void);
684+
STDMETHOD(CreateUpdateNotificationTaskSchedule)(SYSTEMTIME);
685+
STDMETHOD(CreateLogonRebootTaskSchedule)(void);
686+
STDMETHOD(DidUXRebootTaskWakeUpDevice)(int*);
687+
STDMETHOD(RemoveUpdateResultsTaskSchedule)(void);
688+
STDMETHOD(RemoveLogonRebootTaskSchedule)(void);
689+
STDMETHOD(RemoveMigrationResultsTaskSchedule)(void);
690+
STDMETHOD(EnableRebootTasks)(void);
691+
STDMETHOD(DisableRebootTasks)(void);
692+
STDMETHOD(ValidateAndRecoverRebootTasks)(void);
693+
STDMETHOD(RebootToCompleteInstall)(DWORD, int, DWORD*, short, short, DWORD);
694+
STDMETHOD(IsRestartAllowed)(DWORD, int, DWORD, int*);
695+
STDMETHOD(GetIsWaaSOutOfDate)(DWORD, int, int, int*, DWORD*);
696+
STDMETHOD(GetWaaSHoursOutOfDate)(int, int, DWORD*);
697+
STDMETHOD(GetCachedPolicy)(DWORD, VARIANT*, DWORD*, DWORD*);
698+
STDMETHOD(GetEnterpriseCachedPolicy)(DWORD, VARIANT*, DWORD*, DWORD*);
699+
STDMETHOD(GetCachedSettingValue)(DWORD, short, VARIANT*);
700+
STDMETHOD(GetOptInToMU)(int*);
701+
STDMETHOD(SetOptInToMU)(int);
702+
STDMETHOD(SetAndModifyShutdownFlags)(DWORD, DWORD*);
703+
STDMETHOD(GetIsFlightingEnabled)(int*);
704+
STDMETHOD(GetIsCTA)(int*);
705+
STDMETHOD(NotifyStateVariableChange)(void);
706+
STDMETHOD(GetAlwaysAllowMeteredNetwork)(int*);
707+
};
708+
709+
// Win11
710+
MIDL_INTERFACE("B96BA95F-9479-4656-B7A1-6F3A69091910")
711+
IUxUpdateManager_Win11 : public IUnknown
712+
{
713+
STDMETHOD(GetUxStateVariableBOOL)(enum UxUpdateStateVariable, int*, int*);
714+
STDMETHOD(GetUxStateVariableDWORD)(UxUpdateStateVariable, DWORD*, int*);
715+
STDMETHOD(GetUxStateVariableSYSTEMTIME)(UxUpdateStateVariable, SYSTEMTIME*, int*);
716+
STDMETHOD(SetUxStateVariableBOOL)(UxUpdateStateVariable, int);
717+
STDMETHOD(SetUxStateVariableDWORD)(UxUpdateStateVariable, DWORD);
718+
STDMETHOD(SetUxStateVariableSYSTEMTIME)(UxUpdateStateVariable, SYSTEMTIME);
719+
STDMETHOD(DeleteUxStateVariable)(UxUpdateStateVariable);
720+
STDMETHOD(GetNextScheduledRebootTaskRunTime)(SYSTEMTIME*);
721+
STDMETHOD(GetIsRebootTaskScheduledToRun)(int*);
722+
STDMETHOD(CreateRebootTasks)(const wchar_t*, SYSTEMTIME);
723+
STDMETHOD(CreateUpdateResultsTaskSchedule)(void);
724+
STDMETHOD(CreateMigrationResultsTaskSchedule)(void);
725+
STDMETHOD(CreateUpdateLogonNotificationTaskSchedule)(void);
726+
STDMETHOD(CreateUpdateNotificationTaskSchedule)(SYSTEMTIME);
727+
STDMETHOD(CreateLogonRebootTaskSchedule)(void);
728+
STDMETHOD(DidUXRebootTaskWakeUpDevice)(int*);
729+
STDMETHOD(RemoveUpdateResultsTaskSchedule)(void);
730+
STDMETHOD(RemoveLogonRebootTaskSchedule)(void);
731+
STDMETHOD(RemoveMigrationResultsTaskSchedule)(void);
732+
STDMETHOD(EnableRebootTasks)(void);
733+
STDMETHOD(DisableRebootTasks)(void);
734+
STDMETHOD(ValidateAndRecoverRebootTasks)(void);
735+
STDMETHOD(RebootToCompleteInstall)(DWORD, int, DWORD*, int, int, double);
736+
STDMETHOD(IsRestartAllowed)(DWORD, int, double, int*);
737+
STDMETHOD(GetIsWaaSOutOfDate)(DWORD, int, int, int*, DWORD*);
738+
STDMETHOD(GetWaaSHoursOutOfDate)(int, int, DWORD*);
739+
STDMETHOD(GetDeviceEndOfServiceDate)(int, int*, FILETIME*);
740+
STDMETHOD(GetCachedPolicy)(DWORD, VARIANT*, DWORD*, DWORD*);
741+
STDMETHOD(GetEnterpriseCachedPolicy)(DWORD, VARIANT*, DWORD*, DWORD*);
742+
STDMETHOD(GetOptInToMU)(int*);
743+
STDMETHOD(SetOptInToMU)(int);
744+
STDMETHOD(SetAndModifyShutdownFlags)(DWORD, DWORD*);
745+
STDMETHOD(GetIsFlightingEnabled)(int*);
746+
STDMETHOD(GetIsCTA)(int*);
747+
STDMETHOD(NotifyStateVariableChange)(void);
748+
STDMETHOD(GetAlwaysAllowMeteredNetwork)(int*);
749+
STDMETHOD(SetInstallAtShutdown)(int);
750+
STDMETHOD(GetUxStateVariableValueOrDefaultBOOL)(UxUpdateStateVariable, int, int*);
751+
STDMETHOD(GetUxStateVariableValueOrDefaultDWORD)(UxUpdateStateVariable, DWORD, DWORD*);
752+
STDMETHOD(GetUxStateVariableValueOrDefaultSYSTEMTIME)(UxUpdateStateVariable, SYSTEMTIME, SYSTEMTIME*);
753+
STDMETHOD(GetSuggestedRebootTime)(int, SYSTEMTIME, SYSTEMTIME*, int*);
754+
STDMETHOD(GetSuggestedActiveHours)(DWORD, DWORD*, DWORD*, int*);
755+
STDMETHOD(GetIsIntervalAcceptableForActiveHours)(SYSTEMTIME, SYSTEMTIME, int*);
756+
STDMETHOD(GetSmartScheduledPredictionsAccurate)(int*);
757+
STDMETHOD(EvaluateAndStoreRebootDowntimePrediction)(void);
758+
STDMETHOD(GetCachedRebootDowntimePrediction)(DWORD*);
759+
STDMETHOD(GetAlwaysAllowCTADownload)(int*);
760+
};
761+
762+
MIDL_INTERFACE("07F3AFAC-7C8A-4CE7-A5E0-3D24EE8A77E0")
763+
IUpdateSessionOrchestrator : public IUnknown
764+
{
765+
STDMETHOD(CreateUpdateSession)(enum UpdateSessionType, const GUID&, void**);
766+
STDMETHOD(GetCurrentActiveUpdateSessions)(class IUsoSessionCollection**);
767+
STDMETHOD(LogTaskRunning)(const wchar_t*);
768+
STDMETHOD(CreateUxUpdateManager)(IUnknown**);
769+
};
770+
771+
DWORD WindowsUpdateAdjustShutdwonFlags(DWORD flags)
772+
{
773+
DWORD retval = flags;
774+
775+
{
776+
// "EnhancedShutdownEnabled" value must exist if Windows updates are prepared
777+
// otherwise there is no need to do anything
778+
779+
CRegKey key;
780+
if (key.Open(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\WindowsUpdate\\Orchestrator", KEY_READ) != ERROR_SUCCESS)
781+
return retval;
782+
783+
DWORD value;
784+
if (key.QueryDWORDValue(L"EnhancedShutdownEnabled", value) != ERROR_SUCCESS)
785+
return retval;
786+
}
787+
788+
// this is what standard Windows shutdown handling does inside shutdownux!UsoCommitHelper::SetAndModifyShutdownFlags
789+
790+
static const GUID CLSID_UpdateSessionOrchestrator = { 0xb91d5831,0xb1bd,0x4608,{0x81,0x98,0xd7,0x2e,0x15,0x50,0x20,0xf7} };
791+
792+
CComPtr<IUpdateSessionOrchestrator> updateSessionOrchestrator;
793+
if (SUCCEEDED(updateSessionOrchestrator.CoCreateInstance(CLSID_UpdateSessionOrchestrator, nullptr, CLSCTX_LOCAL_SERVER)))
794+
{
795+
CComPtr<IUnknown> mgr;
796+
if (SUCCEEDED(updateSessionOrchestrator->CreateUxUpdateManager(&mgr)))
797+
{
798+
// call to IUxUpdateManager::SetAndModifyShutdownFlags will ensure that Windows updates will be dismissed if there is no `SHUTDOWN_INSTALL_UPDATES` flag provided
799+
// it also provides recommended shutdown flags in some cases (so we will use them as advised)
800+
//
801+
// the method is implemented by `UxUpdateManager::SetAndModifyShutdownFlags` in `usosvc.dll` (Win10) / `usosvcimpl.dll` (Win11)
802+
803+
if (CComPtr<IUxUpdateManager_Win10> updateManager; SUCCEEDED(mgr.QueryInterface(&updateManager)))
804+
{
805+
DWORD newFlags;
806+
if (SUCCEEDED(updateManager->SetAndModifyShutdownFlags(flags, &newFlags)))
807+
retval = newFlags;
808+
}
809+
else if (CComPtr<IUxUpdateManager_Win11> updateManager; SUCCEEDED(mgr.QueryInterface(&updateManager)))
810+
{
811+
DWORD newFlags;
812+
if (SUCCEEDED(updateManager->SetAndModifyShutdownFlags(flags, &newFlags)))
813+
retval = newFlags;
814+
}
815+
}
816+
}
817+
818+
return retval;
819+
}
820+
668821
static TOKEN_ELEVATION_TYPE GetCurrentTokenElevationType()
669822
{
670823
TOKEN_ELEVATION_TYPE retval = TokenElevationTypeDefault;
@@ -740,6 +893,7 @@ static bool ExecuteShutdownCommand(TMenuID menuCommand)
740893
{
741894
if (SetShutdownPrivileges())
742895
{
896+
flags = WindowsUpdateAdjustShutdwonFlags(flags);
743897
InitiateShutdown(NULL, NULL, 0, flags, SHTDN_REASON_FLAG_PLANNED);
744898
}
745899
else
@@ -748,6 +902,8 @@ static bool ExecuteShutdownCommand(TMenuID menuCommand)
748902
// lets try silent elevate via SystemSettingsAdminFlows (for limited admin users only)
749903
if (GetCurrentTokenElevationType() == TokenElevationTypeLimited)
750904
{
905+
flags = WindowsUpdateAdjustShutdwonFlags(flags);
906+
751907
wchar_t cmdLine[32]{};
752908
Sprintf(cmdLine, _countof(cmdLine), L"Shutdown %d %d", flags, SHTDN_REASON_FLAG_PLANNED);
753909

0 commit comments

Comments
 (0)