Overcoming Single Threads with Asynchronous OLE Automation

Scot Hillier

VB4 has taken some knocks because it’s not multithreaded. Okay, it’s not multithreaded. However, here’s 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." What’s a thread, you ask? Actually, it’s 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 threads—that is, it can only execute one line of code at a time. That makes VB single-threaded—much 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 doesn’t 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. . . .

Besides—and here’s the rub—I’m going to use these fun little animations to sneak in a serious discussion about asynchronous OLE.

Asynchronous OLE? What’s that, anyway? It sounds like something you’d say if you’re woken by the phone at 3 a.m. But it’s really not difficult—assuming you’ve 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 I’ll 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 here’s the catch—you’ll 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 isn’t registered, clients can’t 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. You’ll 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 time—like saving a file while you print—which isn’t 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 it’s 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 I’ll 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, let’s 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, you’ll 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 they’re 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 (he’s 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 Group’s 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.