|
THE 'COMPATIBLE OLE SERVER' FIELD on VB4's Tools/Options/Project… dialog is one of the least-understood VB options, and its
description is the one most likely to be fudged in VB programming books. The reason is simple: to properly understand version compatibility you need to know some things about OLE that traditionally were at the fringes
of what Visual Basic developers were required to know. That's changing fast, of course, as ActiveX (formerly OLE) permeates VB5 at almost every level: most of our efforts as developers will focus on building ActiveX
components of one sort or another.To understand version compatibility you need to know a little about Type Libraries, Interfaces and Class IDs. This implies that you'll also know about GUIDs, the globally
unique values that are used to identify these things. I'm going to assume that you have at least a passing familiarity with these terms - if not, see the references at the end. (If you want to read along anyway, the
GUIDs are the 32-character hexadecimal strings labelled 'uuid' in the type library dumps.) The best way to understand the operation of the Compatible OLE Server field is to play with it, so what follows is
a record of the changes we can see in an OLE Server's Type Library as we make various changes to the server. To look at the Type Library I used OLEVIEW.EXE. Unfortunately this doesn't ship with VB4, although you don't
really need it to follow along (you get OLE2VW32.EXE with VB4, but that doesn't decompile the Type Library in the format shown here). If you have VB5 you'll find OLEVIEW in the Tools/Oletools directory.
I've referred to VB4 dialogs throughout, but everything here also applies to VB5 when you select 'Binary Compatibility' in the Version Compatibility section of the Project/Properties/Component tab. You'll also need to
imagine me saying 'ActiveX component' wherever I say 'OLE Server'. The server we're looking at contains a single public class and is compiled to a .EXE file, compatible2.exe. Original Server The original public class definition looks like this:
The Type Library extracted from compatible2.exe looks like this: // Generated .IDL file (by the OLE/COM Object Viewer) // // typelib filename: compatible2.exe
// Forward declare all types defined in this typelib interface _CMarkH;
[ uuid(CE345AB9-028D-11D1-9425-00201860B064), version(1.0), helpstring("MarkH Compatibility test 2") ]
library CMarkHTest { // TLib : OLE Automation : {00020430-0000-0000-C000-000000000046} importlib("stdole32.tlb");
[ odl, uuid(CE345AB7-028D-11D1-9425-00201860B064),
version(1.0), hidden, dual, nonextensible, oleautomation ] interface _CMarkH : IDispatch { [id(0x6003001c)] HRESULT _stdcall Hello();
};
[ uuid(CE345AB8-028D-11D1-9425-00201860B064), version(1.0) ] coclass CMarkH { [default] interface _CMarkH; }; }; Making a VersionCompatible Change Now we'll add a new method to the interface:
We fill in the Compatible OLE Server field (Tools/Options/Project) with the full path of the original compatible2.exe, so when we build the new server VB treats this as a VersionCompatible revision. Here's
the new Type Library: // Generated .IDL file (by the OLE/COM Object Viewer) // // typelib filename: compatible2.exe // Forward declare all types defined in this typelib interface _CMarkH;
[ uuid(CE345AB9-028D-11D1-9425-00201860B064), version(1.1), helpstring("MarkH Compatibility test 2") ] library CMarkHTest { // TLib : OLE Automation :
{00020430-0000-0000-C000-000000000046} importlib("stdole32.tlb");
[ odl, uuid(CE345ABA-028D-11D1-9425-00201860B064), version(1.1), hidden, dual,
nonextensible, oleautomation ] interface _CMarkH : IDispatch { [id(0x6003001c)] HRESULT _stdcall Hello(); [id(0x60030020)]
HRESULT _stdcall Goodbye(); };
[ uuid(CE345AB8-028D-11D1-9425-00201860B064), version(1.1) ] coclass CMarkH { [default] interface _CMarkH; };
}; Of the three GUIDs in the Type Library, only the interface GUID has changed. This makes sense, as we've changed the interface. The Type Library and Class GUIDs remain the same, although the Type Library
version attribute has been incremented to 1.1. We can put the new version of compatible2.exe in place of the old one and existing clients will continue to work. Making a VersionIncompatible Change This time we'll change an existing method by adding a parameter:
Public Sub Hello(ByVal sName As String) MsgBox "Hello " & sName End Sub
Public Sub Goodbye() MsgBox "Goodbye" End Sub
VB treats this as a VersionIncompatible revision, since we've changed part of the existing interface - any client program that calls the Hello method wouldn't work with the new server. VB warns us of the
incompatibility, and the only way we can build the EXE is to clear the Compatible OLE Server field. Here's the new Type Library: // Generated .IDL file (by the OLE/COM Object Viewer) //
// typelib filename: compatible2.exe // Forward declare all types defined in this typelib interface _CMarkH;
[ uuid(CE345ABD-028D-11D1-9425-00201860B064), version(1.0),
helpstring("MarkH Compatibility test 2") ] library CMarkHTest { // TLib : OLE Automation : {00020430-0000-0000-C000-000000000046} importlib("stdole32.tlb");
[
odl, uuid(CE345ABB-028D-11D1-9425-00201860B064), version(1.1), hidden, dual, nonextensible, oleautomation ] interface _CMarkH : IDispatch {
[id(0x6003001c)] HRESULT _stdcall Hello([in] BSTR sName); [id(0x60030020)] HRESULT _stdcall Goodbye(); };
[ uuid(CE345ABC-028D-11D1-9425-00201860B064),
version(1.1) ] coclass CMarkH { [default] interface _CMarkH; }; }; This time all three GUIDs have changed, which means that this server is completely divorced
from the original. If we simply replace the old compatible2.exe with the new one, client programs will fail with error 429 ('OLE Automation server can't create object') when they try to create a CMarkH instance.
Making a VersionIdentical Change
This time we'll simply change the internals of a method:
Public Sub Hello(ByVal sName As String) MsgBox "Hello " & sName End Sub
Public Sub Goodbye() MsgBox "Goodbye, and good riddance!" End Sub
This is the trivial case - we haven't made any changes to the interface, so VB treats this as a VersionIdentical revision. To switch version-checking back on we need to fill in the Compatible OLE Server
field again with the full path of the compatible2.exe we built in the previous step. Here's the new Type Library: // Generated .IDL file (by the OLE/COM Object Viewer) //
// typelib filename: compatible2.exe // Forward declare all types defined in this typelib interface _CMarkH;
[ uuid(CE345ABD-028D-11D1-9425-00201860B064), version(1.0),
helpstring("MarkH Compatibility test 2") ] library CMarkHTest { // TLib : OLE Automation : {00020430-0000-0000-C000-000000000046} importlib("stdole32.tlb");
[
odl, uuid(CE345ABB-028D-11D1-9425-00201860B064), version(1.1), hidden, dual, nonextensible, oleautomation ] interface _CMarkH : IDispatch {
[id(0x6003001c)] HRESULT _stdcall Hello([in] BSTR sName); [id(0x60030020)] HRESULT _stdcall Goodbye(); };
[ uuid(CE345ABC-028D-11D1-9425-00201860B064),
version(1.1) ] coclass CMarkH { [default] interface _CMarkH; }; }; As we would expect, this Type Library is identical to the previous one. No interface changes
were made, so all the GUIDs and the Type Library version number remain the same. References
Bruce McKinney's Stealing Code with Type Libraries
article on the MSDN library disk expands on the coverage from his book Hardcore Visual Basic and is a great introduction to type libraries. Peet Morris's Minutiae chapter in Advanced Visual Basic 5
also looks at type libraries. For an exhaustive treatment of ActiveX from a Visual Basic perspective you're going to want Dan Appleman's Developing ActiveX Components with Visual Basic 5.0
, and if you want the whole story you'll need Inside OLE, by Kraig Brockschmidt (you'll need to understand C and have a copy of Visual C++ to get the most from this book).
Article © 1998 VBUG. Reproduced with permission. |
|