Получить время безотказной работы системы с помощью C#

Есть ли простой способ получить время безотказной работы системы с помощью С#?


person ProgrammingPope    schedule 09.06.2009    source источник
comment
дубликат: stackoverflow.com/questions/265089/   -  person Inisheer    schedule 10.06.2009
comment
Я просто хотел сказать, что в настоящее время время безотказной работы может показаться намного больше, поскольку системы больше не выключаются, а вместо этого переходят в спящий режим (Windows 10).   -  person Thomas Weller    schedule 27.12.2019


Ответы (10)


Я немного опоздал, но другой простой способ — использовать GetTickCount64, которая доступна, начиная с Windows Vista, и не вызывает переполнения, как это делает GetTickCount:

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

[DllImport("kernel32")]
extern static UInt64 GetTickCount64();
person Martin    schedule 21.05.2013
comment
Это было именно то, что мне было нужно, большое спасибо за публикацию этого :) - person Tom Anderson; 02.07.2013
comment
GetTickCount64() возвращает ошибку System.EntryPointNotFoundException: невозможно найти точку входа. - person Lin Song Yang; 24.02.2015
comment
@Goby Windows Vista или новее? Опечатка? Переименовали «GetTickCount64» во что-то другое? - person Martin; 25.02.2015
comment
GetTickCount64 ... does not overflow - Он переполнится, когда они найдут способ заставить мой код работать 585 миллиардов лет. - person rkagerer; 23.11.2015
comment
@rkager, подожди и увидишь. Подобные недальновидные предположения привели нас к 2000 году и концу эпохи 2038 года =D - person Rbjz; 29.05.2017
comment
@Rbjz y2k был далеко не таким богатым на события, как его изображают, точно так же эти API-интерфейсы не основаны на предположениях, они основаны на аппаратных ограничениях; GetTickCount — это API производительности, рассчитанный на основе структур ядра, которые в то время были 32-битными, GetTickCount64 появились после того, как эти структуры ядра стали поддерживать 64-битные значения. Возможно, стоит отметить, что GetTickCount изначально предназначался для обеспечения временной корреляции между счетчиками производительности системы в течение короткого периода, использование, которое позже было заменено QueryPerformanceCounter для корреляции с более высоким разрешением в течение более длительного периода. - person Shaun Wilson; 12.03.2018
comment
Нет необходимости использовать для этого P/Invoke в самых последних версиях .NET, так как класс Environment теперь имеет TickCount64 свойство. - person Steven Rands; 02.03.2021
comment
@StevenRands Хотите опубликовать новый ответ с Environment.TickCount64? Я думаю, что это сейчас лучшее решение. - person Martin; 03.03.2021

System.Environment.TickCount получает количество миллисекунд с момента система была перезапущена.

Остерегайтесь, однако, что это Int32, и он переполнится через 24,9 дня и станет отрицательным. См. примечания к документам MDSN.

person adrianbanks    schedule 09.06.2009
comment
Чтобы быть педантичным, преобразуйте это свойство в миллисекунды с помощью TimeSpan.TicksPerMillisecond (msdn.microsoft.com/en-us/library/) - person Michael Petrotta; 09.06.2009
comment
Или, еще лучше, вызовите TimeSpan.FromMilliseconds - person SLaks; 09.06.2009
comment
@SlLaks - я что-то упустил? Вы должны получить миллисекунды из тиков, прежде чем вы сможете получить TimeSpan из миллисекунд. - person Michael Petrotta; 10.06.2009
comment
@SLaks - или, может быть, вы имеете в виду TimeSpan.FromTicks(). - person Michael Petrotta; 10.06.2009
comment
Как ни странно, Environment.TickCount возвращает количество миллисекунд, а не тиков .Net (тик .Net составляет 100 наносекунд). - person SLaks; 10.06.2009
comment
@SLaks - Узнавайте что-то новое каждый день. Спасибо. - person Michael Petrotta; 10.06.2009
comment
Чтобы получить от него непрерывность на ~49,7 дней, просто примените его к UInt32/uint. - person Slipp D. Thompson; 14.04.2014
comment
Разве это не более быстрый и лучший ответ, если вы действительно хотите безотказной работы? - person Chibueze Opata; 13.08.2014
comment
В самых последних версиях .NET класс Environment имеет TickCount64, что позволяет избежать проблемы переноса. - person Steven Rands; 02.03.2021

Моя машина имеет время безотказной работы 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}");
}
person Quantic    schedule 30.11.2015
comment
Спасибо за сравнение решений, пожалуйста, укажите совместимость. Какое из этих решений будет работать на невыигрышных платформах? - person Rbjz; 29.05.2017
comment
Код ManagementObject, использующий \\.\root\cimv2:Win32_OperatingSystem=@, не работает в Windows XP, потому что Win32_OperatingSystem, по-видимому, не является синглтоном в XP. Вместо этого я обнаружил, что мне нужно использовать запрос, чтобы найти основной экземпляр и перебрать результаты запроса, как показано в этом ответе, который работал для меня как на XP, так и на Win 7: stackoverflow.com/a/7407346/382885 - person PolyTekPatrick; 06.07.2017
comment
Отличный ответ. Спасибо ! Я просто хотел добавить две вещи: GetTickCount64() не поддерживается в версиях Windows до Vista, а PerformanceCounter(System, System Up Time) занимает ОЧЕНЬ много времени на моем ноутбуке W7 (более одной минуты. Не спрашивайте почему.) - person AFract; 26.07.2017

Точно и больше, чем System.Environment.TickCount, без использования ужасных счетчиков производительности ОС, WMI или собственных вызовов:

