Objects from CreateObject
Let’s start with the granddaddy of object creation statements, CreateObject.
In the old days, the only way to define a class was to write it as a COM Automation component in another language (usually C++). You might not think of a COM Automation-enabled application such as Excel or Visio ShapeWorks as a group of classes, but to a Visual Basic program, that’s pretty much what it is.
So let’s say you have the COM Automation-enabled StoryTeller application and you want to create a CStory object in your Visual Basic program. To do this, you’ll call some methods and properties to manipulate stories. (StoryTeller was one of the first COM Automation applications and the first version came out long before type libraries even existed. Stories, Inc., sent you a type library for the old version last year, but you thought it was marketing junk and threw the disk away.) To use a CStory class, you need to declare an object variable:
Dim objStory As Object
You might prefer to declare this object with type CStory or something more specific than Object, but you can’t because you don’t know the name of the class, and even if you did, you can’t use it in Visual Basic at design time without a type library, and you don’t yet know there is such a thing. You have to connect to the class at run time using CreateObject. In order to use CreateObject, you must learn the programmatic ID ProgID string of the class either from documentation or from samples provided by the vendor. By convention, the ProgID string consists of the name of the server application followed by a dot, followed by the name of the class. Nothing enforces this convention except the desire of vendors to meet the expectations of users. Fortunately, the StoryTeller documentation tells the ProgID and even gives an example of how to use it:
Set objStory = CreateObject(“StoryTeller.CStory”)
objStory.Declaim 9, “Easy”
§
Now let’s look at what happens behind the scenes. The CreateObject function looks in the system registry (in HKEY_CLASSES_ROOT) to find the key called StoryTeller.CStory. Under this entry, it finds the CLSID for the CStory class. The CLSID is a 128-bit Globally Unique ID (GUID). (I’ll tell you more about GUIDs in Chapter 10.) CreateObject looks up this number in a table of GUIDs under the CLSID key and learns (among other things) the full path of the StoryTeller program, C:\STELLER\STELLER.EXE. Next it checks to see if STELLER.EXE is already running and, if not, runs it. Finally COM binds the CStory class to the objStory variable.
Binding means that the objStory variable is assigned the base address of the code for CStory objects within the running STELLER.EXE program. Whenever your program uses a CStory property or method, COM will look up the pointer for that property or method code as an offset from the base address of the object. You got all that? Well, never mind. We’ll be getting into more COM details in Chapter 10.
For now, the important point is that the variable is bound to the class, and it’s bound late. You’ll hear a lot about early binding and late binding in the rest of this book. Late binding means that the variable is connected to the class at run time. This is evil. If you learn only one thing from this book, it should be how to avoid late binding. Early binding means that the variable is connected to the class at compile time. This is good. You should always try to achieve early binding. Alas, there was no such thing back in the old days when the story server was written.
When you call the Declaim method of the CStory object, Visual Basic has to look up the method by name in the class data table to determine where the Declaim code is located. It has to pass arguments in Variants (whether the parameters are Variant or not) and do various other time-consuming things to get everything set up. It uses a COM interface called IDispatch (which I’m not going to explain) to do all this messy stuff. The important thing is that late binding
always goes hand in hand with IDispatch calling. Like late binding, IDispatch calling is—well, perhaps evil is too strong a word—but it’s definitely in bad taste. We want to avoid late binding and IDispatch whenever possible.
NOTE Although Visual Basic programmers should avoid CreateObject if at all possible, a similar convention is still commonly used in a related language, VBScript. Objects are created through their CLSIDs (not their ProgIDs) in VBScript, and all object references are late-bound. But that’s another book.