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");
}
public void HandleLog(string message)
{
using (ILogger fileLogger = new FileLogger(this.LogFilename))
{
fileLogger.Log(message);
}
}
public void HandleStartup(string reason)
{
using (var fileLogger = new FileLogger(this.LogFilename))
@ -75,6 +83,7 @@
new ITask[]
{
new StopAudioServiceTask(),
new SleepTask(TimeSpan.FromSeconds(15)),
new DisableAmdVideoTask(this.ShutdownDevicesStatus),
new EnableVirtualVideoTask(this.ShutdownDevicesStatus),
new LastResortDevicesRestoreTask(this.StartupDevicesStatus),

@ -89,12 +89,16 @@
private static void DoInstall()
{
Console.WriteLine("Setting registry values...");
// 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
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...");
ServiceHelpers.InstallService(nameof(RadeonResetBugFixService), typeof(RadeonResetBugFixService));
Console.WriteLine("Starting service...");

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

@ -1,16 +1,10 @@
using Microsoft.Win32;
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
namespace RadeonResetBugFixService
{
using System;
using System.Reflection;
using System.ServiceProcess;
using Microsoft.Win32;
public partial class RadeonResetBugFixService : ServiceBase
{
private MainHandler Handler { get; } = new MainHandler();
@ -18,35 +12,108 @@ namespace RadeonResetBugFixService
public RadeonResetBugFixService()
{
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()
{
SystemEvents.SessionEnding -= this.OnSessionEnding;
this.RequestAdditionalTime(300000);
this.Handler.HandleShutdown("ServiceBase.OnShutdown");
this.Process(
"ServiceBase.OnShutdown",
(string reason) =>
{
this.CallStop();
});
}
protected override void OnStart(string[] args)
{
this.Handler.HandleStartup("ServiceBase.OnStart");
this.RequestAdditionalTime(300000);
SystemEvents.SessionEnding += this.OnSessionEnding;
this.Process(
"ServiceBase.OnStart",
(string reason) =>
{
this.RequestAdditionalTime((int)Constants.ServiceTimeout.TotalMilliseconds);
this.Handler.HandleStartup(reason);
this.EnablePreshutdown();
SystemEvents.SessionEnding += this.OnSessionEnding;
});
}
protected override void OnStop()
{
SystemEvents.SessionEnding -= this.OnSessionEnding;
this.RequestAdditionalTime(300000);
this.Handler.HandleShutdown("ServiceBase.OnStop");
this.Process(
"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)
{
if (args.Reason == SessionEndReasons.SystemShutdown)
{
this.Handler.HandleShutdown("SystemEvents.SessionEnding");
}
this.Process(
"SystemEvents.OnSessionEnding",
(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" />
</ItemGroup>
<ItemGroup>
<Compile Include="Constants.cs" />
<Compile Include="Contracts\DeviceInfo.cs" />
<Compile Include="Contracts\DevicesStatus.cs" />
<Compile Include="Devices\KnownDevices.cs" />
@ -88,6 +89,7 @@
<Compile Include="Tasks\StopAudioServiceTask.cs" />
<Compile Include="ThirdParty\DisableDevice.cs" />
<Compile Include="Devices\DeviceHelper.cs" />
<Compile Include="ThirdParty\ServicePreshutdownHelpers.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />

@ -1,6 +1,7 @@
namespace RadeonResetBugFixService
{
using System;
using System.Collections.Generic;
using Contracts;
using Logging;
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)
{

@ -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