We help IT Professionals succeed at work.

Win Uptime without 49 day limit?

TValley
TValley asked
on
769 Views
Last Modified: 2010-04-05
Anyone have code for obtaining the "real" Windows uptime that works around the 49 day limitation of GetTickCount?

Constraints:  The existing program that will need to display the uptime will be run at least once per day, and may also be running continuously (but not necessarily).
The solution needs to work only on Win2k and XP (and above) platforms - Win98/95/ME support is not required.  It would be best if the solution did not depend on either a continuously-running service or something that needs to auto-start when Windows reboots.  NOTE: One-second resolution is more than acceptable, therefore a Cardinal variable could be used to store the value, with a very acceptable rollover of about 136 YEARS

Some thoughts about how this might be done (need help fleshing them out or new ideas):

1- The first time that the main program runs, have it write an integer var somewhere into Windows memory that won't get wiped out when the main program ends (how to do that?  Dunno).  That number would be incremented each time a rollover is detected (by the main program) to track the number of rollovers.  Rebooting Windows would wipe out that RAM-resident value, therefore resetting it.

2- Use some other counter value from Windows, perhaps the Performance Counters (exactly how, though?).

3- Write a Windows service that runs when Windows boots and provides this service, based on GetTickCount, but with a local variable to track rollovers.  The Main program would access this service to get the real uptime.  This is probably acceptable, but as noted above, not a very satisfactory or desirable solution.

Anyione have any other solutions?

TIA,

Timmer

Comment
Watch Question

Commented:
This one is on us!
(Get your first solution completely free - no credit card required)
UNLOCK SOLUTION

Commented:
I'm not sure about this but i think there is information about this available in some NETBIOS call of some sort...
however it's probably very complicated.. so i don't have time to look up more exact info

regards
//raidos

Commented:
To obtain the time elapsed since the computer was started, retrieve the System Up Time counter in the performance data in the registry key HKEY_PERFORMANCE_DATA. The value returned is an 8-byte value. For more information, see Performance Monitoring.

from msdn

Author

Commented:
akcom,

Easy for you to say....

Try it, though!  The documentation from Microsoft is pathetic, and there's a "known issue" with a memory leak related to obtaining Performance Data.

I really need hard facts / Delphi code.  I already went the search engine route and found the same information, but it's pretty worthless.

Anyone else have any ideas?
Commented:
This one is on us!
(Get your first solution completely free - no credit card required)
UNLOCK SOLUTION

Author

Commented:
MrMidi - You ROCK!!!

Thank you - This was EXACTLY what I needed - and more!  

I bumped up the points because you provided such a complete and functional answer.

FYI - for some reason I had to replace "MessageBox" with "ShowMessage", otherwise the dialog didn't pop up.  



Again, thanks.

TValley

P.S.
Do you ever do any contract programming?

Commented:
Must be 'cause I play in 2 rockbands LOL

Thanks for the compliments, I just thought, I can do that, let's help this guy.

No, i never did any contract programming, I'm doing it just for hobby. But if you have an offer, I'm listening :)
Right, i have found out how to do this however i can't read C++ well enough to convert the code :|

On Windows NT/2000/XP, you use performance counter data via pdh.dll functions (simplied code, no error checking... do not copy directly):

sprintf(szCounterPath, "\\\\%s\\System\\System Up Time", szMachineName);
PdhOpenQuery(NULL, 0, &hQuery)
PdhAddCounter(hQuery, szCounterPath, 0, &uptimeCounter);
PdhCollectQueryData(hQuery);
PdhGetFormattedCounterValue(uptimeCounter, PDH_FMT_LARGE , NULL,
&uptimeValue);
dwUpTime = (DWORD)(uptimeValue.largeValue);

I did some reasearch, i will post in next post to keep it nice and tidy
Var
 Hq: Hquery;
 HC: HCounter;
 UpT: Longint;
 S: String;
begin
 SetLength(S,10);
 PDHOpenQuery(Nil,0,@Hq);
 PdhAddCounter(Hq,Pchar(S),0,@HC);
 PdhCollectQueryData(hQ);
 PdhGetFormattedCounterValue(hc, PDH_FMT_LARGE,NIl,@UpT);
 Edit1.Text:=IntTOStr(High(UpT));
// PdhGetFormattedCounterValue(


