1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
50
51
52
53
54
55
56
57
58
59
60
61
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace AdamsToolkit.Core;
/// <summary>
/// Modo headless: o exe relançado com "--hold-timer <parentPid>" corre AQUI,
/// sem WPF nenhum. Segura a resolução máxima do timer e não faz mais nada —
/// como o processo não tem WPF a pedir 1ms, o pedido de 0.5ms nunca é pisado e
/// o timer do sistema fica cravado no máximo. Morre sozinho quando o Adams
/// Toolkit principal fecha (vigia o PID do pai).
/// </summary>
public static class TimerHoldHost
{
[DllImport("ntdll.dll")]
private static extern int NtQueryTimerResolution(out uint min, out uint max, out uint cur);
[DllImport("ntdll.dll")]
private static extern int NtSetTimerResolution(uint desired, bool set, out uint cur);
[DllImport("kernel32.dll")]
private static extern IntPtr GetCurrentProcess();
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetProcessInformation(IntPtr h, int cls, ref PPTS info, int size);
[StructLayout(LayoutKind.Sequential)]
private struct PPTS { public uint Version; public uint ControlMask; public uint StateMask; }
/// <summary>Corre o loop de segurar o timer. Só regressa quando o pai morre.</summary>
public static void Run(string[] args)
{
int parentPid = 0;
if (args.Length >= 2) int.TryParse(args[1], out parentPid);
// opt-out do power throttling do Win11 (honra o pedido mesmo em background)
try
{
var st = new PPTS { Version = 1, ControlMask = 4 /*IGNORE_TIMER_RESOLUTION*/, StateMask = 0 };
SetProcessInformation(GetCurrentProcess(), 4 /*ProcessPowerThrottling*/, ref st, Marshal.SizeOf<PPTS>());
}
catch { }
Process? parent = null;
try { if (parentPid > 0) parent = Process.GetProcessById(parentPid); } catch { }
// aplica a máxima e re-afirma periodicamente (defensivo; sem WPF quase nunca é preciso)
while (true)
{
if (NtQueryTimerResolution(out _, out var max, out var cur) == 0 && cur > max)
NtSetTimerResolution(max, true, out _);
if (parent != null)
{
try { if (parent.HasExited) break; }
catch { break; }
}
System.Threading.Thread.Sleep(1000);
}
try { NtSetTimerResolution(0, false, out _); } catch { }
}
}