Environment.TickCount недостаточно - PullRequest
4 голосов
/ 10 января 2011

Я хочу знать, когда был последний раз, когда система была запущена.

Environment.TickCount будет работать, но он ломается через 48-49 дней из-за ограничения int.

Это код, который я использовал:

Environment.TickCount & Int32.MaxValue

Кто-нибудь знает как-нибудь о возвращении типа long?

Я использую это, чтобы узнать время простоя системы:

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

Ответы [ 6 ]

8 голосов
/ 10 января 2011
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());
    }
}
6 голосов
/ 10 января 2011

Вы правы, что Environment.TickCount переполнится примерно через 25 дней, поскольку возвращаемое значение является 32-разрядным целым числом.

Но есть лучший способ, чем пытаться сравнить TickCount, если вы хотите определить, когда система запускалась в последний раз. То, что вы ищете, называется время работы системы . Есть несколько способов получить это.

Самый простой способ - использовать класс PerformanceCounter (в пространстве имен System.Diagnostics), который позволяет запрашивать определенный счетчик производительности системы. Попробуйте следующий код:

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());

Кроме того, вы можете сделать это через WMI . Но, похоже, что ответ stian.net покрыл это.

Обратите внимание, наконец, что имя счетчика производительности должно быть локализовано, если вам требуется поддержка международных версий Windows, поэтому правильное решение должно искать локализованные строки для «System» и «System Up Time», используя PdhLookupPerfNameByIndex , или вы должны убедиться, что используете PdhAddEnglishCounter под колпаком, который поддерживается только в Vista и выше. Подробнее об этом здесь .

5 голосов
/ 03 мая 2017

Следующий код извлекает миллисекунды с момента запуска системы (вызов неуправляемого API). Я измерил затраты производительности для этой операции взаимодействия, и она совершенно идентична StopWatch () (но она, конечно, не извлекает время с момента запуска системы).

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

0 голосов
/ 24 октября 2016

Если вы посчитаете каждый оборот, вы можете создать свой собственный 64-битный TickCount, который будет полезен для 2 ^ 63 мс (292 миллиона лет) или 2 ^ 64 мс (585 миллионов лет). Если вам не нужна полная точность 1 мс (фактически только разрешение 10-16 мс) или диапазон, вы можете разделить результат на 1000 и представить до 49 710 дней (136 лет) с разрешением в одну секунду в UInt32.

GetTickCount64() делает это для вас, но доступно только в Vista или более поздней версии.

Здесь я считаю периоды прошедшего времени, а не 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 );
}

AND с Int32.MaxValue не требуется и является плохим примером в документации .NET. Непроверенное вычитание 32-битных целых чисел безопасно переполняется. Знак Environment.TickCount никогда не имеет значения. Вычитание обрабатывает все случаи, которые обертывают вокруг. Пример, перенос по одному: uiT0 = Int32.MaxValue; iTickNow = uiT0 + 1 приводит к Int32.MinValue; наконец, (iTickNow - uiT0) дает 1.

uiElapsed следы истекли время до 49,7 дней, прежде чем оно обнуляется. Каждый раз, когда это происходит, iWrapCount увеличивается. GetElapsedTime () должен вызываться не реже одного раза в 49,7 дня, чтобы обход не обнаруживался.

0 голосов
/ 20 июля 2015

Я не фанат использования GetTickCount () для отметки времени, поскольку она может возвращать отрицательные числа.Хотя использование Abs () может помочь, но это неудобно и не является оптимальным решением.

Лучше использовать Секундомер в .Net или QueryPerformanceCounter в C ++ в качестве метки времени.

В приложении C # я создаю глобальный объект секундомера, запускаю его.Позже в приложении я использую секундомер.ElapsedMiliseconds в качестве метки времени.

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...
            }
        }
    }
}
0 голосов
/ 18 мая 2015

Я думаю, что именно так они и реализовали.

Идет от 0 до максимума, а затем от мин до 0.

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

Я отредактировал код, который вы используете, с http://www.codeproject.com/Articles/13384/Getting-the-user-idle-time-with-C

Почему бы вам просто не получить Абсолютное число?

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