Is the closest thing i got, it compiles but something's wrong, and the unit to get the PdhFunctions are coming now....
//*******************************************************
//
//    Copyright © 1995-2002 by Lucian Radulescu
//    mailto  :  lucian@ez-delphi.com
//    http    :  http://www.ez-delphi.com
//
//*******************************************************

unit PDH;

{$ALIGN ON}
{$MINENUMSIZE 4}

interface

uses
  Windows;

const

  PDH_NO_DATA                      = $800007D5;
  PDH_MEMORY_ALLOCATION_FAILURE    = $C0000BBB;
  PDH_INVALID_HANDLE               = $C0000BBC;
  PDH_INVALID_ARGUMENT             = $C0000BBD;

  PDH_FMT_RAW                      = $00000010;
  PDH_FMT_ANSI                     = $00000020;
  PDH_FMT_UNICODE                  = $00000040;
  PDH_FMT_LONG                     = $00000100;
  PDH_FMT_DOUBLE                   = $00000200;
  PDH_FMT_LARGE                    = $00000400;
  PDH_FMT_NOSCALE                  = $00001000;
  PDH_FMT_1000                     = $00002000;
  PDH_FMT_NODATA                   = $00004000;
  PDH_FMT_NOCAP100                 = $00008000;

type

  PQUERY              = ^HQUERY;
  HQUERY              = THandle;

  PCOUNTER            = ^HCOUNTER;
  HCOUNTER            = THandle;

  PDH_STATUS          = Longint;

  PPERF_COUNTER_BLOCK = ^TPERF_COUNTER_BLOCK;
  _PERF_COUNTER_BLOCK = record                          // pcb
     ByteLength          : DWORD;
  end;
  {$EXTERNALSYM _PERF_COUNTER_BLOCK}
  TPERF_COUNTER_BLOCK = _PERF_COUNTER_BLOCK;
  PERF_COUNTER_BLOCK  = _PERF_COUNTER_BLOCK;
  {$EXTERNALSYM PERF_COUNTER_BLOCK}

  PPERF_COUNTER_DEFINITION = ^TPERF_COUNTER_DEFINITION;
  _PERF_COUNTER_DEFINITION = record                     // pcd
    ByteLength           : DWORD;
    CounterNameTitleIndex: DWORD;
    CounterNameTitle     : LPWSTR;
    CounterHelpTitleIndex: DWORD;
    CounterHelpTitle     : LPWSTR;
    DefaultScale         : DWORD;
    DetailLevel          : DWORD;
    CounterType          : DWORD;
    CounterSize          : DWORD;
    CounterOffset        : DWORD;
  end;
  {$EXTERNALSYM _PERF_COUNTER_DEFINITION}
  TPERF_COUNTER_DEFINITION = _PERF_COUNTER_DEFINITION;
  PERF_COUNTER_DEFINITION  = _PERF_COUNTER_DEFINITION;
  {$EXTERNALSYM PERF_COUNTER_DEFINITION}

  PPERF_DATA_BLOCK = ^TPERF_DATA_BLOCK;
  _PERF_DATA_BLOCK = record                             // pdb
    Signature            : array[0..3] of WCHAR;
    LittleEndian         : DWORD;
    Version              : DWORD;
    Revision             : DWORD;
    TotalByteLength      : DWORD;
    HeaderLength         : DWORD;
    NumObjectTypes       : DWORD;
    DefaultObject        : DWORD;
    SysTime              : SYSTEMTIME;
    PerfTime             : LARGE_INTEGER;
    PerfFreq             : LARGE_INTEGER;
    PerfTime100nSec      : LARGE_INTEGER;
    SystemNameLength     : DWORD;
    SystemNameOffset     : DWORD;
  end;
  {$EXTERNALSYM _PERF_DATA_BLOCK}
  TPERF_DATA_BLOCK  = _PERF_DATA_BLOCK;
  PERF_DATA_BLOCK   = _PERF_DATA_BLOCK;
  {$EXTERNALSYM PERF_DATA_BLOCK}

  PPERF_INSTANCE_DEFINITION = ^TPERF_INSTANCE_DEFINITION;
  _PERF_INSTANCE_DEFINITION = record                    // pid
    ByteLength           : DWORD;
    ParentObjectTitleIndex: DWORD;
    ParentObjectInstance : DWORD;
    UniqueID             : DWORD;
    NameOffset           : DWORD;
    NameLength           : DWORD;
  end;
  {$EXTERNALSYM _PERF_INSTANCE_DEFINITION}
  TPERF_INSTANCE_DEFINITION = _PERF_INSTANCE_DEFINITION;
  PERF_INSTANCE_DEFINITION  = _PERF_INSTANCE_DEFINITION;
  {$EXTERNALSYM PERF_INSTANCE_DEFINITION}

  PPERF_OBJECT_TYPE = ^TPERF_OBJECT_TYPE;
  _PERF_OBJECT_TYPE = record                            // pot
    TotalByteLength      : DWORD;
    DefinitionLength     : DWORD;
    HeaderLength         : DWORD;
    ObjectNameTitleIndex : DWORD;
    ObjectNameTitle      : LPWSTR;
    ObjectHelpTitleIndex : DWORD;
    ObjectHelpTitle      : LPWSTR;
    DetailLevel          : DWORD;
    NumCounters          : DWORD;
    DefaultCounter       : DWORD;
    NumInstances         : DWORD;
    CodePage             : DWORD;
    PerfTime             : LARGE_INTEGER;
    PerfFreq             : LARGE_INTEGER;
  end;
  {$EXTERNALSYM _PERF_OBJECT_TYPE}
  TPERF_OBJECT_TYPE = _PERF_OBJECT_TYPE;
  PERF_OBJECT_TYPE  = _PERF_OBJECT_TYPE;
  {$EXTERNALSYM PERF_OBJECT_TYPE}

  PPDH_FMT_COUNTERVALUE = ^TPDH_FMT_COUNTERVALUE;
  _PDH_FMT_COUNTERVALUE = record
    CStatus              : DWORD;
    longValue            : Longint;
    doubleValue          : double;
    largeValue           : LONGLONG;
    AnsiStringValue      : LPCSTR;
    WideStringValue      : LPCWSTR;
  end;
  {$EXTERNALSYM _PDH_FMT_COUNTERVALUE}
  TPDH_FMT_COUNTERVALUE = _PDH_FMT_COUNTERVALUE;
  PDH_FMT_COUNTERVALUE  = _PDH_FMT_COUNTERVALUE;
  {$EXTERNALSYM PDH_FMT_COUNTERVALUE}

