├── Resources └── phoenix-96.ico ├── UI ├── ToolStripRenderer.cs ├── AboutForm.en.resx ├── MainForm.en.resx ├── AboutForm.cs ├── AboutForm.Designer.cs ├── MainForm.resx └── MainForm.cs ├── Makefile ├── Properties ├── Resources.en.resx ├── AssemblyInfo.cs ├── Resources.Designer.cs ├── Settings.settings ├── Strings.en.resx ├── Strings.resx ├── Resources.resx ├── Settings.Designer.cs └── Strings.Designer.cs ├── Common ├── Hardware │ ├── UpdateVisitor.cs │ └── HardwareMonitor.cs ├── Container │ ├── AppContainer.cs │ └── Container.cs ├── Awake.cs ├── PowerConfig.cs ├── Processor │ ├── AMDProcessor.cs │ └── RyzenAdj.cs ├── Logger │ └── SimpleLogger.cs └── EnergyStar │ ├── Interop │ └── Win32Api.cs │ └── EnergyManager.cs ├── packages.config ├── LICENSE.md ├── RyzenTuner.sln ├── Utils ├── CommonUtils.cs ├── DebugUtils.cs ├── RyzenAdjUtils.cs └── RyzenTunerUtils.cs ├── app.manifest ├── .github └── workflows │ └── debug_build.yml ├── README-CN.md ├── .gitattributes ├── README.md ├── App.config ├── Program.cs ├── .gitignore ├── RyzenTuner.csproj └── docs └── AMD_Ryzen7_6850HS.md /Resources/phoenix-96.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zqhong/RyzenTuner/HEAD/Resources/phoenix-96.ico -------------------------------------------------------------------------------- /UI/ToolStripRenderer.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing; 2 | using System.Windows.Forms; 3 | 4 | namespace RyzenTuner.UI 5 | { 6 | public class ToolStripRenderer : ToolStripProfessionalRenderer 7 | { 8 | protected override void OnRenderItemText(ToolStripItemTextRenderEventArgs e) 9 | { 10 | if (e.Item is ToolStripMenuItem { Checked: true } item) 11 | { 12 | e.TextFont = new Font(item.Font, FontStyle.Bold); 13 | } 14 | 15 | base.OnRenderItemText(e); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | NAME=RyzenTuner 2 | VERSION=$(shell git describe --tags --abbrev=0 || echo "unknown version") 3 | BUILD_TIME=$(shell date "+%Y%m%d%H%M%S") 4 | 5 | RELEASE_FILE_NAME=$(shell echo $(NAME)-$(VERSION)-$(BUILD_TIME).zip) 6 | 7 | release: 8 | rm -rf bin/tmp 9 | cp -r bin/Release bin/tmp 10 | cd bin/tmp && rm -f *.xml *.log *.pdb *.stackdump && rm -rf tmp 11 | cd bin/tmp && sed -i 's#Debug#Warning#g' RyzenTuner.exe.config 12 | cd bin/tmp && zip -r -9 $(RELEASE_FILE_NAME) ./* 13 | mv bin/tmp/$(RELEASE_FILE_NAME) bin/ 14 | rm -rf bin/tmp 15 | -------------------------------------------------------------------------------- /UI/AboutForm.en.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | text/microsoft-resx 4 | 5 | 6 | 1.3 7 | 8 | 9 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 10 | 11 | 12 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 13 | 14 | -------------------------------------------------------------------------------- /UI/MainForm.en.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | text/microsoft-resx 4 | 5 | 6 | 1.3 7 | 8 | 9 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 10 | 11 | 12 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 13 | 14 | -------------------------------------------------------------------------------- /Properties/Resources.en.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | text/microsoft-resx 4 | 5 | 6 | 1.3 7 | 8 | 9 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 10 | 11 | 12 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 13 | 14 | -------------------------------------------------------------------------------- /Common/Hardware/UpdateVisitor.cs: -------------------------------------------------------------------------------- 1 | using LibreHardwareMonitor.Hardware; 2 | 3 | namespace RyzenTuner.Common.Hardware 4 | { 5 | public class UpdateVisitor : IVisitor 6 | { 7 | public void VisitComputer(IComputer computer) 8 | { 9 | // Triggers the "IElement.Accept" method with the given visitor for each device in each group. 10 | computer.Traverse(this); 11 | } 12 | 13 | public void VisitHardware(IHardware hardware) 14 | { 15 | hardware.Update(); 16 | foreach (var subHardware in hardware.SubHardware) subHardware.Accept(this); 17 | } 18 | 19 | public void VisitSensor(ISensor sensor) 20 | { 21 | } 22 | 23 | public void VisitParameter(IParameter parameter) 24 | { 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // 文件说明 5 | [assembly: AssemblyTitle("RyzenTuner")] 6 | [assembly: AssemblyDescription("")] 7 | [assembly: AssemblyConfiguration("")] 8 | [assembly: AssemblyCompany("")] 9 | // 产品名称 10 | [assembly: AssemblyProduct("RyzenTuner")] 11 | // 版权 12 | [assembly: AssemblyCopyright("Copyright © 2025")] 13 | // 合法商标 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // 将 ComVisible 设置为 false 会使此程序集中的类型 18 | //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 19 | //请将此类型的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("ebc428a6-134e-4b8b-8b89-3595ff804785")] 24 | 25 | // Major.Minor.Build.Revision 26 | // 程序集的版本信息由下列四个值组成: 27 | // 28 | // 年份 29 | // 月份 30 | // 版本号 31 | // 修订号 32 | [assembly: AssemblyVersion("2025.6.0.000")] 33 | [assembly: AssemblyFileVersion("2025.6.0.000")] -------------------------------------------------------------------------------- /packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /UI/AboutForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | using RyzenTuner.Common.Container; 4 | 5 | namespace RyzenTuner.UI 6 | { 7 | public partial class AboutForm : Form 8 | { 9 | public AboutForm() 10 | { 11 | InitializeComponent(); 12 | 13 | richTextBox1.Text = richTextBox1.Text.Replace("{version}", 14 | $"V{System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString()}"); 15 | richTextBox1.Text = richTextBox1.Text.Replace("{copyright_year}", DateTime.Now.Year.ToString()); 16 | 17 | richTextBox1.ReadOnly = true; 18 | richTextBox1.Enabled = false; 19 | } 20 | 21 | private void AboutForm_Load(object sender, EventArgs e) 22 | { 23 | // Form 标题 24 | Text = Properties.Strings.TextAbout; 25 | } 26 | 27 | private void richTextBox1_TextChanged(object sender, EventArgs e) 28 | { 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 zqhong 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /RyzenTuner.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.1.32210.238 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RyzenTuner", "RyzenTuner.csproj", "{EBC428A6-134E-4B8B-8B89-3595FF804785}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Release|Any CPU = Release|Any CPU 11 | Debug|Any CPU = Debug|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {EBC428A6-134E-4B8B-8B89-3595FF804785}.Release|Any CPU.ActiveCfg = Release|Any CPU 15 | {EBC428A6-134E-4B8B-8B89-3595FF804785}.Release|Any CPU.Build.0 = Release|Any CPU 16 | {EBC428A6-134E-4B8B-8B89-3595FF804785}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {EBC428A6-134E-4B8B-8B89-3595FF804785}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {F11E1A3C-B828-4300-A194-F827D77B8C63} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /Utils/CommonUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Drawing; 4 | using System.IO; 5 | using System.Runtime.InteropServices; 6 | 7 | namespace RyzenTuner.Utils 8 | { 9 | public static class CommonUtils 10 | { 11 | /** 12 | * 检查系统是否处于锁屏状态 13 | */ 14 | public static bool IsSystemLocked() 15 | { 16 | // logonui,即 Windows Logon User Interface Host,翻译为【登录用户界面】 17 | return Process.GetProcessesByName("logonui").Length > 0; 18 | } 19 | 20 | /** 21 | * 检查字体是否存在 22 | */ 23 | public static bool IsFontExists(string fontName) 24 | { 25 | const float fontSize = 12; 26 | 27 | using var fontTester = new Font( 28 | fontName, 29 | fontSize, 30 | FontStyle.Regular, 31 | GraphicsUnit.Pixel); 32 | 33 | return fontTester.Name == fontName; 34 | } 35 | 36 | /** 37 | * 检查提供的日期是否处于晚上 38 | */ 39 | public static bool IsNight(DateTime now) 40 | { 41 | TimeSpan nightShiftStart = new TimeSpan(23, 59, 0); // 23:59pm 42 | TimeSpan nightShiftEnd = new TimeSpan(7, 0, 0); // 7:00am 43 | 44 | if (now.TimeOfDay > nightShiftStart || now.TimeOfDay < nightShiftEnd) 45 | { 46 | return true; 47 | } 48 | 49 | return false; 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /app.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /Utils/DebugUtils.cs: -------------------------------------------------------------------------------- 1 | using RyzenTuner.Common.Container; 2 | 3 | namespace RyzenTuner.Utils 4 | { 5 | public static class DebugUtils 6 | { 7 | /// 8 | /// 记录 Cpu 在不同限制功耗下的数据 9 | /// 10 | /// 备注:仅在开发使用 11 | /// 12 | public static void LogCpuInfo() 13 | { 14 | AppContainer.HardwareMonitor().Monitor(); 15 | AppContainer.AmdProcessor().SetTctlTemp(90); 16 | 17 | AppContainer.PowerConfig().DisableCpuBoost(); 18 | AppContainer.Logger().Debug("关闭睿频"); 19 | _logCpuInfo(); 20 | 21 | AppContainer.PowerConfig().EnableCpuBoost(); 22 | AppContainer.Logger().Debug("开启睿频"); 23 | _logCpuInfo(); 24 | } 25 | 26 | private static void _logCpuInfo() 27 | { 28 | const int maxPowerLimit = 30; 29 | var hardware = AppContainer.HardwareMonitor(); 30 | 31 | for (var i = 1; i <= maxPowerLimit; i++) 32 | { 33 | AppContainer.AmdProcessor().SetFastPPT(i); 34 | AppContainer.AmdProcessor().SetSlowPPT(i); 35 | AppContainer.AmdProcessor().SetStampPPT(i); 36 | 37 | System.Threading.Thread.Sleep(2048); 38 | AppContainer.HardwareMonitor().Monitor(); 39 | 40 | AppContainer.Logger() 41 | .Debug( 42 | $"功率:限制{i}瓦、实际{hardware.CpuPackagePower:F}瓦,CPU:{hardware.CpuUsage:F}%、{hardware.CpuTemperature:F}℃、{hardware.CpuFreq:F}MHz,GPU:{hardware.VideoCard3DUsage:F}%"); 43 | } 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /Common/Container/AppContainer.cs: -------------------------------------------------------------------------------- 1 | using RyzenTuner.Common.EnergyStar; 2 | using RyzenTuner.Common.Logger; 3 | using RyzenTuner.Common.Processor; 4 | 5 | namespace RyzenTuner.Common.Container 6 | { 7 | /** 8 | * Refer: https://csharpindepth.com/articles/singleton#cctor 9 | */ 10 | public static class AppContainer 11 | { 12 | private static readonly Container Container; 13 | 14 | static AppContainer() 15 | { 16 | Container = new Container(); 17 | 18 | Container.Register(() => new Hardware.HardwareMonitor()) 19 | .AsSingleton(); 20 | 21 | Container.Register(() => new PowerConfig()) 22 | .AsSingleton(); 23 | 24 | Container.Register(() => new AmdProcessor()) 25 | .AsSingleton(); 26 | 27 | Container.Register(() => new EnergyManager()) 28 | .AsSingleton(); 29 | 30 | Container.Register(() => 31 | { 32 | var logger = new SimpleLogger(); 33 | logger.DefaultLogLevel = logger.ToLogLevel(Properties.Settings.Default.LogLevel); 34 | return logger; 35 | }) 36 | .AsSingleton(); 37 | } 38 | 39 | public static Hardware.HardwareMonitor HardwareMonitor() 40 | { 41 | return Container.Resolve(); 42 | } 43 | 44 | public static PowerConfig PowerConfig() 45 | { 46 | return Container.Resolve(); 47 | } 48 | 49 | public static AmdProcessor AmdProcessor() 50 | { 51 | return Container.Resolve(); 52 | } 53 | 54 | public static EnergyManager EnergyManager() 55 | { 56 | return Container.Resolve(); 57 | } 58 | 59 | public static SimpleLogger Logger() 60 | { 61 | return Container.Resolve(); 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /.github/workflows/debug_build.yml: -------------------------------------------------------------------------------- 1 | name: RyzenTuner Debug Build 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | logLevel: 7 | description: 'Log level' 8 | required: true 9 | default: 'warning' 10 | type: choice 11 | options: 12 | - info 13 | - warning 14 | - debug 15 | 16 | jobs: 17 | build: 18 | strategy: 19 | matrix: 20 | configuration: [ Debug ] 21 | platform: [ x64 ] 22 | 23 | runs-on: windows-latest 24 | 25 | env: 26 | Solution_Name: RyzenTuner.sln 27 | Project_Name: RyzenTuner 28 | 29 | steps: 30 | - name: Checkout 31 | uses: actions/checkout@v3 32 | with: 33 | fetch-depth: 0 34 | 35 | - name: Setup MSBuild.exe 36 | uses: microsoft/setup-msbuild@v1.1 37 | 38 | - name: Setup NuGet 39 | uses: nuget/setup-nuget@v1 40 | 41 | - name: Restore Packages 42 | run: nuget restore $env:Solution_Name 43 | 44 | - name: Build 45 | run: msbuild $env:Solution_Name 46 | 47 | - name: Install packages 48 | run: | 49 | choco install wget --no-progress 50 | choco install unzip --no-progress 51 | 52 | - name: Add dependencies 53 | run: | 54 | wget -O libryzenadj-win64.zip https://github.com/FlyGoat/RyzenAdj/releases/download/v0.12.0/libryzenadj-win64.zip 55 | unzip libryzenadj-win64.zip 56 | cp build/libryzenadj.dll ./bin/Debug 57 | cp win32/inpoutx64.dll ./bin/Debug 58 | cp win32/WinRing0x64.dll ./bin/Debug 59 | cp win32/WinRing0x64.sys ./bin/Debug 60 | 61 | - name: Generate artifact name 62 | id: generate-name 63 | run: | 64 | echo "::set-output name=artifact::${{ github.event.repository.name }}.${{ github.ref_name }}.${{ github.run_id }}.zip" 65 | 66 | - name: Upload artifact 67 | uses: actions/upload-artifact@v2 68 | with: 69 | name: ${{ steps.generate-name.outputs.artifact }} 70 | path: "./bin/Debug" -------------------------------------------------------------------------------- /Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // 5 | // Changes to this file may cause incorrect behavior and will be lost if 6 | // the code is regenerated. 7 | // 8 | //------------------------------------------------------------------------------ 9 | 10 | namespace RyzenTuner.Properties { 11 | using System; 12 | 13 | 14 | [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 15 | [System.Diagnostics.DebuggerNonUserCodeAttribute()] 16 | [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 17 | internal class Resources { 18 | 19 | private static System.Resources.ResourceManager resourceMan; 20 | 21 | private static System.Globalization.CultureInfo resourceCulture; 22 | 23 | [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 24 | internal Resources() { 25 | } 26 | 27 | [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] 28 | internal static System.Resources.ResourceManager ResourceManager { 29 | get { 30 | if (object.Equals(null, resourceMan)) { 31 | System.Resources.ResourceManager temp = new System.Resources.ResourceManager("RyzenTuner.Properties.Resources", typeof(Resources).Assembly); 32 | resourceMan = temp; 33 | } 34 | return resourceMan; 35 | } 36 | } 37 | 38 | [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static System.Globalization.CultureInfo Culture { 40 | get { 41 | return resourceCulture; 42 | } 43 | set { 44 | resourceCulture = value; 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /UI/AboutForm.Designer.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace RyzenTuner.UI 4 | { 5 | partial class AboutForm 6 | { 7 | /// 8 | /// Required designer variable. 9 | /// 10 | private IContainer components = null; 11 | 12 | /// 13 | /// Clean up any resources being used. 14 | /// 15 | /// true if managed resources should be disposed; otherwise, false. 16 | protected override void Dispose(bool disposing) 17 | { 18 | if (disposing && (components != null)) 19 | { 20 | components.Dispose(); 21 | } 22 | 23 | base.Dispose(disposing); 24 | } 25 | 26 | #region Windows Form Designer generated code 27 | 28 | /// 29 | /// Required method for Designer support - do not modify 30 | /// the contents of this method with the code editor. 31 | /// 32 | private void InitializeComponent() 33 | { 34 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(AboutForm)); 35 | this.richTextBox1 = new System.Windows.Forms.RichTextBox(); 36 | this.SuspendLayout(); 37 | // 38 | // richTextBox1 39 | // 40 | resources.ApplyResources(this.richTextBox1, "richTextBox1"); 41 | this.richTextBox1.Name = "richTextBox1"; 42 | this.richTextBox1.TextChanged += new System.EventHandler(this.richTextBox1_TextChanged); 43 | // 44 | // AboutForm 45 | // 46 | resources.ApplyResources(this, "$this"); 47 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 48 | this.Controls.Add(this.richTextBox1); 49 | this.Name = "AboutForm"; 50 | this.Load += new System.EventHandler(this.AboutForm_Load); 51 | this.ResumeLayout(false); 52 | } 53 | 54 | private System.Windows.Forms.RichTextBox richTextBox1; 55 | 56 | #endregion 57 | } 58 | } -------------------------------------------------------------------------------- /Properties/Settings.settings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | False 7 | 8 | 9 | False 10 | 11 | 12 | 16 13 | 14 | 15 | 16 16 | 17 | 18 | 8 19 | 20 | 21 | 16 22 | 23 | 24 | 26 25 | 26 | 27 | 45 28 | 29 | 30 | AutoMode 31 | 32 | 33 | Debug 34 | 35 | 36 | example1.exe,example2.exe 37 | 38 | 39 | 100 40 | 41 | 42 | 51 43 | 44 | 45 | 45 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /README-CN.md: -------------------------------------------------------------------------------- 1 | ## RyzenTuner 2 | 3 | [English](README.md) | 简体中文 4 | 5 | RyzenTuner 提供一个 GUI 界面,可以方便调节 Ryzen 移动处理器的功率限制,也支持调整 Windows 进程的 QoS 等级和优先级,从而提升电池使用时间和减少风扇噪声。 6 | 7 | ![preview.jpg](https://s2.loli.net/2022/08/25/YTA9yf8jqOtUEwn.jpg) 8 | 9 | ## 添加内容 10 | 11 | ### 自动模式 12 | 13 | 根据插电/电池/夜晚/活跃时间等不同条件,自动选择不同的工作模式(待机/平衡/性能)。 14 | 15 | 工作模式说明: 16 | 17 | * 待机模式(SleepMode):处于最低能耗状态,只能应对超低负载的工作 18 | * 平衡模式(BalancedMode):能耗比最高的状态,应对突发的任务,比如打开网页、Photoshop 19 | * 性能模式(PerformanceMode):应对需要高性能的场景,比如编译软件、玩大型游戏、渲染等 20 | 21 | ## 使用 22 | 23 | 你可以直接到 [Release](https://github.com/zqhong/RyzenTuner/releases) 下载编译好的程序使用。 24 | 25 | ## FAQ 26 | 27 | ### 如何修改待机模式等其他模式的功率 28 | 29 | 关闭 `RyzenTuner`。打开 `RyzenTuner.exe.config` 文件,修改对应的参数。比如 `SleepMode` 就是 睡眠模式,修改其中的 value 值。 30 | 31 | 示例:将睡眠模式从 1 W 改为 2 W 32 | 33 | 修改前 34 | 35 | ```xml 36 | 37 | 38 | 1 39 | 40 | ``` 41 | 42 | 修改后 43 | 44 | ```xml 45 | 46 | 47 | 2 48 | 49 | ``` 50 | 51 | ## 计划 52 | 53 | - [x] 多语言支持 54 | - [x] GitHub action 55 | - [ ] 开机启动 56 | - [ ] 添加设置面板 57 | - [ ] 添加是否允许修改电源计划的选项,默认关闭 58 | - [ ] 添加应用策略,支持不同应用自定义切换不同的功率限制模式 59 | 60 | ## 附录 61 | 62 | ### 问题记录 63 | 64 | AMD 6850HS 处理器、Windows 11 22H2 系统 65 | 66 | ryzenadj 0.17.0、0.16.0、0.15.0、0.14.0、0.13.0、0.12.0、0.11.1 存在问题: 67 | 68 | * 异常情况 69 | * stamp-limit 设置不生效; 70 | * fast-limit 影响 stamp-limit 和 fast-limit 的值; 71 | * fast-limit 最大值:51瓦;slow-limit 最大值 41瓦;比如设置 fast-limit 为 55 瓦,结果为 51 瓦 72 | * 正常情况 73 | * slow-limit 设置正常生效; 74 | * tctl-temp 设置正常生效 75 | 76 | ## Give a Star! ⭐ 77 | 78 | 如果你喜欢或正在使用这个项目,请给我们一个 star。一个小小的 star 是作者更新项目/回答问题的动力!🤝 79 | 80 | [![Star History Chart](https://api.star-history.com/svg?repos=zqhong/RyzenTuner&type=Date)](#RyzenTuner) 81 | 82 | ## 依赖的项目 83 | 84 | * [Archeb/RyzenTuner](https://github.com/Archeb/RyzenTuner):基于该项目开发 85 | * [FlyGoat/RyzenAdj](https://github.com/FlyGoat/RyzenAdj):Ryzen 移动处理器电源管理工具 86 | * [imbushuo/EnergyStar](https://github.com/imbushuo/EnergyStar):Windows 进程调度,可能有增加续航的效果(未测试) 87 | * [LibreHardwareMonitor](https://github.com/LibreHardwareMonitor/LibreHardwareMonitor):获取硬件信息 88 | * [dahall/Vanara](https://github.com/dahall/Vanara):一套 .NET 库,包含了许多 Windows 原生 API 的 PInvoke 调用的封装 89 | 90 | 感谢以上名单的项目和作者。 91 | 92 | ## License 93 | 94 | RyzenTuner 使用 [MIT](LICENSE.md) 开源协议。 -------------------------------------------------------------------------------- /Properties/Strings.en.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | text/microsoft-resx 4 | 5 | 6 | 1.3 7 | 8 | 9 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 10 | 11 | 12 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 13 | 14 | 15 | Automatic 16 | 17 | 18 | Balance 19 | 20 | 21 | Manual 22 | 23 | 24 | Performance 25 | 26 | 27 | Power Saving 28 | 29 | 30 | Standby 31 | 32 | 33 | Power Limit 34 | 35 | 36 | Other Options 37 | 38 | 39 | Stay Awake 40 | 41 | 42 | Enable EnergyStar 43 | 44 | 45 | About 46 | 47 | 48 | RyzenTuner has an error 49 | 50 | 51 | Only one RyzenTuner program is allowed to run at a time 52 | 53 | 54 | Power: limited {power_limit}W, actual {actual_power_limit}W 55 | 56 | 57 | Exit 58 | 59 | -------------------------------------------------------------------------------- /Properties/Strings.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | text/microsoft-resx 11 | 12 | 13 | 1.3 14 | 15 | 16 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 17 | 18 | 19 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 20 | 21 | 22 | 自动模式 23 | 24 | 25 | 平衡模式 26 | 27 | 28 | 手动模式 29 | 30 | 31 | 性能模式 32 | 33 | 34 | 省电模式 35 | 36 | 37 | 待机模式 38 | 39 | 40 | 功率限制 41 | 42 | 43 | 其他选项 44 | 45 | 46 | 保持唤醒 47 | 48 | 49 | 开启 EnergyStar 50 | 51 | 52 | 关于 53 | 54 | 55 | RyzenTuner 出现错误 56 | 57 | 58 | 同一时间内,只允许运行一个 RyzenTuner 程序 59 | 60 | 61 | 功率:限制{power_limit}W、实际{actual_power_limit}W 62 | 63 | 64 | 退出 65 | 66 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## RyzenTuner 2 | 3 | English | [简体中文](README-CN.md) 4 | 5 | RyzenTuner provides a GUI interface to easily adjust the power limit of Ryzen mobile processors, and also supports adjusting the QoS level and priority of Windows processes, thereby improving battery life and reducing fan noise. 6 | 7 | ![preview-en.jpg](https://s2.loli.net/2022/08/26/Pr1qiykJUOIEspD.jpg) 8 | 9 | ## Feature 10 | 11 | ### Automatic Mode 12 | 13 | Different working modes (standby/balance/performance) are automatically selected according to different conditions such as plug-in/battery/night/active time. 14 | 15 | Description of working mode: 16 | 17 | * Standby mode: in the lowest energy consumption state, can only deal with ultra-low load work 18 | * Balanced Mode: The state with the highest energy consumption ratio, to deal with sudden tasks, such as opening web 19 | pages, Photoshop 20 | * Performance Mode: for scenarios that require high performance, such as compiling software, playing large games, 21 | rendering, etc. 22 | 23 | ## Usage 24 | 25 | You can directly go to [Release](https://github.com/zqhong/RyzenTuner/releases) to download the compiled program and use 26 | it. 27 | 28 | ## FAQ 29 | 30 | ### How to modify the power of other modes such as standby mode 31 | 32 | Close `RyzenTuner`. Open the `RyzenTuner.exe.config` file and modify the corresponding parameters. For example, `SleepMode` is the sleep mode, modify the value in it. 33 | 34 | Example: Change sleep mode from 1 W to 2 W 35 | 36 | Before update 37 | 38 | ```xml 39 | 40 | 41 | 1 42 | 43 | ``` 44 | 45 | After update 46 | 47 | ```xml 48 | 49 | 50 | 2 51 | 52 | ``` 53 | 54 | ## Give a Star! ⭐ 55 | 56 | If you like or are using this project, please give it a star. Thanks! 57 | 58 | [![Star History Chart](https://api.star-history.com/svg?repos=zqhong/RyzenTuner&type=Date)](#RyzenTuner) 59 | 60 | 61 | ## Dependent project 62 | 63 | * [Archeb/RyzenTuner](https://github.com/Archeb/RyzenTuner): Developed based on this project 64 | * [FlyGoat/RyzenAdj](https://github.com/FlyGoat/RyzenAdj): Ryzen mobile processor power management tool 65 | * [imbushuo/EnergyStar](https://github.com/imbushuo/EnergyStar): Windows process scheduling, may have the effect of increasing battery life (not tested) 66 | * [LibreHardwareMonitor](https://github.com/LibreHardwareMonitor/LibreHardwareMonitor): Get hardware information 67 | * [dahall/Vanara](https://github.com/dahall/Vanara): A set of .NET libraries that wraps many PInvoke calls to Windows native APIs 68 | 69 | Thanks to the projects and authors listed above. 70 | 71 | ## License 72 | 73 | RyzenTuner is licensed under [MIT](LICENSE.md). -------------------------------------------------------------------------------- /Common/Awake.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace RyzenTuner.Common 5 | { 6 | /** 7 | * 参考: 8 | * github.com\PowerToys\src\modules\awake\Awake\Core\NativeMethods.cs 9 | * https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setthreadexecutionstate 10 | * https://www.cnblogs.com/guorongtao/p/13918094.html 11 | */ 12 | public static class Awake 13 | { 14 | [Flags] 15 | private enum ExecutionState : uint 16 | { 17 | // 启用离开模式。这个值必须和ES_CONTINUOUS一起指定 18 | ES_AWAYMODE_REQUIRED = 0x00000040, 19 | 20 | // 通知系统,被设置的状态应该保持有效,直到下一次使用ES_CONTINUOUS的调用和其他状态标志之一被清除 21 | ES_CONTINUOUS = 0x80000000, 22 | 23 | // 通过重置显示器的空闲计时器,迫使显示器打开 24 | ES_DISPLAY_REQUIRED = 0x00000002, 25 | 26 | // 通过重置系统空闲定时器,迫使系统处于工作状态 27 | ES_SYSTEM_REQUIRED = 0x00000001, 28 | } 29 | 30 | [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] 31 | private static extern ExecutionState SetThreadExecutionState(ExecutionState esFlags); 32 | 33 | /** 34 | * 保持操作系统清醒 35 | */ 36 | public static bool KeepingSysAwake(bool keepDisplayOn) 37 | { 38 | bool success; 39 | if (keepDisplayOn) 40 | { 41 | success = SetAwakeState(ExecutionState.ES_SYSTEM_REQUIRED | ExecutionState.ES_CONTINUOUS | 42 | ExecutionState.ES_DISPLAY_REQUIRED); 43 | } 44 | else 45 | { 46 | success = SetAwakeState(ExecutionState.ES_SYSTEM_REQUIRED | ExecutionState.ES_CONTINUOUS); 47 | } 48 | 49 | return success; 50 | } 51 | 52 | /** 53 | * 允许系统进入睡眠 54 | */ 55 | public static bool AllowSysSleep() 56 | { 57 | return SetAwakeState(ExecutionState.ES_CONTINUOUS); 58 | } 59 | 60 | /// 61 | /// 使一个应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入睡眠状态或关闭显示器。 62 | /// 63 | /// 在没有ES_CONTINUOUS的情况下调用SetThreadExecutionState,只是简单地重置了空闲计时器;为了保持显示或系统处于工作状态,线程必须定期地调用SetThreadExecutionState。 64 | /// 备注:这个函数不会阻止屏幕保护程序的执行 65 | /// 66 | /// Single or multiple EXECUTION_STATE entries. 67 | /// true if successful, false if failed 68 | private static bool SetAwakeState(ExecutionState state) 69 | { 70 | try 71 | { 72 | var stateResult = SetThreadExecutionState(state); 73 | return stateResult != 0; 74 | } 75 | catch 76 | { 77 | return false; 78 | } 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | False 18 | 19 | 20 | False 21 | 22 | 23 | 16 24 | 25 | 26 | 16 27 | 28 | 29 | 8 30 | 31 | 32 | 16 33 | 34 | 35 | 26 36 | 37 | 38 | 45 39 | 40 | 41 | AutoMode 42 | 43 | 44 | Debug 45 | 46 | 47 | example1.exe,example2.exe 48 | 49 | 50 | 100 51 | 52 | 53 | 51 54 | 55 | 56 | 45 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /Utils/RyzenAdjUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | using RyzenTuner.Common.Container; 4 | using RyzenTuner.Properties; 5 | 6 | namespace RyzenTuner.Utils 7 | { 8 | public class RyzenAdjUtils 9 | { 10 | /** 11 | * 计算自动模式下的限制功率(单位:瓦) 12 | * 13 | * 改进: 14 | * 1、适配不同型号 CPU 15 | */ 16 | private static float AutoModePowerLimit() 17 | { 18 | var hardwareMonitor = AppContainer.HardwareMonitor(); 19 | 20 | var cpuUsage = hardwareMonitor.CpuUsage; 21 | var videoCard3DUsage = hardwareMonitor.VideoCard3DUsage; 22 | var cpuTemperature = hardwareMonitor.CpuTemperature; 23 | 24 | var isNight = CommonUtils.IsNight(DateTime.Now); 25 | 26 | // 自动模式下,在几个模式下切换:待机、平衡、性能 27 | // 插电模式下 28 | var sleepPower = RyzenTunerUtils.GetPowerLimitByMode("SleepMode"); 29 | var balancedPower = RyzenTunerUtils.GetPowerLimitByMode("BalancedMode"); 30 | var performancePower = RyzenTunerUtils.GetPowerLimitByMode("PerformanceMode"); 31 | 32 | 33 | // 电池模式下,最高性能为【平衡】 34 | if (SystemInformation.PowerStatus.PowerLineStatus == PowerLineStatus.Offline) 35 | { 36 | performancePower = RyzenTunerUtils.GetPowerLimitByMode("BalancedMode"); 37 | } 38 | 39 | // 夜晚,最高性能为【平衡】 40 | if (isNight) 41 | { 42 | performancePower = RyzenTunerUtils.GetPowerLimitByMode("BalancedMode"); 43 | } 44 | 45 | // 默认使用【平衡】 46 | var powerLimit = balancedPower; 47 | 48 | // 符合下面条件之一的情况下,使用【待机】 49 | var idleSecond = RyzenTunerUtils.GetIdleSecond(); 50 | if ( 51 | // 条件1:在短时间不活跃使用且笔记本负载较低的情况下,进入待机模式 52 | (idleSecond > 2 && cpuUsage < 5 && videoCard3DUsage < 5) || 53 | 54 | // 条件2:白天 && 非活跃时间超过 x 分钟 && 笔记本低负载 55 | (!isNight && idleSecond > 4 * 60 && cpuUsage < 10 && videoCard3DUsage < 10) || 56 | 57 | // 条件3:夜晚 && 非活跃时间超过 x 分钟 && 笔记本中负载 58 | (isNight && idleSecond > 4 * 60 && cpuUsage < 20 && videoCard3DUsage < 20) || 59 | 60 | // 条件4:锁屏状态下 && 非活跃时间超过 x 秒 && 笔记本中负载 61 | (CommonUtils.IsSystemLocked() && idleSecond > 2 && cpuUsage < 20 && videoCard3DUsage < 20) || 62 | 63 | // 条件5:锁屏状态下 && 非活跃时间超过 x 秒 && 温度大于 x 度 64 | (CommonUtils.IsSystemLocked() && idleSecond > 2 && cpuTemperature > 52) 65 | ) 66 | { 67 | powerLimit = sleepPower; 68 | } 69 | 70 | // 非待机模式 && CPU 占用大于 50%,温度在 65 度以内,使用【性能模式】 71 | if (!RyzenTunerUtils.IsSleepMode(powerLimit) && cpuUsage >= 50 && cpuTemperature < 65) 72 | { 73 | powerLimit = performancePower; 74 | } 75 | 76 | return powerLimit; 77 | } 78 | 79 | public static float GetPowerLimit() 80 | { 81 | var powerLimit = RyzenTunerUtils.GetPowerLimitByMode(Properties.Settings.Default.CurrentMode); 82 | 83 | // 自动模式下,根据系统状态自动调整 84 | if (Settings.Default.CurrentMode == "AutoMode") 85 | { 86 | powerLimit = AutoModePowerLimit(); 87 | } 88 | 89 | // 数值修正 90 | if (powerLimit < 0) 91 | { 92 | powerLimit = 1; 93 | } 94 | 95 | return powerLimit; 96 | } 97 | 98 | public static int GetTctlTemp() 99 | { 100 | return Settings.Default.TctlTemp; 101 | } 102 | } 103 | } -------------------------------------------------------------------------------- /Common/PowerConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Vanara.PInvoke; 3 | using static Vanara.PInvoke.PowrProf; 4 | 5 | namespace RyzenTuner.Common 6 | { 7 | public class PowerConfig 8 | { 9 | private const uint Disabled = 0; 10 | private const uint Enabled = 1; 11 | 12 | /** 13 | * 获取当前激活的电源方案的 GUID 14 | */ 15 | private Guid GetActiveScheme() 16 | { 17 | HKEY powerKey = default; 18 | var result = PowerGetActiveScheme(powerKey, out var activeGuidHandle); 19 | if (result != Win32Error.NO_ERROR) 20 | { 21 | throw new Exception(result.ToString()); 22 | } 23 | 24 | return activeGuidHandle.ToStructure(); 25 | } 26 | 27 | /** 28 | * 检查是否开启了 Cpu Boost 29 | */ 30 | private bool IsEnableCpuBoost() 31 | { 32 | HKEY powerKey = default; 33 | var activeGuid = GetActiveScheme(); 34 | 35 | PowerReadACValueIndex( 36 | powerKey, 37 | activeGuid, 38 | GUID_PROCESSOR_SETTINGS_SUBGROUP, 39 | GUID_PROCESSOR_PERF_BOOST_MODE, 40 | out var acValue 41 | ); 42 | 43 | PowerReadDCValueIndex( 44 | powerKey, 45 | activeGuid, 46 | GUID_PROCESSOR_SETTINGS_SUBGROUP, 47 | GUID_PROCESSOR_PERF_BOOST_MODE, 48 | out var dcValue 49 | ); 50 | 51 | return acValue == Enabled && dcValue == Enabled; 52 | } 53 | 54 | /** 55 | * 启用 Cpu Boost 56 | */ 57 | public bool EnableCpuBoost() 58 | { 59 | if (IsEnableCpuBoost()) 60 | { 61 | return true; 62 | } 63 | 64 | HKEY powerKey = default; 65 | var activeGuid = GetActiveScheme(); 66 | 67 | var r1 = PowerWriteACValueIndex( 68 | powerKey, 69 | activeGuid, 70 | GUID_PROCESSOR_SETTINGS_SUBGROUP, 71 | GUID_PROCESSOR_PERF_BOOST_MODE, 72 | Enabled 73 | ); 74 | 75 | var r2 = PowerWriteDCValueIndex( 76 | powerKey, 77 | activeGuid, 78 | GUID_PROCESSOR_SETTINGS_SUBGROUP, 79 | GUID_PROCESSOR_PERF_BOOST_MODE, 80 | Enabled 81 | ); 82 | 83 | var r3 = PowerSetActiveScheme(powerKey, activeGuid); 84 | 85 | return r1 == Win32Error.NO_ERROR && r2 == Win32Error.NO_ERROR && r3 == Win32Error.NO_ERROR; 86 | } 87 | 88 | /** 89 | * 关闭 Cpu Boost 90 | */ 91 | public bool DisableCpuBoost() 92 | { 93 | if (!IsEnableCpuBoost()) 94 | { 95 | return true; 96 | } 97 | 98 | HKEY powerKey = default; 99 | var activeGuid = GetActiveScheme(); 100 | 101 | var r1 = PowerWriteACValueIndex( 102 | powerKey, 103 | activeGuid, 104 | GUID_PROCESSOR_SETTINGS_SUBGROUP, 105 | GUID_PROCESSOR_PERF_BOOST_MODE, 106 | Disabled 107 | ); 108 | 109 | var r2 = PowerWriteDCValueIndex( 110 | powerKey, 111 | activeGuid, 112 | GUID_PROCESSOR_SETTINGS_SUBGROUP, 113 | GUID_PROCESSOR_PERF_BOOST_MODE, 114 | Disabled 115 | ); 116 | 117 | var r3 = PowerSetActiveScheme(powerKey, activeGuid); 118 | 119 | return r1 == Win32Error.NO_ERROR && r2 == Win32Error.NO_ERROR && r3 == Win32Error.NO_ERROR; 120 | } 121 | } 122 | } -------------------------------------------------------------------------------- /Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Globalization; 4 | using System.Threading; 5 | using System.Windows.Forms; 6 | using RyzenTuner.Common.Container; 7 | using RyzenTuner.UI; 8 | 9 | namespace RyzenTuner 10 | { 11 | internal static class Program 12 | { 13 | [STAThread] 14 | private static void Main() 15 | { 16 | try 17 | { 18 | Application.ThreadException += Application_ThreadException; 19 | AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; 20 | 21 | if (Environment.OSVersion.Version.Major >= 6) 22 | { 23 | SetProcessDPIAware(); 24 | } 25 | 26 | AutoSelectLang(); 27 | 28 | if (Process.GetProcessesByName("RyzenTuner").Length > 1) 29 | { 30 | throw new Exception(Properties.Strings.TextExceptionOnlyOneProgramIsAllowedToRun); 31 | } 32 | 33 | Application.EnableVisualStyles(); 34 | Application.SetCompatibleTextRenderingDefault(false); 35 | Application.Run(new MainForm()); 36 | } 37 | catch (Exception ex) 38 | { 39 | // 检查是否是初始化失败导致的异常 40 | if (ex.InnerException?.GetType() == typeof(DllNotFoundException) || 41 | ex.Message.Contains("libryzenadj.dll")) 42 | { 43 | ShowErrorAndExit("Critical Error", 44 | "libryzenadj.dll not found! This component is required.\n\n" + 45 | "Please download it from the official repository: " + 46 | "https://github.com/FlyGoat/RyzenAdj"); 47 | } 48 | else 49 | { 50 | ShowErrorAndExit("Fatal Error", $"Unhandled exception: {ex.Message}"); 51 | } 52 | } 53 | } 54 | 55 | // 显示错误并退出的辅助方法 56 | static void ShowErrorAndExit(string title, string message) 57 | { 58 | MessageBox.Show(message, title, 59 | MessageBoxButtons.OK, MessageBoxIcon.Error); 60 | Environment.Exit(1); 61 | } 62 | 63 | /// 64 | /// 根据用户系统语言,自动选择合适的语言 65 | /// 66 | private static void AutoSelectLang() 67 | { 68 | var currentCulture = Thread.CurrentThread.CurrentCulture; 69 | 70 | // 非中文环境全部切换为英文 71 | if (!currentCulture.ToString().StartsWith("zh-")) 72 | { 73 | var culture = new CultureInfo("en-US"); 74 | CultureInfo.DefaultThreadCurrentCulture = culture; 75 | CultureInfo.DefaultThreadCurrentUICulture = culture; 76 | } 77 | } 78 | 79 | private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e) 80 | { 81 | _handleUnhandledException(e.Exception); 82 | } 83 | 84 | private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs arg) 85 | { 86 | var e = (Exception)arg.ExceptionObject; 87 | _handleUnhandledException(e); 88 | } 89 | 90 | private static void _handleUnhandledException(Exception ex) 91 | { 92 | MessageBox.Show(ex.Message, Properties.Strings.TextExceptionTitle, 93 | MessageBoxButtons.OK, MessageBoxIcon.Error); 94 | 95 | AppContainer.Logger().LogException(ex); 96 | 97 | Application.Exit(); 98 | } 99 | 100 | [System.Runtime.InteropServices.DllImport("user32.dll")] 101 | private static extern bool SetProcessDPIAware(); 102 | } 103 | } -------------------------------------------------------------------------------- /Utils/RyzenTunerUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using RyzenTuner.Common.Container; 4 | 5 | namespace RyzenTuner.Utils 6 | { 7 | public static class RyzenTunerUtils 8 | { 9 | [DllImport("user32.dll")] 10 | static extern bool GetLastInputInfo(ref LASTINPUTINFO plii); 11 | 12 | // https://www.pinvoke.net/default.aspx/user32.GetLastInputInfo 13 | // https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-lastinputinfo 14 | [StructLayout(LayoutKind.Sequential)] 15 | struct LASTINPUTINFO 16 | { 17 | public static readonly int SizeOf = Marshal.SizeOf(typeof(LASTINPUTINFO)); 18 | 19 | [MarshalAs(UnmanagedType.U4)] public int cbSize; 20 | [MarshalAs(UnmanagedType.U4)] public UInt32 dwTime; 21 | } 22 | 23 | /** 24 | * 返回系统空闲时间,单位:秒 25 | * 26 | * 参考: 27 | * https://stackoverflow.com/questions/203384/how-to-tell-when-windows-is-inactive 28 | */ 29 | public static int GetIdleSecond() 30 | { 31 | var idleTime = 0; 32 | var lastInputInfo = new LASTINPUTINFO(); 33 | lastInputInfo.cbSize = Marshal.SizeOf(lastInputInfo); 34 | lastInputInfo.dwTime = 0; 35 | 36 | var envTicks = Environment.TickCount; 37 | 38 | if (GetLastInputInfo(ref lastInputInfo)) 39 | { 40 | var lastInputTick = (int)lastInputInfo.dwTime; 41 | 42 | idleTime = envTicks - lastInputTick; 43 | } 44 | 45 | return ((idleTime > 0) ? (idleTime / 1000) : idleTime); 46 | } 47 | 48 | 49 | /** 50 | * 检查当前是否处于【待机模式】 51 | */ 52 | public static bool IsSleepMode(float powerLimit) 53 | { 54 | return Math.Abs(powerLimit - GetPowerLimitByMode("SleepMode")) < 0.01; 55 | } 56 | 57 | /** 58 | * 检查当前是否处于【省电模式】 59 | */ 60 | public static bool IsPowerSaveModeMode(float powerLimit) 61 | { 62 | return Math.Abs(powerLimit - GetPowerLimitByMode("PowerSaveMode")) < 0.01; 63 | } 64 | 65 | /** 66 | * 检查当前是否处于【平衡模式】 67 | */ 68 | public static bool IsBalancedMode(float powerLimit) 69 | { 70 | return Math.Abs(powerLimit - GetPowerLimitByMode("BalancedMode")) < 0.01; 71 | } 72 | 73 | /** 74 | * 检查当前是否处于【性能模式】 75 | */ 76 | public static bool IsPerformanceMode(float powerLimit) 77 | { 78 | return Math.Abs(powerLimit - GetPowerLimitByMode("PerformanceMode")) < 0.01; 79 | } 80 | 81 | public static float GetPowerLimitByMode(string mode) 82 | { 83 | return float.Parse(Properties.Settings.Default[mode].ToString()); 84 | } 85 | 86 | public static string GetNoticeText() 87 | { 88 | var hardwareMonitor = AppContainer.HardwareMonitor(); 89 | var powerLimit = RyzenAdjUtils.GetPowerLimit(); 90 | 91 | var powerLimitText = Properties.Strings.TextNoticeText 92 | .Replace("{power_limit}", $"{powerLimit:0}") 93 | .Replace("{actual_power_limit}", $"{hardwareMonitor.CpuPackagePower:0.00}"); 94 | 95 | var noticeText = $@"{Properties.Settings.Default.CurrentMode} 96 | {powerLimitText} 97 | CPU: {hardwareMonitor.CpuUsage:0}%、{hardwareMonitor.CpuTemperature:0}℃、{hardwareMonitor.CpuFreq:0}MHz,GPU: {hardwareMonitor.VideoCard3DUsage:0}%"; 98 | if (noticeText.Length >= 64) 99 | { 100 | noticeText = noticeText.Substring(0, 63); 101 | } 102 | 103 | return noticeText; 104 | } 105 | 106 | public static string GetModeDetailText(string mode) 107 | { 108 | return $"{Properties.Strings.ResourceManager.GetString(mode)}-{GetPowerLimitByMode(mode)}W"; 109 | } 110 | } 111 | } -------------------------------------------------------------------------------- /Common/Processor/AMDProcessor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using RyzenTuner.Common.Container; 3 | 4 | namespace RyzenTuner.Common.Processor 5 | { 6 | public class AmdProcessor 7 | { 8 | public enum PowerType 9 | { 10 | // long 11 | Slow = 0, 12 | Stapm = 1, 13 | Fast = 2, 14 | } 15 | 16 | private readonly IntPtr _ry; 17 | private readonly bool _canChangeTdp; 18 | 19 | public AmdProcessor() 20 | { 21 | try 22 | { 23 | _ry = RyzenAdj.init_ryzenadj(); 24 | if (_ry == IntPtr.Zero) 25 | { 26 | throw new Exception("init ryzenadj failed"); 27 | } 28 | 29 | var family = RyzenAdj.get_cpu_family(_ry); 30 | 31 | switch (family) 32 | { 33 | case RyzenFamily.FamUnknown: 34 | case RyzenFamily.FamEnd: 35 | default: 36 | _canChangeTdp = false; 37 | break; 38 | 39 | case RyzenFamily.FamRaven: 40 | case RyzenFamily.FamPicasso: 41 | case RyzenFamily.FamDali: 42 | case RyzenFamily.FamRenoir: 43 | case RyzenFamily.FamLucienne: 44 | case RyzenFamily.FamCezanne: 45 | case RyzenFamily.FamVangogh: 46 | case RyzenFamily.FamRembrandt: 47 | _canChangeTdp = true; 48 | break; 49 | } 50 | 51 | } 52 | catch (DllNotFoundException ex) 53 | { 54 | throw new Exception($"Failed to load libryzenadj.dll: {ex.Message}", ex); 55 | } 56 | catch (BadImageFormatException ex) 57 | { 58 | throw new Exception($"libryzenadj.dll architecture mismatch (32/64-bit?)", ex); 59 | } 60 | catch (Exception ex) 61 | { 62 | throw new Exception($"init ryzenadj failed: {ex.Message}", ex); 63 | } 64 | } 65 | 66 | /// 67 | /// 返回 stamp limit 数据。 68 | /// 69 | /// 备注: 70 | /// 该方法在部分 CPU 下会返回 NULL 71 | /// 72 | /// 说明: 73 | /// 测试处理器: AMD Ryzen™ 7 PRO 6850HS 74 | /// 测试 RyzenAdj 方法:get_tctl_temp、get_fast_limit、get_slow_limit、get_stapm_limit 75 | /// 返回结果:NaN 76 | /// 77 | /// 78 | /// 79 | public float GetStampLimit() 80 | { 81 | return RyzenAdj.get_stapm_limit(_ry); 82 | } 83 | 84 | 85 | public bool SetTctlTemp(uint temp) 86 | { 87 | var result = RyzenAdj.set_tctl_temp(_ry, temp); 88 | return result == (int)ErrCode.AdjErrNone; 89 | } 90 | 91 | private bool SetTdpLimit(PowerType type, double limit) 92 | { 93 | if (!_canChangeTdp) 94 | { 95 | return false; 96 | } 97 | 98 | // 例如:15W : 15000 mW 99 | limit *= 1000; 100 | 101 | var result = type switch 102 | { 103 | PowerType.Fast => RyzenAdj.set_fast_limit(_ry, (uint)limit), 104 | PowerType.Slow => RyzenAdj.set_slow_limit(_ry, (uint)limit), 105 | PowerType.Stapm => RyzenAdj.set_stapm_limit(_ry, (uint)limit), 106 | _ => throw new ArgumentOutOfRangeException(nameof(type), type, null) 107 | }; 108 | 109 | AppContainer.Logger().Debug($"AMDProcessor.SetTdpLimit: type {type}, limit: {(uint)limit}, result: {result}"); 110 | 111 | return result == (int)ErrCode.AdjErrNone; 112 | } 113 | 114 | public bool SetFastPPT(double limit) 115 | { 116 | return SetTdpLimit(PowerType.Fast, limit); 117 | } 118 | 119 | public bool SetSlowPPT(double limit) 120 | { 121 | return SetTdpLimit(PowerType.Slow, limit); 122 | } 123 | 124 | public bool SetStampPPT(double limit) 125 | { 126 | return SetTdpLimit(PowerType.Stapm, limit); 127 | } 128 | } 129 | } -------------------------------------------------------------------------------- /Properties/Resources.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /Common/Logger/SimpleLogger.cs: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2016 Heiswayi Nrird 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | using System; 26 | using System.Diagnostics; 27 | 28 | namespace RyzenTuner.Common.Logger 29 | { 30 | /** 31 | * Refer: https://gist.github.com/heiswayi/69ef5413c0f28b3a58d964447c275058 32 | */ 33 | public class SimpleLogger 34 | { 35 | [Flags] 36 | public enum LogLevel 37 | { 38 | Trace = 0, 39 | Debug, 40 | Info, 41 | Warning, 42 | Error, 43 | Fatal 44 | } 45 | 46 | private const string FileExt = ".log"; 47 | private readonly object _fileLock = new(); 48 | private readonly string _datetimeFormat; 49 | private readonly string _logFilename; 50 | 51 | public LogLevel DefaultLogLevel; 52 | 53 | /// 54 | /// Initiate an instance of SimpleLogger class constructor. 55 | /// If log file does not exist, it will be created automatically. 56 | /// 57 | public SimpleLogger() 58 | { 59 | _datetimeFormat = "yyyy-MM-dd HH:mm:ss"; 60 | _logFilename = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name + FileExt; 61 | DefaultLogLevel = LogLevel.Warning; 62 | } 63 | 64 | public LogLevel ToLogLevel(string logLevel) 65 | { 66 | return logLevel switch 67 | { 68 | "Trace" => LogLevel.Trace, 69 | "Debug" => LogLevel.Debug, 70 | "Info" => LogLevel.Info, 71 | "Warning" => LogLevel.Warning, 72 | "Error" => LogLevel.Error, 73 | "Fatal" => LogLevel.Fatal, 74 | _ => throw new Exception($"不正确的 log level 类型:{logLevel}") 75 | }; 76 | } 77 | 78 | /// 79 | /// Log a DEBUG message 80 | /// 81 | /// Message 82 | public void Debug(string text) 83 | { 84 | WriteFormattedLog(LogLevel.Debug, text); 85 | } 86 | 87 | /// 88 | /// Log an ERROR message 89 | /// 90 | /// Message 91 | public void Error(string text) 92 | { 93 | WriteFormattedLog(LogLevel.Error, text); 94 | } 95 | 96 | /// 97 | /// Log a FATAL ERROR message 98 | /// 99 | /// Message 100 | public void Fatal(string text) 101 | { 102 | WriteFormattedLog(LogLevel.Fatal, text); 103 | } 104 | 105 | /// 106 | /// Log an INFO message 107 | /// 108 | /// Message 109 | public void Info(string text) 110 | { 111 | WriteFormattedLog(LogLevel.Info, text); 112 | } 113 | 114 | /// 115 | /// Log a TRACE message 116 | /// 117 | /// Message 118 | public void Trace(string text) 119 | { 120 | WriteFormattedLog(LogLevel.Trace, text); 121 | } 122 | 123 | /// 124 | /// Log a WARNING message 125 | /// 126 | /// Message 127 | public void Warning(string text) 128 | { 129 | WriteFormattedLog(LogLevel.Warning, text); 130 | } 131 | 132 | public void LogException(Exception e) 133 | { 134 | // Get stack trace for the exception with source file information 135 | var st = new StackTrace(e, true); 136 | // Get the top stack frame 137 | var frame = st.GetFrame(0); 138 | // Get the line number from the stack frame 139 | var line = frame.GetFileLineNumber(); 140 | 141 | Warning($"Exception: {e.Message}\nLine: {line}\nStackTrace: {st}"); 142 | } 143 | 144 | private void WriteLine(string text, bool append = false) 145 | { 146 | if (string.IsNullOrEmpty(text)) 147 | { 148 | return; 149 | } 150 | 151 | lock (_fileLock) 152 | { 153 | using (System.IO.StreamWriter writer = 154 | new System.IO.StreamWriter(_logFilename, append, System.Text.Encoding.UTF8)) 155 | { 156 | writer.WriteLine(text); 157 | } 158 | } 159 | } 160 | 161 | private void WriteFormattedLog(LogLevel level, string text) 162 | { 163 | if (level < DefaultLogLevel) 164 | { 165 | return; 166 | } 167 | 168 | var pretext = level switch 169 | { 170 | LogLevel.Trace => DateTime.Now.ToString(_datetimeFormat) + " [TRACE] ", 171 | LogLevel.Info => DateTime.Now.ToString(_datetimeFormat) + " [INFO] ", 172 | LogLevel.Debug => DateTime.Now.ToString(_datetimeFormat) + " [DEBUG] ", 173 | LogLevel.Warning => DateTime.Now.ToString(_datetimeFormat) + " [WARNING] ", 174 | LogLevel.Error => DateTime.Now.ToString(_datetimeFormat) + " [ERROR] ", 175 | LogLevel.Fatal => DateTime.Now.ToString(_datetimeFormat) + " [FATAL] ", 176 | _ => "" 177 | }; 178 | 179 | WriteLine(pretext + text, true); 180 | } 181 | } 182 | } -------------------------------------------------------------------------------- /Common/EnergyStar/Interop/Win32Api.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using System.Security; 4 | using System.Text; 5 | 6 | namespace RyzenTuner.Common.EnergyStar.Interop 7 | { 8 | internal class Win32Api 9 | { 10 | [DllImport("kernel32.dll")] 11 | public static extern int GetProcessId(IntPtr handle); 12 | 13 | [DllImport("kernel32.dll", SetLastError = true)] 14 | public static extern bool QueryFullProcessImageName([In] IntPtr hProcess, [In] int dwFlags, 15 | [Out] StringBuilder lpExeName, ref int lpdwSize); 16 | 17 | [DllImport("user32.dll")] 18 | public static extern IntPtr GetForegroundWindow(); 19 | 20 | [DllImport("user32.dll", SetLastError = true)] 21 | public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); 22 | 23 | [DllImport("kernel32.dll", SetLastError = true)] 24 | public static extern IntPtr OpenProcess(uint processAccess, bool bInheritHandle, uint processId); 25 | 26 | 27 | [DllImport("kernel32.dll", SetLastError = true)] 28 | public static extern bool SetProcessInformation([In] IntPtr hProcess, 29 | [In] PROCESS_INFORMATION_CLASS ProcessInformationClass, IntPtr ProcessInformation, 30 | uint ProcessInformationSize); 31 | 32 | [DllImport("kernel32.dll", SetLastError = true)] 33 | public static extern bool SetPriorityClass(IntPtr handle, PriorityClass priorityClass); 34 | 35 | [DllImport("kernel32.dll", SetLastError = true)] 36 | [SuppressUnmanagedCodeSecurity] 37 | [return: MarshalAs(UnmanagedType.Bool)] 38 | public static extern bool CloseHandle(IntPtr hObject); 39 | 40 | public delegate bool WindowEnumProc(IntPtr hwnd, IntPtr lparam); 41 | 42 | [DllImport("user32.dll")] 43 | [return: MarshalAs(UnmanagedType.Bool)] 44 | public static extern bool EnumChildWindows(IntPtr hwnd, WindowEnumProc callback, IntPtr lParam); 45 | 46 | // We don't need to bloat this app with WinForm/WPF to show a simple message box 47 | [DllImport("user32.dll")] 48 | public static extern int MessageBox(IntPtr hInstance, string lpText, string lpCaption, uint type); 49 | 50 | // two message box releated constants 51 | public const int MB_OK = 0x00000000; 52 | public const int MB_ICONERROR = 0x00000010; 53 | 54 | [Flags] 55 | public enum ProcessAccessFlags : uint 56 | { 57 | All = 0x001F0FFF, 58 | Terminate = 0x00000001, 59 | CreateThread = 0x00000002, 60 | VirtualMemoryOperation = 0x00000008, 61 | VirtualMemoryRead = 0x00000010, 62 | VirtualMemoryWrite = 0x00000020, 63 | DuplicateHandle = 0x00000040, 64 | CreateProcess = 0x000000080, 65 | SetQuota = 0x00000100, 66 | SetInformation = 0x00000200, 67 | QueryInformation = 0x00000400, 68 | QueryLimitedInformation = 0x00001000, 69 | Synchronize = 0x00100000 70 | } 71 | 72 | /// 73 | /// 参考:https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ne-processthreadsapi-process_information_class 74 | /// 75 | public enum PROCESS_INFORMATION_CLASS 76 | { 77 | ProcessMemoryPriority, 78 | ProcessMemoryExhaustionInfo, 79 | ProcessAppMemoryInfo, 80 | ProcessInPrivateInfo, 81 | 82 | // The process information is represented by a PROCESS_POWER_THROTTLING_STATE structure. 83 | // Allows applications to configure how the system should throttle the target process’s activity when managing power. 84 | ProcessPowerThrottling, 85 | ProcessReservedValue1, 86 | ProcessTelemetryCoverageInfo, 87 | ProcessProtectionLevelInfo, 88 | ProcessLeapSecondInfo, 89 | ProcessInformationClassMax, 90 | } 91 | 92 | [Flags] 93 | public enum ProcessorPowerThrottlingFlags : uint 94 | { 95 | None = 0x0, 96 | 97 | // 当一个进程设置为 PROCESS_POWER_THROTTLING_EXECUTION_SPEED 时,该进程将被分类为 EcoQoS 98 | // 系统通过对 EcoQoS 进程降低 CPU 频率或使用更多高能效的内核等操作来提高电源效率 99 | // 在 Windows 11 之前,EcoQoS 级别并不存在,进程被标记为 LowQoS 100 | // LowQoS:在电池模式下,选择最有效的CPU频率和调度到高效核心。1709 以后版本可用。 101 | // EcoQos:总是选择最有效的CPU频率,并调度到高效的核心。Windows 11 以后版本可用。 102 | // 参考: 103 | // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setprocessinformation 104 | // https://docs.microsoft.com/en-us/windows/win32/procthread/quality-of-service 105 | PROCESS_POWER_THROTTLING_EXECUTION_SPEED = 0x1, 106 | } 107 | 108 | /// 109 | /// 参考:https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setpriorityclass 110 | /// 111 | public enum PriorityClass : uint 112 | { 113 | // 【实时】具有最高优先级的进程。该进程的线程抢占所有其他进程的线程,包括执行重要任务的操作系统进程 114 | REALTIME_PRIORITY_CLASS = 0x100, 115 | 116 | // 【高】执行必须立即执行的时间关键任务的流程 117 | // 在使用高优先级类时要格外小心,因为高优先级类应用程序几乎可以使用所有可用的CPU时间。 118 | HIGH_PRIORITY_CLASS = 0x80, 119 | 120 | // 【高于正常】优先级高于普通优先级但低于高优先级的进程 121 | ABOVE_NORMAL_PRIORITY_CLASS = 0x8000, 122 | 123 | // 【正常】Process with no special scheduling needs 124 | NORMAL_PRIORITY_CLASS = 0x00000020, 125 | 126 | // 【低于正常】优先级高于空闲优先级但低于正常优先级的进程 127 | BELOW_NORMAL_PRIORITY_CLASS = 0x4000, 128 | 129 | // 【低】仅在系统空闲时才运行线程的进程。进程的线程会被运行在更高优先级类中的任何进程的线程抢占。 130 | // 屏幕保护程序就是一个例子。空闲优先级类由子进程继承 131 | IDLE_PRIORITY_CLASS = 0x00000040, 132 | 133 | // 开始后台处理模式。系统降低了进程(及其线程)的资源调度优先级,以便它可以在不显著影响前台活动的情况下执行后台工作 134 | // PROCESS_MODE_BACKGROUND_BEGIN 不适合用于低优先级的后台工作 135 | // 参考:https://stackoverflow.com/questions/13631644/setthreadpriority-and-setpriorityclass 136 | PROCESS_MODE_BACKGROUND_BEGIN = 0x100000, // Windows Vista/2008 and higher 137 | 138 | // 结束后台处理模式。系统恢复进程(及其线程)进入后台处理模式前的资源调度优先级 139 | PROCESS_MODE_BACKGROUND_END = 0x200000, // Windows Vista/2008 and higher 140 | } 141 | 142 | [StructLayout(LayoutKind.Sequential)] 143 | public struct ProcessPowerThrottlingState 144 | { 145 | public const uint ProcessPowerThrottlingCurrentVersion = 1; 146 | 147 | public uint Version; 148 | public ProcessorPowerThrottlingFlags ControlMask; 149 | public ProcessorPowerThrottlingFlags StateMask; 150 | } 151 | } 152 | } -------------------------------------------------------------------------------- /UI/MainForm.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 17, 17 122 | 123 | 124 | 222, 17 125 | 126 | 127 | 128 | 520, 17 129 | 130 | -------------------------------------------------------------------------------- /Common/Hardware/HardwareMonitor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using LibreHardwareMonitor.Hardware; 5 | using RyzenTuner.Common.Container; 6 | 7 | namespace RyzenTuner.Common.Hardware 8 | { 9 | public class HardwareMonitor 10 | { 11 | private float _cpuUsage; 12 | private float _cpuPackagePower; 13 | private float _cpuTemperature; 14 | private float _cpuFreq; 15 | 16 | private float _videoCard3DUsage; 17 | 18 | private readonly Computer _computer; 19 | 20 | public HardwareMonitor() 21 | { 22 | _computer = new Computer 23 | { 24 | IsCpuEnabled = true, 25 | IsGpuEnabled = true, 26 | IsMemoryEnabled = false, 27 | IsMotherboardEnabled = false, 28 | IsControllerEnabled = false, 29 | IsNetworkEnabled = false, 30 | IsStorageEnabled = false 31 | }; 32 | 33 | _computer.Open(); 34 | } 35 | 36 | // Finalizers / destructor 37 | ~HardwareMonitor() 38 | { 39 | _computer.Close(); 40 | } 41 | 42 | public float CpuUsage => _cpuUsage; 43 | 44 | public float CpuPackagePower => _cpuPackagePower; 45 | 46 | public float CpuTemperature => _cpuTemperature; 47 | public float CpuFreq => _cpuFreq; 48 | 49 | public float VideoCard3DUsage => _videoCard3DUsage; 50 | 51 | private void Update() 52 | { 53 | // Triggers the IVisitor.VisitComputer method for the given observer. 54 | _computer.Accept(new UpdateVisitor()); 55 | } 56 | 57 | /// 58 | /// 获取 CPU 传感器信息 59 | /// 60 | /// CPU 名称示例: 61 | /// AMD Ryzen 7 PRO 6850HS with Radeon Graphics 62 | /// AMD Ryzen 7 4800H with Radeon Graphics 63 | /// 64 | /// 备注:如果有多个 CPU,可能会有问题。可忽略,普通电脑一般只有一个 CPU 插槽 65 | /// 66 | /// 67 | private List FetchHardwareCpu() 68 | { 69 | var hardwareCpu = _computer 70 | .Hardware 71 | .Where(i => i.HardwareType == HardwareType.Cpu) 72 | .SelectMany(s => s.Sensors); 73 | var cpuEnumerable = hardwareCpu.ToList(); 74 | 75 | return cpuEnumerable; 76 | } 77 | 78 | /// 79 | /// 获取核心显卡的传感器信息 80 | /// 81 | /// 显卡名称示例:AMD Radeon(TM) Graphics 82 | /// 备注:如果是 AMD 核心显卡 + AMD 独立显卡,可能会有问题 83 | /// 84 | /// 85 | private List FetchHardwareVideoCard() 86 | { 87 | var hardwareVideoCard = _computer 88 | .Hardware 89 | .Where(i => i.HardwareType == HardwareType.GpuAmd) 90 | .SelectMany(s => s.Sensors); 91 | var videoCardList = hardwareVideoCard.ToList(); 92 | 93 | return videoCardList; 94 | } 95 | 96 | public void Monitor() 97 | { 98 | try 99 | { 100 | Update(); 101 | 102 | var cpuSensorList = FetchHardwareCpu(); 103 | _cpuUsage = FetchCpuUsage(cpuSensorList); 104 | _cpuPackagePower = FetchCpuPackage(cpuSensorList); 105 | _cpuTemperature = FetchCpuTemperature(cpuSensorList); 106 | _cpuFreq = FetchCpuFreq(cpuSensorList); 107 | 108 | var videoCardSensorList = FetchHardwareVideoCard(); 109 | _videoCard3DUsage = FetchVideoCard3DUsage(videoCardSensorList); 110 | } 111 | catch (Exception e) 112 | { 113 | AppContainer.Logger().LogException(e); 114 | } 115 | } 116 | 117 | private float FetchVideoCard3DUsage(IEnumerable videoCardSensorList) 118 | { 119 | var linqVideoCard3D = videoCardSensorList 120 | .Where(s => s.SensorType == SensorType.Load) 121 | .Where(s => s.Name == "D3D 3D") 122 | .Where(s => s.Value != null) 123 | .Select(s => s.Value) 124 | .FirstOrDefault(); 125 | if (linqVideoCard3D is <= 100) 126 | { 127 | return linqVideoCard3D.Value; 128 | } 129 | 130 | return 0; 131 | } 132 | 133 | private float FetchCpuTemperature(IEnumerable cpuEnumerable) 134 | { 135 | // CPU 温度 136 | var linqCpuTemperature = cpuEnumerable 137 | .Where(s => s.SensorType == SensorType.Temperature) 138 | .Where(s => s.Name == "Core (Tctl/Tdie)") 139 | .Where(s => s.Value != null) 140 | .Select(s => s.Value) 141 | .FirstOrDefault(); 142 | if (linqCpuTemperature is <= 150) 143 | { 144 | return linqCpuTemperature.Value; 145 | } 146 | 147 | return 0; 148 | } 149 | 150 | /// 151 | /// 获取 CPU 功耗 152 | /// 153 | /// 154 | /// 155 | private float FetchCpuPackage(IEnumerable cpuEnumerable) 156 | { 157 | // 158 | var linqCpuPackage = cpuEnumerable 159 | .Where(s => s.SensorType == SensorType.Power) 160 | .Where(s => s.Name == "Package") 161 | .Where(s => s.Value != null) 162 | .Select(s => s.Value) 163 | .FirstOrDefault(); 164 | if (linqCpuPackage is <= 1000) 165 | { 166 | return linqCpuPackage.Value; 167 | } 168 | 169 | return 0; 170 | } 171 | 172 | /// 173 | /// 获取 CPU 占用率 174 | /// 175 | /// 176 | /// 177 | private static float FetchCpuUsage(IEnumerable cpuEnumerable) 178 | { 179 | var linqCpuUsage = cpuEnumerable 180 | .Where(s => s.SensorType == SensorType.Load) 181 | .Where(s => s.Name == "CPU Total") 182 | .Where(s => s.Value != null) 183 | .Select(s => s.Value) 184 | .FirstOrDefault(); 185 | 186 | if (linqCpuUsage is <= 100) 187 | { 188 | return linqCpuUsage.Value; 189 | } 190 | 191 | return 0; 192 | } 193 | 194 | /// 195 | /// 获取 CPU 平均频率 196 | /// 197 | /// 198 | /// 199 | private float FetchCpuFreq(IEnumerable cpuEnumerable) 200 | { 201 | var cpuCount = Environment.ProcessorCount; 202 | 203 | float tmpTotal = 0; 204 | var tmpCount = 0; 205 | 206 | for (var i = 1; i <= cpuCount; i++) 207 | { 208 | var linqCpuFreq = cpuEnumerable 209 | .Where(s => s.SensorType == SensorType.Clock) 210 | .Where(s => s.Name == $"Core #{i}") 211 | .Where(s => s.Value != null) 212 | .Select(s => s.Value) 213 | .FirstOrDefault(); 214 | if (linqCpuFreq is > 100) 215 | { 216 | tmpTotal += linqCpuFreq.Value; 217 | tmpCount++; 218 | } 219 | } 220 | 221 | if (tmpCount <= 0) 222 | { 223 | return 0; 224 | } 225 | 226 | return tmpTotal / tmpCount; 227 | } 228 | } 229 | } -------------------------------------------------------------------------------- /Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // 5 | // Changes to this file may cause incorrect behavior and will be lost if 6 | // the code is regenerated. 7 | // 8 | //------------------------------------------------------------------------------ 9 | 10 | namespace RyzenTuner.Properties { 11 | 12 | 13 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 14 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.9.0.0")] 15 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 16 | 17 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 18 | 19 | public static Settings Default { 20 | get { 21 | return defaultInstance; 22 | } 23 | } 24 | 25 | [global::System.Configuration.UserScopedSettingAttribute()] 26 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 27 | [global::System.Configuration.DefaultSettingValueAttribute("False")] 28 | public bool EnergyStar { 29 | get { 30 | return ((bool)(this["EnergyStar"])); 31 | } 32 | set { 33 | this["EnergyStar"] = value; 34 | } 35 | } 36 | 37 | [global::System.Configuration.UserScopedSettingAttribute()] 38 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 39 | [global::System.Configuration.DefaultSettingValueAttribute("False")] 40 | public bool KeepAwake { 41 | get { 42 | return ((bool)(this["KeepAwake"])); 43 | } 44 | set { 45 | this["KeepAwake"] = value; 46 | } 47 | } 48 | 49 | [global::System.Configuration.UserScopedSettingAttribute()] 50 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 51 | [global::System.Configuration.DefaultSettingValueAttribute("16")] 52 | public string CustomMode { 53 | get { 54 | return ((string)(this["CustomMode"])); 55 | } 56 | set { 57 | this["CustomMode"] = value; 58 | } 59 | } 60 | 61 | [global::System.Configuration.UserScopedSettingAttribute()] 62 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 63 | [global::System.Configuration.DefaultSettingValueAttribute("16")] 64 | public string AutoMode { 65 | get { 66 | return ((string)(this["AutoMode"])); 67 | } 68 | set { 69 | this["AutoMode"] = value; 70 | } 71 | } 72 | 73 | [global::System.Configuration.UserScopedSettingAttribute()] 74 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 75 | [global::System.Configuration.DefaultSettingValueAttribute("8")] 76 | public string SleepMode { 77 | get { 78 | return ((string)(this["SleepMode"])); 79 | } 80 | set { 81 | this["SleepMode"] = value; 82 | } 83 | } 84 | 85 | [global::System.Configuration.UserScopedSettingAttribute()] 86 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 87 | [global::System.Configuration.DefaultSettingValueAttribute("16")] 88 | public string PowerSaveMode { 89 | get { 90 | return ((string)(this["PowerSaveMode"])); 91 | } 92 | set { 93 | this["PowerSaveMode"] = value; 94 | } 95 | } 96 | 97 | [global::System.Configuration.UserScopedSettingAttribute()] 98 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 99 | [global::System.Configuration.DefaultSettingValueAttribute("26")] 100 | public string BalancedMode { 101 | get { 102 | return ((string)(this["BalancedMode"])); 103 | } 104 | set { 105 | this["BalancedMode"] = value; 106 | } 107 | } 108 | 109 | [global::System.Configuration.UserScopedSettingAttribute()] 110 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 111 | [global::System.Configuration.DefaultSettingValueAttribute("45")] 112 | public string PerformanceMode { 113 | get { 114 | return ((string)(this["PerformanceMode"])); 115 | } 116 | set { 117 | this["PerformanceMode"] = value; 118 | } 119 | } 120 | 121 | [global::System.Configuration.UserScopedSettingAttribute()] 122 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 123 | [global::System.Configuration.DefaultSettingValueAttribute("AutoMode")] 124 | public string CurrentMode { 125 | get { 126 | return ((string)(this["CurrentMode"])); 127 | } 128 | set { 129 | this["CurrentMode"] = value; 130 | } 131 | } 132 | 133 | [global::System.Configuration.UserScopedSettingAttribute()] 134 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 135 | [global::System.Configuration.DefaultSettingValueAttribute("Debug")] 136 | public string LogLevel { 137 | get { 138 | return ((string)(this["LogLevel"])); 139 | } 140 | set { 141 | this["LogLevel"] = value; 142 | } 143 | } 144 | 145 | [global::System.Configuration.UserScopedSettingAttribute()] 146 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 147 | [global::System.Configuration.DefaultSettingValueAttribute("example1.exe,example2.exe")] 148 | public string EnergyStarBypassProcessList { 149 | get { 150 | return ((string)(this["EnergyStarBypassProcessList"])); 151 | } 152 | set { 153 | this["EnergyStarBypassProcessList"] = value; 154 | } 155 | } 156 | 157 | [global::System.Configuration.UserScopedSettingAttribute()] 158 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 159 | [global::System.Configuration.DefaultSettingValueAttribute("100")] 160 | public int TctlTemp { 161 | get { 162 | return ((int)(this["TctlTemp"])); 163 | } 164 | set { 165 | this["TctlTemp"] = value; 166 | } 167 | } 168 | 169 | [global::System.Configuration.UserScopedSettingAttribute()] 170 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 171 | [global::System.Configuration.DefaultSettingValueAttribute("51")] 172 | public int FastPPT { 173 | get { 174 | return ((int)(this["FastPPT"])); 175 | } 176 | set { 177 | this["FastPPT"] = value; 178 | } 179 | } 180 | 181 | [global::System.Configuration.UserScopedSettingAttribute()] 182 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 183 | [global::System.Configuration.DefaultSettingValueAttribute("45")] 184 | public int SlowPPT { 185 | get { 186 | return ((int)(this["SlowPPT"])); 187 | } 188 | set { 189 | this["SlowPPT"] = value; 190 | } 191 | } 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /Properties/Strings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // 5 | // Changes to this file may cause incorrect behavior and will be lost if 6 | // the code is regenerated. 7 | // 8 | //------------------------------------------------------------------------------ 9 | 10 | namespace RyzenTuner.Properties { 11 | using System; 12 | 13 | 14 | /// 15 | /// A strongly-typed resource class, for looking up localized strings, etc. 16 | /// 17 | // This class was auto-generated by the StronglyTypedResourceBuilder 18 | // class via a tool like ResGen or Visual Studio. 19 | // To add or remove a member, edit your .ResX file then rerun ResGen 20 | // with the /str option, or rebuild your VS project. 21 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 22 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 23 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 24 | internal class Strings { 25 | 26 | private static global::System.Resources.ResourceManager resourceMan; 27 | 28 | private static global::System.Globalization.CultureInfo resourceCulture; 29 | 30 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 31 | internal Strings() { 32 | } 33 | 34 | /// 35 | /// Returns the cached ResourceManager instance used by this class. 36 | /// 37 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 38 | internal static global::System.Resources.ResourceManager ResourceManager { 39 | get { 40 | if (object.ReferenceEquals(resourceMan, null)) { 41 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("RyzenTuner.Properties.Strings", typeof(Strings).Assembly); 42 | resourceMan = temp; 43 | } 44 | return resourceMan; 45 | } 46 | } 47 | 48 | /// 49 | /// Overrides the current thread's CurrentUICulture property for all 50 | /// resource lookups using this strongly typed resource class. 51 | /// 52 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 53 | internal static global::System.Globalization.CultureInfo Culture { 54 | get { 55 | return resourceCulture; 56 | } 57 | set { 58 | resourceCulture = value; 59 | } 60 | } 61 | 62 | /// 63 | /// Looks up a localized string similar to 自动模式. 64 | /// 65 | internal static string AutoMode { 66 | get { 67 | return ResourceManager.GetString("AutoMode", resourceCulture); 68 | } 69 | } 70 | 71 | /// 72 | /// Looks up a localized string similar to 平衡模式. 73 | /// 74 | internal static string BalancedMode { 75 | get { 76 | return ResourceManager.GetString("BalancedMode", resourceCulture); 77 | } 78 | } 79 | 80 | /// 81 | /// Looks up a localized string similar to 手动模式. 82 | /// 83 | internal static string CustomMode { 84 | get { 85 | return ResourceManager.GetString("CustomMode", resourceCulture); 86 | } 87 | } 88 | 89 | /// 90 | /// Looks up a localized string similar to 性能模式. 91 | /// 92 | internal static string PerformanceMode { 93 | get { 94 | return ResourceManager.GetString("PerformanceMode", resourceCulture); 95 | } 96 | } 97 | 98 | /// 99 | /// Looks up a localized string similar to 省电模式. 100 | /// 101 | internal static string PowerSaveMode { 102 | get { 103 | return ResourceManager.GetString("PowerSaveMode", resourceCulture); 104 | } 105 | } 106 | 107 | /// 108 | /// Looks up a localized string similar to 待机模式. 109 | /// 110 | internal static string SleepMode { 111 | get { 112 | return ResourceManager.GetString("SleepMode", resourceCulture); 113 | } 114 | } 115 | 116 | /// 117 | /// Looks up a localized string similar to 关于. 118 | /// 119 | internal static string TextAbout { 120 | get { 121 | return ResourceManager.GetString("TextAbout", resourceCulture); 122 | } 123 | } 124 | 125 | /// 126 | /// Looks up a localized string similar to 开启 EnergyStar. 127 | /// 128 | internal static string TextEnableEnergyStar { 129 | get { 130 | return ResourceManager.GetString("TextEnableEnergyStar", resourceCulture); 131 | } 132 | } 133 | 134 | /// 135 | /// Looks up a localized string similar to 同一时间内,只允许运行一个 RyzenTuner 程序. 136 | /// 137 | internal static string TextExceptionOnlyOneProgramIsAllowedToRun { 138 | get { 139 | return ResourceManager.GetString("TextExceptionOnlyOneProgramIsAllowedToRun", resourceCulture); 140 | } 141 | } 142 | 143 | /// 144 | /// Looks up a localized string similar to RyzenTuner 出现错误. 145 | /// 146 | internal static string TextExceptionTitle { 147 | get { 148 | return ResourceManager.GetString("TextExceptionTitle", resourceCulture); 149 | } 150 | } 151 | 152 | /// 153 | /// Looks up a localized string similar to 退出. 154 | /// 155 | internal static string TextExit { 156 | get { 157 | return ResourceManager.GetString("TextExit", resourceCulture); 158 | } 159 | } 160 | 161 | /// 162 | /// Looks up a localized string similar to 功率:限制{power_limit}W、实际{actual_power_limit}W. 163 | /// 164 | internal static string TextNoticeText { 165 | get { 166 | return ResourceManager.GetString("TextNoticeText", resourceCulture); 167 | } 168 | } 169 | 170 | /// 171 | /// Looks up a localized string similar to 其他选项. 172 | /// 173 | internal static string TextOtherOptions { 174 | get { 175 | return ResourceManager.GetString("TextOtherOptions", resourceCulture); 176 | } 177 | } 178 | 179 | /// 180 | /// Looks up a localized string similar to 功率限制. 181 | /// 182 | internal static string TextPowerLimit { 183 | get { 184 | return ResourceManager.GetString("TextPowerLimit", resourceCulture); 185 | } 186 | } 187 | 188 | /// 189 | /// Looks up a localized string similar to 保持唤醒. 190 | /// 191 | internal static string TextStayAwake { 192 | get { 193 | return ResourceManager.GetString("TextStayAwake", resourceCulture); 194 | } 195 | } 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Oo]ut/ 33 | [Ll]og/ 34 | [Ll]ogs/ 35 | 36 | # Visual Studio 2015/2017 cache/options directory 37 | .vs/ 38 | # Uncomment if you have tasks that create the project's static files in wwwroot 39 | #wwwroot/ 40 | 41 | # Visual Studio 2017 auto generated files 42 | Generated\ Files/ 43 | 44 | # MSTest test Results 45 | [Tt]est[Rr]esult*/ 46 | [Bb]uild[Ll]og.* 47 | 48 | # NUnit 49 | *.VisualState.xml 50 | TestResult.xml 51 | nunit-*.xml 52 | 53 | # Build Results of an ATL Project 54 | [Dd]ebugPS/ 55 | [Rr]eleasePS/ 56 | dlldata.c 57 | 58 | # Benchmark Results 59 | BenchmarkDotNet.Artifacts/ 60 | 61 | # .NET Core 62 | project.lock.json 63 | project.fragment.lock.json 64 | artifacts/ 65 | 66 | # ASP.NET Scaffolding 67 | ScaffoldingReadMe.txt 68 | 69 | # StyleCop 70 | StyleCopReport.xml 71 | 72 | # Files built by Visual Studio 73 | *_i.c 74 | *_p.c 75 | *_h.h 76 | *.ilk 77 | *.meta 78 | *.obj 79 | *.iobj 80 | *.pch 81 | *.pdb 82 | *.ipdb 83 | *.pgc 84 | *.pgd 85 | *.rsp 86 | *.sbr 87 | *.tlb 88 | *.tli 89 | *.tlh 90 | *.tmp 91 | *.tmp_proj 92 | *_wpftmp.csproj 93 | *.log 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio LightSwitch build output 298 | **/*.HTMLClient/GeneratedArtifacts 299 | **/*.DesktopClient/GeneratedArtifacts 300 | **/*.DesktopClient/ModelManifest.xml 301 | **/*.Server/GeneratedArtifacts 302 | **/*.Server/ModelManifest.xml 303 | _Pvt_Extensions 304 | 305 | # Paket dependency manager 306 | .paket/paket.exe 307 | paket-files/ 308 | 309 | # FAKE - F# Make 310 | .fake/ 311 | 312 | # CodeRush personal settings 313 | .cr/personal 314 | 315 | # Python Tools for Visual Studio (PTVS) 316 | __pycache__/ 317 | *.pyc 318 | 319 | # Cake - Uncomment if you are using it 320 | # tools/** 321 | # !tools/packages.config 322 | 323 | # Tabs Studio 324 | *.tss 325 | 326 | # Telerik's JustMock configuration file 327 | *.jmconfig 328 | 329 | # BizTalk build output 330 | *.btp.cs 331 | *.btm.cs 332 | *.odx.cs 333 | *.xsd.cs 334 | 335 | # OpenCover UI analysis results 336 | OpenCover/ 337 | 338 | # Azure Stream Analytics local run output 339 | ASALocalRun/ 340 | 341 | # MSBuild Binary and Structured Log 342 | *.binlog 343 | 344 | # NVidia Nsight GPU debugger configuration file 345 | *.nvuser 346 | 347 | # MFractors (Xamarin productivity tool) working folder 348 | .mfractor/ 349 | 350 | # Local History for Visual Studio 351 | .localhistory/ 352 | 353 | # BeatPulse healthcheck temp database 354 | healthchecksdb 355 | 356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 357 | MigrationBackup/ 358 | 359 | # Ionide (cross platform F# VS Code tools) working folder 360 | .ionide/ 361 | 362 | # Fody - auto-generated XML schema 363 | FodyWeavers.xsd 364 | 365 | .idea 366 | .DS_Store 367 | RyzenTuner.csproj.DotSettings -------------------------------------------------------------------------------- /UI/MainForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | using RyzenTuner.Common; 4 | using RyzenTuner.Common.Container; 5 | using RyzenTuner.Properties; 6 | using RyzenTuner.Utils; 7 | 8 | namespace RyzenTuner.UI 9 | { 10 | public partial class MainForm : Form 11 | { 12 | private Int64 _tickCount; 13 | 14 | public MainForm() 15 | { 16 | InitializeComponent(); 17 | } 18 | 19 | private void Form1_Load(object sender, EventArgs e) 20 | { 21 | checkBoxEnergyStar.Checked = Properties.Settings.Default.EnergyStar; 22 | keepAwakeCheckBox.Checked = Properties.Settings.Default.KeepAwake; 23 | textBox1.Text = Properties.Settings.Default.CustomMode; 24 | SyncEnergyModeSelection(); 25 | 26 | // 设置系统唤醒状态 27 | keepAwakeCheckBox_CheckedChanged(null, EventArgs.Empty); 28 | 29 | WindowState = FormWindowState.Minimized; 30 | } 31 | 32 | private void checkBoxEnergyStar_CheckedChanged(object sender, EventArgs e) 33 | { 34 | Properties.Settings.Default.EnergyStar = checkBoxEnergyStar.Checked; 35 | Properties.Settings.Default.Save(); 36 | 37 | if (checkBoxEnergyStar.Checked) 38 | { 39 | _needRunBoostAllBgProcesses = false; 40 | } 41 | else 42 | { 43 | _needRunBoostAllBgProcesses = true; 44 | } 45 | } 46 | 47 | private void keepAwakeCheckBox_CheckedChanged(object? sender, EventArgs e) 48 | { 49 | Properties.Settings.Default.KeepAwake = keepAwakeCheckBox.Checked; 50 | Properties.Settings.Default.Save(); 51 | 52 | if (Properties.Settings.Default.KeepAwake) 53 | { 54 | Awake.KeepingSysAwake(true); 55 | } 56 | else 57 | { 58 | Awake.AllowSysSleep(); 59 | } 60 | } 61 | 62 | private void AboutAppToolStripMenuItem_Click(object sender, EventArgs e) 63 | { 64 | new AboutForm().ShowDialog(); 65 | } 66 | 67 | private void ExitAppToolStripMenuItem_Click(object sender, EventArgs e) 68 | { 69 | Application.Exit(); 70 | } 71 | 72 | private void Form1_FormClosing(object sender, FormClosingEventArgs e) 73 | { 74 | if (e.CloseReason == CloseReason.UserClosing) 75 | { 76 | if (radioButton6.Checked) 77 | { 78 | ChangeEnergyMode(radioButton6, e); 79 | } 80 | 81 | e.Cancel = true; 82 | Hide(); 83 | } 84 | } 85 | 86 | private void notifyIcon1_MouseDoubleClick(object sender, MouseEventArgs e) 87 | { 88 | Show(); 89 | } 90 | 91 | private void mainFormTimer_Tick(object sender, EventArgs e) 92 | { 93 | _tickCount++; 94 | AppContainer.HardwareMonitor().Monitor(); 95 | 96 | DoPowerLimit(); 97 | 98 | DoProcessManage(); 99 | } 100 | 101 | 102 | private void ChangeEnergyMode(object sender, EventArgs e) 103 | { 104 | if (((RadioButton)sender).Checked) 105 | { 106 | var checkedMode = ((RadioButton)sender).Tag.ToString(); 107 | Settings.Default.CurrentMode = checkedMode; 108 | Settings.Default.Save(); 109 | SyncEnergyModeSelection(); 110 | 111 | DoPowerLimit(); 112 | } 113 | } 114 | 115 | private void DoPowerLimit() 116 | { 117 | try 118 | { 119 | notifyIcon1.Text = RyzenTunerUtils.GetNoticeText(); 120 | var processor = AppContainer.AmdProcessor(); 121 | 122 | var stampLimit = RyzenAdjUtils.GetPowerLimit(); 123 | var tctlTemp = RyzenAdjUtils.GetTctlTemp(); 124 | var fastPpt = Settings.Default.FastPPT; 125 | var slowPpt = Settings.Default.SlowPPT; 126 | 127 | AppContainer.Logger().Debug($"fastPPT: {fastPpt}, slowPPT: {slowPpt}, stampPPT: {stampLimit}, tctlTemp: {tctlTemp}"); 128 | 129 | // 调用 ryzenadj 调整 Cpu 设置 130 | // 说明: 131 | // 假设 fastPPT 为 51 瓦,slowPPT 为 45 瓦,stampPPT 为 30 瓦。 132 | // 假设没有撞到功耗墙 133 | // 当打开网页的时候,处理器功耗会升到 51 瓦(fastPPT 定义) 134 | // 过了 x 秒后(由 SLOW PPT TIME CONSTANT定义) ,处理器功耗变为 45 瓦(slowPPT 定义) 135 | // 再过了 x 后秒(由 STAPM TIME CONSTANT 定义),处理器功耗变为 30 瓦(stampPPT 定义) 136 | // 之后,一直维持在 30 瓦 137 | 138 | // 备注: 139 | // ryzenadj 0.17.0 以及部分旧版本测试在当前环境存在问题: 140 | // 1、stamp limit 设置不生效 141 | // 2、调整 fast limit,会同时修改 fast limit 和 stamp limit 142 | // 因此,设置 fastPPT 的值跟 stamp 一样 143 | // 这样子的话,前期不会有性能爆发,在后面会有一段性能爆发 144 | processor.SetFastPPT(stampLimit); 145 | processor.SetSlowPPT(slowPpt); 146 | processor.SetStampPPT(stampLimit); 147 | 148 | AppContainer.AmdProcessor().SetTctlTemp((uint)tctlTemp); 149 | 150 | // 配置系统电源计划 151 | // 1、仅在【性能模式】下开启睿频 152 | if (RyzenTunerUtils.IsPerformanceMode(stampLimit)) 153 | { 154 | AppContainer.PowerConfig().EnableCpuBoost(); 155 | } 156 | else 157 | { 158 | AppContainer.PowerConfig().DisableCpuBoost(); 159 | } 160 | } 161 | catch (Exception e) 162 | { 163 | MessageBox.Show(e.Message); 164 | radioButton5.Checked = true; 165 | ChangeEnergyMode(radioButton5, EventArgs.Empty); 166 | } 167 | } 168 | 169 | /// 170 | /// 执行进程管理任务(EnergyStar) 171 | /// 172 | private void DoProcessManage() 173 | { 174 | // 如果开启【EnergyStar】 175 | if (checkBoxEnergyStar.Checked) 176 | { 177 | AppContainer.EnergyManager().HandleForeground(); 178 | 179 | // Throttle 当前用户的所有后台进程 180 | if ( 181 | // 首次运行 30 秒 182 | _tickCount == 15 || 183 | // 每 5 分钟 184 | _tickCount % 150 == 0 185 | ) 186 | { 187 | AppContainer.EnergyManager().ThrottleAllUserBackgroundProcesses(); 188 | } 189 | 190 | return; 191 | } 192 | 193 | // 关闭【EnergyStar】的情况 194 | if (_needRunBoostAllBgProcesses) 195 | { 196 | // Boost 当前用户的所有后台进程:每 5 分钟检查一次,一般只运行一次 197 | if (_tickCount % 150 == 0) 198 | { 199 | AppContainer.EnergyManager().BoostAllUserBackgroundProcesses(); 200 | _needRunBoostAllBgProcesses = false; 201 | } 202 | } 203 | } 204 | 205 | private void SyncEnergyModeSelection() 206 | { 207 | foreach (Control c in powerLimitGroupBox.Controls) 208 | { 209 | if (c.Tag != null && c.Tag.ToString() == Properties.Settings.Default.CurrentMode) 210 | { 211 | var rb = (RadioButton)c; 212 | rb.Checked = true; 213 | } 214 | } 215 | 216 | 217 | foreach (ToolStripItem tsmi in contextMenuStrip1.Items) 218 | { 219 | if (tsmi.Tag != null && tsmi.Tag.ToString() == Properties.Settings.Default.CurrentMode) 220 | { 221 | var tsmi2 = (ToolStripMenuItem)tsmi; 222 | tsmi2.Checked = true; 223 | } 224 | else if (tsmi is ToolStripMenuItem) 225 | { 226 | var tsmi2 = (ToolStripMenuItem)tsmi; 227 | tsmi2.Checked = false; 228 | } 229 | } 230 | } 231 | 232 | private void textBox1_TextChanged(object sender, EventArgs e) 233 | { 234 | if (radioButton6.Checked) radioButton5.Checked = true; 235 | Properties.Settings.Default.CustomMode = textBox1.Text; 236 | Properties.Settings.Default.Save(); 237 | } 238 | 239 | private void ToolStripMenuItems_Clicked(object sender, EventArgs e) 240 | { 241 | var menuItem = (ToolStripMenuItem)sender; 242 | if (menuItem.CheckState != CheckState.Checked) 243 | { 244 | menuItem.Checked = true; 245 | } 246 | 247 | // 根据用户点击的托盘菜单选项,自动选中不同的【功率限制】按钮 248 | foreach (Control c in powerLimitGroupBox.Controls) 249 | { 250 | if (c.Tag == null || c.Tag.ToString() != ((ToolStripMenuItem)sender).Tag.ToString()) 251 | { 252 | continue; 253 | } 254 | 255 | var radioButton = (RadioButton)c; 256 | radioButton.Checked = true; 257 | } 258 | } 259 | 260 | private void Form1_Shown(object sender, EventArgs e) 261 | { 262 | if (Environment.GetCommandLineArgs().Length > 1 && Environment.GetCommandLineArgs()[1] == "-hide") 263 | { 264 | Hide(); 265 | } 266 | } 267 | } 268 | } -------------------------------------------------------------------------------- /RyzenTuner.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {EBC428A6-134E-4B8B-8B89-3595FF804785} 8 | WinExe 9 | RyzenTuner 10 | RyzenTuner 11 | v4.8 12 | 512 13 | true 14 | true 15 | 9 16 | enable 17 | 18 | 19 | AnyCPU 20 | none 21 | true 22 | bin\Release\ 23 | TRACE 24 | prompt 25 | 4 26 | false 27 | 28 | 29 | bin\Debug\ 30 | full 31 | true 32 | false 33 | 34 | 35 | app.manifest 36 | 37 | 38 | Resources\phoenix-96.ico 39 | 40 | 41 | 42 | packages\HidSharp.2.1.0\lib\net35\HidSharp.dll 43 | 44 | 45 | packages\LibreHardwareMonitorLib.0.9.0\lib\net47\LibreHardwareMonitorLib.dll 46 | 47 | 48 | 49 | 50 | packages\System.Buffers.4.4.0\lib\netstandard2.0\System.Buffers.dll 51 | 52 | 53 | 54 | 55 | 56 | 57 | packages\System.Memory.4.5.3\lib\netstandard2.0\System.Memory.dll 58 | 59 | 60 | 61 | packages\System.Numerics.Vectors.4.4.0\lib\net46\System.Numerics.Vectors.dll 62 | 63 | 64 | packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | packages\Vanara.Core.3.4.6\lib\net48\Vanara.Core.dll 77 | 78 | 79 | packages\Vanara.PInvoke.Kernel32.3.4.6\lib\net48\Vanara.PInvoke.Kernel32.dll 80 | 81 | 82 | packages\Vanara.PInvoke.PowrProf.3.4.6\lib\net48\Vanara.PInvoke.PowrProf.dll 83 | 84 | 85 | packages\Vanara.PInvoke.Shared.3.4.6\lib\net48\Vanara.PInvoke.Shared.dll 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | True 104 | True 105 | Strings.resx 106 | 107 | 108 | Form 109 | 110 | 111 | AboutForm.cs 112 | 113 | 114 | Form 115 | 116 | 117 | MainForm.cs 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | Resources.resx 126 | 127 | 128 | ResXFileCodeGenerator 129 | Resources.Designer.cs 130 | Designer 131 | 132 | 133 | True 134 | Resources.resx 135 | 136 | 137 | Strings.resx 138 | 139 | 140 | ResXFileCodeGenerator 141 | Strings.Designer.cs 142 | 143 | 144 | AboutForm.cs 145 | 146 | 147 | AboutForm.cs 148 | 149 | 150 | MainForm.cs 151 | 152 | 153 | MainForm.cs 154 | 155 | 156 | 157 | 158 | SettingsSingleFileGenerator 159 | Settings.Designer.cs 160 | 161 | 162 | True 163 | Settings.settings 164 | True 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | False 173 | Microsoft .NET Framework 4.8 %28x86 和 x64%29 174 | true 175 | 176 | 177 | False 178 | .NET Framework 3.5 SP1 179 | false 180 | 181 | 182 | 183 | 184 | 185 | 186 | -------------------------------------------------------------------------------- /Common/Processor/RyzenAdj.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace RyzenTuner.Common.Processor 5 | { 6 | // 参考:https://github.com/FlyGoat/RyzenAdj/blob/master/lib/ryzenadj.h#L15-L26 7 | public enum RyzenFamily 8 | { 9 | FamUnknown = -1, 10 | FamRaven = 0, 11 | FamPicasso, 12 | FamRenoir, 13 | FamCezanne, 14 | FamDali, 15 | FamLucienne, 16 | FamVangogh, 17 | FamRembrandt, 18 | FamEnd 19 | } 20 | 21 | // 错误码,参考:https://github.com/FlyGoat/RyzenAdj/blob/master/lib/ryzenadj.h#L52-L56 22 | public enum ErrCode 23 | { 24 | AdjErrNone = 0, 25 | AdjErrFamUnsupported = -1, 26 | AdjErrSmuTimeout = -2, 27 | AdjErrSmuUnsupported = -3, 28 | AdjErrSmuRejected = -4, 29 | AdjErrMemoryAccess = -5, 30 | } 31 | 32 | /** 33 | * RyzenAdj.cs 34 | * Author:Valkirie 35 | * Refer:https://github.com/Valkirie/ControllerService/blob/main/ControllerCommon/Processor/AMD/RyzenAdj.cs 36 | * 37 | * 依赖: 38 | * inpoutx64.dll 39 | * libryzenadj.dll 40 | * WinRing0x64.dll 41 | * WinRing0x64.sys 42 | * 43 | * 当前 ryzenadj 版本:v0.17.0 44 | */ 45 | public class RyzenAdj 46 | { 47 | [DllImport("libryzenadj.dll")] 48 | public static extern IntPtr init_ryzenadj(); 49 | 50 | [DllImport("libryzenadj.dll")] 51 | public static extern int set_stapm_limit(IntPtr ry, [In] uint value); 52 | 53 | [DllImport("libryzenadj.dll")] 54 | public static extern int set_fast_limit(IntPtr ry, [In] uint value); 55 | 56 | [DllImport("libryzenadj.dll")] 57 | public static extern int set_slow_limit(IntPtr ry, [In] uint value); 58 | 59 | [DllImport("libryzenadj.dll")] 60 | public static extern int set_slow_time(IntPtr ry, [In] uint value); 61 | 62 | [DllImport("libryzenadj.dll")] 63 | public static extern int set_stapm_time(IntPtr ry, [In] uint value); 64 | 65 | [DllImport("libryzenadj.dll")] 66 | public static extern int set_tctl_temp(IntPtr ry, [In] uint value); 67 | 68 | [DllImport("libryzenadj.dll")] 69 | public static extern int set_vrm_current(IntPtr ry, [In] uint value); 70 | 71 | [DllImport("libryzenadj.dll")] 72 | public static extern int set_vrmsoc_current(IntPtr ry, [In] uint value); 73 | 74 | [DllImport("libryzenadj.dll")] 75 | public static extern int set_vrmmax_current(IntPtr ry, [In] uint value); 76 | 77 | [DllImport("libryzenadj.dll")] 78 | public static extern int set_vrmsocmax_current(IntPtr ry, [In] uint value); 79 | 80 | [DllImport("libryzenadj.dll")] 81 | public static extern int set_psi0_current(IntPtr ry, [In] uint value); 82 | 83 | [DllImport("libryzenadj.dll")] 84 | public static extern int set_psi0soc_current(IntPtr ry, [In] uint value); 85 | 86 | [DllImport("libryzenadj.dll")] 87 | public static extern int set_max_gfxclk_freq(IntPtr ry, [In] uint value); 88 | 89 | [DllImport("libryzenadj.dll")] 90 | public static extern int set_min_gfxclk_freq(IntPtr ry, [In] uint value); 91 | 92 | [DllImport("libryzenadj.dll")] 93 | public static extern int set_max_socclk_freq(IntPtr ry, [In] uint value); 94 | 95 | [DllImport("libryzenadj.dll")] 96 | public static extern int set_min_socclk_freq(IntPtr ry, [In] uint value); 97 | 98 | [DllImport("libryzenadj.dll")] 99 | public static extern int set_max_fclk_freq(IntPtr ry, [In] uint value); 100 | 101 | [DllImport("libryzenadj.dll")] 102 | public static extern int set_min_fclk_freq(IntPtr ry, [In] uint value); 103 | 104 | [DllImport("libryzenadj.dll")] 105 | public static extern int set_max_vcn(IntPtr ry, [In] uint value); 106 | 107 | [DllImport("libryzenadj.dll")] 108 | public static extern int set_min_vcn(IntPtr ry, [In] uint value); 109 | 110 | [DllImport("libryzenadj.dll")] 111 | public static extern int set_max_lclk(IntPtr ry, [In] uint value); 112 | 113 | [DllImport("libryzenadj.dll")] 114 | public static extern int set_min_lclk(IntPtr ry, [In] uint value); 115 | 116 | [DllImport("libryzenadj.dll")] 117 | public static extern int set_gfx_clk(IntPtr ry, [In] uint value); 118 | 119 | [DllImport("libryzenadj.dll")] 120 | public static extern int set_oc_clk(IntPtr ry, [In] uint value); 121 | 122 | [DllImport("libryzenadj.dll")] 123 | public static extern int set_per_core_oc_clk(IntPtr ry, [In] uint value); 124 | 125 | [DllImport("libryzenadj.dll")] 126 | public static extern int set_oc_volt(IntPtr ry, [In] uint value); 127 | 128 | [DllImport("libryzenadj.dll")] 129 | public static extern int disable_oc(IntPtr ry); 130 | 131 | [DllImport("libryzenadj.dll")] 132 | public static extern int enable_oc(IntPtr ry); 133 | 134 | [DllImport("libryzenadj.dll")] 135 | public static extern int set_prochot_deassertion_ramp(IntPtr ry, [In] uint value); 136 | 137 | [DllImport("libryzenadj.dll")] 138 | public static extern int set_apu_skin_temp_limit(IntPtr ry, [In] uint value); 139 | 140 | [DllImport("libryzenadj.dll")] 141 | public static extern int set_dgpu_skin_temp_limit(IntPtr ry, [In] uint value); 142 | 143 | [DllImport("libryzenadj.dll")] 144 | public static extern int set_apu_slow_limit(IntPtr ry, [In] uint value); 145 | 146 | [DllImport("libryzenadj.dll")] 147 | public static extern int set_power_saving(IntPtr ry); 148 | 149 | [DllImport("libryzenadj.dll")] 150 | public static extern int set_max_performance(IntPtr ry); 151 | 152 | [DllImport("libryzenadj.dll")] 153 | public static extern int refresh_table(IntPtr ry); 154 | 155 | [DllImport("libryzenadj.dll")] 156 | public static extern IntPtr get_table_values(IntPtr ry); 157 | 158 | [DllImport("libryzenadj.dll")] 159 | public static extern float get_stapm_limit(IntPtr ry); 160 | 161 | [DllImport("libryzenadj.dll")] 162 | public static extern float get_stapm_value(IntPtr ry); 163 | 164 | [DllImport("libryzenadj.dll")] 165 | public static extern float get_stapm_time(IntPtr ry); 166 | 167 | [DllImport("libryzenadj.dll")] 168 | public static extern float get_fast_limit(IntPtr ry); 169 | 170 | [DllImport("libryzenadj.dll")] 171 | public static extern float get_fast_value(IntPtr ry); 172 | 173 | [DllImport("libryzenadj.dll")] 174 | public static extern float get_slow_limit(IntPtr ry); 175 | 176 | [DllImport("libryzenadj.dll")] 177 | public static extern float get_slow_value(IntPtr ry); 178 | 179 | [DllImport("libryzenadj.dll")] 180 | public static extern float get_apu_slow_limit(IntPtr ry); 181 | 182 | [DllImport("libryzenadj.dll")] 183 | public static extern float get_apu_slow_value(IntPtr ry); 184 | 185 | [DllImport("libryzenadj.dll")] 186 | public static extern float get_vrm_current(IntPtr ry); 187 | 188 | [DllImport("libryzenadj.dll")] 189 | public static extern float get_vrm_current_value(IntPtr ry); 190 | 191 | [DllImport("libryzenadj.dll")] 192 | public static extern float get_vrmsoc_current(IntPtr ry); 193 | 194 | [DllImport("libryzenadj.dll")] 195 | public static extern float get_vrmsoc_current_value(IntPtr ry); 196 | 197 | [DllImport("libryzenadj.dll")] 198 | public static extern float get_vrmmax_current(IntPtr ry); 199 | 200 | [DllImport("libryzenadj.dll")] 201 | public static extern float get_vrmmax_current_value(IntPtr ry); 202 | 203 | [DllImport("libryzenadj.dll")] 204 | public static extern float get_vrmsocmax_current(IntPtr ry); 205 | 206 | [DllImport("libryzenadj.dll")] 207 | public static extern float get_vrmsocmax_current_value(IntPtr ry); 208 | 209 | [DllImport("libryzenadj.dll")] 210 | public static extern float get_tctl_temp(IntPtr ry); 211 | 212 | [DllImport("libryzenadj.dll")] 213 | public static extern float get_tctl_temp_value(IntPtr ry); 214 | 215 | [DllImport("libryzenadj.dll")] 216 | public static extern float get_apu_skin_temp_limit(IntPtr ry); 217 | 218 | [DllImport("libryzenadj.dll")] 219 | public static extern float get_apu_skin_temp_value(IntPtr ry); 220 | 221 | [DllImport("libryzenadj.dll")] 222 | public static extern float get_dgpu_skin_temp_limit(IntPtr ry); 223 | 224 | [DllImport("libryzenadj.dll")] 225 | public static extern float get_dgpu_skin_temp_value(IntPtr ry); 226 | 227 | [DllImport("libryzenadj.dll")] 228 | public static extern float get_core_clk(IntPtr ry, uint value); 229 | 230 | [DllImport("libryzenadj.dll")] 231 | public static extern float get_core_temp(IntPtr ry, uint value); 232 | 233 | [DllImport("libryzenadj.dll")] 234 | public static extern float get_core_power(IntPtr ry, uint value); 235 | 236 | [DllImport("libryzenadj.dll")] 237 | public static extern float get_core_volt(IntPtr ry, uint value); 238 | 239 | [DllImport("libryzenadj.dll")] 240 | public static extern float get_l3_logic(IntPtr ry); 241 | 242 | [DllImport("libryzenadj.dll")] 243 | public static extern float get_l3_vddm(IntPtr ry); 244 | 245 | [DllImport("libryzenadj.dll")] 246 | public static extern float get_l3_temp(IntPtr ry); 247 | 248 | [DllImport("libryzenadj.dll")] 249 | public static extern float get_l3_clk(IntPtr ry); 250 | 251 | [DllImport("libryzenadj.dll")] 252 | public static extern float get_gfx_clk(IntPtr ry); 253 | 254 | [DllImport("libryzenadj.dll")] 255 | public static extern float get_gfx_temp(IntPtr ry); 256 | 257 | [DllImport("libryzenadj.dll")] 258 | public static extern float get_gfx_volt(IntPtr ry); 259 | 260 | [DllImport("libryzenadj.dll")] 261 | public static extern float get_mem_clk(IntPtr ry); 262 | 263 | [DllImport("libryzenadj.dll")] 264 | public static extern float get_fclk(IntPtr ry); 265 | 266 | [DllImport("libryzenadj.dll")] 267 | public static extern float get_soc_power(IntPtr ry); 268 | 269 | [DllImport("libryzenadj.dll")] 270 | public static extern float get_soc_volt(IntPtr ry); 271 | 272 | [DllImport("libryzenadj.dll")] 273 | public static extern float get_socket_power(IntPtr ry); 274 | 275 | [DllImport("libryzenadj.dll")] 276 | public static extern RyzenFamily get_cpu_family(IntPtr ry); 277 | } 278 | } -------------------------------------------------------------------------------- /docs/AMD_Ryzen7_6850HS.md: -------------------------------------------------------------------------------- 1 | ## 目的 2 | 3 | 了解 CPU 在日常待机情况下的数据,方便 RyzenTuner 配置合适的参数。 4 | 5 | ## 基本信息 6 | 7 | 1. 测试 CPU:AMD Ryzen™ 7 PRO 6850HS 8 | * TDP:35W 9 | * 基础频率:3.2GHz 10 | * 最大加速时钟频率:4.7GHz 11 | * 最高温度:95 ℃ 12 | 2. 13 | 测试代码:[DebugUtils.cs#L12-L23](https://github.com/zqhong/RyzenTuner/blob/1af00263b1222fdaa9e6a2e1016c2954d65e0d5a/Utils/DebugUtils.cs#L12-L23) 14 | 3. 室温 26 ℃ 15 | 16 | ## 待机 17 | 18 | ### 测试内容 19 | 20 | 1. 后台运行日常软件,比如微信、Chrome、终端 21 | 2. 前台运行 IDE 22 | 23 | ### 关闭睿频 24 | 25 | ```text 26 | 功率:限制1瓦、实际4.28瓦,CPU:19.12%、51.88℃、399.27MHz,GPU:2.87% 27 | 功率:限制2瓦、实际4.36瓦,CPU:29.95%、50.88℃、399.27MHz,GPU:5.80% 28 | 功率:限制3瓦、实际4.07瓦,CPU:16.84%、50.13℃、399.27MHz,GPU:2.75% 29 | 功率:限制4瓦、实际4.06瓦,CPU:6.63%、49.63℃、1343.98MHz,GPU:2.25% 30 | 功率:限制5瓦、实际4.57瓦,CPU:3.50%、49.25℃、1397.45MHz,GPU:2.35% 31 | 功率:限制6瓦、实际5.63瓦,CPU:3.58%、49.13℃、1397.45MHz,GPU:2.61% 32 | 功率:限制7瓦、实际6.92瓦,CPU:2.41%、49.25℃、2720.04MHz,GPU:2.13% 33 | 功率:限制8瓦、实际7.62瓦,CPU:0.71%、49.75℃、3169.22MHz,GPU:1.84% 34 | 功率:限制9瓦、实际7.66瓦,CPU:0.72%、49.88℃、3169.22MHz,GPU:1.72% 35 | 功率:限制10瓦、实际7.64瓦,CPU:0.71%、49.88℃、3169.22MHz,GPU:1.76% 36 | 功率:限制11瓦、实际7.68瓦,CPU:1.09%、49.88℃、3169.22MHz,GPU:1.71% 37 | 功率:限制12瓦、实际7.66瓦,CPU:0.85%、50.00℃、3169.22MHz,GPU:1.79% 38 | 功率:限制13瓦、实际7.61瓦,CPU:0.66%、49.88℃、3169.22MHz,GPU:1.75% 39 | 功率:限制14瓦、实际7.64瓦,CPU:0.66%、50.00℃、3169.22MHz,GPU:1.90% 40 | 功率:限制15瓦、实际7.72瓦,CPU:0.95%、50.00℃、3169.22MHz,GPU:1.78% 41 | 功率:限制16瓦、实际7.64瓦,CPU:0.85%、50.00℃、3169.22MHz,GPU:1.67% 42 | 功率:限制17瓦、实际7.62瓦,CPU:0.70%、50.00℃、3169.22MHz,GPU:1.77% 43 | 功率:限制18瓦、实际7.67瓦,CPU:0.71%、50.13℃、3169.22MHz,GPU:1.83% 44 | 功率:限制19瓦、实际7.64瓦,CPU:0.95%、50.13℃、3169.22MHz,GPU:1.75% 45 | 功率:限制20瓦、实际7.59瓦,CPU:1.10%、50.00℃、3169.22MHz,GPU:1.76% 46 | 功率:限制21瓦、实际7.73瓦,CPU:0.90%、50.00℃、3169.22MHz,GPU:1.68% 47 | 功率:限制22瓦、实际7.72瓦,CPU:1.57%、50.00℃、3169.22MHz,GPU:1.99% 48 | 功率:限制23瓦、实际7.66瓦,CPU:0.72%、50.00℃、3169.22MHz,GPU:1.75% 49 | 功率:限制24瓦、实际7.67瓦,CPU:0.67%、50.00℃、3169.22MHz,GPU:1.73% 50 | 功率:限制25瓦、实际7.62瓦,CPU:0.71%、50.13℃、3169.22MHz,GPU:1.74% 51 | 功率:限制26瓦、实际7.62瓦,CPU:0.76%、50.13℃、3169.22MHz,GPU:1.75% 52 | 功率:限制27瓦、实际7.71瓦,CPU:1.18%、50.13℃、3169.22MHz,GPU:1.81% 53 | 功率:限制28瓦、实际7.80瓦,CPU:0.90%、50.38℃、3169.22MHz,GPU:1.93% 54 | 功率:限制29瓦、实际7.73瓦,CPU:0.71%、50.25℃、3169.22MHz,GPU:1.80% 55 | 功率:限制30瓦、实际7.60瓦,CPU:0.57%、50.25℃、3169.22MHz,GPU:1.78% 56 | ``` 57 | 58 | ### 开启睿频 59 | 60 | ```text 61 | 功率:限制1瓦、实际4.26瓦,CPU:19.17%、49.50℃、399.27MHz,GPU:3.33% 62 | 功率:限制2瓦、实际3.98瓦,CPU:17.43%、48.88℃、399.27MHz,GPU:2.76% 63 | 功率:限制3瓦、实际3.99瓦,CPU:16.78%、48.38℃、399.27MHz,GPU:2.77% 64 | 功率:限制4瓦、实际4.04瓦,CPU:7.57%、48.13℃、1397.45MHz,GPU:2.71% 65 | 功率:限制5瓦、实际4.53瓦,CPU:2.75%、48.00℃、1397.45MHz,GPU:2.53% 66 | 功率:限制6瓦、实际5.73瓦,CPU:3.34%、48.00℃、1397.45MHz,GPU:2.20% 67 | 功率:限制7瓦、实际6.94瓦,CPU:2.15%、48.13℃、1597.09MHz,GPU:2.18% 68 | 功率:限制8瓦、实际8.35瓦,CPU:0.95%、49.00℃、2670.13MHz,GPU:1.91% 69 | 功率:限制9瓦、实际9.39瓦,CPU:1.00%、49.88℃、3437.48MHz,GPU:2.04% 70 | 功率:限制10瓦、实际10.38瓦,CPU:0.72%、50.88℃、3169.22MHz,GPU:1.90% 71 | 功率:限制11瓦、实际11.33瓦,CPU:0.47%、51.63℃、3654.79MHz,GPU:1.84% 72 | 功率:限制12瓦、实际11.76瓦,CPU:0.95%、52.50℃、3169.22MHz,GPU:1.82% 73 | 功率:限制13瓦、实际12.02瓦,CPU:0.75%、53.13℃、3169.22MHz,GPU:1.94% 74 | 功率:限制14瓦、实际12.28瓦,CPU:0.95%、53.88℃、3169.22MHz,GPU:1.83% 75 | 功率:限制15瓦、实际12.18瓦,CPU:0.71%、54.13℃、3169.22MHz,GPU:2.01% 76 | 功率:限制16瓦、实际12.19瓦,CPU:0.66%、54.25℃、3169.22MHz,GPU:1.91% 77 | 功率:限制17瓦、实际12.53瓦,CPU:0.66%、54.63℃、3358.45MHz,GPU:2.05% 78 | 功率:限制18瓦、实际12.55瓦,CPU:0.80%、54.88℃、3169.22MHz,GPU:1.86% 79 | 功率:限制19瓦、实际12.69瓦,CPU:0.90%、55.13℃、3169.22MHz,GPU:1.98% 80 | 功率:限制20瓦、实际12.55瓦,CPU:0.56%、55.38℃、3093.31MHz,GPU:1.84% 81 | 功率:限制21瓦、实际12.57瓦,CPU:0.67%、55.75℃、3169.22MHz,GPU:1.93% 82 | 功率:限制22瓦、实际12.42瓦,CPU:0.76%、55.88℃、3164.02MHz,GPU:1.93% 83 | 功率:限制23瓦、实际12.62瓦,CPU:0.95%、56.13℃、3169.22MHz,GPU:1.96% 84 | 功率:限制24瓦、实际13.01瓦,CPU:0.66%、56.50℃、4071.74MHz,GPU:1.84% 85 | 功率:限制25瓦、实际12.89瓦,CPU:0.62%、56.63℃、3363.65MHz,GPU:1.85% 86 | 功率:限制26瓦、实际12.95瓦,CPU:0.43%、56.75℃、3169.22MHz,GPU:1.88% 87 | 功率:限制27瓦、实际12.71瓦,CPU:1.04%、56.88℃、3169.22MHz,GPU:2.05% 88 | 功率:限制28瓦、实际12.50瓦,CPU:0.95%、56.88℃、3169.22MHz,GPU:1.97% 89 | 功率:限制29瓦、实际12.52瓦,CPU:0.62%、57.00℃、3680.78MHz,GPU:1.96% 90 | 功率:限制30瓦、实际12.66瓦,CPU:1.23%、57.13℃、3358.46MHz,GPU:1.95% 91 | ``` 92 | 93 | ## 轻度使用 94 | 95 | ### 测试内容 96 | 97 | 1. 播放哔哩哔哩 1080P 98 | 高码率视频:[https://www.bilibili.com/video/BV11V4y1J7Rm](https://www.bilibili.com/video/BV11V4y1J7Rm) 99 | 2. 开启弹幕,智能云屏蔽:3级 100 | 101 | ### 关闭睿频 102 | 103 | ```text 104 | 功率:限制1瓦、实际4.97瓦,CPU:49.02%、49.63℃、399.27MHz,GPU:7.26% 105 | 功率:限制2瓦、实际4.92瓦,CPU:50.70%、48.63℃、399.27MHz,GPU:9.86% 106 | 功率:限制3瓦、实际4.91瓦,CPU:58.71%、47.88℃、399.27MHz,GPU:12.65% 107 | 功率:限制4瓦、实际4.75瓦,CPU:54.32%、47.38℃、399.27MHz,GPU:10.22% 108 | 功率:限制5瓦、实际5.14瓦,CPU:50.99%、47.13℃、476.47MHz,GPU:11.07% 109 | 功率:限制6瓦、实际5.66瓦,CPU:13.21%、47.00℃、1397.45MHz,GPU:7.76% 110 | 功率:限制7瓦、实际6.64瓦,CPU:4.65%、46.88℃、1430.72MHz,GPU:6.06% 111 | 功率:限制8瓦、实际7.94瓦,CPU:6.89%、47.00℃、3169.22MHz,GPU:5.53% 112 | 功率:限制9瓦、实际8.42瓦,CPU:2.30%、47.38℃、3169.22MHz,GPU:4.74% 113 | 功率:限制10瓦、实际8.60瓦,CPU:2.37%、47.63℃、3119.31MHz,GPU:4.66% 114 | 功率:限制11瓦、实际9.25瓦,CPU:4.31%、48.00℃、3169.22MHz,GPU:7.42% 115 | 功率:限制12瓦、实际10.04瓦,CPU:5.40%、48.63℃、3169.22MHz,GPU:11.52% 116 | 功率:限制13瓦、实际9.65瓦,CPU:4.15%、48.75℃、3169.22MHz,GPU:11.06% 117 | 功率:限制14瓦、实际9.44瓦,CPU:3.98%、48.88℃、3169.22MHz,GPU:11.36% 118 | 功率:限制15瓦、实际10.17瓦,CPU:4.68%、49.38℃、3169.22MHz,GPU:11.36% 119 | 功率:限制16瓦、实际8.82瓦,CPU:2.62%、49.25℃、3169.22MHz,GPU:6.16% 120 | 功率:限制17瓦、实际9.90瓦,CPU:3.36%、49.75℃、3169.22MHz,GPU:10.97% 121 | 功率:限制18瓦、实际9.82瓦,CPU:4.29%、50.00℃、3169.22MHz,GPU:10.58% 122 | 功率:限制19瓦、实际9.24瓦,CPU:2.98%、50.25℃、3169.22MHz,GPU:9.25% 123 | 功率:限制20瓦、实际9.15瓦,CPU:3.48%、50.38℃、3169.22MHz,GPU:4.71% 124 | 功率:限制21瓦、实际8.70瓦,CPU:2.70%、50.50℃、3169.22MHz,GPU:4.83% 125 | 功率:限制22瓦、实际9.03瓦,CPU:2.82%、50.75℃、3169.22MHz,GPU:4.83% 126 | 功率:限制23瓦、实际9.09瓦,CPU:4.07%、51.00℃、3169.22MHz,GPU:4.54% 127 | 功率:限制24瓦、实际8.93瓦,CPU:3.13%、51.00℃、3169.22MHz,GPU:6.14% 128 | 功率:限制25瓦、实际9.98瓦,CPU:4.79%、51.13℃、3169.22MHz,GPU:11.21% 129 | 功率:限制26瓦、实际9.66瓦,CPU:4.63%、51.00℃、3169.22MHz,GPU:10.97% 130 | 功率:限制27瓦、实际9.15瓦,CPU:3.84%、50.75℃、3169.22MHz,GPU:6.48% 131 | 功率:限制28瓦、实际8.57瓦,CPU:3.20%、50.38℃、3169.22MHz,GPU:4.57% 132 | 功率:限制29瓦、实际8.76瓦,CPU:3.38%、50.25℃、3169.22MHz,GPU:4.71% 133 | 功率:限制30瓦、实际8.50瓦,CPU:2.43%、50.00℃、3169.22MHz,GPU:4.69% 134 | ``` 135 | 136 | ### 开启睿频 137 | 138 | ```text 139 | 功率:限制1瓦、实际4.50瓦,CPU:24.81%、49.13℃、399.27MHz,GPU:8.85% 140 | 功率:限制2瓦、实际4.37瓦,CPU:27.98%、48.25℃、399.27MHz,GPU:8.38% 141 | 功率:限制3瓦、实际4.30瓦,CPU:27.54%、47.75℃、399.27MHz,GPU:8.11% 142 | 功率:限制4瓦、实际4.30瓦,CPU:18.23%、47.25℃、456.64MHz,GPU:8.32% 143 | 功率:限制5瓦、实际4.81瓦,CPU:10.70%、47.00℃、1397.45MHz,GPU:6.44% 144 | 功率:限制6瓦、实际5.52瓦,CPU:7.10%、46.75℃、1397.45MHz,GPU:6.58% 145 | 功率:限制7瓦、实际6.72瓦,CPU:4.41%、46.75℃、1756.80MHz,GPU:6.13% 146 | 功率:限制8瓦、实际7.89瓦,CPU:3.94%、47.38℃、3234.10MHz,GPU:5.13% 147 | 功率:限制9瓦、实际9.32瓦,CPU:4.32%、48.38℃、2036.91MHz,GPU:5.07% 148 | 功率:限制10瓦、实际10.30瓦,CPU:3.39%、49.25℃、3690.14MHz,GPU:4.72% 149 | 功率:限制11瓦、实际11.37瓦,CPU:2.70%、50.50℃、3169.22MHz,GPU:5.23% 150 | 功率:限制12瓦、实际12.39瓦,CPU:2.58%、51.75℃、3040.29MHz,GPU:4.95% 151 | 功率:限制13瓦、实际13.34瓦,CPU:2.77%、53.00℃、3169.22MHz,GPU:5.02% 152 | 功率:限制14瓦、实际14.30瓦,CPU:3.03%、54.38℃、3270.08MHz,GPU:5.02% 153 | 功率:限制15瓦、实际14.90瓦,CPU:2.86%、55.25℃、3535.22MHz,GPU:5.06% 154 | 功率:限制16瓦、实际14.90瓦,CPU:2.62%、55.88℃、3654.79MHz,GPU:4.82% 155 | 功率:限制17瓦、实际15.34瓦,CPU:2.08%、56.63℃、3340.78MHz,GPU:4.96% 156 | 功率:限制18瓦、实际15.08瓦,CPU:2.58%、57.00℃、3169.22MHz,GPU:4.78% 157 | 功率:限制19瓦、实际17.49瓦,CPU:3.05%、58.38℃、3169.22MHz,GPU:5.00% 158 | 功率:限制20瓦、实际15.14瓦,CPU:3.01%、58.13℃、3556.01MHz,GPU:4.54% 159 | 功率:限制21瓦、实际16.50瓦,CPU:3.27%、58.75℃、4041.58MHz,GPU:4.84% 160 | 功率:限制22瓦、实际15.32瓦,CPU:4.28%、58.63℃、3169.22MHz,GPU:4.74% 161 | 功率:限制23瓦、实际15.30瓦,CPU:3.27%、58.63℃、3714.06MHz,GPU:4.94% 162 | 功率:限制24瓦、实际16.21瓦,CPU:3.38%、59.00℃、4148.68MHz,GPU:4.89% 163 | 功率:限制25瓦、实际15.29瓦,CPU:1.85%、58.75℃、3169.22MHz,GPU:4.63% 164 | 功率:限制26瓦、实际15.44瓦,CPU:2.77%、58.75℃、3864.82MHz,GPU:4.75% 165 | 功率:限制27瓦、实际14.94瓦,CPU:2.75%、58.63℃、2975.82MHz,GPU:4.57% 166 | 功率:限制28瓦、实际14.70瓦,CPU:2.37%、58.63℃、3714.06MHz,GPU:4.71% 167 | 功率:限制29瓦、实际14.59瓦,CPU:2.67%、58.50℃、3753.57MHz,GPU:4.71% 168 | 功率:限制30瓦、实际17.01瓦,CPU:4.29%、59.63℃、3169.22MHz,GPU:4.78% 169 | ``` 170 | 171 | ## CPU 满载使用 172 | 173 | ### 测试内容 174 | 175 | 1. AIDA64 运行 Stress FPU 176 | 177 | ### 关闭睿频 178 | 179 | ```text 180 | 功率:限制1瓦、实际5.76瓦,CPU:100.00%、52.50℃、399.27MHz,GPU:3.36% 181 | 功率:限制2瓦、实际5.29瓦,CPU:100.00%、51.13℃、399.27MHz,GPU:4.37% 182 | 功率:限制3瓦、实际5.30瓦,CPU:100.00%、50.13℃、399.27MHz,GPU:6.30% 183 | 功率:限制4瓦、实际5.40瓦,CPU:99.96%、49.50℃、399.27MHz,GPU:7.12% 184 | 功率:限制5瓦、实际5.39瓦,CPU:99.96%、49.00℃、399.27MHz,GPU:4.89% 185 | 功率:限制6瓦、实际5.97瓦,CPU:100.00%、48.88℃、608.63MHz,GPU:3.03% 186 | 功率:限制7瓦、实际7.00瓦,CPU:100.00%、49.00℃、835.97MHz,GPU:2.35% 187 | 功率:限制8瓦、实际8.01瓦,CPU:100.00%、49.38℃、998.18MHz,GPU:2.24% 188 | 功率:限制9瓦、实际9.03瓦,CPU:100.00%、49.88℃、1183.56MHz,GPU:2.11% 189 | 功率:限制10瓦、实际9.93瓦,CPU:100.00%、50.50℃、1397.45MHz,GPU:2.10% 190 | 功率:限制11瓦、实际10.69瓦,CPU:100.00%、50.75℃、1397.45MHz,GPU:2.63% 191 | 功率:限制12瓦、实际10.89瓦,CPU:100.00%、50.88℃、1397.45MHz,GPU:2.31% 192 | 功率:限制13瓦、实际12.57瓦,CPU:100.00%、51.25℃、1397.45MHz,GPU:2.11% 193 | 功率:限制14瓦、实际13.80瓦,CPU:100.00%、52.00℃、1637.01MHz,GPU:2.01% 194 | 功率:限制15瓦、实际15.03瓦,CPU:100.00%、52.88℃、1816.69MHz,GPU:2.13% 195 | 功率:限制16瓦、实际16.04瓦,CPU:100.00%、53.88℃、2096.18MHz,GPU:2.42% 196 | 功率:限制17瓦、实际17.04瓦,CPU:100.00%、54.88℃、2220.95MHz,GPU:2.11% 197 | 功率:限制18瓦、实际18.03瓦,CPU:100.00%、55.88℃、2320.77MHz,GPU:2.04% 198 | 功率:限制19瓦、实际19.04瓦,CPU:100.00%、57.00℃、2345.72MHz,GPU:1.80% 199 | 功率:限制20瓦、实际20.05瓦,CPU:100.00%、58.13℃、2445.54MHz,GPU:1.81% 200 | 功率:限制21瓦、实际21.04瓦,CPU:100.00%、59.25℃、2420.58MHz,GPU:1.93% 201 | 功率:限制22瓦、实际22.05瓦,CPU:100.00%、60.50℃、2470.49MHz,GPU:1.83% 202 | 功率:限制23瓦、实际23.05瓦,CPU:100.00%、61.75℃、2676.37MHz,GPU:1.94% 203 | 功率:限制24瓦、实际24.04瓦,CPU:100.00%、62.88℃、2735.63MHz,GPU:2.00% 204 | 功率:限制25瓦、实际25.05瓦,CPU:100.00%、64.25℃、2695.08MHz,GPU:2.02% 205 | 功率:限制26瓦、实际26.06瓦,CPU:100.00%、65.63℃、2869.76MHz,GPU:1.76% 206 | 功率:限制27瓦、实际27.06瓦,CPU:100.00%、67.00℃、2900.96MHz,GPU:1.87% 207 | 功率:限制28瓦、实际28.05瓦,CPU:100.00%、68.25℃、2944.63MHz,GPU:2.00% 208 | 功率:限制29瓦、实际29.08瓦,CPU:100.00%、69.63℃、3022.61MHz,GPU:1.85% 209 | 功率:限制30瓦、实际30.07瓦,CPU:100.00%、71.00℃、3069.40MHz,GPU:2.05% 210 | ``` 211 | 212 | ### 开启睿频 213 | 214 | ```text 215 | 功率:限制1瓦、实际6.15瓦,CPU:100.00%、63.75℃、399.27MHz,GPU:3.09% 216 | 功率:限制2瓦、实际5.58瓦,CPU:100.00%、59.88℃、399.27MHz,GPU:3.00% 217 | 功率:限制3瓦、实际5.54瓦,CPU:100.00%、57.63℃、399.27MHz,GPU:3.35% 218 | 功率:限制4瓦、实际5.54瓦,CPU:100.00%、56.13℃、399.27MHz,GPU:3.31% 219 | 功率:限制5瓦、实际5.47瓦,CPU:100.00%、55.13℃、399.27MHz,GPU:3.02% 220 | 功率:限制6瓦、实际5.94瓦,CPU:100.00%、54.25℃、543.18MHz,GPU:2.97% 221 | 功率:限制7瓦、实际7.00瓦,CPU:100.00%、53.88℃、736.16MHz,GPU:2.52% 222 | 功率:限制8瓦、实际8.01瓦,CPU:100.00%、53.75℃、998.53MHz,GPU:2.34% 223 | 功率:限制9瓦、实际8.96瓦,CPU:100.00%、53.88℃、1212.07MHz,GPU:2.09% 224 | 功率:限制10瓦、实际9.99瓦,CPU:100.00%、54.25℃、1397.45MHz,GPU:2.05% 225 | 功率:限制11瓦、实际10.72瓦,CPU:100.00%、54.25℃、1397.45MHz,GPU:2.25% 226 | 功率:限制12瓦、实际10.96瓦,CPU:100.00%、54.13℃、1397.45MHz,GPU:2.88% 227 | 功率:限制13瓦、实际12.28瓦,CPU:100.00%、54.13℃、1397.45MHz,GPU:1.94% 228 | 功率:限制14瓦、实际13.86瓦,CPU:100.00%、54.63℃、1617.05MHz,GPU:1.99% 229 | 功率:限制15瓦、实际15.00瓦,CPU:100.00%、55.25℃、1696.90MHz,GPU:1.90% 230 | 功率:限制16瓦、实际16.03瓦,CPU:100.00%、56.00℃、1976.39MHz,GPU:1.95% 231 | 功率:限制17瓦、实际17.03瓦,CPU:100.00%、56.75℃、2171.04MHz,GPU:2.10% 232 | 功率:限制18瓦、实际18.03瓦,CPU:100.00%、57.63℃、2245.90MHz,GPU:1.89% 233 | 功率:限制19瓦、实际19.03瓦,CPU:100.00%、58.50℃、2270.86MHz,GPU:1.90% 234 | 功率:限制20瓦、实际20.04瓦,CPU:100.00%、59.50℃、2470.49MHz,GPU:1.94% 235 | 功率:限制21瓦、实际21.05瓦,CPU:100.00%、60.50℃、2470.49MHz,GPU:1.81% 236 | 功率:限制22瓦、实际22.06瓦,CPU:100.00%、61.63℃、2573.43MHz,GPU:1.77% 237 | 功率:限制23瓦、实际23.04瓦,CPU:100.00%、62.75℃、2695.08MHz,GPU:1.93% 238 | 功率:限制24瓦、实际24.06瓦,CPU:100.00%、64.00℃、2720.04MHz,GPU:1.96% 239 | 功率:限制25瓦、实际25.06瓦,CPU:100.00%、65.13℃、2819.86MHz,GPU:1.81% 240 | 功率:限制26瓦、实际26.06瓦,CPU:100.00%、66.50℃、2863.53MHz,GPU:1.92% 241 | 功率:限制27瓦、实际27.06瓦,CPU:100.00%、67.63℃、2894.72MHz,GPU:2.01% 242 | 功率:限制28瓦、实际28.07瓦,CPU:100.00%、68.88℃、2969.58MHz,GPU:1.96% 243 | 功率:限制29瓦、实际29.07瓦,CPU:100.00%、70.13℃、2994.54MHz,GPU:1.85% 244 | 功率:限制30瓦、实际30.05瓦,CPU:100.00%、71.50℃、2894.72MHz,GPU:1.90% 245 | ``` 246 | -------------------------------------------------------------------------------- /Common/Container/Container.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | // Refer: https://github.com/microsoft/MinIoC/blob/main/Container.cs 4 | 5 | using System; 6 | using System.Collections.Concurrent; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Linq.Expressions; 10 | using System.Reflection; 11 | 12 | namespace RyzenTuner.Common.Container 13 | { 14 | /// 15 | /// Inversion of control container handles dependency injection for registered types 16 | /// 17 | public class Container : Container.IScope 18 | { 19 | #region Public interfaces 20 | 21 | /// 22 | /// Represents a scope in which per-scope objects are instantiated a single time 23 | /// 24 | public interface IScope : IDisposable, IServiceProvider 25 | { 26 | } 27 | 28 | /// 29 | /// IRegisteredType is return by Container.Register and allows further configuration for the registration 30 | /// 31 | public interface IRegisteredType 32 | { 33 | /// 34 | /// Make registered type a singleton 35 | /// 36 | void AsSingleton(); 37 | 38 | /// 39 | /// Make registered type a per-scope type (single instance within a Scope) 40 | /// 41 | void PerScope(); 42 | } 43 | 44 | #endregion 45 | 46 | // Map of registered types 47 | private readonly Dictionary> _registeredTypes = 48 | new Dictionary>(); 49 | 50 | // Lifetime management 51 | private readonly ContainerLifetime _lifetime; 52 | 53 | /// 54 | /// Creates a new instance of IoC Container 55 | /// 56 | public Container() => _lifetime = new ContainerLifetime(t => _registeredTypes[t]); 57 | 58 | /// 59 | /// Registers a factory function which will be called to resolve the specified interface 60 | /// 61 | /// Interface to register 62 | /// Factory function 63 | /// 64 | public IRegisteredType Register(Type @interface, Func factory) 65 | => RegisterType(@interface, _ => factory()); 66 | 67 | /// 68 | /// Registers an implementation type for the specified interface 69 | /// 70 | /// Interface to register 71 | /// Implementing type 72 | /// 73 | public IRegisteredType Register(Type @interface, Type implementation) 74 | => RegisterType(@interface, FactoryFromType(implementation)); 75 | 76 | private IRegisteredType RegisterType(Type itemType, Func factory) 77 | => new RegisteredType(itemType, f => _registeredTypes[itemType] = f, factory); 78 | 79 | /// 80 | /// Returns the object registered for the given type, if registered 81 | /// 82 | /// Type as registered with the container 83 | /// Instance of the registered type, if registered; otherwise 84 | public object GetService(Type type) 85 | { 86 | Func registeredType; 87 | 88 | if (!_registeredTypes.TryGetValue(type, out registeredType)) 89 | { 90 | // 不显示 WARN 91 | return null!; 92 | } 93 | 94 | return registeredType(_lifetime); 95 | } 96 | 97 | /// 98 | /// Creates a new scope 99 | /// 100 | /// Scope object 101 | public IScope CreateScope() => new ScopeLifetime(_lifetime); 102 | 103 | /// 104 | /// Disposes any objects owned by this container. 105 | /// 106 | public void Dispose() => _lifetime.Dispose(); 107 | 108 | #region Lifetime management 109 | 110 | // ILifetime management adds resolution strategies to an IScope 111 | interface ILifetime : IScope 112 | { 113 | object GetServiceAsSingleton(Type type, Func factory); 114 | 115 | object GetServicePerScope(Type type, Func factory); 116 | } 117 | 118 | // ObjectCache provides common caching logic for lifetimes 119 | abstract class ObjectCache 120 | { 121 | // Instance cache 122 | private readonly ConcurrentDictionary _instanceCache = 123 | new ConcurrentDictionary(); 124 | 125 | // Get from cache or create and cache object 126 | protected object GetCached(Type type, Func factory, ILifetime lifetime) 127 | => _instanceCache.GetOrAdd(type, _ => factory(lifetime)); 128 | 129 | public void Dispose() 130 | { 131 | foreach (var obj in _instanceCache.Values) 132 | (obj as IDisposable)?.Dispose(); 133 | } 134 | } 135 | 136 | // Container lifetime management 137 | class ContainerLifetime : ObjectCache, ILifetime 138 | { 139 | // Retrieves the factory functino from the given type, provided by owning container 140 | public Func> GetFactory { get; private set; } 141 | 142 | public ContainerLifetime(Func> getFactory) => GetFactory = getFactory; 143 | 144 | public object GetService(Type type) => GetFactory(type)(this); 145 | 146 | // Singletons get cached per container 147 | public object GetServiceAsSingleton(Type type, Func factory) 148 | => GetCached(type, factory, this); 149 | 150 | // At container level, per-scope items are equivalent to singletons 151 | public object GetServicePerScope(Type type, Func factory) 152 | => GetServiceAsSingleton(type, factory); 153 | } 154 | 155 | // Per-scope lifetime management 156 | class ScopeLifetime : ObjectCache, ILifetime 157 | { 158 | // Singletons come from parent container's lifetime 159 | private readonly ContainerLifetime _parentLifetime; 160 | 161 | public ScopeLifetime(ContainerLifetime parentContainer) => _parentLifetime = parentContainer; 162 | 163 | public object GetService(Type type) => _parentLifetime.GetFactory(type)(this); 164 | 165 | // Singleton resolution is delegated to parent lifetime 166 | public object GetServiceAsSingleton(Type type, Func factory) 167 | => _parentLifetime.GetServiceAsSingleton(type, factory); 168 | 169 | // Per-scope objects get cached 170 | public object GetServicePerScope(Type type, Func factory) 171 | => GetCached(type, factory, this); 172 | } 173 | 174 | #endregion 175 | 176 | #region Container items 177 | 178 | // Compiles a lambda that calls the given type's first constructor resolving arguments 179 | private static Func FactoryFromType(Type itemType) 180 | { 181 | // Get first constructor for the type 182 | var constructors = itemType.GetConstructors(); 183 | if (constructors.Length == 0) 184 | { 185 | // If no public constructor found, search for an internal constructor 186 | constructors = itemType.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic); 187 | } 188 | 189 | var constructor = constructors.First(); 190 | 191 | // Compile constructor call as a lambda expression 192 | var arg = Expression.Parameter(typeof(ILifetime)); 193 | return (Func)Expression.Lambda( 194 | Expression.New(constructor, constructor.GetParameters().Select( 195 | param => 196 | { 197 | var resolve = new Func( 198 | lifetime => lifetime.GetService(param.ParameterType)); 199 | return Expression.Convert( 200 | Expression.Call(Expression.Constant(resolve.Target), resolve.Method, arg), 201 | param.ParameterType); 202 | })), 203 | arg).Compile(); 204 | } 205 | 206 | // RegisteredType is supposed to be a short lived object tying an item to its container 207 | // and allowing users to mark it as a singleton or per-scope item 208 | class RegisteredType : IRegisteredType 209 | { 210 | private readonly Type _itemType; 211 | private readonly Action> _registerFactory; 212 | private readonly Func _factory; 213 | 214 | public RegisteredType(Type itemType, Action> registerFactory, 215 | Func factory) 216 | { 217 | _itemType = itemType; 218 | _registerFactory = registerFactory; 219 | _factory = factory; 220 | 221 | registerFactory(_factory); 222 | } 223 | 224 | public void AsSingleton() 225 | => _registerFactory(lifetime => lifetime.GetServiceAsSingleton(_itemType, _factory)); 226 | 227 | public void PerScope() 228 | => _registerFactory(lifetime => lifetime.GetServicePerScope(_itemType, _factory)); 229 | } 230 | 231 | #endregion 232 | } 233 | 234 | /// 235 | /// Extension methods for Container 236 | /// 237 | static class ContainerExtensions 238 | { 239 | /// 240 | /// Registers an implementation type for the specified interface 241 | /// 242 | /// Interface to register 243 | /// This container instance 244 | /// Implementing type 245 | /// IRegisteredType object 246 | public static Container.IRegisteredType Register(this Container container, Type type) 247 | => container.Register(typeof(T), type); 248 | 249 | /// 250 | /// Registers an implementation type for the specified interface 251 | /// 252 | /// Interface to register 253 | /// Implementing type 254 | /// This container instance 255 | /// IRegisteredType object 256 | public static Container.IRegisteredType Register(this Container container) 257 | where TImplementation : TInterface 258 | => container.Register(typeof(TInterface), typeof(TImplementation)); 259 | 260 | /// 261 | /// Registers a factory function which will be called to resolve the specified interface 262 | /// 263 | /// Interface to register 264 | /// This container instance 265 | /// Factory method 266 | /// IRegisteredType object 267 | /// 不显示 WARN 268 | public static Container.IRegisteredType Register(this Container container, Func factory) 269 | => container.Register(typeof(T), () => factory()!); 270 | 271 | /// 272 | /// Registers a type 273 | /// 274 | /// This container instance 275 | /// Type to register 276 | /// IRegisteredType object 277 | public static Container.IRegisteredType Register(this Container container) 278 | => container.Register(typeof(T), typeof(T)); 279 | 280 | /// 281 | /// Returns an implementation of the specified interface 282 | /// 283 | /// Interface type 284 | /// This scope instance 285 | /// Object implementing the interface 286 | public static T Resolve(this Container.IScope scope) => (T)scope.GetService(typeof(T)); 287 | } 288 | } -------------------------------------------------------------------------------- /Common/EnergyStar/EnergyManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Runtime.InteropServices; 7 | using System.Text; 8 | using RyzenTuner.Common.Container; 9 | using RyzenTuner.Common.EnergyStar.Interop; 10 | using RyzenTuner.Properties; 11 | 12 | namespace RyzenTuner.Common.EnergyStar 13 | { 14 | public class EnergyManager 15 | { 16 | private const string UnknownProcessName = "Unknown-K7Ncy4PUIQBNyGTl.exe"; 17 | 18 | private readonly HashSet _bypassProcessList = new() 19 | { 20 | // Edge 浏览器会自动调度 21 | "msedge.exe", 22 | "WebViewHost.exe", 23 | 24 | // UWP Frame 需要特殊处理 25 | "ApplicationFrameHost.exe", 26 | 27 | // 监控相关 28 | "taskmgr.exe", 29 | "procmon.exe", 30 | "procmon64.exe", 31 | "perfmon.exe", 32 | 33 | // Widgets 34 | "Widgets.exe", 35 | 36 | // 系统进程 37 | "explorer.exe", 38 | "ntoskrnl.exe", 39 | "WerFault.exe", 40 | "backgroundTaskHost.exe", 41 | "backgroundTransferHost.exe", 42 | "winlogon.exe", 43 | "wininit.exe", 44 | "csrss.exe", 45 | "lsass.exe", 46 | "smss.exe", 47 | "services.exe", 48 | "taskeng.exe", 49 | "taskhost.exe", 50 | "dwm.exe", 51 | "conhost.exe", 52 | "svchost.exe", 53 | "sihost.exe", 54 | "ShellExperienceHost.exe", 55 | "StartMenuExperienceHost.exe", 56 | "SearchHost.exe", 57 | "fontdrvhost.exe", 58 | "logonui.exe", 59 | "LockApp.exe", 60 | "WUDFHost.exe", // Windows User-Mode Driver Framework Host 61 | 62 | // 输入法 63 | "ChsIME.exe", 64 | "ctfmon.exe", 65 | 66 | // WUDF 67 | "WUDFRd.exe", 68 | 69 | // Vmware Workstation 70 | "vmware-vmx.exe", 71 | 72 | // 编辑器 73 | "Brackets.exe", 74 | "Code.exe", 75 | "atom.exe", 76 | "sublime_text.exe", 77 | "notepad++.exe", 78 | 79 | // IDE 80 | "clion.exe", 81 | "clion64.exe", 82 | "idea.exe", 83 | "idea64.exe", 84 | "phpstorm.exe", 85 | "phpstorm64.exe", 86 | "pycharm.exe", 87 | "pycharm64.exe", 88 | "rubymine.exe", 89 | "rubymine64.exe", 90 | "webstorm.exe", 91 | "webstorm64.exe", 92 | "rider.exe", 93 | "rider64.exe", 94 | "goland.exe", 95 | "goland64.exe", 96 | "datagrip.exe", 97 | "datagrip64.exe", 98 | 99 | // 其他开发相关 100 | "bash.exe", 101 | "zsh.exe", 102 | "RyzenTuner.exe", 103 | }; 104 | 105 | private readonly IntPtr _pThrottleOn; 106 | private readonly IntPtr _pThrottleOff; 107 | private readonly int _szControlBlock; 108 | 109 | public const string UwpFrameHostApp = "ApplicationFrameHost.exe"; 110 | 111 | public EnergyManager() 112 | { 113 | var bypassSetting = 114 | Settings.Default.EnergyStarBypassProcessList 115 | .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) 116 | .Select(x => x.Trim()); 117 | _bypassProcessList.UnionWith(bypassSetting); 118 | 119 | // 将 _bypassProcessList 的元素全部替代为小写字符串 120 | foreach (var processName in _bypassProcessList.ToArray()) 121 | { 122 | var lowerProcessName = processName.ToLower(); 123 | if (processName == lowerProcessName) 124 | { 125 | continue; 126 | } 127 | 128 | _bypassProcessList.Remove(processName); 129 | _bypassProcessList.Add(lowerProcessName); 130 | } 131 | 132 | _szControlBlock = Marshal.SizeOf(); 133 | _pThrottleOn = Marshal.AllocHGlobal(_szControlBlock); 134 | _pThrottleOff = Marshal.AllocHGlobal(_szControlBlock); 135 | 136 | // 参考:https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setprocessinformation 137 | // EcoQoS:打开 ExecutionSpeed 节流功能 138 | var throttleState = new Win32Api.ProcessPowerThrottlingState 139 | { 140 | Version = Win32Api.ProcessPowerThrottlingState.ProcessPowerThrottlingCurrentVersion, 141 | ControlMask = Win32Api.ProcessorPowerThrottlingFlags.PROCESS_POWER_THROTTLING_EXECUTION_SPEED, 142 | StateMask = Win32Api.ProcessorPowerThrottlingFlags.PROCESS_POWER_THROTTLING_EXECUTION_SPEED, 143 | }; 144 | 145 | // HighQoS:关闭 ExecutionSpeed 节流 146 | var unThrottleState = new Win32Api.ProcessPowerThrottlingState 147 | { 148 | Version = Win32Api.ProcessPowerThrottlingState.ProcessPowerThrottlingCurrentVersion, 149 | ControlMask = Win32Api.ProcessorPowerThrottlingFlags.PROCESS_POWER_THROTTLING_EXECUTION_SPEED, 150 | StateMask = Win32Api.ProcessorPowerThrottlingFlags.None, 151 | }; 152 | 153 | // 如果想要让系统管理所有的功率节流,ControlMask 和 StateMask 设置为 Win32Api.ProcessorPowerThrottlingFlags.None 154 | // var defaultThrottleState = new Win32Api.ProcessPowerThrottlingState 155 | // { 156 | // Version = Win32Api.ProcessPowerThrottlingState.ProcessPowerThrottlingCurrentVersion, 157 | // ControlMask = Win32Api.ProcessorPowerThrottlingFlags.None, 158 | // StateMask = Win32Api.ProcessorPowerThrottlingFlags.None, 159 | // }; 160 | 161 | Marshal.StructureToPtr(throttleState, _pThrottleOn, false); 162 | Marshal.StructureToPtr(unThrottleState, _pThrottleOff, false); 163 | } 164 | 165 | /// 166 | /// 如果 enable 为 true,则切换到【效率模式】(低优先级;低频率;高效核);否则,则是普通模式。 167 | /// 168 | /// 169 | /// 170 | private void ToggleEfficiencyMode(IntPtr hProcess, bool enable) 171 | { 172 | var logger = AppContainer.Logger(); 173 | 174 | try 175 | { 176 | if (hProcess == IntPtr.Zero) 177 | { 178 | return; 179 | } 180 | 181 | var processId = Win32Api.GetProcessId(hProcess); 182 | var appName = GetProcessNameFromHandle(hProcess); 183 | 184 | if (appName == UnknownProcessName) 185 | { 186 | logger.Warning($"ToggleEfficiencyMode: 获取进程名称失败。pid: {processId}"); 187 | return; 188 | } 189 | 190 | if (_bypassProcessList.Contains(appName.ToLower())) 191 | { 192 | logger.Debug($"ToggleEfficiencyMode: 不处理白名单列表中的应用 {appName}"); 193 | return; 194 | } 195 | 196 | var r1 = Win32Api.SetProcessInformation(hProcess, 197 | Win32Api.PROCESS_INFORMATION_CLASS.ProcessPowerThrottling, 198 | enable ? _pThrottleOn : _pThrottleOff, (uint)_szControlBlock); 199 | 200 | var r2 = Win32Api.SetPriorityClass(hProcess, 201 | enable 202 | ? Win32Api.PriorityClass.BELOW_NORMAL_PRIORITY_CLASS 203 | : Win32Api.PriorityClass.NORMAL_PRIORITY_CLASS); 204 | 205 | using (null) 206 | { 207 | var actionText = "Boost"; 208 | if (enable) 209 | { 210 | actionText = "Throttle"; 211 | } 212 | 213 | 214 | logger.Debug( 215 | $"{actionText} {appName}. pid: {processId}, set process information and priority result: {r1 && r2}"); 216 | } 217 | } 218 | catch (Exception e) 219 | { 220 | AppContainer.Logger().LogException(e); 221 | } 222 | } 223 | 224 | /// 225 | /// 通过 hProcess 获取进程名称 226 | /// 227 | /// 228 | /// 229 | private string GetProcessNameFromHandle(IntPtr hProcess) 230 | { 231 | try 232 | { 233 | var capacity = 2048; 234 | var sb = new StringBuilder(capacity); 235 | 236 | if (Win32Api.QueryFullProcessImageName(hProcess, 0, sb, ref capacity)) 237 | { 238 | return Path.GetFileName(sb.ToString()); 239 | } 240 | 241 | return UnknownProcessName; 242 | } 243 | catch (Exception e) 244 | { 245 | AppContainer.Logger().LogException(e); 246 | return UnknownProcessName; 247 | } 248 | } 249 | 250 | /// 251 | /// 处理前台进程 252 | /// 253 | public void HandleForeground() 254 | { 255 | try 256 | { 257 | var hwnd = Win32Api.GetForegroundWindow(); 258 | if (hwnd == IntPtr.Zero) return; 259 | 260 | var windowThreadId = Win32Api.GetWindowThreadProcessId(hwnd, out var processId); 261 | if (windowThreadId == 0 || processId == 0) return; 262 | 263 | var processHandle = NativeOpenProcess((int)processId); 264 | if (processHandle == IntPtr.Zero) return; 265 | 266 | var appName = GetProcessNameFromHandle(processHandle); 267 | if (appName == UwpFrameHostApp) 268 | { 269 | var found = false; 270 | Win32Api.EnumChildWindows(hwnd, (innerHwnd, lParam) => 271 | { 272 | if (found) return true; 273 | if (Win32Api.GetWindowThreadProcessId(innerHwnd, out var innerProcId) <= 0) return true; 274 | if (processId == innerProcId) return true; 275 | 276 | var innerProcHandle = NativeOpenProcess((int)innerProcId); 277 | if (innerProcHandle == IntPtr.Zero) return true; 278 | 279 | // Found. Set flag, reinitialize handles and call it a day 280 | found = true; 281 | Win32Api.CloseHandle(processHandle); 282 | processHandle = innerProcHandle; 283 | processId = innerProcId; 284 | appName = GetProcessNameFromHandle(processHandle); 285 | 286 | return true; 287 | }, IntPtr.Zero); 288 | } 289 | 290 | 291 | ToggleEfficiencyMode(processHandle, false); 292 | 293 | Win32Api.CloseHandle(processHandle); 294 | } 295 | catch (Exception e) 296 | { 297 | AppContainer.Logger().LogException(e); 298 | } 299 | } 300 | 301 | /// 302 | /// 使用统一的 process access 参数调用 Win32Api.OpenProcess 303 | /// 304 | /// 305 | /// 306 | private IntPtr NativeOpenProcess(int processId) 307 | { 308 | const uint processAccess = (uint)( 309 | Win32Api.ProcessAccessFlags.QueryLimitedInformation | 310 | Win32Api.ProcessAccessFlags.SetInformation 311 | ); 312 | return Win32Api.OpenProcess(processAccess, false, (uint)processId); 313 | } 314 | 315 | /// 316 | /// 将除了 PendingProcPid 和 BypassProcessList 外的所有进程,都切换到【效率模式】 317 | /// 318 | public void ThrottleAllUserBackgroundProcesses() 319 | { 320 | AppContainer.Logger().Debug("Throttle All User Background Processes"); 321 | _toggleAllBgProcessesMode(true); 322 | } 323 | 324 | /// 325 | /// 将除了 PendingProcPid 和 BypassProcessList 外的所有进程,都切换到【普通模式】 326 | /// 327 | public void BoostAllUserBackgroundProcesses() 328 | { 329 | AppContainer.Logger().Debug("Boost All User Background Processes"); 330 | _toggleAllBgProcessesMode(false); 331 | } 332 | 333 | private void _toggleAllBgProcessesMode(bool enable) 334 | { 335 | try 336 | { 337 | var runningProcesses = Process.GetProcesses(); 338 | var currentSessionId = Process.GetCurrentProcess().SessionId; 339 | 340 | var sameAsThisSession = runningProcesses.Where(p => p.SessionId == currentSessionId); 341 | 342 | foreach (var proc in sameAsThisSession) 343 | { 344 | try 345 | { 346 | var hProcess = NativeOpenProcess(proc.Id); 347 | ToggleEfficiencyMode(hProcess, enable); 348 | Win32Api.CloseHandle(hProcess); 349 | } 350 | catch (Exception e) 351 | { 352 | AppContainer.Logger().LogException(e); 353 | } 354 | } 355 | } 356 | catch (Exception e) 357 | { 358 | AppContainer.Logger().LogException(e); 359 | } 360 | } 361 | } 362 | } --------------------------------------------------------------------------------