【问题标题】:Retrieve system uptime using C#使用 C# 检索系统正常运行时间
【发布时间】:2009-06-09 19:42:01
【问题描述】:

有没有使用 C# 获得系统正常运行时间的简单方法?

【问题讨论】:

标签: c# .net windows uptime


【解决方案1】:
public TimeSpan UpTime {
    get {
        using (var uptime = new PerformanceCounter("System", "System Up Time")) {
            uptime.NextValue();       //Call this an extra time before reading its value
            return TimeSpan.FromSeconds(uptime.NextValue());
        }
    }
}

【讨论】:

  • 第一次调用 uptime.NextValue 将返回 0。
  • 如果计数器名称本地化怎么办?
  • 获取本地化名称(2 = 系统,674 = 系统运行时间)使用:StringBuilder buffer = new StringBuilder(1024); uint buf_size = (uint)buffer.Capacity; Win32.PdhLookupPerfNameByIndex(null, id, buffer, ref buf_size); return buffer.ToString();
  • 我反对这是“一种简单的方法”。最重要的是,尝试在 *nix 上运行它。
  • @Rbjz 通常使用“使用 C#”来限定答案意味着 OP 不会寻找需要 .NET Framework 中尚未存在的任何内容的答案。截至 2009 年,这是获取非溢出“正常运行时间”的最简单的 C# 方法——替代方法包括查询 WMI 和扫描系统事件日志以获取引导记录。相比之下,这种方法是“最简单的”并且可以解决.NET 支持的所有 Windows 版本 不是“使用 C#”需要 PInvoke; GetTickCount64(并非在所有 Windows 版本中)或 ZwQuerySystemInformation(取消文档)或 sysinfo(在 posix+SVR4 系统上)
【解决方案2】:

我有点晚了,但另一个简单的方法是使用GetTickCount64 函数,该函数从Windows Vista 开始可用,并且不会像GetTickCount 那样溢出:

public static TimeSpan GetUpTime()
{
    return TimeSpan.FromMilliseconds(GetTickCount64());
}

[DllImport("kernel32")]
extern static UInt64 GetTickCount64();

【讨论】:

  • 这正是我所需要的,非常感谢你发布这个:)
  • @Goby Windows Vista 或更高版本?错字?将“GetTickCount64”重命名为其他名称?
  • GetTickCount64 ... does not overflow - 当他们找到让我的代码运行 5850 亿年的方法时它会溢出
  • @rkagerer,拭目以待。像那些短视的假设把我们带到了 y2k 和纪元时间结束 2038 =D
  • 在最新的 .NET 版本中无需为此使用 P/Invoke,因为 Environment 类现在有一个 TickCount64 property
【解决方案3】:

System.Environment.TickCount 获取系统重启后的毫秒数。

注意它是一个 Int32 并且会在 24.9 天后溢出并变为负数。请参阅 MDSN 文档上的备注。

【讨论】:

  • 为了迂腐,使用 TimeSpan.TicksPerMillisecond (msdn.microsoft.com/en-us/library/…) 将该属性转换为毫秒
  • 或者,更好的是,调用 TimeSpan.FromMilliseconds
  • 令人困惑的是,Environment.TickCount 返回毫秒数,而不是 .Net 滴答声(一个 .Net 滴答声是 100 纳秒)。
  • 要从中获得约 49.7 天的连续性,只需将其转换为 UInt32/uint
  • 如果您真的只是想要正常运行时间,这难道不是一个更快更好的答案吗?
【解决方案4】:

根据任务管理器,我的机器的正常运行时间为58 days 17 hours。我在这里尝试了每个答案,快速的答案稍微偏离了一点(大约 1-3 分钟,但正常运行时间超过 58 天):

Stopwatch.GetTimeStamp():                   58days 17hours 11minutes 25seconds
~Time to calculate (ms): 6.8413
DllImport GetTickCount64():                 58days 17hours 13minutes 34seconds
~Time to calculate (ms): 0.2192
PerformanceCounter(System, System Up Time): 58days 17hours 14minutes 02seconds
~Time to calculate (ms): 1233.2854
ManagementObject LastBootUpTime:            58days 17hours 14minutes 02seconds
~Time to calculate (ms): 30.0283

最后两个,使用 PerformanceCounter 或使用 ManagementObject,总是与 Windows 任务管理器在同一秒内(只需要相信我的话,或者使用下面的代码自己尝试)。根据结果​​,我将使用ManagementObject LastBootUpTime 方法,因为它比PerformanceCounter 快得多,但与任务管理器相比仍然非常准确。

请注意,在打印时间之前,我确实从每个方法中减去了当前经过的时间,但整个过程的运行时间不到 2 秒,因此无论如何不能通过不正确地考虑执行时间来解释时间偏移。这是我使用的代码:

[System.Runtime.InteropServices.DllImport("kernel32")]
extern static UInt64 GetTickCount64();