var
  PdhOpenQuery                : function( pReserved: Pointer;
                                          dwUserData: DWORD;
                                          phQuery: PQUERY  ): PDH_STATUS; stdcall;

  PdhCloseQuery               : function( ahQuery: HQUERY ): PDH_STATUS; stdcall;

  PdhAddCounter               : function( ahQuery: HQUERY;
                                          szFullCounterPath: PChar;
                                          dwUserData: DWORD;
                                          phCounter: PCOUNTER ): PDH_STATUS; stdcall;

  PdhRemoveCounter            : function( ahCounter: HCOUNTER ): PDH_STATUS; stdcall;

  PdhCollectQueryData         : function( ahQuery: HQUERY ): PDH_STATUS; stdcall;

  PdhValidatePath             : function( szFullCounterPath: PChar ): PDH_STATUS; stdcall;

  PdhGetFormattedCounterValue : function( ahCounter: HCOUNTER;
                                          dwFormat: DWORD;
                                          lpdwType: LPDWORD;
                                          pValue: PPDH_FMT_COUNTERVALUE): PDH_STATUS; stdcall;

function LoadPdh: Boolean;

implementation

var
  hPdh: THandle = HINSTANCE_ERROR;

function LoadPdh: Boolean;
const
  pdh_lib  = 'pdh.dll';
begin
  Result := hPdh > HINSTANCE_ERROR;
  if Result then Exit;
  hPdh := LoadLibrary( pdh_lib );
  Result := hPdh > HINSTANCE_ERROR;
  if Result then
  begin
    PdhOpenQuery                := GetProcAddress( hPdh, 'PdhOpenQuery' );
    PdhCloseQuery               := GetProcAddress( hPdh, 'PdhCloseQuery' );
    PdhAddCounter               := GetProcAddress( hPdh, 'PdhAddCounterA' );
    PdhRemoveCounter            := GetProcAddress( hPdh, 'PdhRemoveCounter' );
    PdhCollectQueryData         := GetProcAddress( hPdh, 'PdhCollectQueryData' );
    PdhValidatePath             := GetProcAddress( hPdh, 'PdhValidatePathA' );
    PdhGetFormattedCounterValue := GetProcAddress( hPdh, 'PdhGetFormattedCounterValue' );
  end;
