Link to home
Start Free TrialLog in
Avatar of chrislee8
chrislee8

asked on

run function at a interval duration

I have an VB app.
the App run a general function "GenFunction"

"GenFunction" call other functions to complete tasks.

say like the GenFunction take about 3-5 minutes to finish.

now, I want to run the GenFunction every 5 minutes exact.

2 questions:
1. how to run the GenFunction every 5 minutes.

2. what if the previous Genfunction call longer than 5 minutes and the next call is about to start? how to avoid this?

I can't make the interval duration longer, 'cause I don't know how long the previous call will actually take.

I want to have the flexibility to run the second call after the previous one is done.

simplify it:

if 1st call is < 5 min, run 2nd in 5 min.
if 1st call is > 5 min, run 2nd after the 1st is done.

am I asking too much, or it is just very simple to do?

Thanks
ASKER CERTIFIED SOLUTION
Avatar of bobbit31
bobbit31
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of TravisHall
TravisHall

bobbit31's answer is good. There more elegant techniques for getting something to occur at intervals greater than five minutes, but then often elegance does and should take second place to just getting the thing working. I can probably find you a bit of relevant code for that on Monday, if you want it.

A sometimes-useful variation, though, is to have GenFunction set its next execution time as the last thing it does. Again, say if you want to see some code, because I think I've got a little at work.
Hi,

You can also use the system timer :

Dim start as Single

Private Sub Form_Load()

again:
start = Timer

Call GenFunction

Do While Timer <= start + 300   '= 5 minutes'
DoEvents
Loop
GoTo again

End Sub

cheers Marco
Sorry, there is a bug in my code: the Timer function is reset to zero at midnight, therefore the routine I wrote get an endless loop, if you run it less than 5 minutes before midnight (it's Dracula's time ..:-))

To avoid this problem :

Dim start As Single
Private Const MIDN = 86399
Dim startM As Single


Private Sub Form_Load()

again:
start = Timer + 300 'set 5 minutes timing'
If start >= MIDN Then startM = start - MIDN: start = MIDN

Call GenFunction


Midnight:
Do While Timer <= start
DoEvents
Loop

If Timer >= MIDN Or Timer < 2 Then
start = startM
GoTo Midnight
End If

GoTo again
End Sub

Of course you can put this code in a subroutine and not in the form_load event or use a flag setting and not a direct call of your function...

 
Avatar of chrislee8

ASKER

hi, everyone, thanks for the inputs.

I will be working on it today to find the right way, I am still open for any suggestion.

I will be testing the codes to see which one fits my position the best.

TravisHall, please post some codes, i love to learn more in different situation.

Thanks again.
hi, marco and bobbit

does the timer get reset to 0 at midnight by design, or it is only for the function wrote by marco?

bobbit, your sample directs me to the right way, after little mods, I am able to have my app working. Thank you

I am running a test tonight, if it works through midnight, I should grant you the points.

hehe :)
oh, no