public static void Main()
{
    var start = Stopwatch.StartNew();

    var eachStart = Stopwatch.StartNew();
    var ticks = Stopwatch.GetTimestamp();
    var uptime = ((double)ticks) / Stopwatch.Frequency;
    var uptimeTimeSpan = TimeSpan.FromSeconds(uptime);
    Console.WriteLine("Stopwatch.GetTimeStamp():                   " + uptimeTimeSpan.Subtract(start.Elapsed).ToString(@"dd\d\a\y\s\ hh\h\o\u\r\s\ mm\m\i\n\u\t\e\s\ ss\s\e\c\o\n\d\s"));
    Console.WriteLine($"~Time to calculate (ms): {eachStart.Elapsed.TotalMilliseconds}");

    eachStart.Restart();
    Console.WriteLine("DllImport GetTickCount64():                 " + TimeSpan.FromMilliseconds(GetTickCount64()).Subtract(start.Elapsed).ToString(@"dd\d\a\y\s\ hh\h\o\u\r\s\ mm\m\i\n\u\t\e\s\ ss\s\e\c\o\n\d\s"));
    Console.WriteLine($"~Time to calculate (ms): {eachStart.Elapsed.TotalMilliseconds}");

    eachStart.Restart();
    var upTime = new PerformanceCounter("System", "System Up Time");
    upTime.NextValue();       //Call this an extra time before reading its value
    Console.WriteLine("PerformanceCounter(System, System Up Time): " + TimeSpan.FromSeconds(upTime.NextValue()).Subtract(start.Elapsed).ToString(@"dd\d\a\y\s\ hh\h\o\u\r\s\ mm\m\i\n\u\t\e\s\ ss\s\e\c\o\n\d\s"));
    Console.WriteLine($"~Time to calculate (ms): {eachStart.Elapsed.TotalMilliseconds}");

    eachStart.Restart();
    ManagementObject mo = new ManagementObject(@"\\.\root\cimv2:Win32_OperatingSystem=@");
    DateTime lastBootUp = ManagementDateTimeConverter.ToDateTime(mo["LastBootUpTime"].ToString());
    Console.WriteLine("ManagementObject LastBootUpTime:            " + (DateTime.Now.ToUniversalTime() - lastBootUp.ToUniversalTime()).Subtract(start.Elapsed).ToString(@"dd\d\a\y\s\ hh\h\o\u\r\s\ mm\m\i\n\u\t\e\s\ ss\s\e\c\o\n\d\s"));
    Console.WriteLine($"~Time to calculate (ms): {eachStart.Elapsed.TotalMilliseconds}");
}

【讨论】:

  • 感谢比较解决方案,请提及兼容性。哪些解决方案可以在非 Win 平台上运行?
  • ManagementObject 代码使用 "\\.\root\cimv2:Win32_OperatingSystem=@" 在 Windows XP 上不起作用,因为显然 Win32_OperatingSystem 在 XP 上不是单例。相反,我发现我需要使用查询来查找主实例并循环查询结果,如在 XP 和 Win 7 上都适用于我的答案所示:stackoverflow.com/a/7407346/382885
  • 很好的答案。谢谢 !我只想添加两件事:Vista 之前的 Windows 版本不支持 GetTickCount64(),PerformanceCounter("System", "System Up Time") 在我的 W7 笔记本电脑上需要很长时间(超过一分钟。不要问为什么。)
【解决方案5】:

精确且大于System.Environment.TickCount,不涉及操作系统可怕的性能计数器、WMI 或本机调用:

var ticks = Stopwatch.GetTimestamp();
var uptime = ((double)ticks) / Stopwatch.Frequency;
var uptimeSpan = TimeSpan.FromSeconds(uptime);

【讨论】:

  • Acquiring high-resolution time stamps 解释了为什么它会返回正常运行时间:“QueryPerformanceCounter [...] 返回自 Windows 操作系统启动以来发生的总滴答数,包括机器在睡眠状态,例如待机、休眠或连接待机。”
  • @Martin,我是否正确地从你那里得知Stopwatch.GetTimestamp() 不包括计算机睡眠时间?请解释一下。
  • 我只引用了我链接的文章,我没有写它;-)在那篇文章中:“在这种情况下,术语tick指的是等于1÷的时间段(从 QueryPerformanceFrequency 获得的性能计数器的频率)”。我知道一个滴答只是一个时间单位,例如一个“秒”,在我看来,这就是整篇文章中“滴答”这个词的使用方式。因此,计算机不需要运行来使“滴答”“发生”,GetTimestamp()确实包括睡眠时间。但我不知道你的问题的确切答案。
  • 此方法依赖于具有 HPET 的系统,因此在使用前确保 Stopwatch.IsHighResolution 为真。请参阅msdn.microsoft.com/en-us/library/… 文档的备注部分
  • @RyanWilliams,所以如果没有 HPET,有什么替代方法可以得到高精度时间?
