Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people, just like you, are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
Solved

VB6 overflow error when using function TimeGetTime of winmm.dll

Posted on 2014-10-27
7
490 Views
Last Modified: 2014-11-10
I use the function TimeGetTime to control time spans in this way (to do something all 20 s):

Public Declare Function gr_GetTime Lib "winmm.dll" _
  Alias "timeGetTime" _
  () As Long
Public StartNew as long

...

StartNew = gr_GetTime()
Do while True
  DoEvents
  If Abs(gr_GetTime() - StartNew) >= 20000 Then
    'Do something
    StartNew = gr_GetTime()
  end if '20 s later?

This works fine for at most 24.8551 days. Then an overflow error occurs because in VB6 long variagles are signed whereas timeGetTime returns unsigned dword (32 bit). So the difference between actuell time and StartNew exceeds 2,147,483,647 (the range of VB6 longs).

What can I best do to avoid this failure?
(The function's TimeGetTime >>return value wraps around to 0 every 2^32 milliseconds, which is about 49.71 days<<, so that is no problem)
see: http://msdn.microsoft.com/de-de/library/windows/desktop/dd757629%28v=vs.85%29.aspx


Thanks for help
Josef
0
Comment
Question by:joschramm
7 Comments
 
LVL 15

Expert Comment

by:ChloesDad
ID: 40406934
This may work....

Can you save the result in a double rather than a long.

Also why don't you use a timer control or use Now to return the current time and then use DateDiff to check for 20 seconds elapsing.
0
 
LVL 76

Expert Comment

by:GrahamSkan
ID: 40407228
If, for accuracy, you require a very big 'integer' type, you can use the Currency type.

It will deal with at least 14 decimal places, but will not ignore the least significant digits if they have an absolute value greater than .0001.

In fact it, as designed, it can take four (non-lossy) decimal places, so it can be arithmetically manipulated to cater for integers with 18 digits.
0
 

Author Comment

by:joschramm
ID: 40408481
Hi, ChloesDad,
>>Also why don't you use a timer control or use Now to return the current time and then use DateDiff to check for 20 seconds elapsing.<<
Because I needed a precise timer with a great range of time intervalls, which are very nice with long vars. So since years and years I use TimeGetTime in nearly all my projects. Unfortunately it never occurred to me that the said problem exists.
So I would like still to use longs. I need an encapsulating funged gr_GetTime with this features:
VB6 maps hex values to long in this way:
00000000 .. 7FFFFFFF ==> 0 .. 2147483647
80000000 .. FFFFFFFF ==> -2147483648 .. -1
I need this mapping:
00000000 .. 7FFFFFFF ==> 0 .. 2147483647
80000000 .. FFFFFFFF ==> 2147483648 .. 1
How do I achieve this?
0
Free Tool: ZipGrep

ZipGrep is a utility that can list and search zip (.war, .ear, .jar, etc) archives for text patterns, without the need to extract the archive's contents.

One of a set of tools we're offering as a way to say thank you for being a part of the community.

 
LVL 16

Accepted Solution

by:
HooKooDooKu earned 500 total points
ID: 40408636
If you need numbers greater than 2 billion, you MUST move to a different data type like the other experts recommended.  Still pull back a long from the API (since you can not change that) but wrap your function call in another function that returns a different data type.

Once you've done that, you can have your wrapper function take negative numbers and modify them to the appropriate value... something like this:  (Sorry, this is off the cuff as I currently don't have a VB6 editor to check the text).

Public Function GetTimeDouble() as double
Dim lTime as long
Dim dTime as double

    lTime = gr_GetTime();
    if lTime < 0 Then
        dTime = CDbl( lTime )
        GetTimeDouble = 4294967296.0 - dTime;
    else
        GetTimeDouble = CDbl( lTime )
    end if
End Function

Open in new window


Otherwise, you have to find a way to deal with negative numbers.  Interestingly enough, your last statement above basically asks for a way to map 800000000...FFFFFFFF to make to 2147483648 to 1... which you can do by multiplying the result by negative one... except for when you have the value 80000000... since there is no way to represent positive 2147483648 with a signed long.
0
 

Author Closing Comment

by:joschramm
ID: 40408691
Hi HooKooDooKu,
thats it!
Many thanks
Josef
0
 
LVL 15

Expert Comment

by:ChloesDad
ID: 40409016
That solution is still setting the return value into a long, so will still crash when you try and put a value >= 2^31

Public Function GetTimeDouble() as double
Dim dTime as double

    dTime = CDbl(gr_GetTime());
    if dTime < 0 Then
        GetTimeDouble = 4294967296.0 - dTime;
    else
        GetTimeDouble = dTime
    end if
End Function

Open in new window

0
 

Author Comment

by:joschramm
ID: 40433122
Hi CloesDad,  you are right, of course. However: The function's TimeGetTime >>return value wraps around to 0 every 2^32 milliseconds (see above). What is more:
 if dTime < 0 Then
        GetTimeDouble = 4294967296.0 - dTime;
should be
 if dTime < 0 Then
        GetTimeDouble = 4294967296.0 + dTime;
as dTime is clearly negative.
I did some more musing and now found this solution:
Private Declare Function my_GetTime Lib "winmm.dll" _
  Alias "timeGetTime" _
  () As Long




' Procedure : gr_GetTime
' Author    : JSchramm
' Date      : 10.11.2014
' Purpose   : Wrapper to prevent Out Of Range Error through the fact that VB long values are signed,
'             whereas timeGetTime delivers unsigned word

'             Use of gr_GetTime (all times in ms):
'             lTimeStart = gr_GetTime()
'             ...
'             lSomeTimeLater = Abs(gr_GetTime() - lTimeStart)
'
'             Only in case of wraparound between two immediately following calls of my_GetTime
'             over the "magical" border of &H80000000 there is a loss of one millisecond (ms)
'---------------------------------------------------------------------------------------

Public Function gr_GetTime()
  Dim lTimeCur As Long
 
  lTimeCur = my_GetTime()
  If lTimeCur = &H80000000 Then
    'to prevent out of Range thru result of -1 * &H80000000
    'which would be greater than &H7FFFFFFF, the greatest positive long value
    lTimeCur = &H7FFFFFFF
  ElseIf lTimeCur < 0 Then
    lTimeCur = CLng(-1# * CDbl(lTimeCur))
  Else
    'lTimeCur is not changed
  End If 'special cases
  gr_GetTime = lTimeCur
 
End Function
0

Featured Post

Free Tool: Path Explorer

An intuitive utility to help find the CSS path to UI elements on a webpage. These paths are used frequently in a variety of front-end development and QA automation tasks.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

There are many ways to remove duplicate entries in an SQL or Access database. Most make you temporarily insert an ID field, make a temp table and copy data back and forth, and/or are slow. Here is an easy way in VB6 using ADO to remove duplicate row…
Most everyone who has done any programming in VB6 knows that you can do something in code like Debug.Print MyVar and that when the program runs from the IDE, the value of MyVar will be displayed in the Immediate Window. Less well known is Debug.Asse…
Get people started with the process of using Access VBA to control Outlook using automation, Microsoft Access can control other applications. An example is the ability to programmatically talk to Microsoft Outlook. Using automation, an Access applic…
Get people started with the utilization of class modules. Class modules can be a powerful tool in Microsoft Access. They allow you to create self-contained objects that encapsulate functionality. They can easily hide the complexity of a process from…

808 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question