5

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;
}
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
codebased
  • 6,945
  • 9
  • 50
  • 84
  • 1
    So it seems what you want is simply [GetTickCount64()](https://msdn.microsoft.com/library/windows/desktop/ms724411.aspx). No need to strip the sign bit either - simply do this: `return unchecked((uint)Environment.TickCount - GetLastInputTime());` ...of course it's not really needed unless you want the ability to measure idle-time durations in the ~24-48days range (and completely a non-issue if you switch to the method I linked to). – AnorZaken Oct 11 '15 at 02:34

6 Answers6

10

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

Philm
  • 3,448
  • 1
  • 29
  • 28
8
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());
    }
}
stian.net
  • 3,928
  • 4
  • 25
  • 38
  • You need reference to System.Management – stian.net Jan 10 '11 at 09:19
  • 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 Jan 10 '11 at 23:06
  • Yeah that is what am using:internal struct LASTINPUTINFO { public uint cbSize; public uint dwTime; } you can see dwTime is uint. – codebased Jan 11 '11 at 22:56
  • 1
    Do make sure that you dispose the ManagementObjectSearcher instance. – Enigmativity May 18 '15 at 00:26
  • WMI can be disabled on a computer and this code won't work. – Boogier Oct 18 '19 at 06:50
6

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.

Community
  • 1
  • 1
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
  • 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 – stian.net Jan 10 '11 at 09:42
  • @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. – Cody Gray - on strike Jan 10 '11 at 09:44
  • 2
    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. – Martin Liversage Jan 10 '11 at 10:41
  • @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. – Cody Gray - on strike Jan 10 '11 at 10:57
  • 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 Jan 10 '11 at 22:58
0

If you count each wraparound, you can make your own 64-bit TickCount, good for 2^63 ms (292 million years) or 2^64 ms (585 million years). If you don't need the full 1ms precision (actually only 10-16ms resolution) or range, you can divide the result by 1000 and represent up to 49,710 days (136 years), with a resolution of one second, in a UInt32.

GetTickCount64() does this for you, but is only available in Vista or later.

Here I count the wraparounds of elapsed time, not TickCount.

// C# (untested)
...
// Initialize and start timer:
uint uiT0 = unchecked((uint)Environment.TickCount);  // T0 is NOW.  // ms since boot, 10-16ms res.
uint uiElapsedPrev = 0;
uint uiWrapCount = 0;
...
long x = GetElapsedTime();

public static long GetElapsedTime()
{
    uint uiElapsed = unchecked((uint)Environment.TickCount - uiT0)  // 0 to +49.71 days

    if (uiElapsed < uiElapsedPrev)  // IF uiElapsed decreased,
        uiWrapCount++;  // increment the wrap counter.
    uiElapsedPrev = uiElapsed;  // Save the previous value.

    return ( ((long)uiWrapCount << 32) + (long)uiElapsedPrev );
}

ANDing with Int32.MaxValue is unnecessary and a bad example in the .NET documentation. Unchecked subtraction of 32-bit integers overflows safely. The sign of Environment.TickCount never matters. Subtraction handles all cases that wrap around. Example, wraparound by one: uiT0 = Int32.MaxValue; iTickNow = uiT0 + 1 yields Int32.MinValue; finally, (iTickNow - uiT0) yields 1.

uiElapsed tracks elapsed time all the way to 49.7 days before it wraps to zero. Each time it wraps, iWrapCount is incremented. GetElapsedTime() must be called at least once every 49.7 days, so that no wraparound goes undetected.

A876
  • 471
  • 5
  • 8
-1

I think it's just the way they have implemented it.

It goes from 0 to max and then goes from min to 0.

https://msdn.microsoft.com/en-us/library/system.environment.tickcount(v=vs.110).aspx

I have edited the code you are using from http://www.codeproject.com/Articles/13384/Getting-the-user-idle-time-with-C

Why don't you just get the Absolute number?

    Public Shared Function GetIdle() As UInteger
        Dim lii As New LASTINPUTINFO()
        lii.cbSize = Convert.ToUInt32((Marshal.SizeOf(lii)))
        GetLastInputInfo(lii)

        Dim totalTicks As Long = 0

        If Environment.TickCount > 0 Then
            totalTicks = Convert.ToUInt64(Environment.TickCount)
        Else
            totalTicks = Convert.ToUInt64(Environment.TickCount * -1)
        End If

        Return Math.Abs(totalTicks - lii.dwTime)

    End Function
Dasith Wijes
  • 1,328
  • 12
  • 22
-1

I am not a fan of using GetTickCount() for timestamp because it can return negative numbers. Even though using Abs() can help, but it's awkward and not an optimal solution.

It's better to use Stopwatch in .Net or QueryPerformanceCounter in C++ as timestamp.

Within a C# application, I create a global Stopwatch object, start it. Later on in the application, I use the Stopwatch.ElapsedMiliseconds as timestamp.

using System;
using System.Diagnostics;
using System.Windows.Forms;

namespace MiscApp
{
    public partial class Form1 : Form
    {
        private Stopwatch globalTimer = new Stopwatch();
        private long myStamp1 = 0;
        public Form1()
        {
            InitializeComponent();
            globalTimer.Start();
        }

        private void SomeFunction()
        {
            if (globalTimer.ElapsedMilliseconds - myStamp1 >= 3000)
            {
                myStamp1 = globalTimer.ElapsedMilliseconds;
                //Do something here...
            }
        }
    }
}
Community
  • 1
  • 1
Hao Nguyen
  • 528
  • 4
  • 10