I miss a point. :(

how can I put the timer only work with a function instead of form_load or main()?

I want the timer starts ticking after I hit a button or I call a function.

sorry, any suggestion to that?
Well, the most relevant chunk of code I could think of is the code I use to run a clock in an app - time changing once a second, pretty much on the second. Why not just use a timer control? Because I needed to develop the technique so I could code some things that the timer wouldn't quite do right.

BTW, this code creates its own thread. The VB design environment hates that, and generally crashes if you try it in debug mode, but the code works in a compiled version. I don't let this code run in debug mode.

Public Declare Function CreateWaitableTimer Lib "kernel32" Alias "CreateWaitableTimerA" (ByVal lpSemaphoreAttributes As Long, ByVal bManualReset As Long, ByVal lpName As Long) As Long
Public Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Function SetWaitableTimer Lib "kernel32" (ByVal hTimer As Long, lpDueTime As FILETIME, ByVal lPeriod As Long, ByVal pfnCompletionRoutine As Long, ByVal lpArgToCompletionRoutine As Long, ByVal fResume As Long) As Long
Private Declare Function MsgWaitForMultipleObjects Lib "user32" (ByVal nCount As Long, pHandles As Long, ByVal fWaitAll As Long, ByVal dwMilliseconds As Long, ByVal dwWakeMask As Long) As Long
Private Const WAIT_ABANDONED_0& = &H80&
Private Const WAIT_OBJECT_0& = 0
Private Const WAIT_TIMEOUT& = &H102&
Private Const QS_HOTKEY& = &H80
Private Const QS_KEY& = &H1
Private Const QS_MOUSEBUTTON& = &H4
Private Const QS_MOUSEMOVE& = &H2
Private Const QS_PAINT& = &H20
Private Const QS_POSTMESSAGE& = &H8
Private Const QS_SENDMESSAGE& = &H40
Private Const QS_TIMER& = &H10
Private Const QS_MOUSE& = (QS_MOUSEMOVE _
                            Or QS_MOUSEBUTTON)
Private Const QS_INPUT& = (QS_MOUSE _
                            Or QS_KEY)
Private Const QS_ALLEVENTS& = (QS_INPUT _
                            Or QS_POSTMESSAGE _
                            Or QS_TIMER _
                            Or QS_PAINT _
                            Or QS_HOTKEY)
Private Const QS_ALLINPUT& = (QS_SENDMESSAGE _
                            Or QS_PAINT _
                            Or QS_TIMER _
                            Or QS_POSTMESSAGE _
                            Or QS_MOUSEBUTTON _
                            Or QS_MOUSEMOVE _
                            Or QS_HOTKEY _
                            Or QS_KEY)

Private Type SYSTEMTIME
    wYear As Integer
    wMonth As Integer
    wDayOfWeek As Integer
    wDay As Integer
    wHour As Integer
    wMinute As Integer
    wSecond As Integer
    wMilliseconds As Integer
End Type
Private Type FILETIME
    dwLowDateTime As Long
    dwHighDateTime As Long
End Type

Private Declare Function SystemTimeToFileTime Lib "kernel32" (lpSystemTime As SYSTEMTIME, lpFileTime As FILETIME) As Long
Private Declare Function LocalFileTimeToFileTime Lib "kernel32" (lpLocalFileTime As FILETIME, lpFileTime As FILETIME) As Long
Private Declare Function FileTimeToSystemTime Lib "kernel32" (lpFileTime As FILETIME, lpSystemTime As SYSTEMTIME) As Long
Private Declare Function FileTimeToLocalFileTime Lib "kernel32" (lpFileTime As FILETIME, lpLocalFileTime As FILETIME) As Long
Private Const INFINITE = &HFFFF


Public Sub ClockLoop(ByRef hTimer As Long)
Dim lRet As Long
Dim st As SYSTEMTIME
Dim ft As FILETIME
Dim d As Date

d = Now
frmEditor.SetClock d
d = DateAdd("n", 1, d)
DateToSystemTime d, st
st.wSecond = 0
SystemTimeToFileTime st, ft
LocalFileTimeToFileTime ft, ft

SetWaitableTimer hTimer, ft, 0, 0, 0, 0

Do
    lRet = MsgWaitForMultipleObjects(1, hTimer, 0, INFINITE, QS_ALLEVENTS Or QS_ALLINPUT)
    Select Case lRet
    Case WAIT_OBJECT_0
        UTCFileTimeToDate ft, d
        frmEditor.SetClock d
        d = DateAdd("n", 1, d)
        DateToUTCFileTime d, ft
        SetWaitableTimer hTimer, ft, 0, 0, 0, 0
    Case WAIT_OBJECT_0 + 1
        DoEvents
    End Select
Loop
End Sub

Private Sub DateToSystemTime(d As Date, st As SYSTEMTIME)
st.wDay = Day(d)
st.wDayOfWeek = WeekDay(d)
st.wHour = Hour(d)
st.wMilliseconds = 0
st.wMinute = Minute(d)
st.wMonth = Month(d)
st.wSecond = Second(d)
st.wYear = Year(d)
End Sub

Private Sub DateToUTCFileTime(d As Date, ft As FILETIME)
Dim st As SYSTEMTIME

st.wDay = Day(d)
st.wHour = Hour(d)
st.wMilliseconds = 0
st.wMinute = Minute(d)
st.wMonth = Month(d)
st.wSecond = Second(d)
st.wYear = Year(d)
SystemTimeToFileTime st, ft
LocalFileTimeToFileTime ft, ft
End Sub

Private Sub UTCFileTimeToDate(ft As FILETIME, d As Date)
Dim st As SYSTEMTIME
FileTimeToLocalFileTime ft, ft
FileTimeToSystemTime ft, st
d = DateSerial(st.wYear, st.wMonth, st.wDay) + TimeSerial(st.wHour, st.wMinute, st.wSecond)
End Sub


And kick it off with...
Dim lThreadID As Long
mhClockThread = CreateThread(0, 0, AddressOf mdlTimers.ClockLoop, mhClockTimer, 0, lThreadID)
As for getting your timer to work only after hitting a button or calling a function, just use the Enabled property of the control. Set it to False at design-time, and use code like:

Private Sub cmdStart_Click()
    Timer1.Enabled = True
End Sub

(And them's Bobbit's points, don't try to award me points just for pointing out the Enabled property.)
Hi chrislee8,

the reset to 0 is only for the Timer function I used in my code. The Timer control used by bobbit doesn't suffer of the dracula's syndrome.:-)) The limitation of this control is the max timing interval of one timing cycle = 64.000 msec = approx 1 minute.

However I tested successfully the second code I posted for you and it worked fine also trhought midnight. (You can adjust your computer date and time close to midnight and  then test your app).

<<I want the timer starts ticking after I hit a button or I call a function>>

Add a command button (Command1) to your form and put the code in a click event routine:

Private Sub Command1_Click()

again:
start = Timer + 300 'set 5 minutes timing'
If start >= MIDN Then startM = start - MIDN: start = MIDN

Call GenFunction

Midnight:
Do While Timer <= start
DoEvents
Loop

If Timer >= MIDN Or Timer < 2 Then
start = startM
GoTo Midnight
End If

GoTo again


End Sub


When you click the command button your GenFunction and the 5 minutes timing will start.
You can also put the code in a subroutine and call it from everywhere.( Don't forget to declare the constant and variables showed at the beginning of the code)


If you use the Timer control as suggested by bobbit, set the Timer enabled property to false ( Timer is not running) and add a command button :

Private Sub Command1_Click()

Timer1.Enabled = True ' Timer and your function start'

End sub


shouldn't it be numMinutes = 4 instead of 5?

because it is counted as 1 once the timer_timer event is triggered.

travis, marco

Thanks for your inputs, your codes are definitely valuable in the future for me. I am sure to study them and make them alive...

:)
have a good day!