by Peter G. Aitken ©1996
Originally published in Visual Developer magazine
Welcome to the first installment of Basically Visual, a column devoted to you guessed it Visual Basic programming. In a magazine devoted to visual programming, it seemed appropriate to provide regular coverage of the development tool that started it all and which, in the minds of many people, is still king of the mountain. Anything and everything VB-related is fair game for this column, with a slant toward things of interest to the advanced and professional programmer.
For the 98% of you who have never heard of me, a bit of background information may be in order. I cut my BASIC programming teeth about 20 years ago, writing statistical analysis programs on a PDP-8 computer connected to a teletype machine. I remember that we stored our programs on paper tape, which seemed a marvel of convenience compared with the punch cards of old. In the early 1980's we bought one of the first IBM PC's to come off the assembly line. As I recall we splurged to get a system with the unheard of total of 256 kilobytes of memory. It's hard to believe, but that first floppy-disk-only PC cost more than the 133 MHz Pentium that I just bought! I probably had more fun with it, though.
It seems that I have been writing about computers for almost as long as I have been writing programs for them. My start as an author came with the long-gone PC Tech Journal, which in fact is where I met the worthy editor of this publication. I soon moved on to books, and hard as it is for me to believe I have have written over 20 in the past 10 years. While my subjects have ranged far and wide, my constant interest in Basic is revealed by the fact that both my first book (QuickBasic Advanced Techniques, Que Corporation) and my most recent (Visual Basic 4 Programming Explorer, Coriolis Group), as well as several in between, are devoted to Basic programming.
Enough of this "let me tell you a little about myself " stuff, which is probably putting you all to sleep anyway. I had originally thought that an appropriate topic for my first column would be an overview of the new features in VB 4. Then I realized that, by the time you read this, the new VB will have been available for at least a few months and can hardly be called "new" any more. Most of you probably have a pretty good idea of what's new in Version 4, so I thought it would be a lot more interesting to take a look at the most fascinating new feature (at least in my mind), its greatly increased support for objects. I cant explain all the details of VB objects, but I can give you the basics and show you how its done.
No, Visual Basic still does not support object oriented programming (OOP) not in the strict sense, with support for inheritance, polymorphism, and the other object oriented hallmarks that youll find in C++ and Smalltalk. But remember, within Windows the term object is more often used in the general sense of a software component, and in the specific sense of OLE. For those of you who have been living alone in a cave for the past 5 years, OLE stands for Object Linking and Embedding, a central component in Microsofts vision of the future of computing.
Earlier versions of VB were perfectly capable of creating OLE clients, programs that use OLE objects. VB was capable of the linking and embedding side of OLE, in which an OLE object for example, an Excel spreadsheet is displayed within a VB application and can be manipulated using Excels tools and commands. It was also capable of OLE Automation, in which an OLE object is controlled "behind the scenes" to perform tasks required by the VB program. All very nice, but while VB was terrific at utilizing OLE objects, it could not be used to create them. Until now.
At the heart of VBs new object creation abilities is its Class module. Each Class module in a project defines a class and exposes it for use by other applications. A class is exposed by means of its interface. This does not refer to a visual interface, but rather to the properties and methods that the class makes available to other applications. An OLE classs properties and methods are the public variables and procedures you place in the class module.
OLE terminology can be a bit confusing. An OLE class, which you define in a VB class module, is best thought of as a blueprint, or plan. An OLE object is created from this blueprint when requested by an application. A parallel exists with Basic variables. A variable that you declare is an instance (object) of a certain data type (class). An OLE server is a program that exposes one or more classes for use by other applications, called OLE clients.
Theres no teacher like experience, and I know that I understood the whole idea of exposing OLE objects a lot better after I had created and tested my first one. Ill walk you through creating a simple server, and then show you how to test it. Well create something simple, a class to calculate square and cube roots. I dont think you would ever create an OLE object for something as simple as this, but its a good starting point.
Start VB and select New Project from the File menu. Select Options from the Tools menu, then click the Project tab in the Options dialog box. Enter the following settings:
Next, select Module from the Insert menu. Use the Insert Procedure command to add a Sub named Main to this module, making sure that the scope of the Sub is Public (as set in the Insert Procedure dialog box). Because of the Startup Form option we set earlier, this Sub is the first thing to be executed when the server is loaded. Its here that you would place any one-time initialization code required by the server. For this project we dont need any, so Main stays empty.
Finally, we need a Class Module, where the class itself will be defined. Select Class Module from the Insert menu. Use the Properties window to set its properties. The settings and an explanation of each follow:
If youre confused about the relationship between the Instancing and Public properties, welcome to the club. It took me a while to figure it out. If Public is set to False, the value of the Instancing property is ignored. Instances of the class can neither be created nor controlled from outside the project that contains the class. If Public is set to True, but Instancing is Not Creatable, then instances of the class cannot be created outside the project, but if an instance of the class exists (created by its own project, of course) then outside applications can use it.
Once the class modules properties are set we can add the declarations of the variables that will be the classs properties. This is done in the General Declarations section of the class module; heres the code:
Public Name As String
Private dateCreated As Date
Private theNumber As Double
Private theSquareRoot As Double
Public theCubeRoot As Double
The Option Explicit statement should already be present, placed there automatically by VB. If its not, you need to open the Options dialog box and, on the Environment tab, check the Require Variable Declarations option. Lacking the Option Explicit statement in a module, VB does not require variables to be declared. The result is that when you misspell a variable name ( and you will do it, more than once, too) it is not flagged as an "Variable Not Defined" error but is simply created as a new type Variant variable and initialized to 0. This can result in all sorts of pesky errors and bugs, and tracking them down can be a real pain in the you-know-where. Spare yourself this grief by always using Option Explicit. An added benefit is that the discipline of having to declare all variables forces you to think about the proper data type for each rather that taking the sloppy and lazy way out of using type Variant for everything.
If you examine these variable declarations youll see that three of them are Private and one is Public. A Public variable represents a property that is directly accessible, for both reading and writing, to OLE clients simply by referring to the variable (property) name qualified by the class name. A Private variable is not accessible to clients. It can be used for purely internal uses by the class, or it can be exposed to clients, for reading, writing, or both, by means of property procedures.
Property procedures are a central part of OLE server programming. While you may be able to create very simple servers without using property procedures, youll find them essential for most of your projects. You use a property procedure when you want to create a read-only property, perform some action when the property is read or set, or assign a Help topic to the property.
There are three varieties of property procedures. Property Get and Property Let are usually used in tandem, and permit the client to respectively read and set the value of a property in the OLE object. Property Set is used in place of Property Let when the property in question contains a reference to an object. Since the demonstration OLE server does not utilize object reference properties, Ill limit discussion to Property Get and Property Let.
Lets assume we want to create a property named Total. The Property Get procedure would take the following form:
Property Get Total() As Double
Total = theTotal
Here, theTotal is a private type Double variable declared in the module to hold the property value. The Property Let procedure would look like this:
Property Let Total(ByVal NewTotal As Double)
theTotal = NewTotal
The data type of the Property Let procedures argument must be the same as the type of the Property Get procedures return value. To see how these are used, assume that the OLE client has created an object named Ob of the class where these property procedures are defined. Then to set the property, you would write
Ob.Total = SomeValue
and the retrieve the value of the property you would write
AnotherValue = Ob.Total
When a property name (qualified by the object name, as always) is used on the left side of an assignment statement, the corresponding Property Let procedure is called and the value on the right side of the assignment statement is passed to the procedures argument. Likewise, when a property name is used on the right side of an assignment statement, the corresponding Property Get procedure is called and the return value is the value assigned in the procedure code (by the usual method of assigning a value to the procedure name).
As shown in this example, using Property Get and Let procedures doesnt offer any advantages over simply declaring a Public variable named Total in the class module to hold the property. The advantages come when you make the property read-only by omitting the Property Let procedure, or perform additional processing such as value checking in the procedures.
Theres more to property procedures, but weve learned enough to proceed with our sample server. To add a property procedure to the class module, select Procedure from the Insert menu, then select Property as the procedure type. Be sure the Scope remains at its default setting of Public. The procedure name is the name you want to use to refer to the property in client applications. When you add a property procedure, skeletons for both the Get and Let procedures are created. If youre not using the Property Let procedure, just leave it empty.
We have two Property Get procedures to create, one to return the calculated square root (stored in the variable theSquareRoot) and one to return the objects creation time and (date, stored in the variable dateCreated). Well see how these values get placed in these variables soon, but for now here are the two Property Get procedures you should create:
Public Property Get SquareRoot() As Double
SquareRoot = theSquareRoot
Public Property Get Created() As Date
Created = dateCreated
Note that we do not need Property Get procedures for the variables theCubeRoot, because it is Public, and for theNumber, because we will never need to retrieve its value. We will, however, need to set the value of the theNumber, and we will also need to perform the calculations whenever this variables value is set. For this we create a Property Let procedure named Number, as shown here:
Public Property Let Number(vNewValue As Double)
theNumber = vNewValue
theCubeRoot = theNumber ^ (1 / 3)
theSquareRoot = theNumber ^ (0.5)
Two things happen when this procedure is called which happens, as I mentioned earlier, whenever an OLE client references the Number property on the left side of an assignment statement. First, the value assigned to Number by the client is passed in the argument vNewValue and assigned to the private variable theNumber. Then, the cube root and square root of this value are calculated and assigned to the corresponding variables.
Each class module is automatically given Class_Initialize() and Class_Terminate() procedures. These are executed when a client application creates an instance of the class, and when the object is destroyed. You dont have to put any code in them, but if there are any initialization or cleanup chores required by your class, heres where the code would go. If you want to see when your class is instantiated (when an object is created from it) and destroyed, you can place MsgBox() calls in these procedures. Generally, however, code in these procedures should not interact with the user at all.
In our project we need no clean up, and during initialization our only task is to store the date and time in the private variable dateCreated. Open the Class_Initialize() procedure and add the single line of code as shown here:
Private Sub Class_Initialize()
dateCreated = Now
Thats it you can run your OLE server. You should always use the Start With Full Compile command to run an OLE server from within the VB environment, since this improves your chances of catching errors during compilation rather than later when the server is called by an OLE client. When it runs, the server wont appear to do anything its just sitting there waiting to be called by a client. It will continue to run until you select End from the Run menu. Minimize VB, and next Ill show you how to test the server.
In order to test our nice new class, well need an OLE Automation client. Well use VB to create this as well. Since the first copy of VB is busy running the OLE server, youll need to start a second copy of VB for this project. Ill bet a lot of you didnt realize you could do this! Yep, and it can be real handy in certain situations, such as this one. Itll be slow, however, unless you have enough memory to permit Windows to avoid using the swap file. But back to the OLE client. The client app has a single form containing four Command Buttons, two Text Boxes, and two Labels, as shown in the figure. You will be able to figure out the control names from the names of the corresponding event procedures given below.
In order to use the class that we just created, we must add a reference to it to the project, just like with any custom control. Select Reference from the Tools menu, and scroll through the list until you find Sample OLE Server this is the name we assigned for the OLE server projects Application Description project option. Select it, then close the dialog box. If you dont find the server listed here, the most likely explanation is that you forgot to run it from the other copy of VB. This step does not actually add the object to the project, but merely makes it available to the project.
While theres really no need to do so, you can press F2 to open VBs Object Browser to examine the classes (or, in this case, class) and their properties and methods. Pull down the Libraries/Projects list and select MyOLEServer - Sample OLE Server. These are the project name and application description that we entered in the servers Project Options. As shown in the next figure, the Classes/Modules list displays the name of the servers one class (MyDemoClass), and the Methods/Properties list displays the classs properties and, if it has any, methods.
In the forms General declarations section, we create a reference to the OLE class by declaring a variable of that type, as shown here:
Private TestObject As New MyDemoClass
Note the use of the New keyword, necessary because the variable TestObject will refer to a new instance of MyDemoClass. Without New, we could only make TestObject refer to an existing MyDemoClass object (using the Set statement).
Declaring a variable of type MyDemoClass does not actually create an instance of the class. This occurs the first time a property or method of the class is referenced. Well do this in the client apps Form_Load() procedure by assigning a name to the servers Name property:
Private Sub Form_Load()
TestObject.Name = "Object1"
Having the object perform the calculations is a simple matter. Clicking either the Cube Root or Square Root button assigns the number in the first Text Box to the objects Number property. Since the actual calculations are performed in the Number propertys Let procedure, we can immediately read back the answer from the appropriate object property. The code for the two Click event procedures is shown here:
Private Sub cmdCubeRoot_Click()
TestObject.Number = txtInput.Text
txtOutput.Text = TestObject.theCubeRoot
Private Sub cmdSquareRoot_Click()
TestObject.Number = txtInput.Text
txtOutput.Text = TestObject.SquareRoot
Obtaining information about the object is equally simple. The objects Name and Created properties are read and concatenated into a string for display in a Message Box:
Private Sub cmdInfo_Click()
Dim Msg As String
Msg = "Object " & TestObject.Name & _
" was created " & TestObject.Created
Thats all there is to the client app. Run it, enter a value in the first Text Box, and click one of the calculation buttons. Since theres no error checking in the program, be a good fellow and enter a nice positive value no negative numbers, please! Click the Object Information button to see the objects Name and creation time/date. Hey, its OLE in action! Look, Ma, Im a programmer!
When you run an OLE server from within the VB environment, it is automatically, but temporarily, added to the Windows registry. Terminate execution and the registry entry vanishes. If you create an EXE file with the Make EXE File command, the server and its classes are permanently added to the registry. But what about end-users who are not running VB? For them, the class is registered when it is installed using the Setup toolkit, or when it is executed as a regular EXE file.
Theres a lot more to know about creating OLE servers with VB, and I will be revisiting the topic in future columns.