Solved

Help using the timer control

Posted on 2001-08-24
27
196 Views
Last Modified: 2008-02-01
Hi All!

I'm trying to write an SMS gateway program that supports more than 1 GSM Modems. So for example if i have 2 GSM Modems installed at comm port 1 and 2.

I need to check these 2 modems whether there's any new messages... so my method was to create 2 timer objects using late binding that will poll each of the modem

My question is, will these two timer events run concurrently or will it process the modem1 first followed by modem 2??

Thanks
0
Comment
Question by:boonhui78
  • 8
  • 5
  • 5
  • +6
27 Comments
 
LVL 1

Expert Comment

by:kdg2000
Comment Utility
The events can be fulfilled together, only timer events it is necessary start in separate streams.
0
 
LVL 4

Expert Comment

by:wile_e_coyote
Comment Utility
The two timer events will run concurrently.  Assuming that the "interval" property of both timers is equal, the timers will PROBABLY fire in the order in which they are enabled.  e.g. If timer1 is enabled first, then timer1's event will probably fire first.  However, your code should not depend on the order in which the timers fire.  

I assume that your goal is to ensure that your program gives each modem the same amount of attention.  It appears to me that your solution accomplishes that goal.
0
 
LVL 3

Accepted Solution

by:
adg earned 50 total points
Comment Utility
Depends on what you mean by concurrently.  As wile_e_coyote mentions the timers will "run" concurrently but unless you are multi-threading, (which is tricky in VB6) only one port will be processed at a time.  If your timer kicks off processing on com1, processing on com2 won't run concurrently. It will wait for the processing on com1 to complete unless you explictly allow it.  The easy way to do this is with the "doevents" statement which is very slow and will probably cause you to drop data.  

If you are looking for decent thruput, I don't think this is a good solution.  
0
 
LVL 4

Expert Comment

by:wile_e_coyote
Comment Utility
Ooops!  Mea culpa: adq is correct.  Once one of the timer events fires, the code in the associated event handler is going to run to completion before the other timer event fires. As adq said DoEvents is a crude, but dangerous way around this.  Unfortunately, there isn't a real easy way to multi-thread in VB.

Next time, I'll wait for the caffine to take effect before I answer the question <g>
0
 
LVL 3

Expert Comment

by:adg
Comment Utility
Thanks wile_e_coyote!

boonhui78, here is a small example to illustrate.  You can see that timer2 events are triggered but not actually processed until the next second (cause timer1 process is being greedy).

Option Explicit
Private Sub Command1_Click()
    Timer1.Enabled = False
    Timer2.Enabled = False
End Sub
Private Sub Form_Load()
    Timer1.Interval = 400
    Timer2.Interval = 100
End Sub
Private Sub Timer1_Timer()
    Dim s As String
    Debug.Print "Timer1 " & Time$
    s = Time$
    Do While s = Time$ ' hold control until next second
    Loop
End Sub
Private Sub Timer2_Timer()
    Debug.Print "Timer2 " & Time$
End Sub
0
 
LVL 16

Expert Comment

by:Richie_Simonetti
Comment Utility
I think if you want two timer working concurrently, you could create the two timers with API:

http://support.microsoft.com/support/kb/articles/q180/7/36.asp?FR=0



0
 
LVL 12

Expert Comment

by:roverm
Comment Utility
Richie is correct: You can only do this running API's:

place this in a module:

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

Public Timer1 As Long
Public Timer2 As Long

Public Sub TimerProc1()
    If Len(Form1.Label1.Caption) > 0 Then
        Form1.Label1.Caption = ""
    Else
        Form1.Label1.Caption = "Timer 1"
    End If
End Sub

Public Sub TimerProc2()
    If Len(Form1.Label2.Caption) > 0 Then
        Form1.Label2.Caption = ""
    Else
        Form1.Label2.Caption = "Timer 2"
    End If
End Sub

and this in a form:

Private Sub Command1_Click()
    Timer1 = SetTimer(0, 0, 100, AddressOf TimerProc1)
    Timer2 = SetTimer(0, 0, 60, AddressOf TimerProc2)
End Sub

Private Sub Form_Unload(Cancel As Integer)
    KillTimer 0, Timer1
    KillTimer 0, Timer2
End Sub

Place a commandbutton (Command1), and 2 labels (Label1 and Label2) on the form and run.

NOTE!!!: Do NOT end the program using the VB-IDE but using the close button of the form. Then the timers are killed !!

D'Mzzl!
RoverM
0
 
LVL 3

Expert Comment

by:adg
Comment Utility
roverm, I don't think that works any better because it is still in a single-thread. I took your code and modified timerproc1 to hold control until the next second (as in my earlier example).  Timerproc2 is not kicked off until timerproc1 releases control.  

Boonhui78, are you using the MSCOMM control?  If so, why are you polling the ports? You could eliminate a layer of complexity by letting the control trigger the processing rather than trying to set timers.  Of course, you'll still be single-threading but that might not be a problem depending on your processing requirements...
0
 
