tags:

views:

239

answers:

1

COM is known to make backward-compatibility possible when releasing new components or applications. This is possible because interfaces in COM are stable i.e. they do not change.

I tried hard to find a reference or book which deals with a descrition on how to evole a COM interface from version to version.

The following are my requirements:

We have an application which can be driven by ole automation. New versions of this application can be installed in parrallel to older versions.

COM clients of this applications can use the version independent PROGID in which case they work with the latest version of the application or a version dependent PROGID in which case they work with the specific version of the application.

Changes to the COM automation should not break any of the clients.

Lets see an example:

#include <olectl.h>
#include <fxbase\autoif.h>

[
    uuid(A1B3A66F-806F-46a2-82D9-9C278F415148),
    lcid(-1),
    version(1.0)
]

library LIB
{   
        importlib("stdole2.tlb");

        [
            uuid(82CDE055-790A-4505-BF3E-3282170C8FC6),
            helpstring("Document"),
            oleautomation,
            dual,
            nonextensible
        ]
        interface IDocument : IDispatch
        {
                [id(0x00000001), propget, helpcontext(0x0012c94a)]
                HRESULT Name([out, retval] BSTR* psName);

                [id(0x00000001), propput, helpcontext(0x0012c94a)]
                HRESULT Name([in] BSTR psName);
        }

        [
            uuid(919B9E6E-76C0-4c23-A188-5840E5900997),
            helpstring("Application object."),
            oleautomation,
            dual,
            nonextensible
        ]
        interface IApplication : IDispatch
        {
            [id(0x00000001), propget, helpstring("Returns the active document of the application.")]
            HRESULT ActiveDocument([out, retval] IDocument** retval);
        }

        [
            uuid(E0AA6FCA-AEF1-460b-A1F9-26250C28594B),
            helpstring("Application 1.0 Class"),
            appobject
        ]
        coclass Application
        {
            [default] interface IApplication;
                                interface IDispatch;
        }
}

Lets say I want to publish version 2.0 of this application which extends some interfaces. Here is my naive approach to version 2.0:

#include <olectl.h>
#include <fxbase\autoif.h>

[
    uuid(3D4688A2-91F8-4cd8-989A-845810A05557),
    lcid(-1),
    version(2.0)
]

library LIB
{   
        importlib("stdole2.tlb");

        [
            uuid(82CDE055-790A-4505-BF3E-3282170C8FC6),
            helpstring("Document"),
            oleautomation,
            dual
        ]
        interface IDocument10 : IDispatch
        {
                [id(0x00000001), propget, helpcontext(0x0012c94a)]
                HRESULT Name([out, retval] BSTR* psName);

                [id(0x00000001), propput, helpcontext(0x0012c94a)]
                HRESULT Name([in] BSTR psName);
        }

        [
            uuid(AF404510-216A-407e-99F4-0636AF071B68),
            helpstring("Document"),
            oleautomation,
            dual,
            nonextensible
        ]
        interface IDocument : IDocument10
        {
                [id(0x00000001), propget, helpcontext(0x0012c94a)]
                HRESULT Type([out, retval] BSTR* psType);

                [id(0x00000001), propput, helpcontext(0x0012c94a)]
                HRESULT Type([in] BSTR psType);
        }

        [
            uuid(919B9E6E-76C0-4c23-A188-5840E5900997),
            helpstring("Application object."),
            oleautomation,
            dual
        ]
        interface IApplication10 : IDispatch
        {
            [id(0x00000001), propget, helpstring("Returns the active document of the application.")]
            HRESULT ActiveDocument([out, retval] IDocument** retval);
        }

        [
            uuid(6A851C3F-21DF-4f5e-A4D6-2EF5A9D234C6),
            helpstring("Application object."),
            oleautomation,
            dual,
            nonextensible
        ]
        interface IApplication : IApplication10
        {
            [id(0x00000002), propget, helpstring("Is the application visible.")]
            HRESULT Visible([out, retval] BOOL* retval);
        }

        [
            uuid(AA760349-1682-4ab6-BF0C-C02E620715CF),
            helpstring("Application 2.0 Class"),
            appobject
        ]
        coclass Application
        {
            [default] interface IApplication;
                                interface IDispatch;
        }
}

Is this the correct way to do this?

Should I add a class Application10 and Application20 to the Registry to add a possiblitity to instantiate the different versions of the application for script clients?

Is it right to change the version and the GUID of the type library?

IDocument in version 2.0 has a new IID. Can I still use IDocument in IApplication.ActiveDocument?

How should I register the coclass or interface in the Windows Registry in the different versions?

Please note that I do not use ATL or other libraries other than the WIN32-API.

If you know where can I find information on this (books, references, etc.) please suggest one.

I would really appreciate your help.

+8  A: 

Yes, you need to change the class id of your object. However you can maintain a version-independent program id like "Word.Application" or "InternetExplorer.Application" that redirects to the class id of the current version.

If you decide to keep old interfaces (recommended), you need to implement both new and old interfaces in your new object.

You may want to check Microsoft Office's registry entires. It maintains backward compatibility quite well.

Suggested name of new interfaces would be interfact name plus version number, such as IHtmlDocument6.

Sheng Jiang 蒋晟
I really would like to learn from the Microsoft Office registry entries but I do not have an old version. Do you know freeware programs to learn from?
frast
Why is it necessary to change the CLSID? The implementation will still implement all interfaces it did, right?
Kim Gräsman
@frast or check IE's.
Sheng Jiang 蒋晟
@Kim Gräsman If you do not change the CLSID multiple versions can not be installed side-by-side and you clients are forced to use one version. This can cause problems when you implementation changed in a way that is not expected by your clients.
Sheng Jiang 蒋晟
Backward compatibility is very hard to maintain. MSXML apparently gave up the practice of version-independent progID (http://msdn.microsoft.com/en-us/library/ms757837(VS.85).aspx).
Sheng Jiang 蒋晟
I see -- that makes sense, thanks!
Kim Gräsman
I really would like to accept your answer since you are the only one who anwered at all, but I miss something. IDocument in version 2.0 has a new IID. Can I still use IDocument with this new IID in IApplication.ActiveDocument or does that break backward-compatibility?
frast
Once you published an interface, you can never change it, including the IID.
Sheng Jiang 蒋晟
The Microsoft Office Interfaces do change without changing the IID. They only add methods to the end of the interfaces, so that old clients are still binary compatible. But I think new clients do crash when used with an old MS Office version, because there is no possibility to find out if really a newer interface is supported.
frast
Which interface are you talking about? Most of Office programmers use IDispatch to access office objects and that one never changes. It is the implementation of IDispatch changes over time. People write version-specific code that are guarded by version numbers like if(Application.Version.Contains('12.'))
Sheng Jiang 蒋晟
You are right, IDispatch never changes. I was talking about the binary interface which you use in early binding. Late binding is not a problem.
frast
Almost all other interfaces are documented. You can guess from dsoframer's source code, but dsoframer is not supported so there is no guarantee the interfaces won't change in the future.
Sheng Jiang 蒋晟