Environment.TickCount is not enough

12,250

Solution 1

public void BootTime(){    
    SelectQuery query = new SelectQuery("SELECT LastBootUpTime FROM Win32_OperatingSystem WHERE Primary='true'");
    ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);

    foreach (ManagementObject mo in searcher.Get())
    {
        DateTime dtBootTime = ManagementDateTimeConverter.ToDateTime(mo.Properties["LastBootUpTime"].Value.ToString());
        Console.WriteLine(dtBootTime.ToString());
    }
}

Solution 2

The following code retrieves the milliseconds since system start (call to unmanged API). I measured the performance costs for that interop operation, and it is quite identical to StopWatch() (but that doesn't retrieve the time since system start directly of course).

using System.Runtime.InteropServices;

...

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

...

var tickCount64 = GetTickCount64();

https://msdn.microsoft.com/de-de/library/windows/desktop/ms724411(v=vs.85).aspx

Solution 3

You're correct that Environment.TickCount will overflow after approximately 25 days, because the return value is a 32-bit integer.

But there's a better way than trying to compare the TickCount if you want to determine when the system was last started. What you're looking for is called the system up-time. There are a couple of different ways that you can retrieve this.

The easiest way is to use the PerformanceCounter class (in the System.Diagnostics namespace), which lets you query a particular system performance counter. Try the following code:

TimeSpan upTime;
using (var pc = new PerformanceCounter("System", "System Up Time"))
{
    pc.NextValue();    //The first call returns 0, so call this twice
    upTime = TimeSpan.FromSeconds(pc.NextValue());
}
Console.WriteLine(upTime.ToString());

Alternatively, you can do this through WMI. But it looks like stian.net's answer has that covered.

Note, finally, that the performance counter's name must be localized if you need to support international versions of Windows, so the correct solution must look up the localized strings for "System" and "System Up Time" using PdhLookupPerfNameByIndex, or you must ensure you are using the PdhAddEnglishCounter under the hood, which is only supported in Vista or higher. More about this here.

Share:
12,250
codebased
Author by

codebased

Amit Malhotra is a Software Engineer at Commonwealth Bank of Australia. He has an extensive experience in software architecture, design and develop, using agile approach. Amit is an expert in application development in Cloud architecture and development with Microsoft technologies such as .NET, C#, Microsoft Dynamics. He also interact with various open source frameworks such as AngularJS, EmberJS. His work involves Service Oriented Architecture (SOA) using REST, Database Design, Android technologies. Amit is Agile Certified developer. Amit has done MCA from National Institute of Electronics & Information Technology (NIELIT), (erstwhile DOEACC Society). Twit him on @imcodebased

Updated on June 22, 2022

Comments

  • codebased
    codebased almost 2 years

    I want to know on when was the last time the system was started.

    Environment.TickCount will work but it is breaking after 48-49 days because of the limitation of int.

    This is the code I've been using:

    Environment.TickCount & Int32.MaxValue
    

    Does anyone knows about long type return somehow?

    I am using this to know the idle time of the system:

    public static int GetIdleTime()
    {
        return (Environment.TickCount & Int32.MaxValue)- (int)GetLastInputTime();
    }
    
    /// <summary>
    /// Get the last input time from the input devices.
    /// Exception: 
    /// If it cannot get the last input information then it throws an exception with 
    /// the appropriate message.
    /// </summary>
    /// <returns>Last input time in milliseconds.</returns>
    public static uint GetLastInputTime()
    {
        LastInputInfo lastInPut = new LastInputInfo();
        lastInPut.BlockSize = (uint)System.Runtime.InteropServices.Marshal.SizeOf(lastInPut);
        if (!GetLastInputInfo(ref lastInPut))
        {
            throw new Exception(GetLastError().ToString());
        }
    
        return lastInPut.Time;
    }
    
  • stian.net
    stian.net over 13 years
    You need reference to System.Management
  • stian.net
    stian.net over 13 years
    ok, so you can write Console.WriteLine(DateTime.Now - upTime); and get the right date :) But i would still use WMI. On my system PerformanceCounter used 2 sec to display up-time. WMI used 40 ms
  • Cody Gray
    Cody Gray over 13 years
    @stian: Fair enough. PerformanceCounter is probably the "official" .NET way, and it seemed unlikely to me that this was something worth optimizing, given it's not going to be called in a loop. But I agree WMI may well be the better option, and I even suggested its use in my answer.
  • Martin Liversage
    Martin Liversage over 13 years
    Initially I found it rather surprising that using performance counters would be slow given the purpose of that part of Windows. However, on my computer the call to new PerformanceCounter loads no less than 80 DLL's! This takes quite some time but it is a one time only cost and after that performance counters appear to be, well, performing very well.
  • Cody Gray
    Cody Gray over 13 years
    @Martin: Good work testing that out. I was wondering the same thing. That's really an incredible number for what seems like such a simple task. Retrieving the information from running net statistics workstation at the command prompt doesn't take anywhere near that long.
  • codebased
    codebased over 13 years
    Thank you guys... the problem is coming with GetLastInputTime(...) as it is still returning integer. Is any way the tick time I can receive on when the last input was? If I am getting long, milliseconds, then I would also want to know how GetLastInputTime(...) ?
  • codebased
    codebased over 13 years
    Let me rephase here... is there any way about to get the total idle time with at least in long tick(milliseconds). This idle time means that the System UP - Last Input received. With above, I could managed to get the System up time in long, however the last input received is calculating per the uint so after 48 days it will bust.
  • codebased
    codebased over 13 years
    Yeah that is what am using:internal struct LASTINPUTINFO { public uint cbSize; public uint dwTime; } you can see dwTime is uint.
  • Enigmativity
    Enigmativity almost 9 years
    Do make sure that you dispose the ManagementObjectSearcher instance.
  • Josh
    Josh over 8 years
    This is not even close to an applicable answer to the OP's question.
  • Boogier
    Boogier over 4 years
    WMI can be disabled on a computer and this code won't work.