Time functions (GetTickCount) in VB6

Dear experts!

I use widely this function

Public Declare Function GetTickCount Lib "kernel32" () As Long

It works fine. But it limit me to appx 25 days in which I should ask customers using my app to restart his Windows. The question is – how I can make this time longer – much longer – twice or even 10 times bigger than 25 days.

 As I read in fact this id DWord

DWORD WINAPI GetTickCount(void);

So look that I loose about half of possible time with get it like Long. SO may be the question is:

How to get DWord via the function? I can use Currency type in VB6. Actually I make this attempt:

Public Declare Function GetTickCount Lib "kernel32" () As Currency

Msgbox GetTickCount * 10000

And this works in Win 7 32 bits, but under Xp 32 bits I get wrong value (unreal big value in start of software)

I see that there is function

GetTickCount64 - i get its value via Currency in VB6 but under XP it get – Can’t find entry point.

So I see the solution in 2 direction:
1.      Te get the value like DWord (twice more than Long) or better – in Currency
2.      Or do I have options program to set to 0 this timer?

dvplayltdAsked:
Who is Participating?
 
HooKooDooKuCommented:

With GetTickCount(), you are running into a problem with signed verses unsigned values.

In VB, a Long is a 32 bit signed number.  But GetTickCount() returns a DWORD which is an unsigned number.

It is still possible for you to read the value from GetTickCount() in VB6, but it takes some computer trickery.

Public Declare Function GetTickCount Lib "kernel32"() As Long
Public Function MyGetTickCount() as Currency
Dim Count as Long
  Count = GetTickCount()
  MyGetTickCount = Count
  If MyGetTickCount < 0 Then
    MyGetTickCount = MyGetTickCount + 4294967296#
  End If
End Function

Even if GetTickCount() returns a value greater than the largest possible signed value for a Long, the number will still be valid.  But in VB it will appear as a negative number.  To properly interpret this negative number, you simply have to add 4294967296 to it (which is the largest possible unsigned value for a Long).  However, before we can do that, we must first copy the value from a Long to a Currency that can deal with values as big as (and bigger) than an unsigned 32 bit number.

As for GetTickCount64(), that function is only valid for Windows Vista and higher.
0
 
lomo74Commented:
your attempts at casting the return value of GetTickCount to a Currency are completely useless.
the function is supposed to return a DWORD; even if you assign it to a Currency, you can't obtain a value larger that 2^32.
moreover, this declaration is completely wrong:
>> Public Declare Function GetTickCount Lib "kernel32" () As Currency
this way, you tell the compiler to treat the return value of GetTickCount as if it was a Currency, whereas it is not; hence the "unreal big values" you are getting.

having said that -- the solution may depend on what's your goal.
what do you need, exactly? what resolution do you need: second, millisecond, or even higher? what are you measuring? (e.g. your application uptime? the whole system uptime? a time interval between two events?).

you can use performance counters; but they are hard to use.
or you can use high resolution timers; but, again, it depends on what's exactly your goal.
0
 
dvplayltdAuthor Commented:
To lomo74

In you answer have more questions than in main. How do you expect to earn the point ? :-)

To HooKooDooKu

Well, your solution look  fine. But I need 2 things before give u the points:

1. You solution look like will double the time . Fine. You are sure that values bigger than long are get in nagative direction or you guest this? Beacouse if this is not correct I'll understand this after 25 days gone ...a long period ;-). Please confirm that you are use this in some of your projects, or do real test.

2. Well, i can detect OS and under Win 7 to use GetTickCount64 . Is it OK then following declaration:

Public Declare Function GetTickCount64 Lib "kernel32" () As Currency

and later to get the values in:
Msgbox GetTickCount64 * 10000





0
Ultimate Tool Kit for Technology Solution Provider

Broken down into practical pointers and step-by-step instructions, the IT Service Excellence Tool Kit delivers expert advice for technology solution providers. Get your free copy now.

 
lomo74Commented:
well, I ask questions because I care about your problem :-)
and, no: HooKooDooKu' solution won't work.
it will stop after 49.7 days, as clearly stated here: http://msdn.microsoft.com/en-us/library/windows/desktop/ms724408%28v=vs.85%29.aspx
because this is a limitation of the API and there's nothing you can do about it.

GetTickCount counts milliseconds starting from the system boot up, and put it in a DWORD whose max value is 2^32 (= 4,294,967,296).
So: 4,294,967,296 ms = 4,294,967 seconds = 1,193 hours = 49.7 days. Just math.

look at this, instead:
Private Type PDH_FMT_COUNTERVALUE
    CStatus As Long
    pad1 As Long
    longValue As Long
    pad2 As Long
