Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prevent Windows 10 from automatically restarting after an update programmatically

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?

like image 876
Andre72127 Avatar asked Jul 13 '17 14:07

Andre72127


2 Answers

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;
like image 183
Disillusioned Avatar answered Nov 11 '22 14:11

Disillusioned


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.

like image 21
Uwe Raabe Avatar answered Nov 11 '22 13:11

Uwe Raabe