QUESTION: Is there a programmatic way to prevent Windows 10 from automatically restarting after an update?
We work on \"mission-critical\" software
I don't know how robust this is, but it appears that Notepad blocks automatic restarts when the Save As dialogue is open. Could be useful if the accepted fix breaks in future releases.
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.