Geting the system uptime on Windows

Share your advanced PureBasic knowledge/code with the community.
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Geting the system uptime on Windows

Post by Mistrel »

This is accurate up to 292 billion years on Windows Vista and up. It is accurate up to 292 billion on Windows 2000 and XP as long as the system clock is not adjusted backwards in time. Adjusting the clock forwards in time on Windows 200 or XP will result in an increased uptime proportional to the change.

I don't know how to make this any more accurate for 2000/XP. :|

Although it is very accurate, the performance counter is altered by adjusting the system clock on 2000/XP (it is not on Vista/7). The only solution is to rely on GetTickCount() which has the 49.7 day limitation.

Code: Select all

#PDH_FMT_LARGE=$00000400
#PDH_FMT_1000=$00002000

Structure PDH_FMT_COUNTERVALUE
  CStatus.l
  dummy.l
  StructureUnion
    longValue.l
    doubleValue.d
    largeValue.q
    *AnsiStringValue
    *WideStringValue
  EndStructureUnion
EndStructure

CompilerIf #PB_Compiler_Unicode
  Prototype PdhVbAddCounter(hQuery, CounterPath.p-ascii, *hCounter)
CompilerElse
  Prototype PdhVbAddCounter(hQuery, CounterPath.s, *hCounter)
CompilerEndIf

Prototype PdhGetFormattedCounterValue(hCounter, dwFormat, lpdwType, *Value)
Prototype PdhCollectQueryData(hQuery)
Prototype PdhVbOpenQuery(*hQuery)
Prototype PdhCloseQuery(hQuery)

