VBcoders Guest



Don't have an account yet? Register
 


Forgot Password?



Implementing an event stack

by Philippe DesRosiers (1 Submission)
Category: OLE/COM/DCOM/Active-X
Compatability: Visual Basic 3.0
Difficulty: Intermediate
Date Added: Wed 3rd February 2021
Rating: (6 Votes)

Using DCOM? Remote instantiation? well now you can respond to events from a remote component without freezing the server app!

Rate Implementing an event stack



Implementing an event stack




Implementing an Event Stack in VB


text-autospace:none'> 


text-autospace:none'>With the advent of COM, DCOM and COM+, distributed
applications are fast becoming, indeed, have already become, a major focus for
new development tactics. It's just not enough anymore to write a puny little Access
database application and hope you won't need to implement it in a network
environment. More and more distributed applications are relying on an n-tier
model to get the job done. If you haven't yet had to deal with the increasing
demands of LAN, WAN and intranet-deployed apps, you might as well get ready,
because you'll have to eventually.


text-autospace:none'> 


text-autospace:none'>DCOM allows VB programmers to create ActiveX servers that
can run as a standalone EXE on a remote machine. Because they run
out-of-process, unlike DLLs, multiple instances of the same class (perhaps
called by multiple applications on many different machines) can be accessed all
within the same process on the server machine. The EXE loads once, supplies the
class interface to whoever needs it, and when it's no longer needed, the EXE
unloads. Actually, it's much more complex than this, but if you want to learn
DCOM, read a book.


text-autospace:none'> 


So what's the problem?


text-autospace:none'> 


text-autospace:none'>This remote instantiation is all well and good, a
revolution in computing, a watershed in distributed blah blah blah… with one
major drawback (at least for those ActiveX servers developed in VB). It's not
asynchronous. 


text-autospace:none'> 


text-autospace:none'>What does this mean? Asynchronous code is code that,
through multithreading or some other trickiness executes at the same time as
your application code. Instead of calling a method of your class to, say,
retrieve ten thousand records from an SQL database, then waiting while it
executes, then proceeding with your code, asynchronous execution would allow
you to call the method, which would return immediately, allowing you to
continue execution. In this example, when the class instance had completed
fetching the SQL result set, it would, say, raise an event to let your app know
that it was finished. If you use ADO with events, you know what I'm talking
about.


text-autospace:none'> 


text-autospace:none'>The same system works in reverse. That is, when a control
or class raises an event trapped by the parent application, before the control
code can continue executing, the application has to execute its event code. For
example, when the Click event is raised, all the event code executes before
returning execution flow control to the ActiveX control. If you don't believe
me, try it yourself. You'll never receive a MouseUp event before you finish
processing the MouseDown event.


text-autospace:none'> 


text-autospace:none'>Now, normally, with an ActiveX control or DLL, all the
code runs in-process, i.e.: your app is the only one using it, so it doesn't
really matter if the control code stops execution while it waits for the event
to return. In fact, this is probably for the best. Who wants to receive the
MouseUp Event before you finish processing the MouseDown event? But with a DCOM
server component, running on a remote machine, that code can be executed by
hundreds of users at once, could be raising dozens of events at any one time,
from any number of classes. 


text-autospace:none'> 


text-autospace:none'>If the DCOM server component raises each of those events
to your app, then has to wait (while the application executes ten thousand
lines of code) before regaining execution flow control, the server is sitting
there, waiting for you to return flow control. While you're processing the
event code in your app, your preventing the server component from doing it's
job.


text-autospace:none'> 


text-autospace:none'>This is, obviously, not a good thing.


text-autospace:none'> 


text-autospace:none'>What's the answer to this dilemma? You got it, smart guy.
An event stack.


text-autospace:none'> 


What the heck is an event stack?


text-autospace:none'> 


text-autospace:none'>Oooo. A "Stack". Scary word. Pointers.
Shades of linked lists and other murky memories from OOP theory courses you
slept through at school, right? Not so, my skittish friend. It's actually a
piece of cake to implement. 


