以编程方式控制 Windows 10 的“电源模式”

mra*_*hal 5 .net c++ winapi

背景

你好。我有一台 SB2(Surface Book 2),我是不幸的用户之一,面临着困扰许多 SB2 机器的臭名昭著的 0.4GHz 限制问题。问题在于,SB2 突然且非常频繁地根据环境温度从 4GHz 提升到 0.4GHz 严重节流,并停留在那里一两分钟(这会导致整个笔记本电脑的速度严重减慢)。这是非常令人沮丧的,几乎使机器无法用于即使是最简单的工作负载。

微软显然表示它在 2019 年 10 月更新中修复了该问题,但我和其他几位用户仍然面临着这个问题。我非常肯定我的机器是最新的,我什至手动安装了所有最新的 Surface Book 2 固件更新。

以下是问题发生时 CPU 状态的捕获: 图像节流

正如你所看到的,单元本身的温度并不高,但CPU却精确地节流在0.4GHz。

有关此的更多链接:1 2

解决方法

我几乎尝试了一切。降低电压直至屏幕冻结、禁用 BD PROCHOT、禁用 GPE 中的电源节流、搞乱注册表、调整多个 CPU/GPU 设置。什么都没起作用。

当限制开始时你只能做两件事:

  1. 等待它完成(通常需要一两分钟)。
  2. 更改Windows 10 中的电源模式。即使您将其从“最佳性能”更改为“最佳电池寿命”也不重要,重要的是您更改了它。一旦您这样做,节流将在几秒钟内完全停止。这是唯一有效的手动解决方案。

问题

在实践中,无论工作量有多大,每 10 秒更改一次滑块,都可以无限地带来流畅的体验,而不会受到限制。当然,这不是一个可行的手动解决方法。

从理论上讲,我认为如果我能找到一种以编程方式控制此模式的方法,我也许可以通过每 10 秒左右切换一次电源模式来告别这个问题。

我不介意它是在 win32 (winapi) 还是 .net 中。我查了很多,发现了这个有关电源管理的内容,但是win32中似乎没有设置界面。我可能会忽略它,所以这是我的问题:

有没有办法以编程方式控制 Windows 10 中的电源模式?

Aar*_*ron 9

好的...我一直想要命令行或编程访问来调整电源滑块一段时间,并且在研究它时我已经多次看到这篇文章。我很惊讶没有人费心去弄清楚这一点。我今天自己解决了这个问题,原因是 Windows 11 似乎已经从任务栏中删除了电源滑块,你必须深入研究“设置”应用程序来调整它。

\n

如前所述,在注册表项 HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Power\\User\\PowerSchemes 中,您可以找到值“ActiveOverlayAcPowerScheme”和“ActiveOverlayDcPowerScheme”,它们记录了交流电源滑块的当前值和电池电量分别。但是,更改这些值不足以调整功率滑块或系统的操作模式。

\n

事实证明,C:\\Windows\\System32\\powrprof.dll 中有一个名为 PowerSetActiveOverlayScheme 的未记录方法。它需要一个参数。我“猜测”它将采用与 PowerSetActiveScheme 相同的方式获取 GUID,并且它似乎有效。

\n

注意 \xe2\x80\x94 Microsoft 不支持使用未记录的 API。此方法可能会在未来的 Windows 版本中失效。它可以用于个人修补,但我不建议在任何实际的生产项目中使用它。

\n

以下是 C# PInvoke 签名:

\n
[DllImportAttribute("powrprof.dll", EntryPoint = "PowerSetActiveOverlayScheme")]\npublic static extern uint PowerSetActiveOverlayScheme(Guid OverlaySchemeGuid);\n
Run Code Online (Sandbox Code Playgroud)\n

成功时返回零,失败时返回非零。

\n

调用它很简单:

\n
PowerSetActiveOverlayScheme(new Guid("ded574b5-45a0-4f42-8737-46345c09c238"));\n
Run Code Online (Sandbox Code Playgroud)\n

它立即生效。这个特定的 GUID 将滑块一直移动到右侧,并且还更新了注册表中的“ActiveOverlayAcPowerScheme”值。使用全零的 GUID 将滑块重置为中间值。当您将电源滑块设置到不同位置时,只需观察注册表中显示的值即可查看可用的 GUID 选项。

\n

