From bc9c3199b76c44aaf5faffd50883baf9fb97ae46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Trzci=C5=84ski?= Date: Wed, 8 Feb 2023 19:53:02 +0100 Subject: [PATCH] PowerControl: Support RTSS in custom folder --- CommonHelpers/Dependencies.cs | 141 ++++++++++++++++++++++++------- CommonHelpers/RTSS.cs | 75 ++++++++-------- PowerControl/Options/FPSLimit.cs | 13 ++- RELEASE.md | 1 + 4 files changed, 158 insertions(+), 72 deletions(-) diff --git a/CommonHelpers/Dependencies.cs b/CommonHelpers/Dependencies.cs index 3368268..df52dfa 100644 --- a/CommonHelpers/Dependencies.cs +++ b/CommonHelpers/Dependencies.cs @@ -22,7 +22,7 @@ namespace CommonHelpers private static string[] RTSS = new string[] { - "C:\\Program Files (x86)\\RivaTuner Statistics Server\\RTSSHooks64.dll" + "RTSSHooks64.dll" }; private static string VCRuntimeURL = "https://aka.ms/vs/17/release/vc_redist.x64.exe"; @@ -40,87 +40,163 @@ namespace CommonHelpers ValidateDependency(title, "RTSSSharedMemoryNET", RTSSShared, false); } - public static void ValidateRTSS(string title) + public static bool EnsureRTSS(string? title = null) { - InstallDependency(title, "Rivatuner Statistics Server", RTSS, RTSSURL, false, false); + string? libraryPath = null; + + try + { + libraryPath = Microsoft.Win32.Registry.GetValue( + @"HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Unwinder\RTSS", + "InstallDir", + null + ) as string; + } + catch + { + } + + return EnsureDependency(title, "Rivatuner Statistics Server", RTSS, libraryPath, RTSSURL, false); } private static void ValidateVCRuntime(string title) { - InstallDependency(title, "Microsoft Visual C++ Runtime", VCRuntime, VCRuntimeURL, true, false); + InstallDependency(title, "Microsoft Visual C++ Runtime", VCRuntime, null, VCRuntimeURL, true, false); } private static void ValidateDependency(string title, string name, string[] dllNames, bool unload = true) { - if (TryToLoad(dllNames, unload)) + if (TryToLoad(dllNames, null, unload)) return; Log.TraceError("Cannot load: {0}", dllNames); - MessageBox.Show( - "Cannot load: " + string.Join(", ", dllNames) + ".\n\n" + - "Application will exit.\n", + var result = ShowDialog( title, - MessageBoxButtons.OK, - MessageBoxIcon.Error + name, + "Failure in loading the " + string.Join(", ", dllNames) + ".\n\n" + + "This is required dependency. Application will exit.\n", + null, + TaskDialogButton.Close ); Environment.Exit(1); } - private static void InstallDependency(string title, string name, string[] dllNames, string url, bool required = true, bool unload = true) + private static bool EnsureDependency(string? title, string name, string[] dllNames, string? libraryPath, string url, bool required = true) { - if (TryToLoad(dllNames, unload)) + if (TryToLoad(dllNames, libraryPath, false)) + return true; + + Log.TraceError("Cannot load: {0}", dllNames); + + if (title == null) + return false; + + var downloadButton = new TaskDialogButton("Download"); + + var result = ShowDialog( + title, + name, + "Failure in loading the " + string.Join(", ", dllNames) + ".\n\n" + + "The '" + name + "' is likely not installed.\n" + + "Click Download to download it.\n", + url, + downloadButton, + TaskDialogButton.Ignore + ); + + if (result == downloadButton) + ExecuteLink(url); + + return false; + } + + private static void InstallDependency(string title, string name, string[] dllNames, string? libraryPath, string url, bool required = true, bool unload = true) + { + if (TryToLoad(dllNames, libraryPath, unload)) return; Log.TraceError("Cannot load: {0}", dllNames); - var result = MessageBox.Show( - "Missing '" + name + "' (" + string.Join(", ", dllNames) + ").\n\n" + - "Click Yes to download and install?\n", + var downloadButton = new TaskDialogButton("Download"); + var exitButton = new TaskDialogButton("Exit"); + + var result = ShowDialog( title, - MessageBoxButtons.YesNo, - MessageBoxIcon.Error + name, + "Failure in loading the " + string.Join(", ", dllNames) + ".\n\n" + + "The '" + name + "' is likely not installed.\n" + + "Click Download to download it.\n" + + (required ? "Once installed start application again.\n" : ""), + url, + downloadButton, + required ? exitButton : TaskDialogButton.Ignore ); - if (result == DialogResult.Yes) + if (result == downloadButton) { ExecuteLink(url); Environment.Exit(1); } - - if (required) + else if (result == exitButton) { - MessageBox.Show( - "The '" + name + "' is required. " + - "Application will exit now. " + - "Once installed start application again.", - title, - MessageBoxButtons.OK, - MessageBoxIcon.Error - ); Environment.Exit(1); } } - private static bool TryToLoad(string[] dllNames, bool unload = true) + private static bool TryToLoad(string[] dllNames, string? libraryPath, bool unload = true) { foreach (var dllName in dllNames) { - if (!TryToLoad(dllName, unload)) + if (!TryToLoad(dllName, libraryPath, unload)) return false; } return true; } - private static bool TryToLoad(string dllName, bool unload = true) + private static bool IsLoaded(string dllName) { + return GetModuleHandle(dllName) != IntPtr.Zero; + } + + private static bool TryToLoad(string dllName, String? libraryPath = null, bool unload = true) + { + if (IsLoaded(dllName)) + return true; + var handle = LoadLibrary(dllName); + if (handle == IntPtr.Zero && libraryPath is not null) + handle = LoadLibrary(Path.Join(libraryPath, dllName)); if (unload) FreeLibrary(handle); return handle != IntPtr.Zero; } + private static TaskDialogButton ShowDialog(string caption, string heading, string text, string? url, params TaskDialogButton[] buttons) + { + var page = new TaskDialogPage(); + page.Caption = caption; + foreach (var button in buttons) + page.Buttons.Add(button); + page.Icon = TaskDialogIcon.ShieldWarningYellowBar; + page.Heading = heading; + page.Text = text; + if (page.Buttons.Contains(TaskDialogButton.Help) && url is not null) + { + page.Footnote = new TaskDialogFootnote("Click help to download it."); + page.Footnote.Icon = TaskDialogIcon.Information; + + page.HelpRequest += delegate + { + try { ExecuteLink(url); } + catch { } + }; + } + + return TaskDialog.ShowDialog(new Form { TopMost = true }, page, TaskDialogStartupLocation.CenterScreen); + } + private static void ExecuteLink(string link) { try { Process.Start("explorer.exe", link); } @@ -133,5 +209,8 @@ namespace CommonHelpers [DllImport("kernel32.dll")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool FreeLibrary(IntPtr module); + + [DllImport("kernel32.dll")] + private static extern IntPtr GetModuleHandle(string moduleName); } } diff --git a/CommonHelpers/RTSS.cs b/CommonHelpers/RTSS.cs index 276bfed..f240b53 100644 --- a/CommonHelpers/RTSS.cs +++ b/CommonHelpers/RTSS.cs @@ -4,6 +4,14 @@ namespace CommonHelpers { public static class RTSS { + private const uint WM_APP = 0x8000; + private const uint WM_RTSS_UPDATESETTINGS = WM_APP + 100; + private const uint WM_RTSS_SHOW_PROPERTIES = WM_APP + 102; + + private const uint RTSSHOOKSFLAG_OSD_VISIBLE = 1; + private const uint RTSSHOOKSFLAG_LIMITER_DISABLED = 4; + private const string GLOBAL_PROFILE = ""; + public static bool GetProfileProperty(string propertyName, out T value) { var bytes = new byte[Marshal.SizeOf()]; @@ -58,38 +66,6 @@ namespace CommonHelpers PostMessage(WM_RTSS_UPDATESETTINGS, IntPtr.Zero, IntPtr.Zero); } - [return: MarshalAs(UnmanagedType.Bool)] - [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] - static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); - - [DllImport("user32.dll", SetLastError = true)] - static extern IntPtr FindWindow(string lpClassName, string lpWindowName); - - [DllImport("C:\\Program Files (x86)\\RivaTuner Statistics Server\\RTSSHooks64.dll")] - public static extern uint SetFlags(uint dwAND, uint dwXOR); - - [DllImport("C:\\Program Files (x86)\\RivaTuner Statistics Server\\RTSSHooks64.dll", CharSet = CharSet.Ansi)] - public static extern void LoadProfile(string profile = GLOBAL_PROFILE); - - [DllImport("C:\\Program Files (x86)\\RivaTuner Statistics Server\\RTSSHooks64.dll", CharSet = CharSet.Ansi)] - public static extern void SaveProfile(string profile = GLOBAL_PROFILE); - - [DllImport("C:\\Program Files (x86)\\RivaTuner Statistics Server\\RTSSHooks64.dll", CharSet = CharSet.Ansi)] - public static extern void DeleteProfile(string profile = GLOBAL_PROFILE); - - [DllImport("C:\\Program Files (x86)\\RivaTuner Statistics Server\\RTSSHooks64.dll", CharSet = CharSet.Ansi)] - public static extern bool GetProfileProperty(string propertyName, IntPtr value, uint size); - - [DllImport("C:\\Program Files (x86)\\RivaTuner Statistics Server\\RTSSHooks64.dll", CharSet = CharSet.Ansi)] - public static extern bool SetProfileProperty(string propertyName, IntPtr value, uint size); - - [DllImport("C:\\Program Files (x86)\\RivaTuner Statistics Server\\RTSSHooks64.dll", CharSet = CharSet.Ansi)] - public static extern void ResetProfile(string profile = GLOBAL_PROFILE); - - [DllImport("C:\\Program Files (x86)\\RivaTuner Statistics Server\\RTSSHooks64.dll", CharSet = CharSet.Ansi)] - public static extern void UpdateProfiles(); - - private static void PostMessage(uint Msg, IntPtr wParam, IntPtr lParam) { var hWnd = FindWindow(null, "RTSS"); @@ -100,12 +76,35 @@ namespace CommonHelpers PostMessage(hWnd, Msg, wParam, lParam); } - public const uint WM_APP = 0x8000; - public const uint WM_RTSS_UPDATESETTINGS = WM_APP + 100; - public const uint WM_RTSS_SHOW_PROPERTIES = WM_APP + 102; + [return: MarshalAs(UnmanagedType.Bool)] + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); - public const uint RTSSHOOKSFLAG_OSD_VISIBLE = 1; - public const uint RTSSHOOKSFLAG_LIMITER_DISABLED = 4; - public const string GLOBAL_PROFILE = ""; + [DllImport("user32.dll", SetLastError = true)] + static extern IntPtr FindWindow(string lpClassName, string lpWindowName); + + [DllImport("RTSSHooks64.dll")] + public static extern uint SetFlags(uint dwAND, uint dwXOR); + + [DllImport("RTSSHooks64.dll", CharSet = CharSet.Ansi)] + public static extern void LoadProfile(string profile = GLOBAL_PROFILE); + + [DllImport("RTSSHooks64.dll", CharSet = CharSet.Ansi)] + public static extern void SaveProfile(string profile = GLOBAL_PROFILE); + + [DllImport("RTSSHooks64.dll", CharSet = CharSet.Ansi)] + public static extern void DeleteProfile(string profile = GLOBAL_PROFILE); + + [DllImport("RTSSHooks64.dll", CharSet = CharSet.Ansi)] + public static extern bool GetProfileProperty(string propertyName, IntPtr value, uint size); + + [DllImport("RTSSHooks64.dll", CharSet = CharSet.Ansi)] + public static extern bool SetProfileProperty(string propertyName, IntPtr value, uint size); + + [DllImport("RTSSHooks64.dll", CharSet = CharSet.Ansi)] + public static extern void ResetProfile(string profile = GLOBAL_PROFILE); + + [DllImport("RTSSHooks64.dll", CharSet = CharSet.Ansi)] + public static extern void UpdateProfiles(); } } diff --git a/PowerControl/Options/FPSLimit.cs b/PowerControl/Options/FPSLimit.cs index 3b041e8..cca55c2 100644 --- a/PowerControl/Options/FPSLimit.cs +++ b/PowerControl/Options/FPSLimit.cs @@ -26,22 +26,29 @@ namespace PowerControl.Options { try { + if (!Dependencies.EnsureRTSS(null)) + return "?"; + RTSS.LoadProfile(); if (RTSS.GetProfileProperty("FramerateLimit", out int framerate)) return (framerate == 0) ? "Off" : framerate.ToString(); + return null; } - catch(Exception e) + catch (Exception e) { #if DEBUG CommonHelpers.Log.TraceException("RTSS", e); #endif + return "?"; } - return null; }, ApplyValue = (selected) => { try { + if (!Dependencies.EnsureRTSS(Controller.TitleWithVersion)) + return null; + int framerate = 0; if (selected != "Off") framerate = int.Parse(selected); @@ -55,7 +62,7 @@ namespace PowerControl.Options RTSS.UpdateProfiles(); return (framerate == 0) ? "Off" : framerate.ToString(); } - catch(Exception e) + catch (Exception e) { CommonHelpers.Log.TraceException("RTSS", e); } diff --git a/RELEASE.md b/RELEASE.md index 2f7bbd5..37371a8 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -9,6 +9,7 @@ ## 0.6.x +- PowerControl: Support RTSS in custom folder - SteamController: Fix Steam Big Picture detection for non-english - PowerControl: Allow user to configure selectable TDP, CPU and GPU from `PowerControl.dll.ini` - SteamController: Promote RTSS detection to Release - enable by default