QUESTION: Is there a programmatic way to prevent Windows 10 from automatically restarting after an update?
We work on "mission-critical" software that runs in Windows. In general, it is bad if a Windows automatic update interrupts our process as it can mean lost money in scrapped material (you cannot stop and resume later, the job must work from start to finish uninterrupted).
In the past, we were able to get around this by having our software installer set a parameter in the Windows Registry (with the user-installer's consent) that would prevent rebooting automatically after an automatic update when a user is logged in. So, instead of an automatic update, the user would be notified that there is an update requiring a restart and to click a button when they were ready for it. This worked for Windows Vista, 7, and 8/8.1. However, for the latest Windows 10 (I'm working with the Creators' update), the parameter seems to have no effect anymore as I have observed my computer go through an automatic update where I knew that the registry entry was already in effect.
In my research, I found what appears to be one place of hope where a setting can be selected where Windows will offer the user the opportunity to schedule the update instead of just doing it automatically at a time Windows thinks it is appropriate (information here). However, I'm not sure how to go about setting Windows programmatically so that the option to schedule an update becomes the default.
Is there a programmatic way to set Windows 10 (in a friendly manner, preferably) so that it won't auto-restart?
Try the shutdown block reason APIs. ShutdownBlockReasonCreate
The API documentation cites CD burning as an example, but the same would apply to your "mission-critical" process.
Applications should call this function as they begin an operation that cannot be interrupted, such as burning a CD or DVD. When the operation has completed, call the ShutdownBlockReasonDestroy function to indicate that the system can be shut down.
Note the documentation specifically references user shutdown, but I don't see why it shouldn't also apply to update restarts as well.
NB: Remember to check that the function is successful; and to destroy the Shutdown Reason when the process completes.
Based on your comment it seems you need help using the Windows API routines. I suggest you declare the external functions in an appropriate library. (But you can test in the same unit without concern.)
function ShutdownBlockReasonCreate(hWnd: HWND; Reason: LPCWSTR): BOOL; stdcall; external user32;
function ShutdownBlockReasonDestroy(hWnd: HWND): BOOL; stdcall; external user32;
The following demonstrates how to use the API. NB: Pay attention to error checking. I've demonstrated how you can get error information. What you do with it is up to you.
The other important thing to point out (repeated in comments) is that you should not block the main thread. For more information, refer to the Microsoft documentation from when these changes were first introduced in Vista here.
procedure TForm1.JobStartClick(Sender: TObject);
var
LErr: Cardinal;
begin
ListBox1.Items.Add('Attempting to block shutdown:');
if (not ShutdownBlockReasonCreate(Application.MainForm.Handle,
'Super Critical Job')) then
begin
LErr := GetLastError;
ListBox1.Items.Add('... failed: ' + SysErrorMessage(LErr));
//Probably not safe to start your job in this case, but perhaps you
//choose to give it a shot anyway.
Exit;
end;
ListBox1.Items.Add('... success');
FJobRunning := True;
//Start the job.
//However, NB do not run the job here.
//If it takes a long time and is not asynchronous, you should probably
//run your job on a separate thread. ***Do not block the main thread
// otherwise Windows will still kill your app for not responding***
end;
procedure TForm1.JobEndClick(Sender: TObject);
var
LErr: Cardinal;
begin
if (not FJobRunning) then Exit;
//End the job.
//Again, do not block the main thread, so perhaps this is rather something
//to do after you already know the job is done.
FJobRunning := False;
ListBox1.Items.Add('Allow shutdown');
if (not ShutdownBlockReasonDestroy(Application.MainForm.Handle)) then
begin
LErr := GetLastError;
ListBox1.Items.Add('... failed: ' + SysErrorMessage(LErr));
end;
end;
//Declare the handler for the WM_QUERYENDSESSION message as follows.
//procedure WMQueryEndSession(var AMsg : TWMQueryEndSession); message WM_QUERYENDSESSION;
procedure TForm1.WMQueryEndSession(var AMsg: TWMQueryEndSession);
begin
ListBox1.Items.Add('WMQueryEndSession');
if (FJobRunning) then
//NB: This is very important.
//You still need to confirm that your application wants to block
//shutdown whenever you receive this message.
AMsg.Result := 0
else
inherited;
end;
The registry key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings
contains two entries: ActiveHoursStart
and ActiveHoursEnd
. Change these entries as needed inside your program to prohibit a reboot. This way you can control the reboot not to take place while your program runs. Note that you need elevated privilege to change these settings.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With