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
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
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
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
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...
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...
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.
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.
ASKER
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 :)
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 :)
ASKER
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?
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)
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(
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.)
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
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
ASKER
shouldn't it be numMinutes = 4 instead of 5?
because it is counted as 1 once the timer_timer event is triggered.
because it is counted as 1 once the timer_timer event is triggered.
ASKER
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!
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!
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.