Home
Coding Horror
Compatible
Line Numbers

Version Compatibility
in ActiveX Components

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:

    Public Sub Hello()
        MsgBox "Hello"
    End Sub

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:

    Public Sub Hello()
        MsgBox "Hello"
    End Sub

    Public Sub Goodbye()
        MsgBox "Goodbye"
    End Sub

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.

Key Spinner

© 1998 - 2009 Mark Hurst. All rights reserved.   Updated March 01, 2009                             sign the guest book