You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

GetLastInputInfo计数器溢出问题:寻求解决与测试方案

问题解决与测试建议

问题原因

Environment.TickCount是32位有符号整数,最大值为2^31-1毫秒(约24天20小时31分钟),溢出后会变为负数。同时你定义的Lastinputinfo结构体中dwTime用了int类型,但WinAPI的LASTINPUTINFO结构体中dwTime是无符号32位整数(DWORD),溢出后转成int会导致时间计算错误,最终出现LastInput被算成超前DateTime.Now的情况。

修复方案

1. 修正结构体类型

将dwTime的类型从int改为uint,匹配WinAPI的DWORD定义:

private struct LastInputInfo
{
    public uint cbSize;
    public uint dwTime; // 替换原readonly int类型
}

2. 补充完整DllImport定义

确保GetLastInputInfo的调用签名正确:

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetLastInputInfo(ref LastInputInfo plii);

3. 重新实现空闲时间计算

使用无符号整数减法自动处理TickCount的溢出回绕,同时用Environment.TickCount64(64位整数,无24天溢出问题)计算系统启动时间:

private static uint LastInputTicks
{
    get
    {
        var lii = new LastInputInfo { cbSize = (uint)Marshal.SizeOf(typeof(LastInputInfo)) };
        GetLastInputInfo(ref lii);
        return lii.dwTime;
    }
}

public static DateTime LastInput
{
    get
    {
        // 用TickCount64获取系统运行总毫秒数,避免溢出
        long systemUptimeMs = Environment.TickCount64;
        DateTime systemStartupUtc = DateTime.UtcNow.AddMilliseconds(-systemUptimeMs);
        // 转换为本地时间返回
        return systemStartupUtc.AddMilliseconds(LastInputTicks).ToLocalTime();
    }
}

public static TimeSpan IdleTime
{
    get
    {
        uint currentTick = (uint)Environment.TickCount;
        uint lastInputTick = LastInputTicks;
        // 无符号减法自动处理溢出回绕,得到正确的空闲毫秒数
        uint idleMilliseconds = currentTick - lastInputTick;
        return TimeSpan.FromMilliseconds(idleMilliseconds);
    }
}

测试建议

1. 单元测试模拟溢出场景

编写单元测试覆盖溢出前后的情况:

  • 溢出前场景:模拟currentTick = 0x7FFFFFFF(TickCount最大值),lastInputTick = 0x7FFFFFFF - 1000,验证IdleTime返回1秒。
  • 溢出后场景:模拟currentTick = 0x00000001(溢出后),lastInputTick = 0xFFFFFFFE(溢出前),验证IdleTime返回3毫秒(无符号减法会自动计算0x00000001 - 0xFFFFFFFE = 0x00000003)。

2. 模拟TickCount返回值

通过反射或测试桩替换Environment.TickCount的返回值,快速验证溢出逻辑:

// 测试时临时替换TickCount(需用反射实现)
var tickCountField = typeof(Environment).GetField("s_tickCount", BindingFlags.NonPublic | BindingFlags.Static);
tickCountField.SetValue(null, unchecked((int)0x00000001)); // 设置为溢出后的值
// 调用IdleTime并验证结果

3. 长时间运行测试(可选)

在虚拟机中部署程序,设置虚拟机连续运行超过24天20小时,观察程序是否仍能正确计算用户空闲状态(此方法耗时较长,适合最终验证)。

内容的提问来源于stack exchange,提问作者Dhinu Thampi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 17:09:54