Geting the system uptime on Windows
Posted: Sun Nov 14, 2010 4:11 am
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.
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