1

My app's external processes each poll a boolean to see if there is work to do. I now want to check that variable in a Timer event to cut down the 'not responding' messages.

A Timer object has to be placed on a form, which seems to cause some limitations.

Launching the event from a form with Me.Show (vbModal) works great. Only I don't want to actually show that form, I just want to use the timer. Trying to hide the form using Me.Hide then loses the Modal behavior which I need, so that's not a good workaround.

I tried launching the event from a class but it exhibits the same unwanted behavior as Me.Hide: Processing returns to the caller rather than staying in the timer event sub waiting for work.

Is there any way to implement an event based on Timer which doesn't require showing a form and does not immediately return to the caller? The external processes have no screen IO and none is desired.

StayOnTarget
  • 11,743
  • 10
  • 52
  • 81
  • 1
    Seems that you're not understanding the differences and contexts between blocking call and asynchronous call protocols and conventions. If you don't block the caller (via, for instance, a modal form invocation), then of course execution will return to the caller. That's the goal, actually. Since you don't want to display the modal form, you'll need to redesign the caller code so that it expects to receive execution back, but it suspends it's execution until the asynchronous tasks (run via timer) is completed. Etienne's answer and links provide guidance for how to do that in VB6. – MarkL Sep 11 '20 at 13:57
  • Yes, I suppose it is a matter of my expectations being off. Originally I was hoping that there was an easy way to make a form hidden And modal. Having to handle the process after it launched the work loop (thread?) and returned to the caller seemed strange. So now we have (a thread, I guess, that was launched to do the work) but I still need to handle the ret. I suppose the 'thread' actually doing the work occupies the same memory space as the child process (that just returned) so maybe best to just let him provide DoEvents for the worker 'thread' to use. Thanks for your comment. -Bob – user14257002 Sep 11 '20 at 23:32
  • A while back I downloaded and studied a demo created by the estimable Karl E. Peterson, ccrpTmr6.zip. It does a very good job of showing the diff for blocking vs non-blocking in the context of timer event handling. BTW, I think it possible that he be ye and ye be he. So, if I start the demo from sub Main() and put Me.Show in the Form_Load sub, event processing appears to start after the return from Form_Load. However if I put Me.Show(vbModal) in Form_Load, processing begins right there and does not return to the caller. This is the 'not return to caller' behavior that I'd like to emulate. -Bob – user14257002 Sep 12 '20 at 12:19

1 Answers1

1

You can look into using the SetTimer function from the Windows API. Add the following code in a Module and user TimerStart with an interval in milliseconds:

Private Declare Function SetTimer Lib "user32" (ByVal hwnd As Long, ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long

Public Function TimerStart(ByVal p_lngInterval As Long) As Long
    
    ' Start timer
    TimerStart = SetTimer(0, 0, p_lngInterval, AddressOf TimerCallBack)

End Function

Public Sub TimerCallBack(ByVal hwnd As Long, ByVal uMsg As Long, ByVal idEvent As Long, ByVal dwTime As Long)
    
    ' Handle your polling here

End Sub

You do not need a Form so this should suit your needs.

You can also use Sleep from the Windows API in conjuction with Timer to keep your EXE running:

Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)

Public Sub Main()

    TimerStart 2000
    
    Do While True
        DoEvents
        Sleep 1000
    Loop
    
End Sub

See these other answers for more details:

Étienne Laneville
  • 4,697
  • 5
  • 13
  • 29
  • Thanks, Etienne. This worked better than anything I've tried so far! But for some reason it pops out of TimerCallBack. Oddly, it does so after successfully completing 3 to 5 work tasks. I wish I knew what made it return to caller. I set interval to 10. Any ideas would be Greatly appreciated. BTW, thanks for the quick reply. -Bob – user14257002 Sep 10 '20 at 23:01
  • After the TimerCallBack code is complete it will exit, yes. Maybe you don't even need a timer, you just want to keep looking for tasks right? In that case the approach of having a loop that sleeps in between tasks might be better. Perhaps you can post a new question with your code and this new problem, others might have more experience than I have with this. – Étienne Laneville Sep 11 '20 at 20:34
  • "Maybe you don't even need a timer" Yes, I'm coming around to this. I added your .DoEvents Sleep 1000 idea after the return to caller and learned something interesting. Increasing the sleep time slowed down the work process. And vice versa. Since this sleep loop in not a part of the look-for-work, do-work loop I surmise that it is these DoEvents in the sleep loop that the work loop is relying on. Echoing your point, polling could reduce CPU usage using the same technique...and with less code. Cool. Thanks again. – user14257002 Sep 11 '20 at 22:41