LVL 3

Expert Comment

by:adg
Comment Utility
Roverm, just out of curiosity, what does d'Mzzl mean?
0
 
LVL 12

Expert Comment

by:roverm
Comment Utility
Boonhui78:
I think the to timers will work this way. It's just the way that you handle the events.
If you do lengthy coding in the Timer_Event then you could run into a problem, running a single thread.
But if you set a boolean whenever a GSM-call is detected and handle it in the normal code you should get a better result.
But, adg, I agree, to really do this right, you have to runs several threads.

D'Mzzl! (adg: GoodBye!)
RoverM
0
 

Author Comment

by:boonhui78
Comment Utility
So is any1 able to show me a simple example to do this using threads?? btw i heard that vb has a very bad support for threads and it's very unsafe... is it true??

thanks,
eric
0
 
LVL 16

Expert Comment

by:Richie_Simonetti
Comment Utility
Please, don't use "fake" multithread.
Take a look at:
http://www.desaware.com/articles/threadingL3.htm
0
 
LVL 3

Expert Comment

by:adg
Comment Utility
Here's an example of multi-threading in VB6. It processes files, not ports, but the idea is the same.

As mentioned, multithreading in VB6 is tricky.  From what I've read, support for multithreading in VB.NET is much different (better, I hope) so be aware that this approach may not be maintainable in the long-run.  This cannot be tested from within the IDE.  You have to compile it and run it separately.  That is because the IDE is inherently single-threaded.    Note that this is an ACTIVEX EXE project not a standard EXE. See the program comments for   appropriate settings that are essential to making this work.  Note the use of timers in the ProcessFile to start the processing within the previously created threads.  Without that your threads will run sequentially.  You'll still have multiple threads (as proved by report record count which displays the thread id) but the startup of the second and third threads will be delayed.  Also note that sub main is automatically executed at the beginning of each thread (4 times in this example).  Since there is code that must only be done one time, the find window API is used to figure out if we are on the first thread or not.

All that being said, you might want to avoid the entire issue (if possible) by having an app that processes a single port and then running two instances of it.  I would recommend avoiding multi-threading in VB6 if you can.  One reason is that it is very difficult to communicate between threads without creating unintentional serializations,  it can be done but it is tricky.  (yes, even more tricky). You can spend a lot of time making VB6 do things that it doesn't do well.


' MainModule.bas

' ActiveX EXE Name = MTNF
' Startup Object = Sub Main
' Threading Model = Thread per Object
' Start Mode = Standalone
' !!! Must compile and run outside IDE !!!
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal
lpWindowName As String) As Long
Private frmMainThreadForm As MainThreadForm
Private Const PROC_CAPTION = "MTNF Main Thread Form"
Private Thread1  As ProcessFileObject

Sub Main()
   If 0 = FindWindow(vbNullString, PROC_CAPTION) Then
       'MsgBox "Main = " & Hex(App.ThreadID)
       Set frmMainThreadForm = New MainThreadForm ' create first form on pre-existing thread
       frmMainThreadForm.Caption = PROC_CAPTION
       Set frmMainThreadForm = Nothing 'don't need this reference, form loads itself and stays alive
until unloaded
   End If
End Sub

Public Sub CreateThreads()
  Set Thread1 = CreateObject("MTNF.ProcessFileObject")
  Thread1.ProcessFile "a.txt"
  '
  Set Thread1 = CreateObject("MTNF.ProcessFileObject")
  Thread1.ProcessFile "b.txt"
  '
  Set Thread1 = CreateObject("MTNF.ProcessFileObject")
  Thread1.ProcessFile "c.txt"
  Set Thread1 = Nothing
End Sub

Public Sub ReportRecordCount(FileName As String, RecCount As Long)
   MsgBox FileName & " contains " & CStr(RecCount) & " records. Reported by Thread " & Hex(App.ThreadID)
End Sub

==============================================
' MainThreadForm.frm

Private Sub Form_Initialize()
    Timer1.Interval = 100
End Sub

Private Sub Timer1_Timer()
   Timer1.Enabled = False
   CreateThreads
   Unload Me
End Sub

==============================================
' ProcessFileObject.cls

Private frmTimerForm As TimerForm
   
Private Sub Class_Initialize()
   'MsgBox "Worker = " & Hex(App.ThreadID)
End Sub

Public Sub ProcessFile(FileName As String)
   Set frmTimerForm = New TimerForm
   frmTimerForm.SetOwner Me
   frmTimerForm.StartTimer FileName
End Sub

Public Sub RecordCount(FileName As String)
   ' add project reference to Microsoft Scripting Runtime
   ' to use File System Objects
   Dim fso As New FileSystemObject
   Dim fil1 As File
   Dim ts As TextStream
   Set fil1 = fso.GetFile(FileName)
   Set ts = fil1.OpenAsTextStream(ForReading)
   ts.ReadAll
   ReportRecordCount FileName, ts.Line
   ts.Close
   Set frmTimerForm = Nothing
