Welcome to the third and final instalment of our COM Course!
I'm your host Karl Moore and if you haven't yet sat the previous two sessions, be sure to check out part one here and part two here.
This week, we'll be looking in more detail at the nitty-bitty settings available when building your COM applications in Visual Basic. We'll be explaining away that compatibility problem you uncovered last week, take a peek at ActiveX EXE projects and how they differ from DLLs, plus we'll be looking at what you need to do next to take your knowledge onto the next level.
And all that within the next hour. So why waiteth thee? Click onward, fair knave!
At the end of last week, we stumbled across a small problem. Well, rather a big problem actually.
In fact, if you've got time, it was a whopping great huge problem wearing high heels, a frilly skirt and holding a massive sign stating "I am a BIG problem!".
You see - we compiled our ActiveX DLL, then compiled a test program that used our DLL. Then we recompiled our DLL something you usually do after you make changes. Suddenly, our test program no longer worked. Damn.
If we had then recompiled our test program, it would've worked. But let's say your VB program, an Excel spreadsheet and one C++ accounts package all use your DLL. Do they all need to be 'recompiled' every time you make a small change to your ActiveX DLL?
The answer is, thankfully, no.
The reason we experienced problems last week was due to an issue known as compatibility. Or rather, a lack of it.
- Open up your Northwind project in Visual Basic
- Click 'Project', 'Northwind Properties'
- Click the 'Component' tab
Take a peek at the 'Version Compatibility' frame in front of you. Here we have three options. Let's explain these now:
- No Compatibility With every compile, your COM component gets assigned a new 'signature'. This means programs looking for older 'signatures' (the previous version of the DLL) simply flop
- Project Compatibility With every compile, your COM component is assigned a new signature and still, any using-applications still flop. The only change here is that 'big' differences between your current project and a previous DLL project are highlighted as you compile. Ohhh, whoop-de-doo
- Binary Compatibility When compiled, your application attempts to keep the signature of a previously compiled DLL, thus ensuring any applications using it don't magically turn into the Blue Screen of Death. However if the differences between your previously compiled DLL and your to-be-compiled DLL are too great, a new signature must be assigned
So let's test our theory:
- Open last week's test application
- Recompile it
- Trial run your test application it should work
- Open our ActiveX DLL project
- Set the Binary Compatibility option
- Recompile your DLL
- Attempt to run your test application it should still work
Hurrah! It may sounds like complete hogwash, but when recompiling their DLLs, most developers get themselves into an awfully incompatible situation.
But hopefully you won't all thanks to this small aside.
Well, that's compatibility for you. About as exciting as www.PylonOfTheMonth.co.uk.
So far on our journey, we've explored using classes in ActiveX DLLs. That's all fine and dandy, but no COM course would be complete without a brief look at the naughty little brother of DLLs ActiveX EXEs.
Now when it comes down to actually coding an ActiveX EXE project, there's very little difference between the two. Actually, there's no difference. You just fire up Visual Basic, choose 'ActiveX EXE' instead of 'ActiveX DLL' - then start creating your classes as usual.
So why should I even bother mentioning it, I hear you ask? Well, even though you use them in the same way, they work slightly differently.
The first difference involves something called 'process space'. When any Windows program starts, be it Microsoft Word or Oliver Reed's Multimedia Guide to 20th Century Beer, it has a process space. This is like desk space to your program. It's a chunk of computer memory it uses to 'run' and 'work on' and stuff.
Now when you use ActiveX DLLs in your project, the DLL runs inside the process space of the program using it whereas an ActiveX EXE works outside of that space. In other words, an ActiveX EXE has a 'desk' of its own. But what use is that? Let's ponder over the advantages and disadvantages...
For a start, if your ActiveX DLL goes wild and crashes, the program using it is usually set alight and dramatically burnt to smithereens, often complemented by the devilish Blue Screen of Death. That doesn't happen with EXEs. They have their own 'process space' so if they crash, only their own desk is burned. Of course, your program then has to graciously recover having 'lost' that bit.
'Course, in the real world, nothing ever really crashes anyway (hah, who said technology journalists were 'out of touch'?)
Another difference is the load speed. Because a DLL loads within the existing process space, it's can nip up pretty quickly. On the other hand, EXEs take a few extra nanoseconds due to them having to quickly knock up their own process space and so on.
But is there any real difference? Hmm, yes. Two 'real to life' differences.
First off, if you're using certain Windows tools to complement your ActiveX components, you may be forced to use a particular type of project. For example, if you're using MTS, you have to create DLLs. If you're using DCOM, you have to use EXEs. Don't worry if you aren't sure about any of these acronyms right now they're just tools for power users, widgets to bring in when you want COM to work 'remotely'. We'll have tutorials on such technologies coming later on VB-World.
And now enter stage the second real difference.
I once needed to create an application that continuously checked whether something had happened in a database. I first tried doing this with some sort of 'timer' in my program. Every ten minutes or so, everything fired up and the database would be checked. The problem was that when this happened, all regular code in the same process space had to stop and wait for my database check.
Now one really great thing about ActiveX EXEs is that they have their own process space. So if you add a timer to do something there, it buzzes away in its own process space and doesn't affect the program using it. That means I could add an ActiveX EXE to the above project which, whilst checking the database, doesn't freeze the nuts off the program using it. Then if a message needs to be returned to the using program, it can be sent back by raising an event.
Top Tip: This method of running code 'away from' the regular program and raising events to talk to the using application is called asynchronous processing. You can use asynchronous processing when you need to either perform periodic e-mail or database checks, or perhaps when you need to run a long report or calculate big time statistics.
Surprisingly enough, everything we've covered on this page can be summarised in just one sentence. And here it is:
- ActiveX DLLs run 'in-process' and ActiveX EXEs run 'out-of-process'
Dazzling, isn't it?
Top Tip: For more information on which type of ActiveX project is best suited to your project, check out this Microsoft document
In the next section, we'll knock up our very own ActiveX EXE and acquire the little-known skill of 'asynchronous processing'. After that, we'll uncover a wonderful little thing called instancing (which I accidentally-on-purpose forgot to tell you about earlier), plus find out how you can take your mega COM knowledge to the next level.
So izzy wizzy, let's get busy!
<Reader: Groan >
In this section, we're going to knock together our own ActiveX EXE.
Our component will essentially be a file monitorerer. Every sixty seconds or so, it will fire off and check for the existence of a particular file. If found, our component will attempt to raise an event to the calling application. If not, our component will attempt to raise the Spanish Armada from the shores of Cornwall.
Now, if we were to stick all our code into a regular ActiveX DLL project, our normal program code would have to pause and hang about until this file checking code had finished. Because an ActiveX EXE project has its own process space, this code runs of its own accord asynchronously without delaying our normal program code.
So let's get started:
- Create a new 'ActiveX EXE' project
- Name the Project: File
- Name your Class: FileCheck
First off, we're going to need to create something that checks for our file every minute or so. Here, we're just going to slot a Form with a Timer inside our ActiveX EXE (yes, you can have Forms inside your ActiveX EXE/DLL projects!). We won't be showing the Form, but every minute or so will use the Timer control on it to check for a specified file. If found, the Timer will 'tell' our class by raising an event. Let's get coding:
- Click 'Project', 'Add Form'
- Add a Timer to Form1
- Declare the following variable behind your Form:
Public Filename As String
This will hold the name of the file we wish to monitor.
- Declare the following event behind your Form:
Public Event FileFound()
This even will be raised by our Timer code if the above file is found.
- Insert the following code behind Timer1:
Private Sub Timer1_Timer() If Dir(Filename) <> "" Then RaiseEvent FileFound Timer1.Interval = 0 End IfEnd Sub
Here, our code simply checks for the file. If found, it raises the FileFound event, then sets the Timer1 Interval property to zero, stopping all future timer checks.
- Open your FileCheck class
- Declare the following object in General Declarations:
Dim WithEvents objFileCheck As Form1
This is your Form1. You're telling Visual Basic this is a space to hold your Form. The WithEvents keyword means your class can receive the events it transmits, such as our coded FileFound.
- Select 'Class' from the 'Object' drop-down list
- Choose 'Initialize' from the 'Procedure' drop-down list
- Slap the following code behind the Class_Initialize event:
Private Sub Class_Initialize() Set objFileCheck = New Form1End Sub
This code simply sets our objFileCheck to equal a new instance of Form1. Following this, we can use all the functionality we programmed into Form1. Next up, let's write a Sub that the program using our application will use to monitor a file.
- Throw the following code into your FileCheck class:
Public Sub MonitorFile(Filename As String) objFileCheck.Filename = Filename objFileCheck.Timer1.Interval = 60000End Sub
When our user calls this, passing a filename, the Filename variable of our Form is set to the passed filename. Then, the Timer is 'started' by setting the Interval property to 60,000 milliseconds (or a minute to the rest of us).
So, we've created all the jazz to monitor our file. But when it is actually 'found', we need to tell the program using our ActiveX EXE by raising the FileFound event.
- Add the following event declaration to the General Declarations section:
Public Event FileFound(Filename As String)
This code simply 'defines' our FileFound event. Next, we'll add code to fire off this event when appropriate.
- Select 'objFileCheck' from the 'Object' drop-down list
- Choose 'FileFound' from the 'Procedure' drop-down list
You're currently looking in the event our Form will raise when it 'finds' the file. This is where we need to raise our own FileFound event to the program using our EXE.
- Add the following code to the objFileCheck_FileFound event:
And that's our class complete!
When a programmer uses our class, he (or rather more unusually, she) can run the MonitorFile method, passing a filename. That starts off the Form Timer. When the Timer code finds the file, checking every sixty seconds, it raises an event in our FileCheck class. This raises another event to the program using our program, telling the programmer their file has been found.
Clear as mud? Let's test it!
Before we can test our File component, we need to compile it. But just before we do that, let's take a sneak peek at a few extra options given to us by Visual Basic.
Whilst in your FileCheck code window, take a look at the Properties window.
Here we have the Name, DataBindingBehaviour (for 'binding' the class to a data source) and Persistable (used in controls, allows certain class properties to be saved). You should also have Instancing.
Now after you've set the class name, you probably won't need to worry about the first three. But what about Instancing?
The Instancing property determines whether or not your class can be 'seen' by the application using your ActiveX component and if it can, whether more than one 'instance' should be started at any one time.
Let's take a look at the options, in order of popularity:
- MultiUse This is perhaps the most used option. This uses one 'instance' of your component (one 'process space') to serve all the programs using it. Here, all objects are served by one instance of your component. This saves memory space and can allow you to "share global variables" - but has few other real-life advantages
- Private No-one except other objects in your component can see this class. This is generally used when you need to create a class that will solely be used by the other objects in your class
- GlobalMultiUse Allows you to call the various methods and properties of your class as if they were global functions. In other words, in VB, you don't have to explicitly create a new object, then run that method it's all done automatically. You just call it! Every method or property called runs from one single instance of your component
- PublicNotCreatable This means your class is viewable by all, but only your own classes can create an instance of it. In other words, your end user cannot use the New keyword to create one of these your class must do that and pass it back. This is a little like when you open a recordset in DAO you never create a new recordset, but simply get one back from the OpenRecordset method
- SingleUse This means that every time a new instance of a component is started in code, it fires up another 'instance' of your ActiveX component. In other words, each instance gets its own 'process space'. This does have some limited use, but nothing to worry about. Unsurprisingly, this is the opposite of MultiUse
- GlobalSingleUse As in GlobalMultiUse, except for each object created in code, a new instance of your component is fired up
Now you'll rarely use all of these. Undoubtedly the most popular is MultiUse. Private is sometimes used and GlobalMultiUse can be pretty neat, but the rest are kinda unpopular. Knoooow the feeling. You'll also find most of these options available when creating ActiveX DLLs and they work in just the same way.
Well, my profuse apologies for such a boring interlude, but I'd get a scalding from all the COM whizz-kids if I didn't mention incredulous Instancing.
Anyway, let's fire ahead and compile then test our ActiveX EXE component!
- Click 'File', 'Make File.exe'
- Choose a filename then click OK
Now let's create our test application!
Let's dive straight into creating an application to test our ActiveX EXE file-monitoring component:
- Create a new 'Standard EXE' project
Now let's add a reference to our new File component, then throw in a little testing code for good taste:
- Click 'Project', 'References'
- Check the 'File' component, then click OK
- In the General Declarations section behind your Form, add the code:
Dim WithEvents MyFileObject As FileCheck
- Select 'MyFileObject' from the 'Object' drop-down list
- Ensure you're in the FileFound event
- Tap in the following code:
MsgBox "Found: " & Filename
- Add a Command Button to Form1
- Behind your Command Button, type:
Set MyFileObject = New FileCheckMyFileObject.MonitorFile ("c:\test.txt")
Here, we're setting MyFileObject to a new instance of FileCheck, then we're running the MonitorFile method passing c:\test.txt as an argument. Now behind the scenes this will fire off the Timer and every sixty seconds will check for the file.
Now, that test.txt probably doesn't exist on your computer yet. Wait a few minutes. Nothing should happen. Then create a new file in Notepad, calling it c:\test.txt
Within sixty seconds, a message box should pop up stating it has found your file! Well done, you've just created your own ActiveX EXE!
It's worth noting that this particular component uses asynchronous processing. As we discussed earlier, an ActiveX EXE has its own process space. So when your Timer fires up to check for the existence of a file, it doesn't delay the program using it. If you were using a DLL, it would have done.
And you could put anything behind that Timer code to create a whopping great huge report, a mega calculation, anything!
So far in this course, we've created a couple of real groovy components. But we've not talked about distributing them yet. Let's have a quick natter...
So you've created the world's sexiest ActiveX component and need to get it out to the masses. What do you do?
Well thankfully, distributing your COM components is exceptionally easy. Just as you would use the Package & Deployment Wizard to get your regular Visual Basic project on other computers you can do the same with your ActiveX projects!
You can send out your ActiveX component either on its own whereby you run the wizard and create a setup program purely for your ActiveX project or you can distribute it alongside any applications that use it simply create a setup for your regular project and your component will be bundled automatically.
Corr, simple eh?
So you've had a little taste of the COM life and feel it's the route for you?
Well, although we've touched on the most important points there's plenty more for you to go at. Most importantly, I'd like you to find out about Collections and how they let your classes to work together, allowing you to build better, more solid ActiveX components based on real-life objects.
So where can you go from here?
- Guide to Objects - Online Tutorial - Great multi-part guide on how to design and create your own objects in Visual Basic. By Sam Huggill, VBSquare
- VB COM - Book Neat 350-page guide to COM. Covers everything from classes through to MTS. Well-written, fairly compact but a little skimpy in parts. By Thomas Lewis
- Paul Sheriff Videos - Videos If you prefer to learn by watching, you might be interested in X video from Keystone Learning. Nice guide, not thorough but helps clear the mist. Supplementary manual costs a few dollars extra. By Paul Sheriff, Keystone Learning
- Professional VB6 Databases - Book - Covers creating professional objects based around a database design. Also covers lots of database-related whatnots. Excellent mix of content, great for enterprise work. By Charles Williams
- Murach's Visual Basic 6 - Beginner's guide to Visual Basic, but rapidly moves onto classes and the like. Covers a lot in a little, meaning some parts are scantily clad but on the whole, a good read.
Well that's about all for this mini-series so I guess that means it's time to get all conclusional.
Congratulations on completing the COM course!
In this three-part series, we've discovered what COM is, found out how to implement it with classes and ActiveX components, put together our own database class, uncovered that mysterious little thing they call asynchronous processing, plus found out where we can go to take our COM skills even further.
When you get started, you'll find COM to be a great little tool to simplify your programming. Sure, it might make your life a little more difficult at first but then it becomes one heckuva lot easier.
So get using COM in your next application and the best of luck with it!
Until the next time, this is Karl Moore signing off, wishing you all an absolutely supercool evening and the best programming career a developer could ever hope for. So from me - Goodnight!