end;

initialization
  LoadPdh;
finalization
  if LoadPdh then
    FreeLibrary( hPdh );
end.
I think the first person to get my conversion going and or do their own i am sure will be the first person to do this in Delphi :) as i see no other resources...
Since it took me awhile to find all the bits and pieces to make this work I thought I would add it to this entry for the next person to use.  The error that was made when Craig did the translation from C was that the counter path was forgotten.  Also the definiation of the PDH_FMT_CounterValue record is wrong in PDH.PAS. The PDH_FMT_CounterValue.CStatus entry must be 0 for the call to have worked.
The final result (SEC) is the number of seconds since the system booted.

this is also an example of how to get at any of the performance values.

These are all the pieces to get the system uptime.

use this definition in PDH.PAS:
  _PDH_FMT_COUNTERVALUE = packed record
    CStatus              : DWORD;
    case integer of
    0: (longValue            : Longint;);
    1: (doubleValue          : double;);
    2: (largeValue           : LONGLONG;);
    3: (AnsiStringValue      : LPCSTR;);
    4: (WideStringValue      : LPCWSTR;);
  end;

put this in as the FormShow function in your form.

procedure TForm1.FormShow(Sender : TObject);
var
     Sec, Min, Hour, Day: Int64;
     Hq: Hquery;
     HC: HCounter;
     UpT: PDH_FMT_COUNTERVALUE;
     S: String;
     buf : LPTSTR;
     nSize : dword;

 function GetCurrentMachine : string;
  var lpBuffer : LPTSTR;
      nSize : dword;
      s : string;
  begin
  s := '';
  lpBuffer := StrAlloc(255);
  nSize := strbufsize(lpBuffer)-1;
  if (nSize <> 254) then
    messageDlg('Bad nSize in GetCurrentUser '+IntToStr(nSize),mtError,[mbok],0)
  else
    fillchar(lpBuffer^,nSize+1,0);
  if GetComputerName(lpBuffer,nSize) then
    S := lpBuffer;
  StrDispose(lpBuffer);
  GetCurrentMachine := s;
  end;

begin

 Buf := StrAlloc(255);
 nSize := strbufsize(Buf);
 fillchar(buf^,nSize,0);
 S := '\\'+GetCurrentMachine+'\System\System Up Time'#0#0;
 move(S[1],buf^,length(s));
 if PDHOpenQuery(Nil,0,@Hq) <> 0 then MessageDlg('openquery failed',mterror,[mbok],0);
 if PdhAddCounter(Hq,buf,0,@HC) <> 0 then MessageDlg('AddCounter failed',mterror,[mbok],0);
 if PdhCollectQueryData(hQ) <> 0 then MessageDlg('collectquery failed',mterror,[mbok],0);
 if PdhGetFormattedCounterValue(hc, PDH_FMT_LARGE,NIl,@UpT) <> 0 then MessageDlg('fmtvaluequery failed',mterror,[mbok],0); //uptime in seconds
 if PdhCloseQuery(Hq) <> 0 then MessageDlg('closequery failed',mterror,[mbok],0);
 StrDispose(Buf);

  Sec:= UpT.largeValue shr 32;  // upper 32 bits are seconds lower are frequency
  Min:= Sec div 60;
  Hour:= Min div 60;
  Day:= Hour div 24;
  S := IntToStr(Day)+' Days '+IntToStr(Hour mod 24)+':'+IntToStr(Min mod 60)+':'+IntToStr(Sec mod 60);
  MessageDlg(S,mtInformation,[mbOK],0);

End;
Unlock the solution to this question.
Join our community and discover your potential

Experts Exchange is the only place where you can interact directly with leading experts in the technology field. Become a member today and access the collective knowledge of thousands of technology experts.

*This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

OR

Please enter a first name

Please enter a last name

8+ characters (letters, numbers, and a symbol)

By clicking, you agree to the Terms of Use and Privacy Policy.