End Sub
==============================================
' TimerForm.frm

Private objOwner As ProcessFileObject
Private strFileName As String

Public Sub SetOwner(objRef As ProcessFileObject)
   Set objOwner = objRef
End Sub

Public Sub StartTimer(FileName As String)
   strFileName = FileName
   Timer1.Interval = 55
End Sub

Private Sub Timer1_Timer()
   Timer1.Enabled = False
   objOwner.RecordCount (strFileName)
   Set objOwner = Nothing
   Unload Me
End Sub
0
Highfive + Dolby Voice = No More Audio Complaints!

Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

 
LVL 16

Expert Comment

by:Richie_Simonetti
Comment Utility
One thing comes to  me:
Encapsulate code posted by Roverm and Me in an actice x dll, compile it and refrence in your project.
Create TWO instances of it as:

dim withevents Tmr1 as ClsTimer
dim withevents Tmr2 as ClsTimer

form_load
set Tmr1=new ClsTimer
set Tmr2=new ClsTimer
end sub

Those withevents are timer_timer event that belongs to Windowproc from the bas module.
I hope it works.
0
 
LVL 3

Expert Comment

by:adg
Comment Utility
I think that code in a DLL (activex or otherwise) will always run on the calling thread.  So if the calling application is single-threaded, using unique instances of a DLL won't be an improvement in that it will still be single-threaded.  I've never actually tried it though - just an opinion.  
0
 
LVL 16

Expert Comment

by:Richie_Simonetti
Comment Utility
They will run in same memory space, same thread? i don't know. Never did a try.
0
 
LVL 3

Expert Comment

by:adg
Comment Utility
Yeah, I haven't tried it either but my understanding is that when creating an object from a DLL, the object is always in the same process and same thread as the caller regardless of the location of the DLL.  But creating an object from an ActiveX EXE can be same thread, or same process different thread, or even a different process.  And if the object is created in a different process it can even be on a different machine which I find amazing.
0
 
LVL 16

Expert Comment

by:Richie_Simonetti
Comment Utility
so, creating an active x exe...?
0
 
LVL 3

Expert Comment

by:adg
Comment Utility
so creating an active x exe, like the one I posted above, allows creating multiple threads in the same process without API calls.
0
 
LVL 12

Expert Comment

by:roverm
Comment Utility
This is for using the COMM ports (up to 4 !) WITHOUT the MSComm control:

http://www.thescarms.com/vbasic/CommIO.asp

D'Mzzl!
RoverM
0
 
LVL 4

Expert Comment

by:wile_e_coyote
Comment Utility
boonhui78

I suggest you reject my bogus answer and unlock the question so that other people have the opportunity to post their comments as answers.  Sorry about anwering so quickly (and inaccurately <g>)
0
 

Author Comment

by:boonhui78
Comment Utility
Hi RoverM!

It's ok =)
Everyone here is learning rite??

Eric
0
 
LVL 12

Expert Comment

by:roverm
Comment Utility
boohui78:
>>It's ok ??
What's ok ? Is your problem solved ?
0
 
LVL 12

Expert Comment

by:roverm
Comment Utility
boohui78:
You did post a request for deletion.
Is your problem solved? Did any expert help you into the right direction ? If so: Can you close it by grading ?
Or can we PAQ it ?

D'Mzzl!
RoverM
0
 
LVL 3

Expert Comment

by:modder
Comment Utility
monitoring this question

modder
Community Support Administrator @ Experts-Exchange
0
 
LVL 49

Expert Comment

by:DanRollins
Comment Utility
Hi boonhui78,
It appears that you have forgotten this question. I will ask Community Support to close it unless you finalize it within 7 days. I will ask a Community Support Moderator to:

    Accept adg@hp's comment(s) as an answer.

boonhui78, if you think your question was not answered at all or if you need help, just post a new comment here; Community Support will help you.  DO NOT accept this comment as an answer.

EXPERTS: If you disagree with that recommendation, please post an explanatory comment.
==========
DanRollins -- EE database cleanup volunteer
0
 
LVL 5

Expert Comment

by:Netminder
Comment Utility
Per recommendation, force-accepted.

Netminder
CS Moderator
0

Featured Post

Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

Join & Write a Comment

Introduction While answering a recent question (http://www.experts-exchange.com/Q_27402310.html) in the VB classic zone, I wrote some VB code in the (Office) VBA environment, rather than fire up my older PC.  I didn't post completely correct code o…
Enums (shorthand for ‘enumerations’) are not often used by programmers but they can be quite valuable when they are.  What are they? An Enum is just a type of variable like a string or an Integer, but in this case one that you create that contains…
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…
This lesson covers basic error handling code in Microsoft Excel using VBA. This is the first lesson in a 3-part series that uses code to loop through an Excel spreadsheet in VBA and then fix errors, taking advantage of error handling code. This l…

728 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

Need Help in Real-Time?

Connect with top rated Experts

8 Experts available now in Live!

Get 1:1 Help Now