|1||ActiveX Controls|| |
Free-Threaded In-Progress Indicators
Animated In-Progess Indicators which Don't Stop just because VB does
One thing that occurs time and time again in applications is the need to show the user
that something is happening whilst performing a long operation. Sometimes you know
how many steps there are in the operation, in which case you can use a
ProgressBar to show to the user how much has been
processed and how much there is left.
However, often you don't know. For these cases you want to show an In-Progress indicator - something that runs in a loop whilst work is being done. Both Internet Explorer and Navigator make a big feature of this technique with a logo in the top-left corner of their apps. Whilst it would be nice to do this in your own app, there are two problems. The first is that suitable graphics are hard to come by and can be huge. Building multi-frame animations like the IE animated logo is not something to be undertaken lightly! The second is that the only control provided with VB to do this is the COMCTL32.DLL Animation control. This only accepts AVI files, a format which is not widely available and has no (AFAIK) good freeware tools for creating the files.
The two In-Progress indicators here help solve these problems by providing simple animations, the first being a Netscape/Knight-Rider style and the second using widely available Animated Cursors.
And if you're a hardcore VB coder, perhaps the more interesting thing to note is that just like the animation control both these samples are free-threaded, meaning they run even when VB is blocked.
|Free-Threading in VB|
Free-threading was widely believed to be impossible to do in VB. You can read a little about the
issues and history of the problem in the article A History Of In-Process Threading in VB.
In fact there are some midgets who still
believe that to be the case today and should be forced to relinquish control of Bruce McKinney's previously
excellent Win.TLB at gunpoint if possible.
Luckily for us Matthew Currland showed how to do it in VBPJ article. The downside is that to get a free-threaded app up and running isn't easy. For a start, it is impossible to debug your application in VB: to create your class on a new thread the code must use the CoCreateInstance COM function to get an instance of your class, and whilst you are running in the IDE VB messes around with ProgIDs and CLSIDs preventing this from succeeding.
Multi-threaded code in general is very tricky. Throughout you need to consider things like
re-entrancy (where the same procedure is entered again whilst it is already executing from a
prior call). The programming model for Matthew Currland's multi-threading library allows you
to avoid worries about this, at the cost of a loss of flexibility and ease of use.
In this model, you can create a class on a new thread using the ProgID for the class. There is only one chance to communicate with the class: when it is started. You initialise the class through an implemented interface ThreadLaunch which only has one method: Go. It is possible to pass one variant in as a parameter. Normally the thread terminates when the IThreadLaunch_Go method completes. The only other control you have once the ThreadLaunch_Go method has been called is that you should write the thread to poll on a shared variable m_Notify. When the owner of the thread wants to terminate, it sets m_Notify to True, and the next time the code in IThreadLaunch_Go polls the value it should exit.
|The Good, The Bad and The Out-of-Control|
As you can see, this is not the usual way to code with objects in VB! Basically what it means is
that apart from stopping the thread there is a one shot chance of communicating information from the
owner to the thread.
However, there are distinct stability advantages to this programming model. The main one is you do not need to consider re-entrancy or other thread-related problems in your free-threaded class - because they simply cannot happen! All the places where there might be re-entrancy are already covered by Matthew Currland's code.
It does leave the issue of how to communicate between the threaded class and your code though. I have used a Window as the communication method in my examples. The variant data passed in to the thread is a window handle. All other variables which need to be passed between the caller and the free-threaded class are then obtained via the window handle; in the case of this code I have used the Windows Properties database (GetProp,SetProp and RemoveProp API calls) but you could likewise consider using subclassing and posting messages backwards and forwards between the windows.
My implementations of the free-threaded classes include at least two public classes:
|Try the Code|