From 3282196d719981637bc6c8e4aebe4642f6dda0e2 Mon Sep 17 00:00:00 2001 From: Inga Lovinde <52715130+inga-lovinde@users.noreply.github.com> Date: Sat, 30 Nov 2019 23:16:51 +0300 Subject: [PATCH] Naive implementation --- RadeonResetBugFix.sln | 25 ++ RadeonResetBugFixService/App.config | 6 + .../Contracts/DeviceInfo.cs | 25 ++ RadeonResetBugFixService/Contracts/ILogger.cs | 10 + .../Devices/DeviceHelper.cs | 83 ++++ .../Devices/KnownDevices.cs | 18 + .../Logging/FileLogger.cs | 26 ++ .../Logging/TaskLoggerWrapper.cs | 26 ++ RadeonResetBugFixService/MainHandler.cs | 66 ++++ RadeonResetBugFixService/Program.cs | 118 ++++++ .../ProjectInstaller.Designer.cs | 62 +++ RadeonResetBugFixService/ProjectInstaller.cs | 29 ++ .../ProjectInstaller.resx | 129 ++++++ .../Properties/AssemblyInfo.cs | 36 ++ .../RadeonResetBugFixService.Designer.cs | 41 ++ .../RadeonResetBugFixService.cs | 37 ++ .../RadeonResetBugFixService.csproj | 99 +++++ .../RadeonResetBugFixService.resx | 123 ++++++ .../Tasks/AbstractDriverTask.cs | 52 +++ .../Tasks/AbstractServiceTask.cs | 80 ++++ .../Tasks/DisableAmdVideoTask.cs | 13 + .../Tasks/DisableVirtualVideoTask.cs | 12 + .../Tasks/EnableAmdVideoTask.cs | 13 + .../Tasks/EnableVirtualVideoTask.cs | 12 + RadeonResetBugFixService/Tasks/ITask.cs | 11 + .../Tasks/StopAudioServiceTask.cs | 14 + RadeonResetBugFixService/TasksProcessor.cs | 33 ++ .../ThirdParty/DisableDevice.cs | 368 ++++++++++++++++++ .../ThirdParty/ServiceHelpers.cs | 122 ++++++ 29 files changed, 1689 insertions(+) create mode 100644 RadeonResetBugFix.sln create mode 100644 RadeonResetBugFixService/App.config create mode 100644 RadeonResetBugFixService/Contracts/DeviceInfo.cs create mode 100644 RadeonResetBugFixService/Contracts/ILogger.cs create mode 100644 RadeonResetBugFixService/Devices/DeviceHelper.cs create mode 100644 RadeonResetBugFixService/Devices/KnownDevices.cs create mode 100644 RadeonResetBugFixService/Logging/FileLogger.cs create mode 100644 RadeonResetBugFixService/Logging/TaskLoggerWrapper.cs create mode 100644 RadeonResetBugFixService/MainHandler.cs create mode 100644 RadeonResetBugFixService/Program.cs create mode 100644 RadeonResetBugFixService/ProjectInstaller.Designer.cs create mode 100644 RadeonResetBugFixService/ProjectInstaller.cs create mode 100644 RadeonResetBugFixService/ProjectInstaller.resx create mode 100644 RadeonResetBugFixService/Properties/AssemblyInfo.cs create mode 100644 RadeonResetBugFixService/RadeonResetBugFixService.Designer.cs create mode 100644 RadeonResetBugFixService/RadeonResetBugFixService.cs create mode 100644 RadeonResetBugFixService/RadeonResetBugFixService.csproj create mode 100644 RadeonResetBugFixService/RadeonResetBugFixService.resx create mode 100644 RadeonResetBugFixService/Tasks/AbstractDriverTask.cs create mode 100644 RadeonResetBugFixService/Tasks/AbstractServiceTask.cs create mode 100644 RadeonResetBugFixService/Tasks/DisableAmdVideoTask.cs create mode 100644 RadeonResetBugFixService/Tasks/DisableVirtualVideoTask.cs create mode 100644 RadeonResetBugFixService/Tasks/EnableAmdVideoTask.cs create mode 100644 RadeonResetBugFixService/Tasks/EnableVirtualVideoTask.cs create mode 100644 RadeonResetBugFixService/Tasks/ITask.cs create mode 100644 RadeonResetBugFixService/Tasks/StopAudioServiceTask.cs create mode 100644 RadeonResetBugFixService/TasksProcessor.cs create mode 100644 RadeonResetBugFixService/ThirdParty/DisableDevice.cs create mode 100644 RadeonResetBugFixService/ThirdParty/ServiceHelpers.cs diff --git a/RadeonResetBugFix.sln b/RadeonResetBugFix.sln new file mode 100644 index 0000000..e09a1e2 --- /dev/null +++ b/RadeonResetBugFix.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29519.87 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RadeonResetBugFixService", "RadeonResetBugFixService\RadeonResetBugFixService.csproj", "{1B9A6DE9-69F9-48B2-B70D-632E5766404A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1B9A6DE9-69F9-48B2-B70D-632E5766404A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1B9A6DE9-69F9-48B2-B70D-632E5766404A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1B9A6DE9-69F9-48B2-B70D-632E5766404A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1B9A6DE9-69F9-48B2-B70D-632E5766404A}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {67C6E2F8-1519-4E55-9C93-1EAD4050026F} + EndGlobalSection +EndGlobal diff --git a/RadeonResetBugFixService/App.config b/RadeonResetBugFixService/App.config new file mode 100644 index 0000000..56efbc7 --- /dev/null +++ b/RadeonResetBugFixService/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/RadeonResetBugFixService/Contracts/DeviceInfo.cs b/RadeonResetBugFixService/Contracts/DeviceInfo.cs new file mode 100644 index 0000000..7fb7ed1 --- /dev/null +++ b/RadeonResetBugFixService/Contracts/DeviceInfo.cs @@ -0,0 +1,25 @@ +namespace RadeonResetBugFixService.Contracts +{ + using System; + + class DeviceInfo + { + public Guid ClassGuid { get; set; } + + public string ClassName { get; set; } + + public string DeviceId { get; set; } + + public long? ErrorCode { get; set; } + + public bool IsDisabled => this.ErrorCode == 22; + + public bool IsPresent { get; set; } + + public string Manufacturer { get; set; } + + public string Name { get; set; } + + public string Service { get; set; } + } +} diff --git a/RadeonResetBugFixService/Contracts/ILogger.cs b/RadeonResetBugFixService/Contracts/ILogger.cs new file mode 100644 index 0000000..e1d9885 --- /dev/null +++ b/RadeonResetBugFixService/Contracts/ILogger.cs @@ -0,0 +1,10 @@ +namespace RadeonResetBugFixService.Contracts +{ + using System; + + interface ILogger : IDisposable + { + void Log(string message); + void LogError(string message); + } +} diff --git a/RadeonResetBugFixService/Devices/DeviceHelper.cs b/RadeonResetBugFixService/Devices/DeviceHelper.cs new file mode 100644 index 0000000..9ae2bac --- /dev/null +++ b/RadeonResetBugFixService/Devices/DeviceHelper.cs @@ -0,0 +1,83 @@ +namespace RadeonResetBugFixService.Devices +{ + using System; + using System.Collections.Generic; + using System.Management; + using Contracts; + + class DeviceHelper + { + private static T GetProperty(PropertyDataCollection properties, string key) + { + try + { + return (T)properties[key].Value; + } + catch (Exception) + { + return default; + } + } + + private static Guid GuidTryParse(string input) + { + Guid.TryParse(input, out var result); + return result; + } + + private static DeviceInfo ConvertDeviceInfo(PropertyDataCollection deviceProperties) + { + return new DeviceInfo + { + ClassGuid = GuidTryParse(GetProperty(deviceProperties, "ClassGuid")), + ClassName = GetProperty(deviceProperties, "PNPClass") ?? string.Empty, + DeviceId = GetProperty(deviceProperties, "PNPDeviceId") ?? string.Empty, + ErrorCode = GetProperty(deviceProperties, "ConfigManagerErrorCode"), + IsPresent = GetProperty(deviceProperties, "Present"), + Manufacturer = GetProperty(deviceProperties, "Manufacturer") ?? string.Empty, + Name = GetProperty(deviceProperties, "Name") ?? string.Empty, + Service = GetProperty(deviceProperties, "Service") ?? string.Empty, + }; + } + + public static IEnumerable GetDevices() + { + ManagementPath path = new ManagementPath + { + Server = ".", + NamespacePath = @"root\CIMV2", + RelativePath = @"Win32_PnPentity", + }; + + using (var devs = new ManagementClass(new ManagementScope(path), path, new ObjectGetOptions(null, TimeSpan.FromMinutes(1), false))) + { + ManagementObjectCollection moc = devs.GetInstances(); + foreach (ManagementObject mo in moc) + { + /*Console.WriteLine("==================================="); + Console.WriteLine("New device: " + mo.Path.Path); + PropertyDataCollection devsProperties = mo.Properties; + foreach (PropertyData devProperty in devsProperties) + { + if (devProperty.Type != CimType.DateTime) + { + Console.WriteLine("Property = {0}\tValue = {1}\tType={2}", devProperty.Name, devProperty.Value, devProperty.Value?.GetType()?.Name); + } + }*/ + + yield return ConvertDeviceInfo(mo.Properties); + } + } + } + + public static void DisableDevice(DeviceInfo deviceInfo) + { + ThirdParty.DisableDevice.DeviceHelper.SetDeviceEnabled(deviceInfo.ClassGuid, deviceInfo.DeviceId, false); + } + + public static void EnableDevice(DeviceInfo deviceInfo) + { + ThirdParty.DisableDevice.DeviceHelper.SetDeviceEnabled(deviceInfo.ClassGuid, deviceInfo.DeviceId, true); + } + } +} diff --git a/RadeonResetBugFixService/Devices/KnownDevices.cs b/RadeonResetBugFixService/Devices/KnownDevices.cs new file mode 100644 index 0000000..814a933 --- /dev/null +++ b/RadeonResetBugFixService/Devices/KnownDevices.cs @@ -0,0 +1,18 @@ +namespace RadeonResetBugFixService.Devices +{ + using Contracts; + + static class KnownDevices + { + public static bool IsAmdVideo(DeviceInfo device) + { + return (device.Manufacturer.ToLowerInvariant() == "amd" || device.Manufacturer.ToLowerInvariant().Contains("advanced micro devices")) && + (device.Service.ToLowerInvariant() == "hdaudbus" || device.ClassName.ToLowerInvariant() == "display"); + } + + public static bool IsVirtualVideo(DeviceInfo device) + { + return device.Service.ToLowerInvariant() == "hypervideo"; + } + } +} diff --git a/RadeonResetBugFixService/Logging/FileLogger.cs b/RadeonResetBugFixService/Logging/FileLogger.cs new file mode 100644 index 0000000..330cebe --- /dev/null +++ b/RadeonResetBugFixService/Logging/FileLogger.cs @@ -0,0 +1,26 @@ +namespace RadeonResetBugFixService.Logging +{ + using System; + using System.IO; + using Contracts; + + class FileLogger : ILogger + { + private string Filename { get; } + + public FileLogger(string filename) + { + this.Filename = filename; + } + + private void LogString(string message) => File.AppendAllLines(this.Filename, new[] { $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] {message}" }); + + void ILogger.Log(string message) => LogString(message); + + void ILogger.LogError(string message) => LogString($"Error: {message}"); + + void IDisposable.Dispose() + { + } + } +} diff --git a/RadeonResetBugFixService/Logging/TaskLoggerWrapper.cs b/RadeonResetBugFixService/Logging/TaskLoggerWrapper.cs new file mode 100644 index 0000000..323a393 --- /dev/null +++ b/RadeonResetBugFixService/Logging/TaskLoggerWrapper.cs @@ -0,0 +1,26 @@ +namespace RadeonResetBugFixService.Logging +{ + using System; + using Contracts; + + class TaskLoggerWrapper : ILogger + { + private ILogger InnerLogger { get; } + + private string Prefix { get; } + + public TaskLoggerWrapper(ILogger innerLogger, string taskName) + { + this.InnerLogger = innerLogger; + this.Prefix = $"[{taskName}]"; + + innerLogger.Log($"{this.Prefix} begin"); + } + + void ILogger.Log(string message) => this.InnerLogger.Log($"{this.Prefix} {message}"); + + void ILogger.LogError(string message) => this.InnerLogger.LogError($"{this.Prefix} {message}"); + + void IDisposable.Dispose() => this.InnerLogger.Log($"{this.Prefix} end"); + } +} diff --git a/RadeonResetBugFixService/MainHandler.cs b/RadeonResetBugFixService/MainHandler.cs new file mode 100644 index 0000000..3d810d5 --- /dev/null +++ b/RadeonResetBugFixService/MainHandler.cs @@ -0,0 +1,66 @@ +namespace RadeonResetBugFixService +{ + using System; + using System.IO; + using System.Reflection; + using Contracts; + using Logging; + using Tasks; + + class MainHandler + { + private string LogFilename { get; } + + private object Mutex = new object(); + + public MainHandler() + { + var date = DateTime.Now; + this.LogFilename = Path.Combine( + Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), + "logs", + $"radeonfix_{date:yyyyMMdd}_{date:HHmmss}.log"); + } + + public void HandleStartup() + { + using (var fileLogger = new FileLogger(this.LogFilename)) + { + using (ILogger logger = new TaskLoggerWrapper(fileLogger, "Startup")) + { + lock (this.Mutex) + { + TasksProcessor.ProcessTasks( + logger, + new ITask[] + { + new DisableVirtualVideoTask(), + new EnableAmdVideoTask(), + }); + } + } + } + } + + public void HandleShutdown() + { + using (var fileLogger = new FileLogger(this.LogFilename)) + { + using (ILogger logger = new TaskLoggerWrapper(fileLogger, "Shutdown")) + { + lock (this.Mutex) + { + TasksProcessor.ProcessTasks( + logger, + new ITask[] + { + new StopAudioServiceTask(), + new DisableAmdVideoTask(), + new EnableVirtualVideoTask(), + }); + } + } + } + } + } +} diff --git a/RadeonResetBugFixService/Program.cs b/RadeonResetBugFixService/Program.cs new file mode 100644 index 0000000..9a5516b --- /dev/null +++ b/RadeonResetBugFixService/Program.cs @@ -0,0 +1,118 @@ +namespace RadeonResetBugFixService +{ + using System; + using System.Security.Principal; + using System.ServiceProcess; + using ThirdParty.ServiceHelpers; + + public static class Program + { + /// + /// The main entry point for the application. + /// + public static int Main(string[] args) + { + if (Environment.UserInteractive) + { + if (!HasAdministratorPrivileges()) + { + Console.Error.WriteLine("Access Denied."); + Console.Error.WriteLine("Administrator permissions are needed to use the selected options."); + Console.Error.WriteLine("Use an administrator command prompt to complete these tasks."); + return 740; // ERROR_ELEVATION_REQUIRED + } + + MainConsole(args); + return 0; + } + else + { + return MainService(); + } + } + + private static int MainService() + { + ServiceBase[] ServicesToRun; + ServicesToRun = new ServiceBase[] + { + new RadeonResetBugFixService() + }; + ServiceBase.Run(ServicesToRun); + + return 0; + } + + private static void MainConsole(string[] args) + { + if (args.Length != 1) + { + ShowHelp(); + return; + } + + switch (args[0].ToLowerInvariant()) + { + case "install": + DoInstall(); + return; + case "uninstall": + DoUninstall(); + return; + case "startup": + DoStartup(); + return; + case "shutdown": + DoShutdown(); + return; + default: + ShowHelp(); + return; + } + } + + private static void ShowHelp() + { + var exeName = Environment.GetCommandLineArgs()[0]; + Console.WriteLine("Usage:"); + Console.WriteLine($"\t{exeName} install"); + Console.WriteLine("\t\tInstalls service"); + Console.WriteLine($"\t{exeName} uninstall"); + Console.WriteLine("\t\tUninstalls service"); + Console.WriteLine($"\t{exeName} startup"); + Console.WriteLine("\t\tPerforms startup sequence"); + Console.WriteLine($"\t{exeName} shutdown"); + Console.WriteLine("\t\tPerforms shutdown sequence"); + } + + private static void DoInstall() + { + ServiceHelpers.InstallService(nameof(RadeonResetBugFixService), typeof(RadeonResetBugFixService)); + ServiceHelpers.StartService(nameof(RadeonResetBugFixService), typeof(RadeonResetBugFixService)); + } + + private static void DoUninstall() + { + ServiceHelpers.StopService(nameof(RadeonResetBugFixService), typeof(RadeonResetBugFixService)); + ServiceHelpers.UninstallService(nameof(RadeonResetBugFixService), typeof(RadeonResetBugFixService)); + } + + private static void DoStartup() + { + new MainHandler().HandleStartup(); + } + + private static void DoShutdown() + { + new MainHandler().HandleShutdown(); + } + + // 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); + } + } +} diff --git a/RadeonResetBugFixService/ProjectInstaller.Designer.cs b/RadeonResetBugFixService/ProjectInstaller.Designer.cs new file mode 100644 index 0000000..16bec57 --- /dev/null +++ b/RadeonResetBugFixService/ProjectInstaller.Designer.cs @@ -0,0 +1,62 @@ +namespace RadeonResetBugFixService +{ + partial class ProjectInstaller + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.serviceProcessInstaller1 = new System.ServiceProcess.ServiceProcessInstaller(); + this.serviceInstaller1 = new System.ServiceProcess.ServiceInstaller(); + // + // serviceProcessInstaller1 + // + this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem; + this.serviceProcessInstaller1.Password = null; + this.serviceProcessInstaller1.Username = null; + this.serviceProcessInstaller1.AfterInstall += new System.Configuration.Install.InstallEventHandler(this.serviceProcessInstaller1_AfterInstall); + // + // serviceInstaller1 + // + this.serviceInstaller1.Description = "https://github.com/inga-lovinde/RadeonResetBugFixService"; + this.serviceInstaller1.DisplayName = "Radeon Reset Bug fixing service"; + this.serviceInstaller1.ServiceName = "RadeonResetBugFixService"; + this.serviceInstaller1.StartType = System.ServiceProcess.ServiceStartMode.Automatic; + this.serviceInstaller1.AfterInstall += new System.Configuration.Install.InstallEventHandler(this.serviceInstaller1_AfterInstall); + // + // ProjectInstaller + // + this.Installers.AddRange(new System.Configuration.Install.Installer[] { + this.serviceProcessInstaller1, + this.serviceInstaller1}); + + } + + #endregion + + private System.ServiceProcess.ServiceProcessInstaller serviceProcessInstaller1; + private System.ServiceProcess.ServiceInstaller serviceInstaller1; + } +} \ No newline at end of file diff --git a/RadeonResetBugFixService/ProjectInstaller.cs b/RadeonResetBugFixService/ProjectInstaller.cs new file mode 100644 index 0000000..d48a816 --- /dev/null +++ b/RadeonResetBugFixService/ProjectInstaller.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Configuration.Install; +using System.Linq; +using System.Threading.Tasks; + +namespace RadeonResetBugFixService +{ + [RunInstaller(true)] + public partial class ProjectInstaller : System.Configuration.Install.Installer + { + public ProjectInstaller() + { + InitializeComponent(); + } + + private void serviceInstaller1_AfterInstall(object sender, InstallEventArgs e) + { + + } + + private void serviceProcessInstaller1_AfterInstall(object sender, InstallEventArgs e) + { + + } + } +} diff --git a/RadeonResetBugFixService/ProjectInstaller.resx b/RadeonResetBugFixService/ProjectInstaller.resx new file mode 100644 index 0000000..ba986de --- /dev/null +++ b/RadeonResetBugFixService/ProjectInstaller.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 60 + + + 229, 17 + + + False + + \ No newline at end of file diff --git a/RadeonResetBugFixService/Properties/AssemblyInfo.cs b/RadeonResetBugFixService/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..9aa02c4 --- /dev/null +++ b/RadeonResetBugFixService/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("RadeonResetBugFixService")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("RadeonResetBugFixService")] +[assembly: AssemblyCopyright("Copyright © 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("1b9a6de9-69f9-48b2-b70d-632e5766404a")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// 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")] diff --git a/RadeonResetBugFixService/RadeonResetBugFixService.Designer.cs b/RadeonResetBugFixService/RadeonResetBugFixService.Designer.cs new file mode 100644 index 0000000..5f83ddc --- /dev/null +++ b/RadeonResetBugFixService/RadeonResetBugFixService.Designer.cs @@ -0,0 +1,41 @@ +namespace RadeonResetBugFixService +{ + partial class RadeonResetBugFixService + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + // + // RadeonResetBugFixService + // + this.CanShutdown = true; + this.ServiceName = "RadeonResetBugFixService"; + + } + + #endregion + } +} diff --git a/RadeonResetBugFixService/RadeonResetBugFixService.cs b/RadeonResetBugFixService/RadeonResetBugFixService.cs new file mode 100644 index 0000000..6740ba3 --- /dev/null +++ b/RadeonResetBugFixService/RadeonResetBugFixService.cs @@ -0,0 +1,37 @@ +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 +{ + public partial class RadeonResetBugFixService : ServiceBase + { + private MainHandler Handler { get; } = new MainHandler(); + + public RadeonResetBugFixService() + { + InitializeComponent(); + } + + protected override void OnShutdown() + { + this.Handler.HandleShutdown(); + } + + protected override void OnStart(string[] args) + { + this.Handler.HandleStartup(); + } + + protected override void OnStop() + { + this.Handler.HandleShutdown(); + } + } +} diff --git a/RadeonResetBugFixService/RadeonResetBugFixService.csproj b/RadeonResetBugFixService/RadeonResetBugFixService.csproj new file mode 100644 index 0000000..6568400 --- /dev/null +++ b/RadeonResetBugFixService/RadeonResetBugFixService.csproj @@ -0,0 +1,99 @@ + + + + + Debug + AnyCPU + {1B9A6DE9-69F9-48B2-B70D-632E5766404A} + Exe + RadeonResetBugFixService + RadeonResetBugFixService + v4.7.2 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + + + + + + + + + + + + + + + + + + + + + + + + + Component + + + ProjectInstaller.cs + + + Component + + + RadeonResetBugFixService.cs + + + + + + + + + + + + + + + + + + + + + + + ProjectInstaller.cs + + + RadeonResetBugFixService.cs + + + + \ No newline at end of file diff --git a/RadeonResetBugFixService/RadeonResetBugFixService.resx b/RadeonResetBugFixService/RadeonResetBugFixService.resx new file mode 100644 index 0000000..e5858cc --- /dev/null +++ b/RadeonResetBugFixService/RadeonResetBugFixService.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + False + + \ No newline at end of file diff --git a/RadeonResetBugFixService/Tasks/AbstractDriverTask.cs b/RadeonResetBugFixService/Tasks/AbstractDriverTask.cs new file mode 100644 index 0000000..36d7a6a --- /dev/null +++ b/RadeonResetBugFixService/Tasks/AbstractDriverTask.cs @@ -0,0 +1,52 @@ +namespace RadeonResetBugFixService.Tasks +{ + using System.Linq; + using Contracts; + using Devices; + + abstract class AbstractDriverTask : ITask + { + protected virtual bool ShouldDisable(DeviceInfo deviceInfo) => false; + + protected virtual bool ShouldEnable(DeviceInfo deviceInfo) => false; + + public abstract string TaskName { get; } + + void ITask.Run(ILogger logger) + { + var devices = DeviceHelper.GetDevices().ToArray(); + foreach (var device in devices) + { + //logger.Log($"Present({device.IsPresent}) ErrorCode({device.ErrorCode}) Disabled({device.IsDisabled}) ClassName({device.ClassName}) Service({device.Service}) Manufacturer({device.Manufacturer}) Name({device.Name}) Id({device.DeviceId}) ClassGuid({device.ClassGuid})"); + var deviceDescription = $"{device.Name} ({device.DeviceId}, {device.ClassGuid})"; + + if (this.ShouldDisable(device)) + { + if (device.IsDisabled) + { + logger.Log($"{deviceDescription} already disabled"); + } + else + { + logger.Log($"Disabling {deviceDescription}"); + DeviceHelper.DisableDevice(device); + logger.Log($"Disabled {deviceDescription}"); + } + } + else if (this.ShouldEnable(device)) + { + if (!device.IsDisabled) + { + logger.Log($"{deviceDescription} already enabled"); + } + else + { + logger.Log($"Enabling {deviceDescription}"); + DeviceHelper.EnableDevice(device); + logger.Log($"Enabled {deviceDescription}"); + } + } + } + } + } +} diff --git a/RadeonResetBugFixService/Tasks/AbstractServiceTask.cs b/RadeonResetBugFixService/Tasks/AbstractServiceTask.cs new file mode 100644 index 0000000..621233b --- /dev/null +++ b/RadeonResetBugFixService/Tasks/AbstractServiceTask.cs @@ -0,0 +1,80 @@ +namespace RadeonResetBugFixService.Tasks +{ + using System; + using System.ServiceProcess; + using Contracts; + + abstract class AbstractServiceTask : ITask + { + protected virtual bool ShouldStart(ServiceController serviceInfo) => false; + + protected virtual bool ShouldStop(ServiceController serviceInfo) => false; + + public abstract string TaskName { get; } + + void ITask.Run(ILogger logger) + { + foreach (var originalService in ServiceController.GetServices()) + { + string serviceDescription = $"{originalService.DisplayName} ({originalService.ServiceName})"; + + if (this.ShouldStart(originalService)) + { + var service = new ServiceController(originalService.ServiceName); + if (service.Status == ServiceControllerStatus.Running) + { + logger.Log($"{serviceDescription} is already running"); + } + else + { + if (service.Status != ServiceControllerStatus.StartPending) + { + logger.Log($"Starting service {serviceDescription}"); + service.Start(); + logger.Log($"Initiated service start for"); + } + + logger.Log($"Waiting for service {serviceDescription} to start"); + service.WaitForStatus(ServiceControllerStatus.Running, TimeSpan.FromSeconds(30)); + if (service.Status == ServiceControllerStatus.Running) + { + logger.Log($"Service is running"); + } + else + { + logger.Log($"Failed; service state is {service.Status}"); + } + } + } + else if (this.ShouldStop(originalService)) + { + var service = new ServiceController(originalService.ServiceName); + if (service.Status == ServiceControllerStatus.Stopped) + { + logger.Log($"{serviceDescription} is already stopped"); + } + else + { + if (service.Status != ServiceControllerStatus.StopPending) + { + logger.Log($"Stopping service {serviceDescription}"); + service.Stop(); + logger.Log($"Initiated service stop"); + } + + logger.Log($"Waiting for service {serviceDescription} to stop"); + service.WaitForStatus(ServiceControllerStatus.Stopped, TimeSpan.FromSeconds(15)); + if (service.Status == ServiceControllerStatus.Stopped) + { + logger.Log($"Service is stopped"); + } + else + { + logger.Log($"Failed; service state is {service.Status}"); + } + } + } + } + } + } +} diff --git a/RadeonResetBugFixService/Tasks/DisableAmdVideoTask.cs b/RadeonResetBugFixService/Tasks/DisableAmdVideoTask.cs new file mode 100644 index 0000000..fe63086 --- /dev/null +++ b/RadeonResetBugFixService/Tasks/DisableAmdVideoTask.cs @@ -0,0 +1,13 @@ +using RadeonResetBugFixService.Contracts; + +namespace RadeonResetBugFixService.Tasks +{ + using Contracts; + using Devices; + + class DisableAmdVideoTask : AbstractDriverTask + { + public override string TaskName => "Disabling AMD video"; + protected override bool ShouldDisable(DeviceInfo deviceInfo) => KnownDevices.IsAmdVideo(deviceInfo); + } +} diff --git a/RadeonResetBugFixService/Tasks/DisableVirtualVideoTask.cs b/RadeonResetBugFixService/Tasks/DisableVirtualVideoTask.cs new file mode 100644 index 0000000..fac89e2 --- /dev/null +++ b/RadeonResetBugFixService/Tasks/DisableVirtualVideoTask.cs @@ -0,0 +1,12 @@ +namespace RadeonResetBugFixService.Tasks +{ + using Contracts; + using Devices; + + class DisableVirtualVideoTask : AbstractDriverTask + { + public override string TaskName => "Disabling virtual video"; + + protected override bool ShouldDisable(DeviceInfo deviceInfo) => KnownDevices.IsVirtualVideo(deviceInfo); + } +} diff --git a/RadeonResetBugFixService/Tasks/EnableAmdVideoTask.cs b/RadeonResetBugFixService/Tasks/EnableAmdVideoTask.cs new file mode 100644 index 0000000..fc82a45 --- /dev/null +++ b/RadeonResetBugFixService/Tasks/EnableAmdVideoTask.cs @@ -0,0 +1,13 @@ +namespace RadeonResetBugFixService.Tasks +{ + using Contracts; + using Devices; + + class EnableAmdVideoTask : AbstractDriverTask + { + public override string TaskName => "Enabling AMD video"; + + protected override bool ShouldEnable(DeviceInfo deviceInfo) => KnownDevices.IsAmdVideo(deviceInfo); + + } +} diff --git a/RadeonResetBugFixService/Tasks/EnableVirtualVideoTask.cs b/RadeonResetBugFixService/Tasks/EnableVirtualVideoTask.cs new file mode 100644 index 0000000..83cd82e --- /dev/null +++ b/RadeonResetBugFixService/Tasks/EnableVirtualVideoTask.cs @@ -0,0 +1,12 @@ +namespace RadeonResetBugFixService.Tasks +{ + using Contracts; + using Devices; + + class EnableVirtualVideoTask : AbstractDriverTask + { + public override string TaskName => "Enabling virtual video"; + + protected override bool ShouldEnable(DeviceInfo deviceInfo) => KnownDevices.IsVirtualVideo(deviceInfo); + } +} diff --git a/RadeonResetBugFixService/Tasks/ITask.cs b/RadeonResetBugFixService/Tasks/ITask.cs new file mode 100644 index 0000000..e7725a0 --- /dev/null +++ b/RadeonResetBugFixService/Tasks/ITask.cs @@ -0,0 +1,11 @@ +namespace RadeonResetBugFixService.Tasks +{ + using Contracts; + + interface ITask + { + string TaskName { get; } + + void Run(ILogger logger); + } +} diff --git a/RadeonResetBugFixService/Tasks/StopAudioServiceTask.cs b/RadeonResetBugFixService/Tasks/StopAudioServiceTask.cs new file mode 100644 index 0000000..fcbb3c7 --- /dev/null +++ b/RadeonResetBugFixService/Tasks/StopAudioServiceTask.cs @@ -0,0 +1,14 @@ +using System.ServiceProcess; + +namespace RadeonResetBugFixService.Tasks +{ + class StopAudioServiceTask : AbstractServiceTask + { + public override string TaskName => "Stopping audio service"; + + protected override bool ShouldStop(ServiceController serviceInfo) + { + return serviceInfo.ServiceName.ToLowerInvariant() == "audiosrv"; + } + } +} diff --git a/RadeonResetBugFixService/TasksProcessor.cs b/RadeonResetBugFixService/TasksProcessor.cs new file mode 100644 index 0000000..beba7e0 --- /dev/null +++ b/RadeonResetBugFixService/TasksProcessor.cs @@ -0,0 +1,33 @@ +namespace RadeonResetBugFixService +{ + using System; + using Contracts; + using Logging; + using Tasks; + + static class TasksProcessor + { + private static void ProcessTask(ILogger logger, ITask task) + { + using (ILogger taskLogger = new TaskLoggerWrapper(logger, task.TaskName)) + { + try + { + task.Run(taskLogger); + } + catch (Exception e) + { + taskLogger.LogError(e.ToString()); + } + } + } + + public static void ProcessTasks(ILogger logger, ITask[] tasks) + { + foreach (var task in tasks) + { + ProcessTask(logger, task); + } + } + } +} diff --git a/RadeonResetBugFixService/ThirdParty/DisableDevice.cs b/RadeonResetBugFixService/ThirdParty/DisableDevice.cs new file mode 100644 index 0000000..d5eaf5f --- /dev/null +++ b/RadeonResetBugFixService/ThirdParty/DisableDevice.cs @@ -0,0 +1,368 @@ +namespace RadeonResetBugFixService.ThirdParty.DisableDevice +{ + // Code taken from https://stackoverflow.com/a/1610140 + + using System; + using System.Text; + using System.Collections.Generic; + using System.Runtime.InteropServices; + using System.ComponentModel; + using Microsoft.Win32.SafeHandles; + using System.Security; + using System.Runtime.ConstrainedExecution; + + + [Flags()] + internal enum SetupDiGetClassDevsFlags + { + Default = 1, + Present = 2, + AllClasses = 4, + Profile = 8, + DeviceInterface = (int)0x10 + } + + internal enum DiFunction + { + SelectDevice = 1, + InstallDevice = 2, + AssignResources = 3, + Properties = 4, + Remove = 5, + FirstTimeSetup = 6, + FoundDevice = 7, + SelectClassDrivers = 8, + ValidateClassDrivers = 9, + InstallClassDrivers = (int)0xa, + CalcDiskSpace = (int)0xb, + DestroyPrivateData = (int)0xc, + ValidateDriver = (int)0xd, + Detect = (int)0xf, + InstallWizard = (int)0x10, + DestroyWizardData = (int)0x11, + PropertyChange = (int)0x12, + EnableClass = (int)0x13, + DetectVerify = (int)0x14, + InstallDeviceFiles = (int)0x15, + UnRemove = (int)0x16, + SelectBestCompatDrv = (int)0x17, + AllowInstall = (int)0x18, + RegisterDevice = (int)0x19, + NewDeviceWizardPreSelect = (int)0x1a, + NewDeviceWizardSelect = (int)0x1b, + NewDeviceWizardPreAnalyze = (int)0x1c, + NewDeviceWizardPostAnalyze = (int)0x1d, + NewDeviceWizardFinishInstall = (int)0x1e, + Unused1 = (int)0x1f, + InstallInterfaces = (int)0x20, + DetectCancel = (int)0x21, + RegisterCoInstallers = (int)0x22, + AddPropertyPageAdvanced = (int)0x23, + AddPropertyPageBasic = (int)0x24, + Reserved1 = (int)0x25, + Troubleshooter = (int)0x26, + PowerMessageWake = (int)0x27, + AddRemotePropertyPageAdvanced = (int)0x28, + UpdateDriverUI = (int)0x29, + Reserved2 = (int)0x30 + } + + internal enum StateChangeAction + { + Enable = 1, + Disable = 2, + PropChange = 3, + Start = 4, + Stop = 5 + } + + [Flags()] + internal enum Scopes + { + Global = 1, + ConfigSpecific = 2, + ConfigGeneral = 4 + } + + internal enum SetupApiError + { + NoAssociatedClass = unchecked((int)0xe0000200), + ClassMismatch = unchecked((int)0xe0000201), + DuplicateFound = unchecked((int)0xe0000202), + NoDriverSelected = unchecked((int)0xe0000203), + KeyDoesNotExist = unchecked((int)0xe0000204), + InvalidDevinstName = unchecked((int)0xe0000205), + InvalidClass = unchecked((int)0xe0000206), + DevinstAlreadyExists = unchecked((int)0xe0000207), + DevinfoNotRegistered = unchecked((int)0xe0000208), + InvalidRegProperty = unchecked((int)0xe0000209), + NoInf = unchecked((int)0xe000020a), + NoSuchHDevinst = unchecked((int)0xe000020b), + CantLoadClassIcon = unchecked((int)0xe000020c), + InvalidClassInstaller = unchecked((int)0xe000020d), + DiDoDefault = unchecked((int)0xe000020e), + DiNoFileCopy = unchecked((int)0xe000020f), + InvalidHwProfile = unchecked((int)0xe0000210), + NoDeviceSelected = unchecked((int)0xe0000211), + DevinfolistLocked = unchecked((int)0xe0000212), + DevinfodataLocked = unchecked((int)0xe0000213), + DiBadPath = unchecked((int)0xe0000214), + NoClassInstallParams = unchecked((int)0xe0000215), + FileQueueLocked = unchecked((int)0xe0000216), + BadServiceInstallSect = unchecked((int)0xe0000217), + NoClassDriverList = unchecked((int)0xe0000218), + NoAssociatedService = unchecked((int)0xe0000219), + NoDefaultDeviceInterface = unchecked((int)0xe000021a), + DeviceInterfaceActive = unchecked((int)0xe000021b), + DeviceInterfaceRemoved = unchecked((int)0xe000021c), + BadInterfaceInstallSect = unchecked((int)0xe000021d), + NoSuchInterfaceClass = unchecked((int)0xe000021e), + InvalidReferenceString = unchecked((int)0xe000021f), + InvalidMachineName = unchecked((int)0xe0000220), + RemoteCommFailure = unchecked((int)0xe0000221), + MachineUnavailable = unchecked((int)0xe0000222), + NoConfigMgrServices = unchecked((int)0xe0000223), + InvalidPropPageProvider = unchecked((int)0xe0000224), + NoSuchDeviceInterface = unchecked((int)0xe0000225), + DiPostProcessingRequired = unchecked((int)0xe0000226), + InvalidCOInstaller = unchecked((int)0xe0000227), + NoCompatDrivers = unchecked((int)0xe0000228), + NoDeviceIcon = unchecked((int)0xe0000229), + InvalidInfLogConfig = unchecked((int)0xe000022a), + DiDontInstall = unchecked((int)0xe000022b), + InvalidFilterDriver = unchecked((int)0xe000022c), + NonWindowsNTDriver = unchecked((int)0xe000022d), + NonWindowsDriver = unchecked((int)0xe000022e), + NoCatalogForOemInf = unchecked((int)0xe000022f), + DevInstallQueueNonNative = unchecked((int)0xe0000230), + NotDisableable = unchecked((int)0xe0000231), + CantRemoveDevinst = unchecked((int)0xe0000232), + InvalidTarget = unchecked((int)0xe0000233), + DriverNonNative = unchecked((int)0xe0000234), + InWow64 = unchecked((int)0xe0000235), + SetSystemRestorePoint = unchecked((int)0xe0000236), + IncorrectlyCopiedInf = unchecked((int)0xe0000237), + SceDisabled = unchecked((int)0xe0000238), + UnknownException = unchecked((int)0xe0000239), + PnpRegistryError = unchecked((int)0xe000023a), + RemoteRequestUnsupported = unchecked((int)0xe000023b), + NotAnInstalledOemInf = unchecked((int)0xe000023c), + InfInUseByDevices = unchecked((int)0xe000023d), + DiFunctionObsolete = unchecked((int)0xe000023e), + NoAuthenticodeCatalog = unchecked((int)0xe000023f), + AuthenticodeDisallowed = unchecked((int)0xe0000240), + AuthenticodeTrustedPublisher = unchecked((int)0xe0000241), + AuthenticodeTrustNotEstablished = unchecked((int)0xe0000242), + AuthenticodePublisherNotTrusted = unchecked((int)0xe0000243), + SignatureOSAttributeMismatch = unchecked((int)0xe0000244), + OnlyValidateViaAuthenticode = unchecked((int)0xe0000245) + } + + [StructLayout(LayoutKind.Sequential)] + internal struct DeviceInfoData + { + public int Size; + public Guid ClassGuid; + public int DevInst; + public IntPtr Reserved; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct PropertyChangeParameters + { + public int Size; + // part of header. It's flattened out into 1 structure. + public DiFunction DiFunction; + public StateChangeAction StateChange; + public Scopes Scope; + public int HwProfile; + } + + internal class NativeMethods + { + + private const string setupapi = "setupapi.dll"; + + private NativeMethods() + { + } + + [DllImport(setupapi, CallingConvention = CallingConvention.Winapi, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SetupDiCallClassInstaller(DiFunction installFunction, SafeDeviceInfoSetHandle deviceInfoSet, [In()] +ref DeviceInfoData deviceInfoData); + + [DllImport(setupapi, CallingConvention = CallingConvention.Winapi, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SetupDiEnumDeviceInfo(SafeDeviceInfoSetHandle deviceInfoSet, int memberIndex, ref DeviceInfoData deviceInfoData); + + [DllImport(setupapi, CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode, SetLastError = true)] + public static extern SafeDeviceInfoSetHandle SetupDiGetClassDevs([In()] +ref Guid classGuid, [MarshalAs(UnmanagedType.LPWStr)] +string enumerator, IntPtr hwndParent, SetupDiGetClassDevsFlags flags); + + /* + [DllImport(setupapi, CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SetupDiGetDeviceInstanceId(SafeDeviceInfoSetHandle deviceInfoSet, [In()] +ref DeviceInfoData did, [MarshalAs(UnmanagedType.LPTStr)] +StringBuilder deviceInstanceId, int deviceInstanceIdSize, [Out()] +ref int requiredSize); + */ + [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SetupDiGetDeviceInstanceId( + IntPtr DeviceInfoSet, + ref DeviceInfoData did, + [MarshalAs(UnmanagedType.LPTStr)] StringBuilder DeviceInstanceId, + int DeviceInstanceIdSize, + out int RequiredSize + ); + + [SuppressUnmanagedCodeSecurity()] + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + [DllImport(setupapi, CallingConvention = CallingConvention.Winapi, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SetupDiDestroyDeviceInfoList(IntPtr deviceInfoSet); + + [DllImport(setupapi, CallingConvention = CallingConvention.Winapi, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SetupDiSetClassInstallParams(SafeDeviceInfoSetHandle deviceInfoSet, [In()] +ref DeviceInfoData deviceInfoData, [In()] +ref PropertyChangeParameters classInstallParams, int classInstallParamsSize); + + } + + internal class SafeDeviceInfoSetHandle : SafeHandleZeroOrMinusOneIsInvalid + { + + public SafeDeviceInfoSetHandle() + : base(true) + { + } + + protected override bool ReleaseHandle() + { + return NativeMethods.SetupDiDestroyDeviceInfoList(this.handle); + } + + } + + public sealed class DeviceHelper + { + + private DeviceHelper() + { + } + + /// + /// Enable or disable a device. + /// + /// The class guid of the device. Available in the device manager. + /// The device instance id of the device. Available in the device manager. + /// True to enable, False to disable. + /// Will throw an exception if the device is not Disableable. + public static void SetDeviceEnabled(Guid classGuid, string instanceId, bool enable) + { + SafeDeviceInfoSetHandle diSetHandle = null; + try + { + // Get the handle to a device information set for all devices matching classGuid that are present on the + // system. + diSetHandle = NativeMethods.SetupDiGetClassDevs(ref classGuid, null, IntPtr.Zero, SetupDiGetClassDevsFlags.Present); + // Get the device information data for each matching device. + DeviceInfoData[] diData = GetDeviceInfoData(diSetHandle); + // Find the index of our instance. i.e. the touchpad mouse - I have 3 mice attached... + int index = GetIndexOfInstance(diSetHandle, diData, instanceId); + // Disable... + EnableDevice(diSetHandle, diData[index], enable); + } + finally + { + if (diSetHandle != null) + { + if (diSetHandle.IsClosed == false) + { + diSetHandle.Close(); + } + diSetHandle.Dispose(); + } + } + } + + private static DeviceInfoData[] GetDeviceInfoData(SafeDeviceInfoSetHandle handle) + { + List data = new List(); + DeviceInfoData did = new DeviceInfoData(); + int didSize = Marshal.SizeOf(did); + did.Size = didSize; + int index = 0; + while (NativeMethods.SetupDiEnumDeviceInfo(handle, index, ref did)) + { + data.Add(did); + index += 1; + did = new DeviceInfoData(); + did.Size = didSize; + } + return data.ToArray(); + } + + // Find the index of the particular DeviceInfoData for the instanceId. + private static int GetIndexOfInstance(SafeDeviceInfoSetHandle handle, DeviceInfoData[] diData, string instanceId) + { + const int ERROR_INSUFFICIENT_BUFFER = 122; + for (int index = 0; index <= diData.Length - 1; index++) + { + StringBuilder sb = new StringBuilder(1); + int requiredSize = 0; + bool result = NativeMethods.SetupDiGetDeviceInstanceId(handle.DangerousGetHandle(), ref diData[index], sb, sb.Capacity, out requiredSize); + if (result == false && Marshal.GetLastWin32Error() == ERROR_INSUFFICIENT_BUFFER) + { + sb.Capacity = requiredSize; + result = NativeMethods.SetupDiGetDeviceInstanceId(handle.DangerousGetHandle(), ref diData[index], sb, sb.Capacity, out requiredSize); + } + if (result == false) + throw new Win32Exception(); + if (instanceId.Equals(sb.ToString())) + { + return index; + } + } + // not found + return -1; + } + + // enable/disable... + private static void EnableDevice(SafeDeviceInfoSetHandle handle, DeviceInfoData diData, bool enable) + { + PropertyChangeParameters @params = new PropertyChangeParameters(); + // The size is just the size of the header, but we've flattened the structure. + // The header comprises the first two fields, both integer. + @params.Size = 8; + @params.DiFunction = DiFunction.PropertyChange; + @params.Scope = Scopes.Global; + if (enable) + { + @params.StateChange = StateChangeAction.Enable; + } + else + { + @params.StateChange = StateChangeAction.Disable; + } + + bool result = NativeMethods.SetupDiSetClassInstallParams(handle, ref diData, ref @params, Marshal.SizeOf(@params)); + if (result == false) throw new Win32Exception(); + result = NativeMethods.SetupDiCallClassInstaller(DiFunction.PropertyChange, handle, ref diData); + if (result == false) + { + int err = Marshal.GetLastWin32Error(); + if (err == (int)SetupApiError.NotDisableable) + throw new ArgumentException("Device can't be disabled (programmatically or in Device Manager)."); + else if (err >= (int)SetupApiError.NoAssociatedClass && err <= (int)SetupApiError.OnlyValidateViaAuthenticode) + throw new Win32Exception("SetupAPI error: " + ((SetupApiError)err).ToString()); + else + throw new Win32Exception(); + } + } + } +} diff --git a/RadeonResetBugFixService/ThirdParty/ServiceHelpers.cs b/RadeonResetBugFixService/ThirdParty/ServiceHelpers.cs new file mode 100644 index 0000000..2997913 --- /dev/null +++ b/RadeonResetBugFixService/ThirdParty/ServiceHelpers.cs @@ -0,0 +1,122 @@ +namespace RadeonResetBugFixService.ThirdParty.ServiceHelpers +{ + // Code taken from https://stackoverflow.com/a/1195621 and lightly modified + + using System; + using System.Collections; + using System.Configuration.Install; + using System.ServiceProcess; + + class ServiceHelpers + { + public static bool IsInstalled(string serviceName) + { + using (ServiceController controller = new ServiceController(serviceName)) + { + try + { + ServiceControllerStatus status = controller.Status; + } + catch + { + return false; + } + return true; + } + } + + public static bool IsRunning(string serviceName) + { + using (ServiceController controller = new ServiceController(serviceName)) + { + if (!IsInstalled(serviceName)) return false; + return (controller.Status == ServiceControllerStatus.Running); + } + } + + public static AssemblyInstaller GetInstaller(Type serviceType) + { + AssemblyInstaller installer = new AssemblyInstaller(serviceType.Assembly, null); + installer.UseNewContext = true; + return installer; + } + + public static void InstallService(string serviceName, Type serviceType) + { + if (IsInstalled(serviceName)) + { + Console.WriteLine("Already installed"); + return; + } + + using (AssemblyInstaller installer = GetInstaller(serviceType)) + { + IDictionary state = new Hashtable(); + try + { + installer.Install(state); + installer.Commit(state); + Console.WriteLine("installed"); + } + catch + { + try + { + installer.Rollback(state); + } + catch { } + throw; + } + } + } + + public static void UninstallService(string serviceName, Type serviceType) + { + if (!IsInstalled(serviceName)) + { + Console.WriteLine("Service not installed"); + return; + } + + using (AssemblyInstaller installer = GetInstaller(serviceType)) + { + IDictionary state = new Hashtable(); + try + { + installer.Uninstall(state); + } + catch + { + throw; + } + } + } + + public static void StartService(string serviceName, Type serviceType) + { + if (!IsInstalled(serviceName)) return; + + using (ServiceController controller = new ServiceController(serviceName)) + { + if (controller.Status != ServiceControllerStatus.Running) + { + controller.Start(); + controller.WaitForStatus(ServiceControllerStatus.Running, TimeSpan.FromSeconds(10)); + } + } + } + + public static void StopService(string serviceName, Type serviceType) + { + if (!IsInstalled(serviceName)) return; + using (ServiceController controller = new ServiceController(serviceName)) + { + if (controller.Status != ServiceControllerStatus.Stopped) + { + controller.Stop(); + controller.WaitForStatus(ServiceControllerStatus.Stopped, TimeSpan.FromSeconds(10)); + } + } + } + } +}