Content Supported by Sourcelens Consulting
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
END
Attribute VB_Name = "CoffeeMonitor2"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = True
Option Explicit
' > For an overview of this sample application, search
' online Help for Coffee.
' > AboutCof.Txt, in the Related Documents folder of
' CoffWat2.vbp, also contains information about the sample.
' CoffeeMonitor2 class
' --------------------
' Like the CoffeeMonitor object, CoffeeMonitor2
' monitors an imaginary serial interface to a high-
' tech coffee pot, using a timer to determine
' how often to check the coffee status.
'
' Instead of raising an event when the coffee's ready,
' the CoffeeMonitor2 object invokes a call-back method
' that must be implemented by one of the client's
' classes. The call-back method is declared in the
' ICallBack class.
'
' (Since the high-tech coffee pot has not yet been
' invented, this sample application simply invokes the
' call-back method every ten seconds.)
'
' IMPORTANT: To simplify a rather complex example, the
' ICallBack class has been included in this project.
' This will NOT work in real-life systems, which usually
' go through many versions. If a second version of
' Coffee2 is made, the interface ID version number will
' be incremented, and earlier clients will NOT work with
' the new version of Coffee2. Standard interfaces like
' ICallBack should be created by themselves in small DLLs
' which can be referenced by both client and component.
' Once an interface is in use by finished applications,
' it must never be changed. For more information, search
' for "polymorphism" in Books Online.
'
' Note that the CoffeeMonitor2 class's Instancing property
' is set to PublicNotCreatable. This means that clients
' cannot create a CoffeeMonitor2; they can only get a
' reference to the shared CoffeeMonitor2 by creating a
' Connector2 object and accessing its CoffeeMonitor2
' property.
'
' Like the CoffeeMonitor class, the CoffeeMonitor2 class
' fixes the bug described in the topic "Using the Shared
' CoffeeMonitor," in "Creating an ActiveX Exe Component,"
' in Books Online, whereby multiple CoffeeMonitor objects
' could sometimes be created.
' =======================================================
' WARNING! Code-only timers are inherently dangerous
' in the Visual Basic development environment,
' becaue the system blindly calls back into your
' code until the timer is turned off with an API
' call. It's safer to use Timer controls during
' most of the development process, and only switch
' to call-back timers at the very end.
' =======================================================
Const ICN_ARRAYINCREMENT = 10
' maicnClients stores references to all the clients that have
' ------------ requested call-backs. (Note that this is
' different from the use of events in CoffeeMonitor;
' one event can be received by any number of clients, while
' call-backs must be made one by one.) An array is used,
' rather than a Collection, because Collection objects keep
' objects in Variants, resulting in late binding.
Private maicnClients() As ICoffeeNotify
Private mlngMaxClients As Long
' mXTimer holds a reference to a code-only timer that
' ------- tells CoffeeMonitor2 when to check the pot.
' Because the variable is declared WithEvents, the
' CoffeeMonitor2 object receives the XTimer object's Tick
' events (see Sub mwXTimer_Tick, below). Code for the
' XTimer object can be found in XTimers.vbp.
Private WithEvents mwXTimer As XTimer
Attribute mwXTimer.VB_VarHelpID = -1
Private Sub Class_Initialize()
' Allocate some space in the array of client objects.
mlngMaxClients = ICN_ARRAYINCREMENT
ReDim maicnClients(1 To mlngMaxClients)
'
' Create the XTimer object. When this assignment is
' made, Visual Basic connects the XTimer's Tick event
' to the mwXTimer_Tick event procedure (see below).
Set mwXTimer = New XTimer
'
' The timer is set to tick every ten seconds (10,000
' milliseconds).
mwXTimer.Interval = 10000
mwXTimer.Enabled = True
End Sub
Private Sub Class_Terminate()
Dim intCt As Integer
' It's important to disable the XTimer before releasing
' it. As described in XTimers.vbp, abandoning a
' running XTimer essentially leaks a system timer
' until XTimers.DLL finally shuts down.
mwXTimer.Enabled = False
Set mwXTimer = Nothing
'
' Release all remaining call-back clients, in case they
' released CoffeeMonitor2 without first requesting
' an end to notifications.
For intCt = 1 To mlngMaxClients
Set maicnClients(intCt) = Nothing
Next
'
Debug.Print "CoffeeMonitor2 (call-backs) terminated at " & Now
End Sub
' TellMeReady is called by a client who wants to receive a
' ----------- call-back when the coffee is ready. The
' client must implement the ICoffeeNotify interface,
' defined in the ICoffeeNotify class.
'
Public Sub TellMeReady(ByVal icn As ICoffeeNotify)
Dim lngCt As Long
' Find an opening in the array of interfaces.
For lngCt = 1 To mlngMaxClients
If maicnClients(lngCt) Is Nothing Then Exit For
Next
'
' If there were no openings, grow the array.
If lngCt > mlngMaxClients Then
mlngMaxClients = mlngMaxClients + ICN_ARRAYINCREMENT
ReDim Preserve maicnClients(1 To mlngMaxClients)
End If
'
Set maicnClients(lngCt) = icn
'
' Give the object the index of its entry, as a key for
' quick lookup when disconnection is requested.
icn.NotifyID = lngCt
End Sub
' CeaseCallBacks removes the client from the list of objects
' -------------- receiving call-back notifications, using
' the key the object was assigned when it requested
' notifications.
'
Public Sub CeaseCallBacks(ByVal icn As ICoffeeNotify)
Set maicnClients(icn.NotifyID) = Nothing
End Sub
' mwXTimer_Tick is the event procedure CoffeeMonitor2 uses
' ------------- to receive the XTimer object's Tick
' events. The name of an event procedure that's
' associated with a WithEvents variable always has the
' variable name as a prefix.
'
Private Sub mwXTimer_Tick()
Dim lngCt As Long
' (Code to test serial port omitted.)
'
On Error Resume Next
'
' The call-back method must be called for each object
' that has requested a notification.
For lngCt = 1 To mlngMaxClients
If Not maicnClients(lngCt) Is Nothing Then
maicnClients(lngCt).CoffeeReady
If Err.Number <> 0 Then
' Error &H80010005 is ignored, because it
' can be caused by the client object being
' temporarily unresponsive.
If Err.Number <> &H80010005 Then
' If a client application has closed without
' ending the notifications, remove it from
' the list.
Set maicnClients(lngCt) = Nothing
End If
'
' When On Error Resume Next is used, the
' error number must be cleared after each
' error.
Err.Number = 0
End If
End If
Next
End Sub