One of COM's most powerful features is that it lets you
In addition to improving the
The question then becomes, "How do you strike a balance between maximum object extensibility and backward compatibility?" Understanding the rules of COM versioning is critical. Clients that use custom vTable binding expect every method in every interface to
The best way to approach COM versioning is to think through the expectations that each
When it comes to versioning
What does a vTable-bound client application know about? It knows about each CLSID and IID defined in the type library of your server. It also knows the method signatures defined within each interface. You can revise a COM server used by custom vTable-bound clients if you don't change any of these things.
You should understand all aspects of version control. You must also consider two points of view when it comes to extending an object. The
The Visual Basic documentation and the Visual Basic IDE recognize three levels of compatibility when you rebuild a component:
version identical,
version compatible,
and
version incompatible
. When a later release of a component serves up its interfaces in a version-identical manner, this means that the physical vTables and the calling syntax for each method remain the same. This is consistent with the vision of the COM
According to the COM specification, to extend your object by adding new methods, you must create a new interface. Chapter 2 demonstrated this technique in Visual Basic using two interfaces, IDog and IDog2 . The new object is responsible for implementing both the old interface and the new interface. Old clients use the original interface, and new clients use the extended interface. With this versioning scheme, you can continue to extend your objects over time by creating other interfaces such as IDog3 , IDog4 , and IDog5 .
Unfortunately, the COM
Here's how it works. You can safely extend an interface by adding new methods if you don't change the signatures of any existing method. Adding methods to a preexisting interface means extending the physical layout of the vTable by adding new entries at the end. An extended vTable defines the same method signatures as before, and it adds one or more new methods below. This type of vTable is version compatible . Old clients are oblivious to the new entries in the vTable. They can use a version-compatible vTable in the same way they use the original. New clients, on the other hand, can take advantage of the new methods added to the second version. This means that you can extend the functionality of an object without explicitly creating a new interface.
This versioning technique is important to Visual Basic programmers who don't use user-defined interfaces. As you know, when a programmer compiles a creatable class with public
So what's the problem with a version-compatible interface? Why does the idea make COM purists sick to their stomachs? The problem has to do with a scenario in which a new client comes in contact with an older version of the object. For instance, suppose an object's default interface had 5 methods in the first version and was then extended to contain 10 methods in a
From the COM purist's point of view, an interface (as specified by an IID) is a contract that can never change. Adding methods to the end of the vTable makes the contract invalid. The Visual Basic team believes that it is acceptable to add methods to the end of the vTable as long as you ensure that a new client never activates an older version of the object. If a new client were to activate an older version of the object, it would expect methods that are absent in the original interface. As you will see, Visual Basic generates a trappable run-time error whenever a new client tries to activate an object from an earlier, incompatible, version of the server.
Visual Basic uses a mechanism known as
IID forwarding
to make interface compatibility work in a production environment. As you'll see in the
Whenever a new interface is either explicitly or implicitly created in your server, Visual Basic automatically generates a new IID for it. When you create a version-compatible interface by adding new methods in a subsequent release, Visual Basic generates a second IID for it. (This means that Visual Basic is adhering to the rules of interface
The old version of the object doesn't know anything about the new IID. When a new client calls QueryInterface on the old object and attempts to bind using the new IID, the call fails. And this is the behavior you would expect from any COM object. If an object doesn't support the behavior behind an IID, it should fail the call to QueryInterface and put the responsibility on the client to degrade gracefully and decide what to do next.
Visual Basic also builds the self-registration code into the second version of the server to add entries for both the original IID and the new IID. It modifies the original IID by adding a special Forward Registry key, as shown in Figure 5-3. The universal marshaler uses this key to locate the type library for the original IID on the local machine to generate a proxy and a stub. When the universal
Figure 5-3. Visual Basic uses interface forwarding to make version-compatible interfaces work in the production environment. The universal marshaler can use the definition of either the original interface or a version-compatible interface when it builds a proxy or a stub.
Figure 5-4 shows the Component tab of the Project Properties dialog box. When you build or rebuild a server, you must assign to the project one of the three version compatibility modes: No Compatibility, Project Compatibility, or Binary Compatibility. The default setting for a new project is Project Compatibility, but you should be aware of which compatibility setting is in use when you rebuild your server. If you don't watch out, Visual Basic will change your GUIDs every time you rebuild your server. This can lead to broken compiled client applications and can confuse application developers who work with your server.
Figure 5-4. A project's version compatibility setting determines the level of compatibility built into a server when you choose the Make command from the File menu.
No Compatibility mode always generates a new set of GUIDs. A new GUID is created for each IID and CLSID in the server as well as for the server's type library. Precompiled client applications can't use subsequent builds when you select this mode. Also, because the GUID of the type library is changed on every build, application developers who use your server in the Visual Basic IDE will find that your server reference is missing when they try to use your new build. The developer must fix this by rereferencing the type library in the References dialog box (which you
You should use Project Compatibility mode when you define the methods in your classes and interfaces. This mode assumes that none of your work is cast in stone and that you don't yet expect client applications to be compiled against your server. When you work in this mode, Visual Basic 5 creates a new set of CLSIDs and IIDs every time you rebuild your project. When you overwrite a server in this mode, Visual Basic unregisters it before it's overwritten. This is important. If Visual Basic didn't unregister your server, your Registry would fill up with invalid IIDs and CLSIDs. Note also that you should always unregister a server before deleting it manually. Using discipline with server registration and unregistration will keep the Registry of your development workstation as clean as possible.
The only GUID that is held constant during a project-compatible build is the GUID for the type library. In the early stages of component prototyping, this is quite useful because other Visual Basic projects for client applications can retain a logical pointer to your server's type library. When you create a new build, any other programmer can use it from within the Visual Basic IDE. Because client application developers always see the newest version of your type library, they see the latest definitions of your
The main limitation of project compatibility is that a compiled client application can't use the next build of your server. This is because new CLSIDs and IIDs are generated for each build. Visual Basic 6
You can put your project into Binary Compatibility mode by selecting the appropriate option on the Components tab of the project's Properties dialog box. You must also point to a compiled server file that will serve as a reference. One way to do this is to create an initial build of your server in a subdirectory named \Release1. After you point the Visual Basic IDE to a reference, you can rebuild your server in Binary Compatibility mode. In this mode, you can't overwrite the server that's being used as a reference. You should keep track of each server binary that's released into the production environment. Many developers create a set of directories for this purpose named Release1, Release2, Release3, and so forth.
Binary Compatibility mode does more than just retain your CLSIDs and IIDs. It also
In Binary Compatibility mode, you can't alter an existing method signature. This means you can't change the
You can safely change any existing method implementations in Binary Compatibility mode. When you think about it, this is what COM is all about. You should always be able to change an implementation as long as you don't change the physical nature of the calling syntax. Visual Basic lets you go one step further and add new methods to your classes and interfaces. When you add methods to an interface in a second version of your server, Visual Basic creates a new interface that is version compatible with the original.
When you add new methods to the default interface of a class in a Visual Basic project in Binary Compatibility mode, some interesting things happen when the server is rebuilt. For example, suppose you are working on a server with a class named CDog that defines eight public methods. The first time you build the server, Visual Basic automatically creates an interface named _CDog with an IID that defines these eight methods. If you add four new methods to this class and rebuild the server in Binary Compatibility mode, Visual Basic creates a new IID for the new interface that contains all 12 methods. Visual Basic also provides support in the new object and server to deal with clients using either IID.
The implementation of
QueryInterface
behind the
CDog
object hands out a reference for the new IID when a client asks for either the new IID or the old one. Clients built against the original version of the server get a reference to the new IID, but they will know about only the first 8 of the 12 methods defined in the vTable. Because the new interface is version compatible with the old one, it meets the expectations of the client and things work fine. Remember that version-compatible servers also contain registration code. Each server must add a Forward key for each
Visual Basic lets you build version upon version of compatible servers. For instance, version 4 can be compatible with version 3, which can be compatible with version 2, which can be compatible with the original version. A client that knows about only the original version of the interface might be forwarded across many compatible IIDs before it
At some point, you might need to change your server so much that it doesn't make sense to worry about compatibility with clients already in production. This can happen if you have many method signatures that need to be overhauled or if you have a long line of version-compatible servers that are
To create a build that is version incompatible, you change your projectwide compatibility setting to No Compatibility. When you rebuild your server in this mode, Visual Basic changes all the GUIDs, including the one for the type library. It also ignores any compatibility support for earlier interfaces defined in earlier versions of the server. After you create a build in No Compatibility mode, you should set the project back to one of the other compatibility modes.
After this build, no existing client can use the new server. Any references to your type library will also be invalid. If you plan to leave earlier client applications in production using an older version of the server, you should change the project name and the project description to avoid confusion. For instance, you should name the new server DogServer2.dll so that all the client applications using DogServer.dll can continue to run in the production environment without encountering a naming conflict.
Compared with vTable clients, automation clients aren't very knowledgeable about your server. They don't know about any GUIDs or the physical layout of any custom vTable. Instead, they know the ProgID and the
When dealing with automation clients, you can use any of the compatibility modes because automation clients use ProgIDs, not CLSIDs and IIDs. Moreover, automation clients don't require any custom vTable binding. The only concern you have is that method signatures must be compatible with any dependencies that automation clients have built. You certainly have a lot more flexibility when method signatures only have to be compatible as opposed to identical . For instance, you can change an integer parameter to a different type, such as a long or a double. Or you can add optional parameters to the end of a method signature. As you can see, if you can assume that your server will be used only by automation clients, your versioning concerns become far less complex.