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

PowerShell计算机空闲时长检测异常:30分钟误判为超1小时

问题:PowerShell空闲时长检测异常导致提前休眠

我参考了一篇关于用PowerShell获取计算机空闲时长的文章,写了个脚本想实现「空闲满1小时自动休眠」的功能,但运行出问题了——明明只空闲了30分钟,脚本却检测到空闲时长超过1小时,直接触发了休眠。附上脚本和日志片段,求排查思路。

脚本代码

Add-Type @'
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace PInvoke.Win32 {

    public static class UserInput {

        [DllImport("user32.dll", SetLastError=false)]
        private static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);

        [StructLayout(LayoutKind.Sequential)]
        private struct LASTINPUTINFO {
            public uint cbSize;
            public int dwTime;
        }

        public static DateTime LastInput {
            get {
                DateTime bootTime = DateTime.UtcNow.AddMilliseconds(-Environment.TickCount);
                DateTime lastInput = bootTime.AddMilliseconds(LastInputTicks);
                return lastInput;
            }
        }

        public static TimeSpan IdleTime {
            get {
                return DateTime.UtcNow.Subtract(LastInput);
            }
        }

        public static int LastInputTicks {
            get {
                LASTINPUTINFO lii = new LASTINPUTINFO();
                lii.cbSize = (uint)Marshal.SizeOf(typeof(LASTINPUTINFO));
                GetLastInputInfo(ref lii);
                return lii.dwTime;
            }
        }
    }
}
'@
do{
Log-IdleState c:\sleeplog.txt
if(([PInvoke.Win32.UserInput]::IdleTime) -ge (new-timespan -hours 1)){
    Add-Type -Assembly System.Windows.Forms
    [System.Windows.Forms.Application]::SetSuspendState("Suspend", $false, $true)
}
start-sleep -seconds 10
} while(1 -eq 1)

日志片段

No Action performed at 05/10/2024 11:55:58 | Detected idle for : 00:25:52.4530000
No Action performed at 05/10/2024 11:56:08 | Detected idle for : 00:26:02.4840000
No Action performed at 05/10/2024 11:56:18 | Detected idle for : 00:26:12.5160000
No Action performed at 05/10/2024 11:56:28 | Detected idle for : 00:26:22.5470000
No Action performed at 05/10/2024 11:56:38 | Detected idle for : 00:26:32.5780000
No Action performed at 05/10/2024 11:56:48 | Detected idle for : 00:26:42.6090000
No Action performed at 05/10/2024 11:56:58 | Detected idle for : 00:26:52.6410000
No Action performed at 05/10/2024 11:57:08 | Detected idle for : 00:27:02.6870000
No Action performed at 05/10/2024 11:57:18 | Detected idle for : 00:27:12.7190000
No Action performed at 05/10/2024 11:57:28 | Detected idle for : 00:27:22.7340000
No Action performed at 05/10/2024 11:57:38 | Detected idle for : 00:27:32.7500000
No Action performed at 05/10/2024 11:57:48 | Detected idle for : 00:27:42.7810000
No Action performed at 05/10/2024 11:57:58 | Detected idle for : 00:27:52.8280000
No Action performed at 05/10/2024 11:58:08 | Detected idle for : 00:28:02.8440000
No Action performed at 05/10/2024 11:58:18 | Detected idle for : 00:28:12.8590000
No Action performed at 05/10/2024 11:58:28 | Detected idle for : 00:28:22.8910000
No Action performed at 05/10/2024 11:58:38 | Detected idle for : 00:28:32.9220000
No Action performed at 05/10/2024 11:58:48 | Detected idle for : 00:28:42.9370000
No Action performed at 05/10/2024 11:58:58 | Detected idle for : 00:28:52.9840000
No Action performed at 05/10/2024 11:59:08 | Detected idle for : 00:29:03.0310000
No Action performed at 05/10/2024 11:59:18 | Detected idle for : 00:29:13.0620000
No Action performed at 05/10/2024 11:59:28 | Detected idle for : 00:29:23.0940000
No Action performed at 05/10/2024 11:59:38 | Detected idle for : 00:29:33.1250000
No Action performed at 05/10/2024 11:59:48 | Detected idle for : 00:29:43.1560000
No Action performed at 05/10/2024 11:59:58 | Detected idle for : 00:29:53.1870000
Slept at 05/10/2024 12:00:02 | Detected idle for : 1.03:56:12.4680000

解决思路

问题根源

脚本里用的Environment.TickCount是32位有符号整数,系统运行超过24.9天(2^31-1毫秒≈24.9天)后,这个值会溢出变成负数。计算启动时间bootTime = DateTime.UtcNow.AddMilliseconds(-Environment.TickCount)时,负数会变成超大的正数,导致LastInput计算完全错误,最终IdleTime出现像日志里1.03:56:12这种离谱的结果。

修复方案

替换Environment.TickCount为不会溢出的64位计时方法,推荐用GetTickCount64()(Windows API提供的64位无符号计时函数),修改后的脚本如下:

Add-Type @'
using System;
using System.Runtime.InteropServices;

namespace PInvoke.Win32 {

    public static class UserInput {

        [DllImport("user32.dll", SetLastError=false)]
        private static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);

        [DllImport("kernel32.dll")]
        private static extern ulong GetTickCount64();

        [StructLayout(LayoutKind.Sequential)]
        private struct LASTINPUTINFO {
            public uint cbSize;
            public uint dwTime; // 改成uint,匹配GetLastInputInfo的返回类型
        }

        public static DateTime LastInput {
            get {
                // 用GetTickCount64计算启动时间,无溢出问题
                DateTime bootTime = DateTime.UtcNow.AddMilliseconds(-(double)GetTickCount64());
                DateTime lastInput = bootTime.AddMilliseconds(LastInputTicks);
                return lastInput;
            }
        }

        public static TimeSpan IdleTime {
            get {
                return DateTime.UtcNow.Subtract(LastInput);
            }
        }

        public static uint LastInputTicks {
            get {
                LASTINPUTINFO lii = new LASTINPUTINFO();
                lii.cbSize = (uint)Marshal.SizeOf(typeof(LASTINPUTINFO));
                GetLastInputInfo(ref lii);
                return lii.dwTime;
            }
        }
    }
}
'@

# 补充原脚本缺失的Log-IdleState函数
function Log-IdleState($logPath) {
    $idleTime = [PInvoke.Win32.UserInput]::IdleTime
    $currentTime = Get-Date -Format "MM/dd/yyyy HH:mm:ss"
    if ($idleTime -ge (New-TimeSpan -Hours 1)) {
        $logEntry = "Slept at $currentTime | Detected idle for : $idleTime"
    } else {
        $logEntry = "No Action performed at $currentTime | Detected idle for : $idleTime"
    }
    Add-Content -Path $logPath -Value $logEntry
}

do{
Log-IdleState c:\sleeplog.txt
if(([PInvoke.Win32.UserInput]::IdleTime) -ge (New-TimeSpan -Hours 1)){
    Add-Type -Assembly System.Windows.Forms
    [System.Windows.Forms.Application]::SetSuspendState("Suspend", $false, $true)
}
Start-Sleep -Seconds 10
} while($true)

额外说明

  • 原脚本里的LASTINPUTINFO结构中dwTime是int类型,实际应该用uint,因为GetLastInputInfo返回的是无符号毫秒数,避免类型转换导致的潜在问题。
  • 补充了原脚本缺失的Log-IdleState函数,否则脚本运行会报错。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 05:24:55