Compare commits

...

11 Commits

  1. 56
      README.md
  2. 9
      RadeonResetBugFixService/Contracts/ServiceContext.cs
  3. 18
      RadeonResetBugFixService/Devices/KnownDevices.cs
  4. 49
      RadeonResetBugFixService/EnvironmentHelper.cs
  5. 59
      RadeonResetBugFixService/MainHandler.cs
  6. 57
      RadeonResetBugFixService/Program.cs
  7. 25
      RadeonResetBugFixService/ProjectInstaller.cs
  8. 3
      RadeonResetBugFixService/Properties/AssemblyInfo.cs
  9. 47
      RadeonResetBugFixService/Properties/app.manifest
  10. 57
      RadeonResetBugFixService/RadeonResetBugFixService.cs
  11. 63
      RadeonResetBugFixService/RadeonResetBugFixService.csproj
  12. 16
      RadeonResetBugFixService/RegistryHelper.cs
  13. 2
      RadeonResetBugFixService/Tasks/BasicTasks/AbstractDevicesTask.cs
  14. 2
      RadeonResetBugFixService/Tasks/BasicTasks/AbstractServiceTask.cs
  15. 2
      RadeonResetBugFixService/Tasks/BasicTasks/DisableAmdVideoTask.cs
  16. 2
      RadeonResetBugFixService/Tasks/BasicTasks/DisableBasicDisplayStartupTask.cs
  17. 2
      RadeonResetBugFixService/Tasks/BasicTasks/DisableVirtualVideoTask.cs
  18. 2
      RadeonResetBugFixService/Tasks/BasicTasks/EnableAmdVideoTask.cs
  19. 2
      RadeonResetBugFixService/Tasks/BasicTasks/EnableBasicDisplayStartupTask.cs
  20. 2
      RadeonResetBugFixService/Tasks/BasicTasks/EnableVirtualVideoTask.cs
  21. 2
      RadeonResetBugFixService/Tasks/BasicTasks/FixMonitorTask.cs
  22. 2
      RadeonResetBugFixService/Tasks/BasicTasks/LastResortDevicesRestoreTask.cs
  23. 19
      RadeonResetBugFixService/Tasks/BasicTasks/ListDevicesTask.cs
  24. 2
      RadeonResetBugFixService/Tasks/BasicTasks/SleepTask.cs
  25. 15
      RadeonResetBugFixService/Tasks/BasicTasks/StartAudioServiceTask.cs
  26. 2
      RadeonResetBugFixService/Tasks/BasicTasks/StopAudioServiceTask.cs
  27. 13
      RadeonResetBugFixService/Tasks/ComplexTasks/AbstractSequentialTask.cs
  28. 16
      RadeonResetBugFixService/Tasks/ComplexTasks/DiagnoseTask.cs
  29. 29
      RadeonResetBugFixService/Tasks/ComplexTasks/ShutdownTask.cs
  30. 30
      RadeonResetBugFixService/Tasks/ComplexTasks/StartupTask.cs
  31. 2
      RadeonResetBugFixService/TasksProcessor.cs

