Environment.TickCount is not enough

南笙酒味 提交于 2019-12-07 10:46:50

问题


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;
}

回答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());
    }
}



回答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




回答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.




回答4:


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.




回答5:


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



回答6:


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...
            }
        }
    }
}


来源:https://stackoverflow.com/questions/4645171/environment-tickcount-is-not-enough

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!