End Type

Private Declare Function PdhOpenQuery Lib "Pdh.dll" Alias "PdhOpenQueryA" ( _
    ByVal szDataSource As String, _
    ByVal dwUserData As LongPtr, _
    ByRef phQuery As Long) As Long
Private Declare Function PdhAddCounter Lib "Pdh.dll" Alias "PdhAddCounterA" ( _
    ByVal hQuery As Long, _
    ByVal szFullCounterPath As String, _
    ByVal dwUserData As LongPtr, _
    ByRef phCounter As Long) As Long
Private Declare Function PdhCollectQueryData Lib "Pdh.dll" (ByVal hQuery As Long) As Long
Private Declare Function PdhGetFormattedCounterValue Lib "Pdh.dll" ( _
    ByVal hCounter As Long, _
    ByVal dwFormat As Long, _
    ByRef lpdwType As Long, _
    ByRef pValue As PDH_FMT_COUNTERVALUE) As Long
Private Declare Function PdhCloseQuery Lib "Pdh.dll" (ByVal hQuery As Long) As Long

Const PDH_FMT_LONG = &H100

Public Function UpTime() As Long
    Dim hQuery As Long
    Dim hTotal As Long
    Dim v As PDH_FMT_COUNTERVALUE
    Dim dwType As Long
    Dim ret As Long
    
    ret = PdhOpenQuery(vbNullString, 0, hQuery)
    ret = PdhAddCounter(hQuery, "\System\System up time", 0, hTotal)
    ret = PdhCollectQueryData(hQuery)
    ret = PdhGetFormattedCounterValue(hTotal, PDH_FMT_LONG, dwType, v)
    UpTime = v.longValue
    ret = PdhCloseQuery(hQuery)
End Function

Open in new window


this function uses performance counters, it returns a Long but it counts seconds; so it can last as long as ~136 years.
0
 
HooKooDooKuCommented:
lomo74,

No I'm not guessing.  The 'trickery' is simply understanding how 32 bit values are interpreted.

In the case of an unsigned 32 bit number (what the API returns):
0000 0000 0000 0000 0000 0000 0000 0001 = 1
0000 0000 0000 0000 0000 0000 0000 0010 = 2
0111 1111 1111 1111 1111 1111 1111 1110 = 2,147,483,646
0111 1111 1111 1111 1111 1111 1111 1111 = 2,147,483,647
1000 0000 0000 0000 0000 0000 0000 0000 = 2,147,483,648
1000 0000 0000 0000 0000 0000 0000 0001 = 2,147,483,649
1000 0000 0000 0000 0000 0000 0000 0010 = 2,147,483,650
1111 1111 1111 1111 1111 1111 1111 1111 = 4,294,967,295

BUT in the case of a signed 32 bit number (what VB uses)
0000 0000 0000 0000 0000 0000 0000 0001 = 1
0000 0000 0000 0000 0000 0000 0000 0010 = 2
0111 1111 1111 1111 1111 1111 1111 1110 = 2,147,483,646
0111 1111 1111 1111 1111 1111 1111 1111 = 2,147,483,647 (Largest Possible signed 32 bit number)
1000 0000 0000 0000 0000 0000 0000 0000 = -2,147,483,648 (NEGATIVE NUMBER)
1000 0000 0000 0000 0000 0000 0000 0001 =- 2,147,483,647 (NEGATIVE NUMBER)
1000 0000 0000 0000 0000 0000 0000 0010 =- 2,147,483,646 (NEGATIVE NUMBER)
1111 1111 1111 1111 1111 1111 1111 1111 = -1 (NEGATIVE NUMBER)

Notice how when you increment the binary value of the largest possible POSITIVE value, the SIGNED interpreted value flips to a negative number, specifically the largest possible negative number.  As you continue to add one to the binary value, the interpreted negative number gets smaller (moves toward zero) until finally a binary value of all ones is interpreted as -1.

That is the "computer trickery", understanding HOW 32 bit binary values are interpreted.

So when the 32 bit value is all ones, it gets interpreted as -1 by a signed Long.  By adding 4,294,967,296 to -1, you get 4,294,967,295, the largest possible value of an unsigned 32 bit value.
Also, you can see that when the 32 bit value flips to the 1st negative value, that value is -2,147,483,648.  When you add 4,294,967,296 to that number, you get 2,147,483,648... the 1st positive value AFTER the largest possible value of a signed 32 bit number.

So it's not guessing, just understanding how 32 bit numbers are interpreted.
0
 
HooKooDooKuCommented:
Sorry, messing up names...

@lomo74:

Yes, what I'm stating will work... but as you point out, it only gives you 49.7 days (which is what the OP asked for, the ability to at least double what we was getting out of GetTickCounter() within VB.

@dvplayltd,

My previous post was directed at you, NOT lomo74 (sorry).
0
 
lomo74Commented:
@HooKooDooKu:
I never said you were guessing anything about how 32 bit integers work.
The author did. He said
>> You are sure that values bigger than long are get in nagative direction or you guest this?

So... thank you for your clarification, but I know how 32 bit numbers are represented in memory.

Nonetheless, the trickery you're proposing is useless: GetTickCount returns a DWORD; you can do whatever you want with that value (put it into a Currency, turn it into an unsigned long, and so on), but you'll never be able to overcome the limitation of the GetTickCount API itself: it will restart from zero, after 49.7 days of system uptime.
0
 
dvplayltdAuthor Commented:
To lomo74

well, yes - HooKooDooKu solution will make my time double, not a perfect solution. But it will be easer to implement as I already have a HUGE code rely on this way.
And most of the code is written by other person .. this is other which make me thinks to make time double or to go to your offer ...

In any case - now you answer look much more better ;-). Sorry for my joke.

Well, i need a counter to measure time diferences in miliseconds. I save current time, then I add my duration and in reguliar period check does my file play come. Someting like that. I will check your code - just a note  -I need it in miliseconds, not in sec. Could you please correct it ? And also I need this code in C++ , however I'm sure this is not a problem.
0
 
lomo74Commented:
ok sorry,
did not see you messed up names...
we posted together I guess :-P

ok, he asked:
>> twice or even 10 times bigger than 25 days

if twice is enough, your solution is ok.
if asking for more, such as 10 times, your trickery won't work anymore.
0
 
lomo74Commented:
@dvplayltd:
>> And also I need this code in C++ , however I'm sure this is not a problem.
oh my God
I wrote it in C++, then translated to VB because it was the primary zone LoL
0
 
lomo74Commented:
@dvplayltd:
>> I need it in miliseconds, not in sec.

you can now see that my initial questions were pertinent.
good solutions come after knowledge of the problem.
"System up time" performance counter will meause uptime with 0.1 sec resolution at best on Windows 7 (and maybe less on older OSs).
it could be a good solution for a long-but-innacurate-measurement, but it is useless for your purposes.
0
 
lomo74Commented:
what the heck, it works instead. I'm damn tired, sorry --
//returns system uptime with 1ms resolution
__int64 GetTickCountEx()
{
	PDH_HQUERY uptQuery;
	PDH_HCOUNTER uptTotal;
	PDH_FMT_COUNTERVALUE counterVal;

	PdhOpenQuery(NULL, NULL, &uptQuery);
	PdhAddCounter(uptQuery, _T("\\System\\System Up Time"), NULL, &uptTotal);
	PdhCollectQueryData(uptQuery);
	PdhGetFormattedCounterValue(uptTotal, PDH_FMT_DOUBLE, NULL, &counterVal);
	PdhCloseQuery(uptQuery);
	
	return counterVal.doubleValue * 1000;
}

Open in new window

0
 
dvplayltdAuthor Commented:
10x. I will consider which solution to use, both are very interesting. To lomo74 you already have all in C++ - could you please post it ?
0
 
lomo74Commented:
here it is. I wrapped an object around the various APIs; casting it to a double, you get seconds with decimal part; casting to __int64 you get milliseconds.
#include "stdafx.h"

class CTickCounter
{
private:
	PDH_HQUERY uptQuery;
	PDH_HCOUNTER uptTotal;
	PDH_FMT_COUNTERVALUE counterVal;

	double getDoubleValue()
	{
		PdhCollectQueryData(uptQuery);
		PdhGetFormattedCounterValue(uptTotal, PDH_FMT_DOUBLE, NULL, &counterVal);
		return counterVal.doubleValue;
	}

public:
	CTickCounter()
	{
		PdhOpenQuery(NULL, NULL, &uptQuery);
		PdhAddCounter(uptQuery, _T("\\System\\System Up Time"), NULL, &uptTotal);
	}

	~CTickCounter() { PdhCloseQuery(uptQuery); }

	operator double()
	{
		return getDoubleValue();
	}

	operator __int64()
	{
		return getDoubleValue() * 1000;
	}
};

int _tmain(int argc, _TCHAR* argv[])
{
	CTickCounter tick;

	std::cout << (__int64)tick << std::endl;
	std::cout << (double)tick << std::endl;

	return 0;
}

Open in new window

obviously, you'd better create the object once in your software and use it multiple times; rather than create it everytime you need to read the tick count.
in this way, you smooth the overhead of creating the query, adding the counter object, and so on.
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.