text-autospace:none'> 


text-autospace:none'>Disclaimer: Before you hard-core coders out there
start sending me e-mails, what we're doing here is not technically a stack.
Since a stack uses a Last In First Out (LIFO) implementation, it's unsuitable
for processing events in the order that they arrive (unless you're into that
sort of thing). Technically, I guess you could call this an event pipe, or
list, or funnel, but stack sounds cooler. Say it with me. Stack.


text-autospace:none'> 


text-autospace:none'>An event stack exists for one purpose: to trap events and
store them for later processing. It's sort of like the transmission on your
car. Your transmission allows the engine and drive shaft to spin at two
different speeds without killing you and destroying your car. An event stack
allows your app to run and receive events from the remote DCOM component,
without taking execution flow away from that component. All right, so think of
your app as the wheels, and the DCOM component as the engine. Make more sense
now? No? Well I'm a VB geek, not a mechanic. Just stay tuned and you'll get it
eventually.


text-autospace:none'> 


Okay, so what do I need?


text-autospace:none'> 


text-autospace:none'>Know anything about arrays? User-defined types? The VB
timer control? If so, you've got all the knowledge you need to implement an
event stack. If not, well... I guess you're out of luck.


text-autospace:none'> 


Get to the point, already.


text-autospace:none'> 


text-autospace:none'>What follows is the most basic way I know to create an
event stack. Obviously there are any number of improvements and changes you
could make. Among others: 


none;mso-layout-grid-align:none;text-autospace:none'>Symbol'>·      Define a cEvent
class with a parameters collection instead of a UDT to hold your event
information.


none;mso-layout-grid-align:none;text-autospace:none'>Symbol'>·      Define an EventStack
collection with Push and Pop methods to contain the various events.


none;mso-layout-grid-align:none;text-autospace:none'>Symbol'>·      Use the SetTimer API
instead of the VB Timer control to trigger stack processing.


none;mso-layout-grid-align:none;text-autospace:none'> 


Step 1: Creating the event


 


text-autospace:none'>First, we need to create a variable to hold
the information we're going to be receiving as parameters from the event. Let's
take a grossly simplified example. I've created an ActiveX DCOM component
that's running on a server machine. It exposes a class called Xchat,
whose purpose in life is to receive information via a Post method:


text-autospace:none'> 


Public Sub style='color:windowtext'>Post (style='color:navy'>Optional Info1 style='color:navy'>As Long, _style='color:navy'>


yes">                 Optional Info2
As String, _style='color:navy'>


yes">                 Optional Info3
As String)


none;text-autospace:none'> 


text-autospace:none'>And call an underlying function in a public
module which will pass this information to all the instances of the Xchat
class, by raising the Dookie event:


text-autospace:none'> 


Public Event
Dookie (Info1
As Long, _


yes">                     Info2
As String, _


style="mso-spacerun: yes">                     Info3 style='color:navy'>As String)


none;text-autospace:none'> 


text-autospace:none'>This kind of DCOM server could be useful in hundreds
of ways; allowing a machine to poll the server to see how many connections are
active, as a component in a simple chat program, or a low-tech communications
protocol between apps running on different PCs. You get the idea.


text-autospace:none'> 


text-autospace:none'>In this case we only want to trap one event, whose
parameters we know, so let's create a User-defined type (UDT) in a regular .BAS
module, to hold the event parameters.


text-autospace:none'> 


none;text-autospace:none'>"Courier New";color:navy'>Public Type10.0pt;font-family:"Courier New";color:blue'> 10.0pt;font-family:"Courier New"'>t_Eventstyle='color:blue'>


none;text-autospace:none'>"Courier New";color:blue'>   style='mso-bidi-font-size:10.0pt;font-family:"Courier New"'>FirstParamstyle='color:blue'> As Longstyle='color:blue'>


none;text-autospace:none'>"Courier New";color:blue'>   style='mso-bidi-font-size:10.0pt;font-family:"Courier New"'>SecondParamstyle='color:blue'> As Stringstyle='color:blue'>