【解决方案6】:

最简单和正确的方法是

public static TimeSpan GetUptime()
{
    ManagementObject mo = new ManagementObject(@"\\.\root\cimv2:Win32_OperatingSystem=@");
    DateTime lastBootUp = ManagementDateTimeConverter.ToDateTime(mo["LastBootUpTime"].ToString());
    return DateTime.Now.ToUniversalTime() - lastBootUp.ToUniversalTime();
}

【讨论】:

  • 这会返回异常信息:Invalid object path on Windows 2003 Server 在 IIS6 的 Web 服务中运行
  • 如果在 *nix 上运行会很有趣
  • 和启动时间?
【解决方案7】:

简单,但可以做到:

    static DateTime getLastBootTime(ManagementObject mObject)
    {
        PropertyData pd = mObject.Properties["LastBootUpTime"];
        string name = pd.Name.ToString();
        DateTime lastBoot = parseCmiDateTime(pd.Value.ToString());
        return lastBoot;
    }

    static ManagementObject getServerOSObject(string serverName)
    {
        ManagementObjectSearcher mSearcher = new ManagementObjectSearcher("Select * From Win32_OperatingSystem");
        mSearcher.Scope = new ManagementScope(String.Format(@"\\{0}\root\cimv2", serverName));
        ManagementObjectCollection mObjects = mSearcher.Get();
        if (mObjects.Count != 1) throw new Exception(String.Format("Expected 1 object, returned {0}.", mObjects.Count));
        foreach (ManagementObject m in mObjects)
        {
            //No indexing on collection
            return m;
        }
        throw new Exception("Something went wrong!");
    }

【讨论】:

    【解决方案8】:

    如果您使用的是更高版本的 .NET(Core 3.0/.NET 5.0 或更高版本),那么 Environment 类现在有一个 TickCount64 property

    这不会受到 TickCount 属性的环绕问题的影响,您也不必求助于 P/Invoke 来获取值。

    long tickCountMs = Environment.TickCount64;
    var uptime = TimeSpan.FromMilliseconds(tickCountMs);
    

    【讨论】:

      【解决方案9】:

      我知道问题既老又解决,但我能想到的最简单的解决方案就是使用 Enviroment.TickCount 属性,它返回自系统启动以来的毫秒数:

      System.DateTime SystemStartTime = DateAndTime.Now.AddMilliseconds(-Environment.TickCount);
      System.DateTime Uptime = DateAndTime.Now - SystemStartTime;
      

      这个解决方案比公认的答案快很多。

      【讨论】:

      • TickCount 以整数形式返回,但系统计时器具有更高的分辨率,因此在很长一段时间内,tick 计数为 int.MaxValue,然后最终降至 int.MinValue。此方法对于一次运行数月的服务器没有用。从技术上讲,系统计时器分辨率也只能精确到 10 到 16 毫秒的块。
      【解决方案10】:

      目前为止(唯一的)正确答案:

      使用 32 位定时器非常危险,除了有限的使用外,其他人都容易出错。

      我不确定 NativeMethods 类的东西何时被添加到 .net,但确实如此。您肯定希望避免 P/Invoke 开销。这样做:

      using System;
      using System.Runtime.InteropServices;
      
      namespace Mu
      {
      
          // prevents PInvoke (not in NativeMethods class) or Stack walk (NativeMethods class) performance penalties.
          internal static partial class SafeNativeMethods
          {
              [DllImport("kernel32")]
              internal extern static UInt64 GetTickCount64();
      
          }
          public static class MuTime
          {
              public static UInt64 UpTimeMillis {  get { return SafeNativeMethods.GetTickCount64();  } }
          }
      }
      
      /*
      Dual License (use either, not both). To avoid CC-BY-SA, access a copy of this 
      code at (https://pastebin.com/6EKTWsSf) to use under BSD 0-clause license,
      
      
      Copyright (c) 2020 Robin Davies 
      CC-BY-SA 3.0 (due to StackExchange terms of use). Not my fault, blame StackExchange. Fix this 
      please, StackExchange!
      
      
      BSD 0-Clause
      Copyright 2020 Robin Davies.
      
      Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
      
      THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 
      WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, 
      OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 
      OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 
      THIS SOFTWARE.
        */
      

      【讨论】:

      • 我不确定你所说的 “你肯定想避免 P/Invoke 开销。” 使用 [DllImport] 使用 P /调用,确定吗?
      • 直接调用不需要编组的方法,IF 它们包含在名为 SafeNativeMethods 的类中。在 4.x 时间范围内某处潜入 .net 的功能。
      • 有趣。你有那个链接吗?我能找到的最好的是this,它声明“SafeNativeMethods - 此类抑制堆栈遍历以获得非托管代码权限。”
      猜你喜欢
      • 2014-07-27
      • 2013-01-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-25
      • 2016-07-12
      • 1970-01-01
      相关资源
      最近更新 更多