var ticks = Stopwatch.GetTimestamp();
var uptime = ((double)ticks) / Stopwatch.Frequency;
var uptimeSpan = TimeSpan.FromSeconds(uptime);
person Rbjz    schedule 14.03.2014
comment
Получение отметок времени с высоким разрешением объясняет, почему это возвращает время безотказной работы: «QueryPerformanceCounter [...] возвращает общее количество тиков, произошедших с момента запуска операционной системы Windows, включая время, когда машина находилась в состоянии сна, например в режиме ожидания, спящий режим или режим ожидания с подключением». - person Martin; 08.09.2014
comment
@ Мартин, правильно ли я понял от вас, что Stopwatch.GetTimestamp() тогда не включает время сна компьютера? Пожалуйста, объясни. - person Rbjz; 12.01.2015
comment
Я только процитировал из статьи, на которую дал ссылку, не писал ;-) В той статье: «В данном контексте под тиком понимается период времени, равный 1 ÷ (частота полученного счетчика производительности из QueryPerformanceFrequency)». Я бы понял, что тик — это просто единица времени, как, например. «секунда», и мне кажется, что именно так употребляется слово «галочка» на протяжении всей статьи. Таким образом, компьютеру не нужно запускаться, чтобы «появлялись» «тики», и GetTimestamp() включает время сна. Но я не знаю точного ответа на ваш вопрос. - person Martin; 12.01.2015
comment
Этот метод зависит от системы, имеющей HPET, поэтому перед его использованием убедитесь, что Stopwatch.IsHighResolution имеет значение true. См. раздел «Примечания» документации по адресу msdn.microsoft.com/en-us/library/ - person Ryan Williams; 02.05.2017
comment
@RyanWilliams, так что, если нет HPET, какие есть альтернативы для получения времени с высокой точностью? - person Rbjz; 13.05.2017

Самый простой и правильный способ сделать это

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();
}
person Jakub Chodounsky    schedule 02.08.2010
comment
Это возвращает исключение с сообщением: Invalid object path на сервере Windows 2003, работающем в веб-службе в IIS6. - person Hugh Jeffner; 11.01.2011
comment
Это так развлечет вас, если запустить на * nix - person Rbjz; 29.05.2017
comment
и время запуска? - person Kiquenet; 03.04.2019

Просто, нет, но это можно сделать:

    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!");
    }
person C. Ross    schedule 09.06.2009

Если вы используете более позднюю версию .NET (Core 3.0/.NET 5.0 или выше), то класс Environment теперь имеет свойство TickCount64.

Это не страдает от проблем с переносом свойства TickCount, и вам не нужно прибегать к P/Invoke, чтобы получить значение.

long tickCountMs = Environment.TickCount64;
var uptime = TimeSpan.FromMilliseconds(tickCountMs);
person Steven Rands    schedule 03.03.2021

Я знаю, что вопрос и старый, и решенный, но самое простое решение, которое я могу придумать, - это просто использовать свойство Enviroment.TickCount, которое возвращает количество миллисекунд с момента запуска системы:

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

Это решение намного быстрее, чем принятый ответ.

person BlackCap    schedule 31.07.2013
comment
TickCount возвращается как целое число, но системный таймер имеет большее разрешение, поэтому существует длительный период времени, когда счетчик тиков равен int.MaxValue, а затем, в конце концов, снижается до int.MinValue. Этот метод бесполезен для серверов, которые работают месяцами. Технически разрешение системного таймера также точно только до блоков от 10 до 16 миллисекунд. - person TheXenocide; 11.10.2013

(Один и единственный) правильный ответ на данный момент:

Использование 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.
  */
person Robin Davies    schedule 01.05.2020
comment
Я не уверен, что вы подразумеваете под Вы определенно хотите избежать накладных расходов P/Invoke. Использование [DllImport] является использованием P/Invoke, верно? - person Steven Rands; 02.03.2021
comment
Методы, не требующие сортировки, вызываются напрямую, IF они включены в класс SafeNativeMethods. Функция, которая прокралась в .net где-то во временных рамках 4.x. - person Robin Davies; 03.03.2021
comment
Интересно. У вас есть ссылка на это? Лучшее, что я смог найти, это это в котором указано SafeNativeMethods — этот класс подавляет обход стека для разрешения неуправляемого кода. - person Steven Rands; 03.03.2021

person    schedule
comment
Первый вызов uptime.NextValue вернет 0. - person SLaks; 10.06.2009
comment
Что делать, если имя счетчика локализовано? - person abatishchev; 05.03.2013
comment
Получите локализованные имена (2 = система, 674 = время работы системы), используя: StringBuilder buffer = new StringBuilder(1024); uint buf_size = (uint)buffer.Capacity; Win32.PdhLookupPerfNameByIndex(null, id, buffer, ref buf_size); return buffer.ToString(); - person Oliver Bock; 24.07.2013
comment
Я возражаю, что это «простой путь». Кроме того, попробуйте запустить его на * nix. - person Rbjz; 29.05.2017
comment
@Rbjz обычно квалифицирует ответ с помощью C #, что означает, что OP не ищет ответ, который требует чего-либо, чего еще нет в .NET Framework. По состоянию на 2009 год это был самый простой метод C # для получения времени безотказной работы - альтернативы включали запрос WMI и сканирование журнала системных событий на наличие загрузочной записи ... и для сравнения этот метод является самым простым и будет работать во всех версиях Windows. поддерживается .NET. Чтобы не использовать C#, требуется PInvoke; GetTickCount64 (не во всех версиях Windows) или ZwQuerySystemInformation (отменить документ), или sysinfo (в системах posix+SVR4) - person Shaun Wilson; 12.03.2018
comment
Эта функция не возвращает время работы, она возвращает продолжительность между последним перезапуском и текущим. Включение и выключение компьютера не влияет на это поведение. - person Guido Kleijer; 01.08.2019