【问题标题】:Windows Last Boot Date & Time using Delphi使用 Delphi 的 Windows 上次启动日期和时间
【发布时间】:2011-06-29 13:19:43
【问题描述】:

如何在 Windows 2008/2003 机器上获取上次启动/重启/重启的日期和时间?

我从命令提示符知道我们可以使用“网络统计”,但是如何通过 Delphi 来实现呢?

谢谢。

【问题讨论】:

    标签: windows delphi uptime


    【解决方案1】:

    您可以使用Win32_OperatingSystem WMI 类的LastBootUpTime 属性,它返回Date and time the operating system was last restarted(注意:该属性的返回值是UTC 格式)。

    查看此示例应用

    {$APPTYPE CONSOLE}
    
    uses
      SysUtils,
      ActiveX,
      Variants,
      ComObj;
    
    
    //Universal Time (UTC) format of YYYYMMDDHHMMSS.MMMMMM(+-)OOO.
    //20091231000000.000000+000
    function UtcToDateTime(const V : OleVariant): TDateTime;
    var
      Dt : OleVariant;
    begin
      Result:=0;
      if VarIsNull(V) then exit;
      Dt:=CreateOleObject('WbemScripting.SWbemDateTime');
      Dt.Value := V;
      Result:=Dt.GetVarDate;
    end;
    
    procedure  GetWin32_OperatingSystemInfo;
    const
      WbemUser            ='';
      WbemPassword        ='';
      WbemComputer        ='localhost';
      wbemFlagForwardOnly = $00000020;
    var
      FSWbemLocator : OLEVariant;
      FWMIService   : OLEVariant;
      FWbemObjectSet: OLEVariant;
      FWbemObject   : OLEVariant;
      oEnum         : IEnumvariant;
      iValue        : LongWord;
    begin;
      FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
      FWMIService   := FSWbemLocator.ConnectServer(WbemComputer, 'root\CIMV2', WbemUser, WbemPassword);
      FWbemObjectSet:= FWMIService.ExecQuery('SELECT * FROM Win32_OperatingSystem','WQL',wbemFlagForwardOnly);
      oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
      if oEnum.Next(1, FWbemObject, iValue) = 0 then
      begin
        Writeln(Format('Last BootUp Time    %s',[FWbemObject.LastBootUpTime]));// In utc format
        Writeln(Format('Last BootUp Time    %s',[formatDateTime('dd-mm-yyyy hh:nn:ss',UtcToDateTime(FWbemObject.LastBootUpTime))]));// Datetime
      end;
    end;
    
    
    begin
     try
        CoInitialize(nil);
        try
          GetWin32_OperatingSystemInfo;
        finally
          CoUninitialize;
        end;
     except
        on E:Exception do
            Writeln(E.Classname, ':', E.Message);
     end;
     Writeln('Press Enter to exit');
     Readln;
    end.
    

    【讨论】:

    • ——我越是关注这个晦涩难懂的解决方案,就越不喜欢它。 Esp:“LastBootUpTime 数据类型:datetime”字符串类型的脚本,对吧?
    • @Downvoter:我必须同意 RRUZ。 WMI 通过 MS Platform SDK 进行了很好的记录。这里没有晦涩难懂。
    • +1;几小时前我通过你的WMI Delphi Code Creator 找到了LastBootUpTime,但我知道你会想发布答案:) 当我前段时间浏览the reference 时,我不相信WMI 有多少秘密。跨度>
    • @Misha,你怎么知道哪个时间不对?尝试将返回的值与此 systeminfo | find "System Boot Time:" 进行比较(从 CMD.exe 运行)
    • @Misha 阅读这篇文章petri.co.il/check-uptime-in-server-2008.htm 在这里您会发现确定 windows 正常运行时间或 windows 上次启动时间的方法之间的区别。
    【解决方案2】:

    这是一个完整的命令行应用程序,可以满足您的要求。 我已对此进行了修改,以避免在不依赖外部函数调用的情况下出现 GetTickCount 溢出问题。

    示例输出:

    Windows was last rebooted at: 06/29/2011 9:22:47 AM
    

    玩得开心!

    program lastboottime;
    
    {$APPTYPE CONSOLE}
    
    uses
      SysUtils,
      Windows;
    
    function UptimeInDays: double;
    const
      c_SecondsInADay = 86400;
    var
      cnt, freq: Int64;
    begin
      QueryPerformanceCounter(cnt);
      QueryPerformanceFrequency(freq);
      Result := (cnt / freq) / c_SecondsInADay;
    end;
    
    function LastBootTime: TDateTime;
    begin
      Result := Now() - UptimeInDays;
    end;
    
    begin
      try
        WriteLn('Windows was last rebooted at: ' + DateTimeToStr(LastBootTime));
        ReadLn;
      except on E: Exception do
        Writeln(E.ClassName, ': ', E.Message);
      end;
    end.
    

    【讨论】:

    • 我想我不在这里也投反对票是不公平的......对不起...... :(
    • 这基本上是我在我的 Win7 64 笔记本电脑上使用和工作的,而 WMI 版本没有。对于 1 周的正常运行时间,我的版本准确到 +/- 10 秒左右
    • WMI 版本似乎也可以工作,但是由于我已经重新启动了有问题的机器,所以无法对其进行测试。
    【解决方案3】:

    GetTickCount 函数(请参阅MSDN)返回自系统启动以来经过的毫秒数,因此将其除以 1000 得到秒,除以 60 000 得到分钟等。

    我链接的主题也包含这一点:

    要获取自计算机启动以来经过的时间,请在注册表项 HKEY_PERFORMANCE_DATA 的性能数据中检索系统运行时间计数器。返回的值是一个 8 字节的值。有关详细信息,请参阅性能计数器。

    【讨论】:

    • +1,你可以从当前时间中减去运行时间,得到开机时间。
    • -1 因为这不起作用。 GetTickCount 返回一个 32 位无符号整数,其最大值为 4294967295。解释为毫秒数,这等于略小于 50 天。也就是说,当计算机已经运行了 50 天时,tick count-approach 会错误地认为计算机刚刚启动。
    • 这就是你在 Vista+ 上使用GetTickCount64 的原因,但与正确答案非常接近。
    • @Andreas 它将在 49.7 天后翻转。还有一个 GetTickCount64 函数,“应该对每个人都足够了”,但从 Vista 开始就支持它(顺便说一句,现在注意到这是 OP 正在使用的)。我还引用了不受 32 位限制的性能计数器信息。
    • 当你暂停你的机器时会怎样?这不会停止滴答计数器吗?
    【解决方案4】:

    这里有一段代码,如果可用则使用GetTickCount64,如果不可用则回退到GetTickCount,以计算系统启动的日期和时间。这不是一个完美的解决方案,因为 GetTickCount64 仅在 Vista+ 上受支持:如果您使用的是旧版 Windows,则计数器每 49 天返回 0。

    program Project29;
    
    {$APPTYPE CONSOLE}
    
    uses
      SysUtils, Windows;
    
    type
      TGetTickCount64 = function : Int64; stdcall;
    
    var
      H_K32: HMODULE;
      Tick64Proc: TGetTickCount64;
    
    function BootTime: TDateTime;
    var UpTime: Int64;
        Seconds, Minutes, Hours: Int64;
    begin
      if H_K32 = 0 then
      begin
        H_K32 := LoadLibrary(kernel32);
        if H_K32 = 0 then
          RaiseLastOSError
        else
          begin
            Tick64Proc := GetProcAddress(H_K32, 'GetTickCount64');
         end;
      end;
    
      if Assigned(Tick64Proc) then
        UpTime := Tick64Proc
      else
        UpTime := GetTickCount;
    
      Result := Now - EncodeTime(0, 0, 0, 1) * UpTime;
    end;
    
    begin
      WriteLn(DateTimeToStr(BootTime));
      ReadLn;
    end.
    

    【讨论】:

    • 我认为这是我能得到的最好的。在 Windows 2003 服务器上测试,它使用 GetTickCount。而 Windows 2008 服务器使用 GetTickCount64。可惜没有简单的 API 来获取这些信息。
    • @ewlung,有“系统运行时间”性能计数器,请参阅 ain 的答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多