;/ Uptime is in milliseconds
Procedure GetSystemUptime()
  Protected LibID.i
  Protected Ptr.i
  Protected PDHError.i
  Protected hQuery.i
  Protected hCounter.i
  Protected ElapsedTime.PDH_FMT_COUNTERVALUE
  Protected PdhVbOpenQuery.PdhVbOpenQuery
  Protected PdhVbAddCounter.PdhVbAddCounter
  Protected PdhGetFormattedCounterValue.PdhGetFormattedCounterValue
  Protected PdhCollectQueryData.PdhCollectQueryData
  Protected PdhCloseQuery.PdhCloseQuery
  Protected ElapsedTimeQ.q
  Protected ElapsedTime49.l ;/ Elapsed uptime only accurate up to 49.7 days
  
  ;/ This method is the most accurate but is only supported by Vista and above
  Ptr=GetProcAddress_(GetModuleHandle_("Kernel32.dll"),"GetTickCount64")
  If Ptr
    ProcedureReturn CallFunctionFast(Ptr)
  EndIf
  
  LibID=OpenLibrary(#PB_Any,"pdh.dll")
  
  If Not LibID
    PDHError=#True
  EndIf
  
  If Not PDHError
    PdhVbOpenQuery=GetFunction(LibID,"PdhVbOpenQuery")
    PdhVbAddCounter=GetFunction(LibID,"PdhVbAddCounter")
    PdhGetFormattedCounterValue=GetFunction(LibID,"PdhGetFormattedCounterValue")
    PdhCollectQueryData=GetFunction(LibID,"PdhCollectQueryData")
    PdhCloseQuery=GetFunction(LibID,"PdhCloseQuery")
  EndIf
  
  If Not PdhVbOpenQuery&PdhVbAddCounter&PdhGetFormattedCounterValue&PdhCollectQueryData&PdhCloseQuery
    PDHError=#True
    ProcedureReturn #False
  EndIf
  
  If Not PDHError And PdhVbOpenQuery(@hQuery)
    PDHError=#True
  EndIf
  
  If Not PDHError And PdhVbAddCounter(hQuery,"\System\System Up Time",@hCounter)
    PDHError=#True
  EndIf
  
  If Not PDHError And PdhCollectQueryData(hQuery)
    PDHError=#True
  EndIf
  
  If Not PDHError And PdhGetFormattedCounterValue(hCounter,#PDH_FMT_LARGE|#PDH_FMT_1000,0,@ElapsedTime)
    PDHError=#True
  EndIf
  
  If hQuery
    PdhCloseQuery(hQuery)
  EndIf
  
  If LibID
    CloseLibrary(LibID)
  EndIf
  
  ;/ If the clock has been set backwards such that the uptime would be
  ;/ a negative value, the result will be "0"
  ElapsedTimeQ.q=ElapsedTime\largeValue
  
  ;/ Even though GetTickCount is only valid for up to 49.7 days, it should still be evaluated. On
  ;/ 2000/XP/2003 systems, The performance counter method will fail and return 0 if the system
  ;/ clock has been rolled back to a date from before its startup date where ElapsedTime.q would
  ;/ have been negative. Also on 2000/XP/2003 systems, it will return a future date if the system
  ;/ clock has been adjusted into the future. GetTickCount will still be positive if the
  ;/ performance counter fails, even if it may be wrong. If the performance counter is failing
  ;/ then it is suggestive that the system clock is being manipulated and attempting to calculate
  ;/ uptime is moot anyways.
  Ptr=GetProcAddress_(GetModuleHandle_("Kernel32.dll"),"GetTickCount")
  If Ptr
    ElapsedTime49=CallFunctionFast(Ptr)
    
    If ElapsedTime49>ElapsedTimeQ.q
      ElapsedTimeQ.q=ElapsedTime49
    EndIf
  EndIf
  
  ProcedureReturn ElapsedTimeQ.q
EndProcedure
Last edited by Mistrel on Sat Nov 20, 2010 2:09 am, edited 4 times in total.
buddymatkona
Enthusiast
Enthusiast
Posts: 252
Joined: Mon Aug 16, 2010 4:29 am

Re: Geting the system uptime on Windows

Post by buddymatkona »

I do not need a lot of accuracy so I compute CURRENTDateTime - SYSTEMSTARTDateTime.

You can use the "Date Modified" property from one of the hive files updated during a reboot.
e.g. UpTime = DateTime.Now - GetLastWriteTime("C:\Windows\System32\config\SECURITY")

REF System.IO in mscorlib.dll...sorry about not converting the VB :)
cas
Enthusiast
Enthusiast
Posts: 597
Joined: Mon Nov 03, 2008 9:56 pm

Re: Geting the system uptime on Windows

Post by cas »

I haven't heard of anyone having 49 days uptime on any client OS from Microsoft. :)
But anyway, thanks for this code snippet.

@buddymatkona:
Relying on system date/time which can be changed by user is not a good idea, even when you don't need a lot of accuracy. Just recently, i saw PC with dead on-board CMOS battery so every time Windows XP starts up, date is reset to 1.1.2000 00:00 and when that PC connects to Internet, it gets time synchronized with Internet time server. So your application would show 10years of uptime. :|
PB
PureBasic Expert
PureBasic Expert
Posts: 7581
Joined: Fri Apr 25, 2003 5:24 pm

Re: Geting the system uptime on Windows

Post by PB »

> I haven't heard of anyone having 49 days uptime

It happens and is quite common. I've worked at places where the
PC is on 24/7 forever. Even my current job has one. I should check
the uptime on it one day and see what it currently is.

> Relying on system date/time which can be changed by user is not a good idea

I use GetTickCount_() in my app to check for uptime, and if the
result is a negative value (due to >49.7 days) then I check the
modified date status of pagefile.sys (which the system creates,
not the user). So far it's been 100% accurate with the uptime
result when I compare it to other third-party uptime apps.

I hear what you're saying about a dead CMOS battery, but the
PC would have to be on for >49.7 days for my app to suffer from
that problem when reading pagefile.sys, so it's not a big issue
for most end users. Besides, if the battery is dead then of course
the user can't expect the uptime to be accurate.
I compile using 5.31 (x86) on Win 7 Ultimate (64-bit).
"PureBasic won't be object oriented, period" - Fred.
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Re: Geting the system uptime on Windows

Post by Mistrel »

PB wrote:I check themodified date status of pagefile.sys (which the system creates, not the user).
Not everyone uses a page file. I disable it on my laptop which has 6GB of memory to save on writes to its SSD.
PB
PureBasic Expert
PureBasic Expert
Posts: 7581
Joined: Fri Apr 25, 2003 5:24 pm

Re: Geting the system uptime on Windows

Post by PB »

> Not everyone uses a page file

True, which is why I only check it as a last resort if GetTickCount_()
shows that the PC has been running for over 49.7 days. I might have
to use your code after all, but damn I hate long blocks of code to do
such a simple task. :)

[Edit] Just tried your code on Win XP (SP2) and it fails with this:

Code: Select all

