PreShutdown implemented; more workarounds and hacks and fixes

master
Inga 🏳‍🌈 4 years ago
parent 3bcbf00a0f
commit ee3de9516b
  1. 9
      RadeonResetBugFixService/Constants.cs
  2. 9
      RadeonResetBugFixService/MainHandler.cs
  3. 6
      RadeonResetBugFixService/Program.cs
  4. 17
      RadeonResetBugFixService/ProjectInstaller.cs
  5. 117
      RadeonResetBugFixService/RadeonResetBugFixService.cs
  6. 2
      RadeonResetBugFixService/RadeonResetBugFixService.csproj
  7. 3
      RadeonResetBugFixService/TasksProcessor.cs
  8. 197
      RadeonResetBugFixService/ThirdParty/ServicePreshutdownHelpers.cs

@ -0,0 +1,9 @@
namespace RadeonResetBugFixService
{
using System;
static class Constants
{
public static TimeSpan ServiceTimeout { get; } = TimeSpan.FromMinutes(5);
}
}

@ -26,6 +26,14 @@
$"radeonfix_{date:yyyyMMdd}_{date:HHmmss}.log"); $"radeonfix_{date:yyyyMMdd}_{date:HHmmss}.log");
} }
public void HandleLog(string message)
{
using (ILogger fileLogger = new FileLogger(this.LogFilename))
{
fileLogger.Log(message);
}
}
public void HandleStartup(string reason) public void HandleStartup(string reason)
{ {
using (var fileLogger = new FileLogger(this.LogFilename)) using (var fileLogger = new FileLogger(this.LogFilename))
@ -75,6 +83,7 @@
new ITask[] new ITask[]
{ {
new StopAudioServiceTask(), new StopAudioServiceTask(),
new SleepTask(TimeSpan.FromSeconds(15)),
new DisableAmdVideoTask(this.ShutdownDevicesStatus), new DisableAmdVideoTask(this.ShutdownDevicesStatus),
new EnableVirtualVideoTask(this.ShutdownDevicesStatus), new EnableVirtualVideoTask(this.ShutdownDevicesStatus),
new LastResortDevicesRestoreTask(this.StartupDevicesStatus), new LastResortDevicesRestoreTask(this.StartupDevicesStatus),

@ -89,12 +89,16 @@
private static void DoInstall() private static void DoInstall()
{ {
Console.WriteLine("Setting registry values..."); Console.WriteLine("Setting registry values...");
// Prevent Windows from killing services that take up to 300 seconds to shutdown // Prevent Windows from killing services that take up to 300 seconds to shutdown
Registry.SetValue(@"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control", "WaitToKillServiceTimeout", "300000", RegistryValueKind.String); Registry.SetValue(@"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control", "WaitToKillServiceTimeout", (int)Constants.ServiceTimeout.TotalMilliseconds, RegistryValueKind.String);
// Disable fast restart // Disable fast restart
Registry.SetValue(@"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Power", "HiberbootEnabled", 0, RegistryValueKind.DWord); Registry.SetValue(@"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Power", "HiberbootEnabled", 0, RegistryValueKind.DWord);
// Allow interactive services (FixMonitorTask only works correctly in interactive mode)
Registry.SetValue(@"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Windows", "NoInteractiveServices", 0, RegistryValueKind.DWord);
Console.WriteLine("Installing service..."); Console.WriteLine("Installing service...");
ServiceHelpers.InstallService(nameof(RadeonResetBugFixService), typeof(RadeonResetBugFixService)); ServiceHelpers.InstallService(nameof(RadeonResetBugFixService), typeof(RadeonResetBugFixService));
Console.WriteLine("Starting service..."); Console.WriteLine("Starting service...");

@ -1,14 +1,9 @@
using System; namespace RadeonResetBugFixService
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration.Install;
using System.Linq;
using System.Management;
using System.Threading.Tasks;
namespace RadeonResetBugFixService
{ {
using System.ComponentModel;
using System.Configuration.Install;
using System.Management;
[RunInstaller(true)] [RunInstaller(true)]
public partial class ProjectInstaller : System.Configuration.Install.Installer public partial class ProjectInstaller : System.Configuration.Install.Installer
{ {
@ -35,6 +30,8 @@ namespace RadeonResetBugFixService
if (wmiService != null) if (wmiService != null)
wmiService.Dispose(); wmiService.Dispose();
} }
ThirdParty.ServicePreshutdownHelpers.ServicePreshutdownHelpers.SetPreShutdownTimeOut(this.serviceInstaller1.ServiceName, (uint)Constants.ServiceTimeout.TotalMilliseconds);
} }
private void serviceProcessInstaller1_AfterInstall(object sender, InstallEventArgs e) private void serviceProcessInstaller1_AfterInstall(object sender, InstallEventArgs e)

@ -1,16 +1,10 @@
using Microsoft.Win32; namespace RadeonResetBugFixService
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
namespace RadeonResetBugFixService
{ {
using System;
using System.Reflection;
using System.ServiceProcess;
using Microsoft.Win32;
public partial class RadeonResetBugFixService : ServiceBase public partial class RadeonResetBugFixService : ServiceBase
{ {
private MainHandler Handler { get; } = new MainHandler(); private MainHandler Handler { get; } = new MainHandler();
@ -18,35 +12,108 @@ namespace RadeonResetBugFixService
public RadeonResetBugFixService() public RadeonResetBugFixService()
{ {
InitializeComponent(); InitializeComponent();
this.EnablePreshutdown();
}
private void EnablePreshutdown()
{
const int SERVICE_ACCEPT_PRESHUTDOWN = 0x100;
var acceptedCommandsFieldInfo = typeof(ServiceBase).GetField("acceptedCommands", BindingFlags.Instance | BindingFlags.NonPublic);
if (acceptedCommandsFieldInfo == null)
{
throw new Exception("acceptedCommands field not found");
}
var value = (int)acceptedCommandsFieldInfo.GetValue(this);
acceptedCommandsFieldInfo.SetValue(this, value | SERVICE_ACCEPT_PRESHUTDOWN);
}
private void CallStop()
{
var deferredStopMethodInfo = typeof(ServiceBase).GetMethod("DeferredStop", BindingFlags.Instance | BindingFlags.NonPublic);
deferredStopMethodInfo.Invoke(this, null);
}
private void Process(string reason, Action<string> handle)
{
this.Handler.HandleLog($"{reason} initiated");
try
{
handle(reason);
this.Handler.HandleLog($"{reason} successfully finished");
}
catch (Exception e)
{
this.Handler.HandleLog($"{reason} error: {e}");
}
} }
protected override void OnShutdown() protected override void OnShutdown()
{ {
SystemEvents.SessionEnding -= this.OnSessionEnding; this.Process(
this.RequestAdditionalTime(300000); "ServiceBase.OnShutdown",
this.Handler.HandleShutdown("ServiceBase.OnShutdown"); (string reason) =>
{
this.CallStop();
});
} }
protected override void OnStart(string[] args) protected override void OnStart(string[] args)
{ {
this.Handler.HandleStartup("ServiceBase.OnStart"); this.Process(
this.RequestAdditionalTime(300000); "ServiceBase.OnStart",
SystemEvents.SessionEnding += this.OnSessionEnding; (string reason) =>
{
this.RequestAdditionalTime((int)Constants.ServiceTimeout.TotalMilliseconds);
this.Handler.HandleStartup(reason);
this.EnablePreshutdown();
SystemEvents.SessionEnding += this.OnSessionEnding;
});
} }
protected override void OnStop() protected override void OnStop()
{ {
SystemEvents.SessionEnding -= this.OnSessionEnding; this.Process(
this.RequestAdditionalTime(300000); "ServiceBase.OnStop",
this.Handler.HandleShutdown("ServiceBase.OnStop"); (string reason) =>
{
this.RequestAdditionalTime((int)Constants.ServiceTimeout.TotalMilliseconds);
this.Handler.HandleShutdown(reason);
SystemEvents.SessionEnding -= this.OnSessionEnding;
});
}
protected override void OnCustomCommand(int command)
{
const int SERVICE_CONTROL_PRESHUTDOWN = 0xf;
this.Process(
"ServiceBase.OnCustomCommand",
(string reason) =>
{
this.Handler.HandleLog($"Custom command: {command}");
if (command == SERVICE_CONTROL_PRESHUTDOWN)
{
this.CallStop();
}
});
} }
private void OnSessionEnding(object sender, SessionEndingEventArgs args) private void OnSessionEnding(object sender, SessionEndingEventArgs args)
{ {
if (args.Reason == SessionEndReasons.SystemShutdown) this.Process(
{ "SystemEvents.OnSessionEnding",
this.Handler.HandleShutdown("SystemEvents.SessionEnding"); (string reason) =>
} {
this.Handler.HandleLog($"Session end reason: ${args.Reason}");
if (args.Reason == SessionEndReasons.SystemShutdown)
{
this.Handler.HandleShutdown(reason);
}
});
} }
} }
} }

@ -51,6 +51,7 @@
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Constants.cs" />
<Compile Include="Contracts\DeviceInfo.cs" /> <Compile Include="Contracts\DeviceInfo.cs" />
<Compile Include="Contracts\DevicesStatus.cs" /> <Compile Include="Contracts\DevicesStatus.cs" />
<Compile Include="Devices\KnownDevices.cs" /> <Compile Include="Devices\KnownDevices.cs" />
@ -88,6 +89,7 @@
<Compile Include="Tasks\StopAudioServiceTask.cs" /> <Compile Include="Tasks\StopAudioServiceTask.cs" />
<Compile Include="ThirdParty\DisableDevice.cs" /> <Compile Include="ThirdParty\DisableDevice.cs" />
<Compile Include="Devices\DeviceHelper.cs" /> <Compile Include="Devices\DeviceHelper.cs" />
<Compile Include="ThirdParty\ServicePreshutdownHelpers.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="App.config" /> <None Include="App.config" />

@ -1,6 +1,7 @@
namespace RadeonResetBugFixService namespace RadeonResetBugFixService
{ {
using System; using System;
using System.Collections.Generic;
using Contracts; using Contracts;
using Logging; using Logging;
using Tasks; using Tasks;
@ -22,7 +23,7 @@
} }
} }
public static void ProcessTasks(ILogger logger, ITask[] tasks) public static void ProcessTasks(ILogger logger, IEnumerable<ITask> tasks)
{ {
foreach (var task in tasks) foreach (var task in tasks)
{ {

@ -0,0 +1,197 @@
namespace RadeonResetBugFixService.ThirdParty.ServicePreshutdownHelpers
{
// Code taken from https://social.msdn.microsoft.com/Forums/vstudio/en-US/d14549e2-d0bc-47fb-bb01-7e0ac57fa712/keep-windows-service-alive-for-more-then-3-minutes-when-system-shut-down
using System;
using System.Runtime.InteropServices;
[Flags]
public enum SERVICE_ACCESS : uint
{
STANDARD_RIGHTS_REQUIRED = 0xF0000,
SERVICE_QUERY_CONFIG = 0x00001,
SERVICE_CHANGE_CONFIG = 0x00002,
SERVICE_QUERY_STATUS = 0x00004,
SERVICE_ENUMERATE_DEPENDENTS = 0x00008,
SERVICE_START = 0x00010,
SERVICE_STOP = 0x00020,
SERVICE_PAUSE_CONTINUE = 0x00040,
SERVICE_INTERROGATE = 0x00080,
SERVICE_USER_DEFINED_CONTROL = 0x00100,
SERVICE_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED |
SERVICE_QUERY_CONFIG |
SERVICE_CHANGE_CONFIG |
SERVICE_QUERY_STATUS |
SERVICE_ENUMERATE_DEPENDENTS |
SERVICE_START |
SERVICE_STOP |
SERVICE_PAUSE_CONTINUE |
SERVICE_INTERROGATE |
SERVICE_USER_DEFINED_CONTROL)
}
[Flags]
public enum SCM_ACCESS : uint
{
STANDARD_RIGHTS_REQUIRED = 0xF0000,
SC_MANAGER_CONNECT = 0x00001,
SC_MANAGER_CREATE_SERVICE = 0x00002,
SC_MANAGER_ENUMERATE_SERVICE = 0x00004,
SC_MANAGER_LOCK = 0x00008,
SC_MANAGER_QUERY_LOCK_STATUS = 0x00010,
SC_MANAGER_MODIFY_BOOT_CONFIG = 0x00020,
SC_MANAGER_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED |
SC_MANAGER_CONNECT |
SC_MANAGER_CREATE_SERVICE |
SC_MANAGER_ENUMERATE_SERVICE |
SC_MANAGER_LOCK |
SC_MANAGER_QUERY_LOCK_STATUS |
SC_MANAGER_MODIFY_BOOT_CONFIG
}
[StructLayout(LayoutKind.Sequential)]
public struct SERVICE_STATUS
{
public int serviceType;
public int currentState;
public int controlsAccepted;
public int win32ExitCode;
public int serviceSpecificExitCode;
public int checkPoint;
public int waitHint;
}
public enum SERVICE_STATE : uint
{
SERVICE_STOPPED = 0x00000001,
SERVICE_START_PENDING = 0x00000002,
SERVICE_STOP_PENDING = 0x00000003,
SERVICE_RUNNING = 0x00000004,
SERVICE_CONTINUE_PENDING = 0x00000005,
SERVICE_PAUSE_PENDING = 0x00000006,
SERVICE_PAUSED = 0x00000007
}
public enum INFO_LEVEL : uint
{
SERVICE_CONFIG_DESCRIPTION = 0x00000001,
SERVICE_CONFIG_FAILURE_ACTIONS = 0x00000002,
SERVICE_CONFIG_DELAYED_AUTO_START_INFO = 0x00000003,
SERVICE_CONFIG_FAILURE_ACTIONS_FLAG = 0x00000004,
SERVICE_CONFIG_SERVICE_SID_INFO = 0x00000005,
SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO = 0x00000006,
SERVICE_CONFIG_PRESHUTDOWN_INFO = 0x00000007,
SERVICE_CONFIG_TRIGGER_INFO = 0x00000008,
SERVICE_CONFIG_PREFERRED_NODE = 0x00000009
}
[StructLayout(LayoutKind.Sequential)]
public struct SERVICE_PRESHUTDOWN_INFO
{
public UInt32 dwPreshutdownTimeout;
}
[Flags]
public enum SERVICE_CONTROL : uint
{
STOP = 0x00000001,
PAUSE = 0x00000002,
CONTINUE = 0x00000003,
INTERROGATE = 0x00000004,
SHUTDOWN = 0x00000005,
PARAMCHANGE = 0x00000006,
NETBINDADD = 0x00000007,
NETBINDREMOVE = 0x00000008,
NETBINDENABLE = 0x00000009,
NETBINDDISABLE = 0x0000000A,
DEVICEEVENT = 0x0000000B,
HARDWAREPROFILECHANGE = 0x0000000C,
POWEREVENT = 0x0000000D,
SESSIONCHANGE = 0x0000000E
}
public enum ControlsAccepted
{
ACCEPT_STOP = 1,
ACCEPT_PAUSE_CONTINUE = 2,
ACCEPT_SHUTDOWN = 4,
ACCEPT_PRESHUTDOWN = 0xf,
ACCEPT_POWER_EVENT = 64,
ACCEPT_SESSION_CHANGE = 128
}
internal class Interop
{
[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool ControlService(
IntPtr hService,
SERVICE_CONTROL dwControl,
ref SERVICE_STATUS lpServiceStatus);
[DllImport("advapi32.dll", EntryPoint = "OpenSCManagerW", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern IntPtr OpenSCManager(
string machineName,
string databaseName,
uint dwAccess);
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
internal static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, uint dwDesiredAccess);
[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool CloseServiceHandle(IntPtr hSCObject);
[DllImport("advapi32.dll", EntryPoint = "QueryServiceStatus", CharSet = CharSet.Auto)]
internal static extern bool QueryServiceStatus(IntPtr hService, ref SERVICE_STATUS dwServiceStatus);
[DllImport("advapi32.dll")]
internal static extern bool SetServiceStatus(IntPtr hServiceStatus, ref SERVICE_STATUS lpServiceStatus);
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ChangeServiceConfig2(
IntPtr hService,
int dwInfoLevel,
IntPtr lpInfo);
}
class ServicePreshutdownHelpers
{
public static void SetPreShutdownTimeOut(string serviceName, uint milliseconds)
{
// get sc manager handle
IntPtr hMngr = Interop.OpenSCManager(null, null, (uint)SCM_ACCESS.SC_MANAGER_ALL_ACCESS);
if (hMngr == IntPtr.Zero)
throw new Exception("Failed to open SC Manager handle");
else
{
// get the service's handle
IntPtr hSvc = Interop.OpenService(hMngr, serviceName, (uint)SCM_ACCESS.SC_MANAGER_ALL_ACCESS);
if (hSvc == IntPtr.Zero)
throw new Exception("Failed to open service handle");
else
{
SERVICE_PRESHUTDOWN_INFO spi = new SERVICE_PRESHUTDOWN_INFO();
spi.dwPreshutdownTimeout = milliseconds;
IntPtr lpInfo = Marshal.AllocHGlobal(Marshal.SizeOf(spi));
if (lpInfo == IntPtr.Zero)
{
throw new Exception(String.Format("Unable to allocate memory for service action, error was: 0x{0:X}", Marshal.GetLastWin32Error()));
}
Marshal.StructureToPtr(spi, lpInfo, false);
// apply the new timeout value
if (!Interop.ChangeServiceConfig2(hSvc, (int)INFO_LEVEL.SERVICE_CONFIG_PRESHUTDOWN_INFO, lpInfo))
throw new Exception("Failed to change service timeout");
Interop.CloseServiceHandle(hSvc);
}
Interop.CloseServiceHandle(hMngr);
}
}
}
}
Loading…
Cancel
Save