none;text-autospace:none'>"Courier New";color:blue'>   style='mso-bidi-font-size:10.0pt;font-family:"Courier New"'>ThirdParamstyle='color:blue'> As Stringstyle='color:blue'>


none;text-autospace:none'>"Courier New";color:navy'>End Type


none;text-autospace:none'> 


text-autospace:none'>If you want to hold different events in your
stack (let's say a Timer event and an Error event), you might want to add an EventID
member to your UDT so the eventual processor of the events knows which event
it's processing. Likewise, you could add a ControlID if you want to trap
events from different controls, etc.


text-autospace:none'> 


text-autospace:none'>"But what if we don't know that all the events will
contain the same parameters?", I hear you ask. Good question. Like I said,
this is the simplest way to create an event stack. If you don't know
what parameters you'll be receiving, you could implement a Parameters
collection as a member of an Event class, which in turn would be
contained in an EventStack collection, etc., etc.


text-autospace:none'> 


text-autospace:none'>Easy enough, right? Wait. It gets even easier. 


text-autospace:none'> 


Step 2: Creating the stack


 


text-autospace:none'>Now we need to create a global variable in the same module
to hold all of our event information:


text-autospace:none'> 


none;text-autospace:none'>"Courier New";color:navy'>Publicfont-family:"Courier New";color:blue'> 10.0pt;font-family:"Courier New"'>a_EventStack() style='color:navy'>As t_Eventstyle='color:blue'>


none;text-autospace:none'> 


text-autospace:none'>This array will hold all of our miscellaneous event
information. We'll add events to the array, one at a time, as we receive them.


text-autospace:none'> 


Step 3: Trapping the event


 


text-autospace:none'>Let's assume you're using the Xchat class in your
app. If you want to receive events from this component, you need to declare it
using the WithEvents keyword. So create a form in VB. In the code view for
the form, right after the "Courier New"'>Option Explicit statement (you do use Option
Explicit, don't you? Good.) type the following:


text-autospace:none'> 


none;text-autospace:none'>"Courier New";color:navy'>Dim WithEvents10.0pt;font-family:"Courier New";color:blue'> 10.0pt;font-family:"Courier New"'>xc_Remote style='color:navy'>As Xchatstyle='color:blue'>


none;text-autospace:none'>"Courier New";color:navy'>Dimfont-family:"Courier New";color:blue'> 10.0pt;font-family:"Courier New"'>b_LockStackProcessing
As Boolean


none;text-autospace:none'> 


text-autospace:none'>This, of course assumes you've correctly referenced the
remote component type libraries, configured it with Dcomcnfg.exe, and a
whole bunch of other stuff that is beyond the scope of this article.


text-autospace:none'> 


text-autospace:none'>Now, if you click on the object combo box at the top of
the code view window, you should see xc_Remote show up in the list of available
objects. Click on it, and like magic, we're transported to the Dookie
event.


text-autospace:none'> 


text-autospace:none'>Here is where the major advantage of an event stack starts
to become apparent. In the normal course of things, if this were a regular
class, a DLL, or an ActiveX control, you would do something like this:


text-autospace:none'> 


none;text-autospace:none'>"Courier New";color:navy'>Private Sub10.0pt;font-family:"Courier New";color:blue'> 10.0pt;font-family:"Courier New"'>xc_Remote_Dookie (Info1style='color:blue'> As Long,style='color:blue'> Info2 style='color:navy'>As String, Info3style='color:blue'> As String)style='color:blue'>


none;text-autospace:none'>"Courier New";color:green'>   '.style='color:green'>


none;text-autospace:none'>"Courier New";color:green'>   '.style='color:green'>


none;text-autospace:none'>"Courier New";color:green'>   '.style='color:green'>


none;text-autospace:none'>"Courier New";color:green'>   'Execute
ten thousand lines of 


none;text-autospace:none'>"Courier New";color:green'>  
'time-consuming, processor intensive codestyle='color:green'>


none;text-autospace:none'>"Courier New";color:green'>   '.style='color:green'>


none;text-autospace:none'>"Courier New";color:green'>   '.style='color:green'>


none;text-autospace:none'>"Courier New";color:green'>   '.style='color:green'>


none;text-autospace:none'>"Courier New";color:navy'>End Sub


none;text-autospace:none'> 


text-autospace:none'>However, it's not. This component is raising dozens of
events per second, possibly to multiple clients, each of which wants to execute
its ten thousand lines of code before returning control to the server. See a
problem?


text-autospace:none'> 


text-autospace:none'>In order to get around this dilemma, we'll replace the
traditional event code with something like this:


text-autospace:none'> 


text-autospace:none'>style="mso-spacerun: yes">    Private Sub xc_Remote_Dookie(Info1style='color:navy'> As Long, Info2 As String, Info3style='color:navy'> As String)style='color:navy'>


text-autospace:none'>color:navy'>       Dim style='mso-bidi-font-size:10.0pt;font-family:"Courier New"'>l_Countstyle='color:navy'> As Long


text-autospace:none'>color:navy'>   style="mso-spacerun: yes"> 


text-autospace:none'>color:navy'>       If style='mso-bidi-font-size:10.0pt;font-family:"Courier New"'>b_LockStackProcessingstyle='color:navy'> Then


text-autospace:none'>color:navy'>           Exit Substyle='color:navy'>


text-autospace:none'>color:navy'>       End Ifstyle='color:navy'>


text-autospace:none'>color:navy'>   style="mso-spacerun: yes"> 


text-autospace:none'>color:navy'>       On Error GoTo style='mso-bidi-font-size:10.0pt;font-family:"Courier New"'>err_EmptyArraystyle='color:navy'>


text-autospace:none'>color:navy'>       style='mso-bidi-font-size:10.0pt;font-family:"Courier New"'>l_Countstyle='color:navy'> = UBound(a_EventStack)style='color:navy'>


text-autospace:none'>color:navy'>       ReDim Preserve style='mso-bidi-font-size:10.0pt;font-family:"Courier New"'>a_EventStack(l_Count
+ 1)


text-autospace:none'>color:navy'>   style="mso-spacerun: yes"> 


text-autospace:none'>color:navy'>    err_Reentry:style='color:navy'>


text-autospace:none'>color:navy'>       On Error GoTo style='mso-bidi-font-size:10.0pt;font-family:"Courier New"'>0


text-autospace:none'>style="mso-spacerun: yes">       a_EventStack(l_Count + 1).FirstParam =
Info1


text-autospace:none'>style="mso-spacerun: yes">       a_EventStack(l_Count + 1).SecondParam =
Info2


text-autospace:none'>style="mso-spacerun: yes">       a_EventStack(l_Count + 1).ThirdParam =
Info3


text-autospace:none'>color:navy'>       Exit Substyle='color:navy'>


text-autospace:none'>color:navy'>   style="mso-spacerun: yes"> 


text-autospace:none'>color:navy'>    err_EmptyArray:style='color:navy'>


text-autospace:none'>style="mso-spacerun: yes">       l_Count = 0


text-autospace:none'>color:navy'>       ReDim style='mso-bidi-font-size:10.0pt;font-family:"Courier New"'>a_EventStack(l_Count)style='color:navy'>


text-autospace:none'>color:navy'>       Resume style='mso-bidi-font-size:10.0pt;font-family:"Courier New"'>err_Reentrystyle='color:navy'>


text-autospace:none'>color:navy'>    End Sub


text-autospace:none'> 


text-autospace:none'>All this really does is grab the event
information and slap it into our event stack, and then returns control to the
component that raised the event. Since this code will execute in a fraction of
the time it would take to actually fully process the event, it doesn't take
control away from the server component for too long, and allows the
server to continue doing its job.


text-autospace:none'> 


Step 4: Processing the event


 


text-autospace:none'>Now let's add a timer control tmr_Event to the
form, and set the interval property to some suitably small period, say 200 milliseconds.
The Timer event is where we'll process all the events we've trapped in
our stack, so we want to process the stack often enough to stay abreast of the
events being raised by the server, but not so often that we're constantly
interrupting client execution to handle the stack. After all, presumably this
application has other things to do.


text-autospace:none'> 


text-autospace:none'>Back into the code view for the form, let's add some code
to the Timer event:


text-autospace:none'> 


text-autospace:none'>color:navy'>    Private Sub style='mso-bidi-font-size:10.0pt;font-family:"Courier New"'>tmr_Event_Timerstyle='color:navy'>()


text-autospace:none'>color:navy'>       Static style='mso-bidi-font-size:10.0pt;font-family:"Courier New"'>b_Reentrystyle='color:navy'> As Booleanstyle='color:navy'>


text-autospace:none'>color:navy'>       Dim style='mso-bidi-font-size:10.0pt;font-family:"Courier New"'>l_Countstyle='color:navy'> As long


text-autospace:none'>color:navy'>       Dim style='mso-bidi-font-size:10.0pt;font-family:"Courier New"'>istyle='color:navy'> As Integerstyle='color:navy'>


text-autospace:none'>color:navy'>   style="mso-spacerun: yes"> 


text-autospace:none'>color:navy'>       If style='mso-bidi-font-size:10.0pt;font-family:"Courier New"'>b_Reentrystyle='color:navy'> Then


none;text-autospace:none'>"Courier New";color:navy'>          
Exit Sub


text-autospace:none'>color:navy'>       End If


text-autospace:none'>color:navy'>   style="mso-spacerun: yes"> 


text-autospace:none'>color:navy'>       On Error Goto style='mso-bidi-font-size:10.0pt;font-family:"Courier New"'>err_EmptyArraystyle='color:navy'>


text-autospace:none'>color:navy'>       style='mso-bidi-font-size:10.0pt;font-family:"Courier New"'>l_Count =style='color:navy'> UBound(a_EventStack)style='color:navy'>


text-autospace:none'>color:navy'>       On Error Goto style='mso-bidi-font-size:10.0pt;font-family:"Courier New"'>0


text-autospace:none'>style="mso-spacerun: yes">    


text-autospace:none'>color:navy'>       style='mso-bidi-font-size:10.0pt;font-family:"Courier New"'>b_Reentry =style='color:navy'> True


text-autospace:none'>color:navy'>   style="mso-spacerun: yes"> 


text-autospace:none'>color:navy'>       style='mso-bidi-font-size:10.0pt;font-family:"Courier New";color:green'>'.style='color:green'>


text-autospace:none'>color:green'>      style="mso-spacerun: yes"> '.green'>


text-autospace:none'>color:green'>       '.style='color:green'>


text-autospace:none'>color:green'>       'At this point, we
can execute some 


text-autospace:none'>color:green'>       'code to process
a_EventStack(0), since it is


text-autospace:none'>color:green'>       'the oldest event in
the stack. 


text-autospace:none'>color:green'>       '.style='color:green'>


text-autospace:none'>color:green'>       '.style='color:green'>


text-autospace:none'>color:green'>       '.style='color:green'>


text-autospace:none'>color:navy'>   style="mso-spacerun: yes"> 


text-autospace:none'>color:green'>       'Remove the oldest
event.


text-autospace:none'>New"'>      
b_LockStackProcessing = Truestyle='color:navy'>


text-autospace:none'>color:navy'> yes">   style='color:navy'>


text-autospace:none'>color:navy'>       If style='mso-bidi-font-size:10.0pt;font-family:"Courier New"'>l_Count = 0style='color:navy'> Then


none;text-autospace:none'>"Courier New";color:navy'>         
Erase New"'>a_EventStackstyle='color:navy'>


text-autospace:none'>color:navy'>       Elsestyle='color:navy'>


text-autospace:none'>color:navy'>   style="mso-spacerun: yes"> 


none;text-autospace:none'>"Courier New";color:navy'>          For style='mso-bidi-font-size:10.0pt;font-family:"Courier New"'>i = 0style='color:navy'> To l_Count - 1


none;text-autospace:none'>"Courier New";color:navy'>              style='mso-bidi-font-size:10.0pt;font-family:"Courier New"'>a_EventStack(i) =
a_EventStack(i + 1)


none;text-autospace:none'>"Courier New";color:navy'>          Nextstyle='color:navy'>


none;text-autospace:none'>"Courier New";color:navy'>         
Redim Preserve "Courier New"'>a_EventStack(l_Count + 1)


none;text-autospace:none'>"Courier New";color:navy'>   style="mso-spacerun: yes"> 


text-autospace:none'>color:navy'>       End Ifstyle='color:navy'>


text-autospace:none'>style="mso-spacerun: yes">       b_LockStackProcessing =style='color:navy'> False


text-autospace:none'>color:navy'>   style="mso-spacerun: yes"> 


text-autospace:none'>color:navy'>    err_EmptyArray:style='color:navy'>


text-autospace:none'>color:navy'>       style='mso-bidi-font-size:10.0pt;font-family:"Courier New"'>b_Reentry =style='color:navy'> False


text-autospace:none'>color:navy'>       style="mso-spacerun: yes"> 


none;text-autospace:none'>"Courier New";color:navy'>    End Substyle='color:navy'>


text-autospace:none'>    


text-autospace:none'>As you can see, we implement reentrancy protection on this
procedure with the b_Reentry variable, since the timer might tick ten
times before we finish processing the event stack, and we don't want to process
events out of turn. 


text-autospace:none'> 


text-autospace:none'>In addition to the standard reentrancy
protection, I've also added a global stack protection variable b_LockStackProcessing.
I use this so that no events can be added to stack while I'm resizing it. Since
the server component is running asynchronously to the application, it is
possible (though unlikely) to receive server events while resizing the stack. I
don't mind receiving events while I'm processing the stack, but I don't want to
overwrite existing events while I'm resizing, so I lock the stack. 


text-autospace:none'> 


text-autospace:none'>In this particular example, I only process one event off
the stack every time the timer ticks. Obviously if you plan on receiving more
than one event between timer ticks, you may want to process the entire stack
every time the timer ticks. Also, since the stack is an array, it takes some
time to shuffle all the events up one slot. You may want to implement a
collection instead, which, though it takes more memory and resources to handle,
can allow you to remove the event from the stack with one line of code.


text-autospace:none'> 


text-autospace:none'>Note: Another possible improvement on the code
here, which I leave you to implement, is a flexible timer. Every couple of
times you process the stack, you check to see if there are a large number of
events in the stack. If there are, you decrease the timer interval. If there
are very few events in the stack, you increase the timer interval. This means
that when the server is flipping out and flooding you with events, your app can
devote more time to handling those events, and when the server component is
twiddling its thumbs, your app can devote more processing time to other tasks.


text-autospace:none'> 


That's it?


 


text-autospace:none'>That's all she wrote, boys and girls. See how easy that
was? While an event stack may not be necessary for small client-server
applications, it sure can save your bacon if you're deploying on an
Enterprise-wide scale. It can also be of enormous value if you want to dispatch
events asynchronously, just for the heck of it. Best of all, this technique can
be used in reverse, within your ActiveX server components, as a method stack,
processing method calls asynchronously so that the server component interface
is always available to clients.


text-autospace:none'> 


text-autospace:none'>Now go forth and multiply. Asynchronously.


text-autospace:none'> 


text-autospace:none'>Philippe DesRosiers


text-autospace:none'>e: [email protected]






Download this snippet    Add to My Saved Code

Implementing an event stack Comments

No comments have been posted about Implementing an event stack. Why not be the first to post a comment about Implementing an event stack.

Post your comment

Subject:
Message:
0/1000 characters