Overcoming Single Threads with Asynchronous OLE Automation
Scot Hillier
VB4 has taken some knocks because its not multithreaded. Okay, its not multithreaded. However, heres a workaround that you can use to mimic multithreading. The trick? Our old friend the timer control.
I hear a lot of talk these days about VB4 and the subject of "threads." Whats a thread, you ask? Actually, its nothing more than techno-jargon for the concept of an "execution point" inside your code, the line currently being executed by VB. The path that the execution follows is called the "thread." The problem is that VB has only one of these threadsthat is, it can only execute one line of code at a time. That makes VB single-threadedmuch to the delight of competing 32-bit development tool vendors that can lay claim to having products that are (get ready), multithreaded.
The advantage of multithreaded applications is that they can do more than one thing at a time. They can print a document while the user is querying a database, for example, or they can save a file while accepting user input. So, what do you do if you want to do more than one thing at a time with VB4? Are you doomed by the inherent single-threaded nature of the development tool? Of course not! You conquer the problem using the technology of Object Linking and Embedding (OLE) and save the day by creating a special kind of OLE server known as an asynchronous OLE server.
Fun with file input
Inputting and outputting data is certainly a major task in many VB applications. But is it pretty? I think not. After all, VB4 doesnt yet support the stylish animated cursors found in Win95 and WinNT, nor does it support those cool animations that occur during file operations. About the only thing you can do in VB is provide a status bar, progress bar, and an hourglass to indicate to the user that your program is busy. This might be enough for some people, but after seeing the animations in Win95, well. . . .
Besidesand heres the rubIm going to use these fun little animations to sneak in a serious discussion about asynchronous OLE.
Asynchronous OLE? Whats that, anyway? It sounds like something youd say if youre woken by the phone at 3 a.m. But its really not difficultassuming youve got a fundamental knowledge of creating OLE servers under your belt. If not, the exercises here should be simple enough to do anyway, but proceed with caution.
The strategy Ill use to create a Win95 animation is to simply place a series of image controls on a form (see Figure 1) and make them visible sequentially. For the series of pictures, I used a combination of icons already available in VB4 plus a few I created myself using the Image Editor, an application that ships with VB4 that lets you draw your own bitmaps, icons, and cursors. You can find it in the TOOLS directory on the VB4 CD.
Figure 1. The form with image controls that will be displayed sequentially.
Generating the animation is simply a matter of using a timer control to cycle through the images on the form and displaying them one at a time. But heres the catchyoull make this little animation app into an OLE server that you can call from any application you write. Additionally, the animation takes place asynchronously, meaning that it runs in a separate memory space from the client application so that you can more efficiently use the resources of the calling application. Listing 1 shows the code for the animation OLE server.
Listing 1. Form, class, and module source code for the animation OLE server.
'frmAnimate
Option Explicit
Private Sub timAnimate_Timer()
'Author: Scot P. Hillier
'Purpose: Animate the File IO
'11/22/95 Original
On Error GoTo TimerErr
Static intSequence As Integer
intSequence = intSequence + 1
If intSequence < 1 Then intSequence = 1
If intSequence > 6 Then intSequence = 1
Select Case intSequence
Case 1
imgFile(6).Visible = False
imgFile(7).Visible = True
imgFile(1).Visible = True
Case 2
imgFile(1).Visible = False
imgFile(2).Visible = True
Case 3
imgFile(2).Visible = False
imgFile(3).Visible = True
Case 4
imgFile(3).Visible = False
imgFile(4).Visible = True
Case 5
imgFile(4).Visible = False
imgFile(5).Visible = True
Case 6
imgFile(5).Visible = False
imgFile(7).Visible = False
imgFile(6).Visible = True
End Select
TimerExit:
Exit Sub
TimerErr:
'We certainly don't expect any errors in
'this simple routine, but if we get one,
'we'll only show it if this is the Beta
'version.
#If Beta Then
MsgBox Err.Description & Chr$(10) & Err.Number
#End If
Resume TimerExit
End Sub
'File Class
BEGIN
MultiUse = -1 'True
END
Attribute VB_Name = "File"
Attribute VB_Creatable = True
Attribute VB_Exposed = True
Option Explicit
'Class Properties are implemented as Private
'Variables embedded in Property procedures
Private m_Enabled As Boolean
Private m_Visible As Boolean
Private Sub Class_Initialize()
'Load the Animation Form
Load frmAnimate
End Sub
Private Sub Class_Terminate()
'Unload the Animation Form
Unload frmAnimate
Set frmAnimate = Nothing
End Sub
Public Property Get Enabled() As Boolean
Enabled = m_Enabled
End Property
Public Property Let Enabled(blnEnabled As Boolean)
'Change the Timer State as save to variable
frmAnimate.timAnimate.Enabled = blnEnabled
m_Enabled = blnEnabled
End Property
Public Property Get Visible() As Boolean
Visible = m_Visible
End Property
Public Property Let Visible(blnVisible As Boolean)
'Show or Hide the Form and save
'the state to the Private Variable
frmAnimate.Visible = blnVisible
m_Visible = blnVisible
End Property
'modFile Module
Option Explicit
'Animation Constants
Public Const myFileCopy% = 0
Public Sub Main()
'This procedure simply gives the OLE
'server a place to start. No code is
'executed here.
End Sub
With the code from Listing 1 in your VB project, you need to complete the OLE server. This requires going to the Tools-Options selection on the VB menu and clicking on the Project tab. In the Project options, give your OLE server the name ProcessAnimator and the description "Displays animations while system processes occur." Also, be sure to change the StartMode option to OLE Server. Then save the project and create an executable. Make sure the executable is registered with the system registry by running the finished app one time from the File Manager. (The executable OLE servers you create in VB automatically register themselves with the system registry whenever they are run directly from Windows. If the server isnt registered, clients cant call the server.)
Using the animation
To test the new OLE server, create a client application (Listing 2) that calls the server. Although the client is a simple application that saves some data to a text file, it demonstrates how to call the server from any application. Be sure to set a Reference to the new OLE server by using the Tools-References dialog box. Run the application and press the Save button. Youll start the animation process while saving the file.
Listing 2. The client application.
'frmClient
Option Explicit
Private Sub cmdSave_Click()
Dim i As Long
'Turn on the animation
pobjAnimate.Visible = True
pobjAnimate.Enabled = True
'Now try to save some data to a file
Open App.Path & "\temp.txt" For Output As #1
For i = 1 To 10000
Print #1, Format$(i)
Next
Close #1
'Turn off animation
pobjAnimate.Visible = False
pobjAnimate.Enabled = False
End Sub
Private Sub Form_Unload(Cancel As Integer)
Set pobjAnimate = Nothing
End Sub
'Main Module
Option Explicit
Public pobjAnimate As ProcessAnimator.File
Public Sub Main()
frmClient.Show
Set pobjAnimate = New ProcessAnimator.File
End Sub
The significance of this simple demonstration is that by using an OLE server, you effectively start a second process. That is, the animation runs in its own memory space while the file is being saved. This means that you can do more than one thing at a timelike saving a file while you printwhich isnt possible in a simple single-threaded environment. The key to accomplishing the asynchronous action is the timer control. The timer control starts the animation for you, like a second user starting the process. By using the timer control, you can start OLE servers without explicitly writing the code in the client. This makes asynchronous OLE possible.
Call me sometime
Asynchronous OLE automation has many more powerful uses than merely displaying animations. The height of this power is reached when you implement a callback from an asynchronous OLE server. This means that you can give a chunk of work to an asynchronous OLE server, go away, and the server will call your application when its done. The work you give can be a set of records to commit, a file to print, data to save, or virtually anything else you can imagine. Now Ill show you how to use asynchronous OLE automation to create a System Resource monitor that calls your application if free system resources decrease below a certain level.
First, lets review the steps required to create an Asynchronous OLE server with a Call Back feature:
Instantiate a "CallBack" object in the client application. The CallBack object is the object that receives the "phone call" from the OLE server. You give this object a method called "Low Resources" that is called by the asynchronous OLE server.
Instantiate the OLE server. Start the OLE server, but no monitoring is performed yet.
Pass a reference from the CallBack object to the server. By passing a reference from the client to the server, the OLE server can then use the "LowResources" method."
Begin monitoring in the server. In this case you build an OLE server that monitors free resources. The monitoring is triggered by a timer control. Just as with the animation server, the timer plays a critical role in starting the second process.
Call the client when the server detects low resources. By using the reference to the CallBack object, the server calls the "LowResources" method, notifying the client that the free resources are below the allowable threshold.
This strategy is the same for every asynchronous server. To create the resource server, you first build a Form called frmMonitor and place a single label on it, named lblServer, to display the free resources. The code for the server is on the Companion Disk.
To complete the OLE server, make sure that the Startup Form option is set to Sub Main and the Start Mode option is set to OLE Server. Under the Project Name option, type ResourceServer as one word and describe your server as Free System Resource Server. Run the server and minimize it.
Now build a simple client application to use the monitor. In a "real world" app, your client would probably be a lot more complex. Begin by constructing a form similar to the one in Figure 2. Give the controls these names:
Check1 | chkEnabled |
Check2 | chkVisible |
Command1 | cmdQuit |
Hscroll1 | scrThreshold |
Figure 2. Sample form for accessing the resource monitor.
Add the code in Listing 3 to the client application. Set a reference to the asynchronous OLE server by using the Tools-References dialog box, and then run the client. You can use the horizontal scroll bar to change the low resources threshold and get a message back from the OLE server.
Listing 3. Code that implements the client portion of the asynchronous resource monitor.
'Callback Class
BEGIN
MultiUse = 0 'False
END
Attribute VB_Name = "Callback"
Attribute VB_Creatable = True
Attribute VB_Exposed = True
Option Explicit
Public Sub LowResources()
MsgBox "Low System Resources"
End Sub
'frmClient
Attribute VB_Name = "frmClient"
Attribute VB_Creatable = False
Attribute VB_Exposed = False
Option Explicit
'The object for the OLE Server
Public pobjMonitor As ResourceServer.Monitor
'The object to receive the "Low Resources" Event
Private m_Callback As Callback
Private Sub chkEnabled_Click()
pobjMonitor.Enabled = -1 * chkEnabled.Value
End Sub
Private Sub chkVisible_Click()
pobjMonitor.Visible = -1 * chkVisible.Value
End Sub
Private Sub cmdQuit_Click()
Unload frmClient
Set frmClient = Nothing
End
End Sub
Private Sub Form_Load()
Set pobjMonitor = New ResourceServer.Monitor
Set m_Callback = New Callback
pobjMonitor.Connect m_Callback
End Sub
Private Sub Form_Unload(Cancel As Integer)
pobjMonitor.Disconnect m_Callback
Set pobjMonitor = Nothing
Set m_Callback = Nothing
End Sub
Private Sub scrThreshold_Change()
lblThreshold.Caption = Format$(scrThreshold.Value)
pobjMonitor.Threshold = scrThreshold.Value
End Sub
Asynchronous OLE automation lets you overcome the limitations inherent in single-threaded VB. Once you master the technique, youll realize that there are lots of ways to use this kind of server. You could use an async server to monitor a network and redirect traffic to different OLE objects based on load, for example, or use it to start other OLE servers before theyre needed. Experiment!
Scot Hillier is a veteran developer/instructor who has used Visual Basic since 1993 and has taught hundreds of professional developers the ins and outs of VB programming. Before joining New Technology Solutions, where, among other things, he teaches VB Bootcamp, Scot managed multiple projects with million-dollar budgets as an engineer in the nuclear power industry (hes a veteran nuclear-class submarine officer and a graduate of the Virginia Military Institute). Scot is also the acting director of the Connecticut Visual Basic Special Interest Groups Southern District and serves as editor of the CTVBSIG newsletter, VB Methods. CompuServe 71773,3612.
To find out more about Visual Basic Developer and Pinnacle Publishing, visit their website at http://www.pinppub.com/vbd/
Note: This is not a Microsoft Corporation website.
Microsoft is not responsible for its content.This article is reproduced from the May 1996 issue of Visual Basic Developer Copyright 1996, by Pinnacle Publishing, Inc., unless otherwise noted. All rights are reserved. Visual Basic Developer is an independently produced publication of Pinnacle Publishing, Inc. No part of this article may be used or reproduced in any fashion (except in brief quotations used in critical articles and reviews) without prior consent of Pinnacle Publishing, Inc. To contact Pinnacle Publishing, Inc., please call (800) 788-1900 or (206) 251-1900.