@ -21,22 +21,31 @@ With it, you will be able to use Radeon GPU as your only GPU,
with your actual display connected to Radeon as a primary display,
and reboot your VM without triggering AMD reset bug - even installing Windows updates!
In effect, you can use VM with pass-through GPU plus pass-through keyboard and mouse
as entirely separate PC.
## Limitations
Currently this project is only tested with Hyper-V VMs,
and probably also supports KVM and QEMU,
but it should be trivial to add other hypervisors support
(the relevant files are `Tasks\DisableVirtualVideoTask.cs` and `EnableVirtualVideoTask.cs`).
Currently this project is only tested with Windows 10 VMs on Hyper-V,
and was not tested with Windows 7 VMs, KVM, QEMU, VMWare and VirtualBox,
although the preliminary support for these systems has been implemented
and they should probably work too.
Bug reports are welcome!
**Note that you will still have to add a virtual GPU to your VM,
otherwise Windows won't boot.**
Note that it will add 1-5 minutes both to startup and to shutdown time.
Note that it will add around 1 minute or up to 5 minutes in some cases to startup time,
and a fraction of minute or up to several minutes in some cases to shutdown time.
So don't panic if your screen is black immediately after VM startup,
it is expected.
## Install instructions
Make sure you have added an active virtual/synthetic GPU to your VM,
and that you have installed all the relevant drivers / Guest Additions.
Put `RadeonResetBugFixService.exe` in a permanent location.
In elevated command prompt in Guest VM, run
@ -72,20 +81,43 @@ RadeonResetBugFixService.exe uninstall
```
The screen may go blank several times during the process.
It may take up to 5 minutes total (but should take less than 2).
It may take up to 5 minutes total (but should take several seconds).
## Debugging
## Debugging, bug reports
The service stores its verbose log files in `logs` directory located next to the executable.
## Frequent issues
If the service does not work correctly on your system or does not solve the reset bug,
please also run the diagnostics command
(which will gather the detailed information about your hardware and will also write it to log):
### Connecting to VM from the Host
```
RadeonResetBugFixService.exe diagnose
```
Attach to your bug report the diagnose log file plus the relevant recent service logs:
one from the latest startup when you encountered a problem,
and another one from the startup before that.
This service disables Hyper-V video adapter,
so you can no longer connect to VM using Basic sessions.
## Known issues
### Connecting to VM from the Host
Enhanced sessions (which use RDP protocol) continue to work fine.
This service disables virtual video adapter,
so you can no longer connect to VM from a host, using Basic sessions.
It will only work during startup / shutdown, but not during normal usage.
If you connected to VM from a host during startup,
the screen will go blank in a couple of minutes
(as the display connected to Radeon GPU wil light up),
that is expected.
That is one of the points of this service;
otherwise, virtual display would always be present as primary or secondary display,
even when you are not connected to VM from the host.
Enhanced sessions in Hyper-V (which use RDP protocol) continue to work fine.
Other hypervisors can miss the enhanced sessions feature,
but you still can connect to VM using RDP.
### Unsuccessful reboots

@ -0,0 +1,9 @@
namespace RadeonResetBugFixService.Contracts
{
class ServiceContext
{
public DevicesStatus StartupDevicesStatus { get; } = new DevicesStatus();
public DevicesStatus ShutdownDevicesStatus { get; } = new DevicesStatus();
}
}

@ -1,6 +1,7 @@
namespace RadeonResetBugFixService.Devices
{
using System;
using System.Linq;
using Contracts;
static class KnownDevices
@ -13,10 +14,19 @@
public static bool IsVirtualVideo(DeviceInfo device)
{
return (
device.Service.Equals("hypervideo", StringComparison.OrdinalIgnoreCase) || // Hyper-V video adapter
device.Service.Equals("qxldod", StringComparison.OrdinalIgnoreCase) // virtio/libvirt for Win8+
);
return new[]
{
"hypervideo", // Hyper-V video adapter
"qxldod", // virtio/libvirt for Win8+
"qxl", // virtio/libvirt for Win7
"VBoxVideo", // virtualbox for some windows versions?
"VBoxWDDM", // virtualbox for Win8+ (or for Win7 too?)
"vm3dmp", // one of these supports "VMWare SVGA 3D"
"vm3dmp-debug", // one of these supports "VMWare SVGA 3D"
"vm3dmp-stats", // one of these supports "VMWare SVGA 3D"
"vm3dmp_loader", // one of these supports "VMWare SVGA 3D"
"VM3DService ", // one of these supports "VMWare SVGA 3D"
}.Contains(device.Service, StringComparer.OrdinalIgnoreCase);
}
}
}

@ -0,0 +1,49 @@
namespace RadeonResetBugFixService
{
using System;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security.Principal;
static class EnvironmentHelper
{
private static class NativeMethods
{
[DllImport("kernel32.dll")]
public static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
public static extern bool IsWindowVisible(IntPtr hWnd);
}
private static Version VistaVersion { get; } = new Version(6, 0);
private static Version Windows8Version { get; } = new Version(6, 2);
// Code taken from https://stackoverflow.com/a/53716169
public static bool IsConsoleVisibleOnWindows() => NativeMethods.IsWindowVisible(NativeMethods.GetConsoleWindow());
private static bool IsWindows() => RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
// Code taken from https://stackoverflow.com/a/2679654
public static bool HasAdministratorPrivileges()
{
WindowsIdentity id = WindowsIdentity.GetCurrent();
WindowsPrincipal principal = new WindowsPrincipal(id);
return principal.IsInRole(WindowsBuiltInRole.Administrator);
}
public static bool IsWindows8OrNewer() => IsWindows() && Environment.OSVersion.Version >= Windows8Version;
public static bool IsVistaOrNewer() => IsWindows() && Environment.OSVersion.Version >= VistaVersion;
// Code taken from https://stackoverflow.com/a/826850
public static DateTime GetServiceBuildDate()
{
var version = Assembly.GetExecutingAssembly().GetName().Version;
return new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Unspecified)
.AddDays(version.Build)
.AddSeconds(version.Revision * 2);
}
}
}

@ -24,70 +24,19 @@
$"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 HandleEntryPoint(string name, Action<ILogger> handle)
{
using (var fileLogger = new FileLogger(this.LogFilename))
{
using (ILogger logger = new TaskLoggerWrapper(fileLogger, "Startup"))
using (ILogger logger = new TaskLoggerWrapper(fileLogger, name))
{
logger.Log($"Reason: {reason}");
try
{
lock (this.Mutex)
{
TasksProcessor.ProcessTasks(
logger,
new ITask[]
{
new EnableBasicDisplayStartupTask(),
new SleepTask(TimeSpan.FromSeconds(40)),
new EnableAmdVideoTask(this.StartupDevicesStatus),
new DisableVirtualVideoTask(this.StartupDevicesStatus),
new SleepTask(TimeSpan.FromSeconds(20)),
new FixMonitorTask(),
new DisableVirtualVideoTask(this.StartupDevicesStatus),
new FixMonitorTask(),
});
}
}
catch (Exception e)
{
logger.LogError(e.ToString());
}
}
}
}
logger.Log($"Build date: {EnvironmentHelper.GetServiceBuildDate()}");
public void HandleShutdown(string reason)
{
using (var fileLogger = new FileLogger(this.LogFilename))
{
using (ILogger logger = new TaskLoggerWrapper(fileLogger, "Shutdown"))
{
logger.Log($"Reason: {reason}");
try
{
lock (this.Mutex)
{
TasksProcessor.ProcessTasks(
logger,
new ITask[]
{
new StopAudioServiceTask(),
new EnableVirtualVideoTask(this.ShutdownDevicesStatus),
new DisableAmdVideoTask(this.ShutdownDevicesStatus),
new LastResortDevicesRestoreTask(this.StartupDevicesStatus),
new LastResortDevicesRestoreTask(this.StartupDevicesStatus), // just in case
new DisableBasicDisplayStartupTask(this.StartupDevicesStatus),
});
handle(logger);
}
}
catch (Exception e)

@ -1,8 +1,9 @@
namespace RadeonResetBugFixService
{
using System;
using System.Security.Principal;
using System.ServiceProcess;
using Contracts;
using Tasks.ComplexTasks;
using ThirdParty.ServiceHelpers;
public static class Program
@ -17,9 +18,15 @@
throw new ArgumentNullException(nameof(args));
}
if (Environment.UserInteractive)
if (!EnvironmentHelper.IsVistaOrNewer())
{
if (!HasAdministratorPrivileges())
Console.Error.WriteLine("This program only runs on Windows Vista or newer");
return -1;
}
if (EnvironmentHelper.IsConsoleVisibleOnWindows())
{
if (!EnvironmentHelper.HasAdministratorPrivileges())
{
Console.Error.WriteLine("Access Denied.");
Console.Error.WriteLine("Administrator permissions are needed to use this tool.");
@ -36,20 +43,18 @@
}
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope", Justification = "Windows service initialization")]
private static int MainService()
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new RadeonResetBugFixService()
};
ServiceBase.Run(ServicesToRun);
ServiceBase.Run(new RadeonResetBugFixService());
return 0;
}
private static void MainConsole(string[] args)
{
Console.WriteLine($"Build date: {EnvironmentHelper.GetServiceBuildDate()}");
var command = args.Length == 1 ? args[0] : string.Empty;
if (command.Equals("install", StringComparison.OrdinalIgnoreCase)) {
@ -71,6 +76,10 @@
{
DoShutdown();
}
else if (command.Equals("diagnose", StringComparison.OrdinalIgnoreCase))
{
DoDiagnose();
}
else
{
ShowHelp();
@ -91,6 +100,8 @@
Console.WriteLine("\t\tPerforms startup sequence (development command, does not affect services)");
Console.WriteLine($"\t{exeName} shutdown");
Console.WriteLine("\t\tPerforms shutdown sequence (development command, does not affect services)");
Console.WriteLine($"\t{exeName} diagnose");
Console.WriteLine("\t\tPerforms diagnose (see result file in logs folder)");
}
private static void DoInstall()
@ -105,6 +116,7 @@
ServiceHelpers.StopService(Constants.ServiceName);
Console.WriteLine("Starting service...");
ServiceHelpers.StartService(Constants.ServiceName);
Console.WriteLine("Service started");
}
private static void DoUninstall()
@ -125,22 +137,19 @@
DoInstall();
}
private static void DoStartup()
{
new MainHandler().HandleStartup("Program.DoStartup");
}
private static void DoStartup() => new MainHandler().HandleEntryPoint(
"Program.DoStartup",
(logger) => TasksProcessor.ProcessTask(logger, new StartupTask(new ServiceContext()))
);
private static void DoShutdown()
{
new MainHandler().HandleShutdown("Program.DoShutdown");
}
private static void DoShutdown() => new MainHandler().HandleEntryPoint(
"Program.DoShutdown",
(logger) => TasksProcessor.ProcessTask(logger, new ShutdownTask(new ServiceContext()))
);
// Code taken from https://stackoverflow.com/a/2679654
private static bool HasAdministratorPrivileges()
{
WindowsIdentity id = WindowsIdentity.GetCurrent();
WindowsPrincipal principal = new WindowsPrincipal(id);
return principal.IsInRole(WindowsBuiltInRole.Administrator);
}
private static void DoDiagnose() => new MainHandler().HandleEntryPoint(
"Program.DoDiagnose",
(logger) => TasksProcessor.ProcessTask(logger, new DiagnoseTask())
);
}
}

@ -15,21 +15,26 @@
InitializeComponent();
}
private void LogMessage(string message)
{
this.Context.LogMessage($"* {message}");
}
private void serviceInstaller1_AfterInstall(object sender, InstallEventArgs e)
{
Console.WriteLine($"Creating log directory ({Constants.LogDirectory})");
this.LogMessage($"Creating log directory ({Constants.LogDirectory})");
Directory.CreateDirectory(Constants.LogDirectory);
Console.WriteLine("Preventing Windows from killing services that take up to 300 seconds to shutdown");
this.LogMessage("Preventing Windows from killing services that take up to 300 seconds to shutdown");
RegistryHelper.SetWaitToKillServiceTimeout((int)Constants.ServiceTimeout.TotalMilliseconds);
Console.WriteLine("Disabling fast reboot");
this.LogMessage("Disabling fast reboot");
RegistryHelper.SetFastRebootStatus(false);
Console.WriteLine("Allowing interactive services");
this.LogMessage("Allowing interactive services");
RegistryHelper.SetInteractiveServicesStatus(true);
Console.WriteLine("Configuring service as interactive");
this.LogMessage("Configuring service as interactive");
using (var wmiService = new ManagementObject($"Win32_Service.Name='{this.serviceInstaller1.ServiceName}'"))
{
using (var InParam = wmiService.GetMethodParameters("Change"))
@ -39,25 +44,29 @@
}
}
Console.WriteLine("Setting preshutdown timeout for service");
this.LogMessage("Setting preshutdown timeout for service");
ThirdParty.ServicePreshutdownHelpers.ServicePreshutdownHelpers.SetPreShutdownTimeOut(this.serviceInstaller1.ServiceName, (uint)Constants.ServiceTimeout.TotalMilliseconds);
Console.WriteLine("Adding service to preshutdown order");
this.LogMessage("Adding service to preshutdown order");
var preshutdownOrder = RegistryHelper.GetPreshutdownOrder();
if (!preshutdownOrder.Contains(this.serviceInstaller1.ServiceName))
{
RegistryHelper.SetPreshutdownOrder(new[] { this.serviceInstaller1.ServiceName }.Concat(preshutdownOrder).ToArray());
}
this.LogMessage("Completed AfterInstall sequence");
}
private void serviceInstaller1_AfterUninstall(object sender, InstallEventArgs e)
{
Console.WriteLine("Removing service from preshutdown order");
this.LogMessage("Removing service from preshutdown order");
var preshutdownOrder = RegistryHelper.GetPreshutdownOrder();
if (preshutdownOrder.Contains(this.serviceInstaller1.ServiceName))
{
RegistryHelper.SetPreshutdownOrder(preshutdownOrder.Where((name) => name != this.serviceInstaller1.ServiceName).ToArray());
}
this.LogMessage("Completed AfterUninstall sequence");
}
}
}

@ -32,5 +32,4 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyVersion("1.0.*")]

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="RadeonResetBugFixService"/>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<!-- UAC Manifest Options
If you want to change the Windows User Account Control level replace the
requestedExecutionLevel node with one of the following.
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
Specifying requestedExecutionLevel element will disable file and registry virtualization.
Remove this element if your application requires this virtualization for backwards
compatibility.
-->
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- A list of the Windows versions that this application has been tested on
and is designed to work with. Uncomment the appropriate elements
and Windows will automatically select the most compatible environment. -->
<!-- Windows Vista -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
</assembly>

@ -4,9 +4,13 @@
using System.Reflection;
using System.ServiceProcess;
using Microsoft.Win32;
using Contracts;
using Tasks.ComplexTasks;
public partial class RadeonResetBugFixService : ServiceBase
{
private ServiceContext Context { get; } = new ServiceContext();
private MainHandler Handler { get; } = new MainHandler();
public RadeonResetBugFixService()
@ -35,38 +39,22 @@
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()
{
this.Process(
this.Handler.HandleEntryPoint(
"ServiceBase.OnShutdown",
(string reason) =>
{
this.CallStop();
});
(logger) => this.CallStop()
);
}
protected override void OnStart(string[] args)
{
this.Process(
this.Handler.HandleEntryPoint(
"ServiceBase.OnStart",
(string reason) =>
(logger) =>
{
this.RequestAdditionalTime((int)Constants.ServiceTimeout.TotalMilliseconds);
this.Handler.HandleStartup(reason);
TasksProcessor.ProcessTask(logger, new StartupTask(this.Context));
this.EnablePreshutdown();
SystemEvents.SessionEnding += this.OnSessionEnding;
});
@ -74,12 +62,12 @@
protected override void OnStop()
{
this.Process(
this.Handler.HandleEntryPoint(
"ServiceBase.OnStop",
(string reason) =>
(logger) =>
{
this.RequestAdditionalTime((int)Constants.ServiceTimeout.TotalMilliseconds);
this.Handler.HandleShutdown(reason);
TasksProcessor.ProcessTask(logger, new ShutdownTask(this.Context));
SystemEvents.SessionEnding -= this.OnSessionEnding;
});
}
@ -88,30 +76,33 @@
{
const int SERVICE_CONTROL_PRESHUTDOWN = 0xf;
this.Process(
this.Handler.HandleEntryPoint(
"ServiceBase.OnCustomCommand",
(string reason) =>
(logger) =>
{
this.Handler.HandleLog($"Custom command: {command}");
if (command == SERVICE_CONTROL_PRESHUTDOWN)
{
logger.Log("Custom command: preshutdown");
this.CallStop();
}
else
{
logger.Log("Unknown custom command: {command}");
}
});
}
private void OnSessionEnding(object sender, SessionEndingEventArgs args)
{
this.Process(
this.Handler.HandleEntryPoint(
"SystemEvents.OnSessionEnding",
(string reason) =>
(logger) =>
{
this.Handler.HandleLog($"Session end reason: ${args.Reason}");
logger.Log($"Session end reason: ${args.Reason}");
if (args.Reason == SessionEndReasons.SystemShutdown)
{
this.Handler.HandleShutdown(reason);
TasksProcessor.ProcessTask(logger, new ShutdownTask(this.Context));
}
});
}

@ -16,7 +16,7 @@
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic>
<Deterministic>false</Deterministic>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
@ -44,67 +44,79 @@
<PropertyGroup>
<StartupObject />
</PropertyGroup>
<PropertyGroup>
<ApplicationManifest>Properties\app.manifest</ApplicationManifest>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Configuration.Install" />
<Reference Include="System.Core" />
<Reference Include="System.Management" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Management" />
<Reference Include="System.Net.Http" />
<Reference Include="System.ServiceProcess" />
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq" />
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup>
<Compile Include="Contracts\ServiceContext.cs" />
<Compile Include="Devices\DeviceHelper.cs" />
<Compile Include="EnvironmentHelper.cs" />
<Compile Include="Constants.cs" />
<Compile Include="Contracts\DeviceInfo.cs" />
<Compile Include="Contracts\DevicesStatus.cs" />
<Compile Include="Contracts\ILogger.cs" />
<Compile Include="Devices\KnownDevices.cs" />
<Compile Include="Logging\FileLogger.cs" />
<Compile Include="Contracts\ILogger.cs" />
<Compile Include="Logging\TaskLoggerWrapper.cs" />
<Compile Include="MainHandler.cs" />
<Compile Include="Program.cs" />
<Compile Include="ProjectInstaller.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="ProjectInstaller.Designer.cs">
<DependentUpon>ProjectInstaller.cs</DependentUpon>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RadeonResetBugFixService.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="RadeonResetBugFixService.Designer.cs">
<DependentUpon>RadeonResetBugFixService.cs</DependentUpon>
</Compile>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Logging\TaskLoggerWrapper.cs" />
<Compile Include="RegistryHelper.cs" />
<Compile Include="Tasks\DisableBasicDisplayStartupTask.cs" />
<Compile Include="Tasks\EnableBasicDisplayStartupTask.cs" />
<Compile Include="Tasks\FixMonitorTask.cs" />
<Compile Include="Tasks\LastResortDevicesRestoreTask.cs" />
<Compile Include="Tasks\SleepTask.cs" />
<Compile Include="ThirdParty\MonitorChanger.cs" />
<Compile Include="ThirdParty\ServiceHelpers.cs" />
<Compile Include="TasksProcessor.cs" />
<Compile Include="Tasks\AbstractDevicesTask.cs" />
<Compile Include="Tasks\AbstractServiceTask.cs" />
<Compile Include="Tasks\DisableAmdVideoTask.cs" />
<Compile Include="Tasks\DisableVirtualVideoTask.cs" />
<Compile Include="Tasks\EnableAmdVideoTask.cs" />
<Compile Include="Tasks\EnableVirtualVideoTask.cs" />
<Compile Include="Tasks\BasicTasks\AbstractDevicesTask.cs" />
<Compile Include="Tasks\BasicTasks\AbstractServiceTask.cs" />
<Compile Include="Tasks\BasicTasks\DisableAmdVideoTask.cs" />
<Compile Include="Tasks\BasicTasks\DisableBasicDisplayStartupTask.cs" />
<Compile Include="Tasks\BasicTasks\DisableVirtualVideoTask.cs" />
<Compile Include="Tasks\BasicTasks\EnableAmdVideoTask.cs" />
<Compile Include="Tasks\BasicTasks\EnableBasicDisplayStartupTask.cs" />
<Compile Include="Tasks\BasicTasks\EnableVirtualVideoTask.cs" />
<Compile Include="Tasks\BasicTasks\FixMonitorTask.cs" />
<Compile Include="Tasks\BasicTasks\LastResortDevicesRestoreTask.cs" />
<Compile Include="Tasks\BasicTasks\ListDevicesTask.cs" />
<Compile Include="Tasks\BasicTasks\SleepTask.cs" />
<Compile Include="Tasks\BasicTasks\StartAudioServiceTask.cs" />
<Compile Include="Tasks\BasicTasks\StopAudioServiceTask.cs" />
<Compile Include="Tasks\ComplexTasks\AbstractSequentialTask.cs" />
<Compile Include="Tasks\ComplexTasks\DiagnoseTask.cs" />
<Compile Include="Tasks\ComplexTasks\ShutdownTask.cs" />
<Compile Include="Tasks\ComplexTasks\StartupTask.cs" />
<Compile Include="Tasks\ITask.cs" />
<Compile Include="Tasks\StopAudioServiceTask.cs" />
<Compile Include="TasksProcessor.cs" />
<Compile Include="ThirdParty\DisableDevice.cs" />
<Compile Include="Devices\DeviceHelper.cs" />
<Compile Include="ThirdParty\MonitorChanger.cs" />
<Compile Include="ThirdParty\ServiceHelpers.cs" />
<Compile Include="ThirdParty\ServicePreshutdownHelpers.cs" />
</ItemGroup>
<ItemGroup>
<None Include=".editorconfig" />
<None Include="App.config" />
<None Include="packages.config" />
<None Include="Properties\app.manifest" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="ProjectInstaller.resx">
@ -125,6 +137,7 @@
<Analyzer Include="..\packages\Microsoft.NetFramework.Analyzers.2.9.8\analyzers\dotnet\cs\Microsoft.NetFramework.Analyzers.dll" />
<Analyzer Include="..\packages\Microsoft.NetFramework.Analyzers.2.9.8\analyzers\dotnet\cs\Microsoft.NetFramework.CSharp.Analyzers.dll" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>

@ -19,15 +19,21 @@
}
}
private static RegistryValuePath PreshutdownOrderPath = new RegistryValuePath(@"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control", "PreshutdownOrder");
private static string BasicDisplayServiceName { get; } = (
EnvironmentHelper.IsWindows8OrNewer()
? "BasicDisplay"
: "vga"
);
private static RegistryValuePath WaitToKillServiceTimeoutPath = new RegistryValuePath(@"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control", "WaitToKillServiceTimeout");
private static RegistryValuePath PreshutdownOrderPath { get; } = new RegistryValuePath(@"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control", "PreshutdownOrder");
private static RegistryValuePath FastRebootPath = new RegistryValuePath(@"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Power", "HiberbootEnabled");
private static RegistryValuePath WaitToKillServiceTimeoutPath { get; } = new RegistryValuePath(@"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control", "WaitToKillServiceTimeout");
private static RegistryValuePath NoInteractiveServicesPath = new RegistryValuePath(@"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Windows", "NoInteractiveServices");
private static RegistryValuePath FastRebootPath { get; } = new RegistryValuePath(@"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Power", "HiberbootEnabled");
private static RegistryValuePath BasicDisplayStartTypePath = new RegistryValuePath(@"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\BasicDisplay", "Start");
private static RegistryValuePath NoInteractiveServicesPath { get; } = new RegistryValuePath(@"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Windows", "NoInteractiveServices");
private static RegistryValuePath BasicDisplayStartTypePath { get; } = new RegistryValuePath(@"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\" + BasicDisplayServiceName, "Start");
private static T GetValue<T>(RegistryValuePath path, T defaultValue = default) => (T)Registry.GetValue(path.KeyName, path.ValueName, defaultValue);

@ -1,4 +1,4 @@
namespace RadeonResetBugFixService.Tasks
namespace RadeonResetBugFixService.Tasks.BasicTasks
{
using System;
using System.Linq;

@ -1,4 +1,4 @@
namespace RadeonResetBugFixService.Tasks
namespace RadeonResetBugFixService.Tasks.BasicTasks
{
using System;
using System.ServiceProcess;

@ -1,4 +1,4 @@
namespace RadeonResetBugFixService.Tasks
namespace RadeonResetBugFixService.Tasks.BasicTasks
{
using Contracts;
using Devices;

@ -1,4 +1,4 @@
namespace RadeonResetBugFixService.Tasks
namespace RadeonResetBugFixService.Tasks.BasicTasks
{
using System;
using Contracts;

@ -1,4 +1,4 @@
namespace RadeonResetBugFixService.Tasks
namespace RadeonResetBugFixService.Tasks.BasicTasks
{
using Contracts;
using Devices;

@ -1,4 +1,4 @@
namespace RadeonResetBugFixService.Tasks
namespace RadeonResetBugFixService.Tasks.BasicTasks
{
using Contracts;
using Devices;

@ -1,4 +1,4 @@
namespace RadeonResetBugFixService.Tasks
namespace RadeonResetBugFixService.Tasks.BasicTasks
{
using Contracts;

@ -1,4 +1,4 @@
namespace RadeonResetBugFixService.Tasks
namespace RadeonResetBugFixService.Tasks.BasicTasks
{
using Contracts;
using Devices;

@ -1,4 +1,4 @@
namespace RadeonResetBugFixService.Tasks
namespace RadeonResetBugFixService.Tasks.BasicTasks
{
using System;
using System.Collections.Generic;

@ -1,4 +1,4 @@
namespace RadeonResetBugFixService.Tasks
namespace RadeonResetBugFixService.Tasks.BasicTasks
{
using System;
using Contracts;

@ -0,0 +1,19 @@
namespace RadeonResetBugFixService.Tasks.BasicTasks
{
using System.Linq;
using Contracts;
using Devices;
class ListDevicesTask : ITask
{
string ITask.TaskName => "Listing devices";
void ITask.Run(ILogger logger)
{
foreach (var device in DeviceHelper.GetDevices().ToArray())
{
logger.Log($"Found device {device.Description}: manufacturer='{device.Manufacturer}', service='{device.Service}', class='{device.ClassName}', isPresent={device.IsPresent}, isDisabled={device.IsDisabled}, errorCode={device.ErrorCode}, isAmdGPU={KnownDevices.IsAmdVideo(device)}, isVirtualVideo={KnownDevices.IsVirtualVideo(device)}");
}
}
}
}

@ -1,4 +1,4 @@
namespace RadeonResetBugFixService.Tasks
namespace RadeonResetBugFixService.Tasks.BasicTasks
{
using System;
using System.Threading;

@ -0,0 +1,15 @@
namespace RadeonResetBugFixService.Tasks.BasicTasks
{
using System;
using System.ServiceProcess;
class StartAudioServiceTask : AbstractServiceTask
{
public override string TaskName => "Stopping audio service";
protected override bool ShouldStart(ServiceController serviceInfo)
{
return serviceInfo.ServiceName.Equals("audiosrv", StringComparison.OrdinalIgnoreCase);
}
}
}

@ -1,4 +1,4 @@
namespace RadeonResetBugFixService.Tasks
namespace RadeonResetBugFixService.Tasks.BasicTasks
{
using System;
using System.ServiceProcess;

@ -0,0 +1,13 @@
namespace RadeonResetBugFixService.Tasks.ComplexTasks
{
using Contracts;
abstract class AbstractSequentialTask : ITask
{
public abstract string TaskName { get; }
protected abstract ITask[] Subtasks { get; }
void ITask.Run(ILogger logger) => TasksProcessor.ProcessTasks(logger, this.Subtasks);
}
}

@ -0,0 +1,16 @@
namespace RadeonResetBugFixService.Tasks.ComplexTasks
{
using BasicTasks;
using Contracts;
using System;
class DiagnoseTask : AbstractSequentialTask
{
public override string TaskName => "Diagnose";
protected override ITask[] Subtasks => new ITask[]
{
new ListDevicesTask(),
};
}
}

@ -0,0 +1,29 @@
namespace RadeonResetBugFixService.Tasks.ComplexTasks
{
using BasicTasks;
using Contracts;
using System;
class ShutdownTask : AbstractSequentialTask
{
public ShutdownTask(ServiceContext context)
{
this.Context = context;
}
private ServiceContext Context { get; }
public override string TaskName => "Shutdown";
protected override ITask[] Subtasks => new ITask[]
{
new StopAudioServiceTask(),
new EnableVirtualVideoTask(this.Context.ShutdownDevicesStatus),
new DisableAmdVideoTask(this.Context.ShutdownDevicesStatus),
new LastResortDevicesRestoreTask(this.Context.StartupDevicesStatus),
new LastResortDevicesRestoreTask(this.Context.StartupDevicesStatus), // just in case
new StartAudioServiceTask(),
new DisableBasicDisplayStartupTask(this.Context.StartupDevicesStatus),
};
}
}

@ -0,0 +1,30 @@
namespace RadeonResetBugFixService.Tasks.ComplexTasks
{
using BasicTasks;
using Contracts;
using System;
class StartupTask : AbstractSequentialTask
{
public StartupTask(ServiceContext context)
{
this.Context = context;
}
private ServiceContext Context { get; }
public override string TaskName => "Startup";
protected override ITask[] Subtasks => new ITask[]
{
new EnableBasicDisplayStartupTask(),
new SleepTask(TimeSpan.FromSeconds(40)),
new EnableAmdVideoTask(this.Context.StartupDevicesStatus),
new DisableVirtualVideoTask(this.Context.StartupDevicesStatus),
new SleepTask(TimeSpan.FromSeconds(20)),
new FixMonitorTask(),
new DisableVirtualVideoTask(this.Context.StartupDevicesStatus),
new FixMonitorTask()
};
}
}

@ -8,7 +8,7 @@
static class TasksProcessor
{
private static void ProcessTask(ILogger logger, ITask task)
public static void ProcessTask(ILogger logger, ITask task)
{
using (ILogger taskLogger = new TaskLoggerWrapper(logger, task.TaskName))
{

Loading…
Cancel
Save