有两种方法可用于读取滑块的当前位置。我不确定它们之间有什么区别,它们在我的测试中每次都返回相同的值。

\n
[DllImportAttribute("powrprof.dll", EntryPoint = "PowerGetActualOverlayScheme")]\npublic static extern uint PowerGetActualOverlayScheme(out Guid ActualOverlayGuid);\n\n[DllImportAttribute("powrprof.dll", EntryPoint = "PowerGetEffectiveOverlayScheme")]\npublic static extern uint PowerGetEffectiveOverlayScheme(out Guid EffectiveOverlayGuid);\n
Run Code Online (Sandbox Code Playgroud)\n

它们还会在成功时返回零,在失败时返回非零。他们可以被称为...

\n
if (PowerGetEffectiveOverlayScheme(out Guid activeScheme) == 0)\n{\n    Console.WriteLine(activeScheme);\n}\n
Run Code Online (Sandbox Code Playgroud)\n

还有一种名为“PowerGetOverlaySchemes”的方法,我认为该方法可用于获取可以使用的可用 GUID 列表。它似乎需要三个参数,但我没有费心去弄清楚。

\n

我创建了一个可用于设置电源模式的命令行程序,可以在https://github.com/AaronKelley/PowerMode找到它。

\n


小智 6

亚伦的回答非常棒,对我帮助很大,谢谢。

\n

如果你和我一样

\n
    \n
  1. 没有 Visual Studio 准备好为您自己和/或编译他的工具

    \n
  2. \n
  3. 不一定要运行 GitHub 上的任意可执行文件(无意冒犯),

    \n
  4. \n
\n

您可以使用Python(在本例中为3)来完成同样的事情。

\n

为了完整起见,我将复制免责声明:

\n
\n

注意 \xe2\x80\x94 Microsoft 不支持使用未记录的 API。此方法可能会在未来的 Windows 版本中失效。它可以用于个人修补,但我不建议在任何实际的生产项目中使用它。

\n
\n

另请注意,以下只是基本的概念验证代码!

\n

获取当前活动的字节序列:

\n
import ctypes\n\noutput_buffer = ctypes.create_string_buffer(b"",16)\n\nctypes.windll.powrprof.PowerGetEffectiveOverlayScheme(output_buffer)\nprint("Current Effective Byte Sequence: " + output_buffer.value.hex())\n\nctypes.windll.powrprof.PowerGetActualOverlayScheme(output_buffer)\nprint("Current Actual Byte Sequence: " + output_buffer.value.hex())\n
Run Code Online (Sandbox Code Playgroud)\n

在我的系统上,这会产生以下值:

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
模式字节序列
更好的电池77c71c9647259d4f81747d86181b8a7a
更好的性能00000000000000000000000000000000
最棒的表演b574d5dea045424f873746345c09c238
\n
\n

显然,亚伦和我的系统具有相同的特点,其中“更好的性能”字节序列只是全零(而不是“预期”值3af9B8d9-7c97-431d-ad78-34a8bfea439f)。

\n

请注意,字节序列77c71c9647259d4f81747d86181b8a7a相当于 GUID 961cc777-2547-4f9d-8174-7d86181b8a7ab574d5dea045424f873746345c09c238代表ded574b5-45a0-4f42-8737-46345c09c238

\n

这是因为 GUID 的书写方式与它们在内存中的实际表示方式不同。(如果我们假设 GUID 的字节被写入ABCD-EF-GH-IJ-KLMN其字节序列表示形式最终为DCBAFEHGIJKLMN)。请参阅/sf/answers/486724521/(特别是“二进制编码可能不同”下的段落和表格)和/或https://uuid.ramsey.dev/en/latest/nonstandard/guid.html如果您想知道更多。

\n

设置一个值(本例中为“更好的电池”)的工作原理如下:

\n
import ctypes\n\nmodes = {\n    "better_battery":     "77c71c9647259d4f81747d86181b8a7a",\n    "better_performance": "00000000000000000000000000000000",\n    "best_performance":   "b574d5dea045424f873746345c09c238"\n}\n\nctypes.windll.powrprof.PowerSetActiveOverlayScheme(bytes.fromhex(modes["better_battery"]))\n\n
Run Code Online (Sandbox Code Playgroud)\n

对我来说,这是一个尝试 Python 的好机会ctypes:)。

\n