PowerControl: Support RTSS in custom folder

This commit is contained in:
Kamil Trzciński 2023-02-08 19:53:02 +01:00
parent e1a4073a7c
commit bc9c3199b7
4 changed files with 158 additions and 72 deletions

View file

@ -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);
}
}

View file

@ -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<T>(string propertyName, out T value)
{
var bytes = new byte[Marshal.SizeOf<T>()];
@ -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();
}
}

View file

@ -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);
}

View file

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