---------------------------
PureBasic
---------------------------
Line 8: Structure not found: PDH_FMT_COUNTERVALUE.
---------------------------
OK
---------------------------
I compile using 5.31 (x86) on Win 7 Ultimate (64-bit).
"PureBasic won't be object oriented, period" - Fred.
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Re: Geting the system uptime on Windows

Post by Mistrel »

PB wrote:True, which is why I only check it as a last resort if GetTickCount_()
shows that the PC has been running for over 49.7 days.
How can you check if the computer has exceeded 49.7 days if it loops.. ?
Just tried your code on Win XP (SP2) and it fails with this:

Code: Select all

---------------------------
PureBasic
---------------------------
Line 8: Structure not found: PDH_FMT_COUNTERVALUE.
---------------------------
OK
---------------------------
Sorry about that. I've added the missing code to my first post.
freak
PureBasic Team
PureBasic Team
Posts: 5940
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Re: Geting the system uptime on Windows

Post by freak »

Modern Windows versions should also have an event log entry for the date and time the system was booted.

Don't ask me how to read that though :D
quidquid Latine dictum sit altum videtur
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Re: Geting the system uptime on Windows

Post by Mistrel »

freak wrote:Modern Windows versions should also have an event log entry for the date and time the system was booted.
I believe you need administrative rights to access those logs though. :|
PB
PureBasic Expert
PureBasic Expert
Posts: 7581
Joined: Fri Apr 25, 2003 5:24 pm

Re: Geting the system uptime on Windows

Post by PB »

> How can you check if the computer has exceeded 49.7 days if it loops.. ?

The result becomes a negative. I know this from experience and testing.
I compile using 5.31 (x86) on Win 7 Ultimate (64-bit).
"PureBasic won't be object oriented, period" - Fred.
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Re: Geting the system uptime on Windows

Post by Mistrel »

But in another 49.7 days it becomes positive..
PB
PureBasic Expert
PureBasic Expert
Posts: 7581
Joined: Fri Apr 25, 2003 5:24 pm

Re: Geting the system uptime on Windows

Post by PB »

I didn't know that (my PC doesn't go 99.4 days without a reboot).
I compile using 5.31 (x86) on Win 7 Ultimate (64-bit).
"PureBasic won't be object oriented, period" - Fred.
PB
PureBasic Expert
PureBasic Expert
Posts: 7581
Joined: Fri Apr 25, 2003 5:24 pm

Re: Geting the system uptime on Windows

Post by PB »

I just created this small tip from Wikipedia (http://en.wikipedia.org/wiki/Uptime).
But, it does take a couple of seconds to execute, so it's not good for real-time use.
Still, it's short and sweet, which is how I like it (and my girls!). ;)

Code: Select all

Procedure.s UpTime() ; For XP or higher. Takes a couple of seconds to execute. :(
  p=RunProgram("systeminfo","","",#PB_Program_Hide|#PB_Program_Open|#PB_Program_Read)
  If p
    While ProgramRunning(p) : o$+ReadProgramString(p)+#CRLF$ : Wend : CloseProgram(p)
    up$=Trim(Mid(o$,FindString(o$,"Time:",1)+5)) : up$=Left(up$,FindString(up$,#CRLF$,1)-1)
  EndIf
  ProcedureReturn up$
EndProcedure

Debug UpTime()
I compile using 5.31 (x86) on Win 7 Ultimate (64-bit).
"PureBasic won't be object oriented, period" - Fred.
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Re: Geting the system uptime on Windows

Post by Mistrel »

Err..

Code: Select all

Original Install Date:     11/13/2010, 4:57:02 AM
System Boot Time:          11/13/2010, 6:18:45 PM
The boot time is waay off.
PB
PureBasic Expert
PureBasic Expert
Posts: 7581
Joined: Fri Apr 25, 2003 5:24 pm

Re: Geting the system uptime on Windows

Post by PB »

> The boot time is waay off

That's Microsoft for you. They're the ones providing the info.

But, isn't it plausible that Windows was installed at 4:57:02 AM
and then the system last booted later that day at 6:18:45 PM?

Besides, we're not concerned with install time; just the uptime.
So Microsoft is saying the system has been booted and running
since 6:18:45 PM on that PC. Are you saying that's not the case?
The uptime results from SystemInfo are correct for me on XP.
I compile using 5.31 (x86) on Win 7 Ultimate (64-bit).
"PureBasic won't be object oriented, period" - Fred.
Post Reply