3
votes

I am updating an MFC application that contains a custom ActiveX control. As a part of the update I have had cause to add new methods to the ActiveX control and so it now has a different interface to the old version. The changes had no impact on the original methods and so older clients can still use the new component.

I've got everything working but I know that what I have done is smelly! What is the correct way of updating a COM/ActiveX interface.

This component was built using MFC and Googling does not provide much help beyond basic 'Create an ActiveX control with MFC' type tutorials. I can find loads of stuff about ATL but I don't want to port the component over.

I have had various suggestions from colleagues such as change the guids and inherit the interface but nothing definitive.

So generally what is considered best practise for updating COM interfaces?

And if you happen to know how this is specifically done in an MFC environment that would be really helpful too.

I've tried creating a second interface (see below) as suggested by MSalters but I'm not sure that I've gone about it correctly. I've created a new interface and a new coclass in the odl file. This results in two separate wrapper classes being generated by MFC in the client Application, one derived from CWnd for coclass Test and one derived from COleDispatchDriver for coclass Test2 - I would have expected two similar wrapper classes....

library TestLib
{
    importlib(STDOLE_TLB);

    // This is the original interface.......

    [ uuid(D2F8E5A8-8A95-463C-814F-B3CF84286223)]
    dispinterface _DTest
    {
        properties:
        methods:
            [id(1)] short TestMethod();
    };

    //  Class information for CTestCtrl
    [ uuid(1DBD2333-2073-4FB6-89AC-E4B200ADED48), control ]
    coclass Test
    {
        [default] dispinterface _DTest;
    };


    //  This is the new interface.

    [ uuid(D2F8E5A8-8A95-463C-814F-B3CF84286224)]
    dispinterface _DTest2
    {
        properties:
        methods:
            [id(1)] short TestMethod();
            [id(2)] short TestMethod2();
    };

    //  Class information for CTestCtrl2

    [ uuid(1DBD2333-2073-4FB6-89AC-E4B200ADED49), control ]
    coclass Test2
    {
        [default] dispinterface _DTest2;
    };
};
2

2 Answers

4
votes

Depends.

If you do have customers that are compiling their own code (C++ or C# or VB) against your controls's type library, .h file, or .idl file, you likely need to change the COM guids.

Here are the cases where you don't have to change the COM guids:

  1. No 3rd party developers are consuming your code. No one would be broken if you changed the interface.

  2. It's an ActiveX control hosted in a webbrowser and accessed through Javascript.

  3. All the software depending on your COM DLL ships together with the updated version of your control.

  4. It's "internal". Anyone dependent can quickly recompile if needed.

If any one of the above is true, then you don't have to change COM guids. Just add the new methods to the existing interface declaration. Recompile all dependent software with the change.

Here are the cases where you should be careful.

  1. Someone else has already compiled (C++, C#, or VB) and shipped software against your existing interface - and they can't immediately upgrade when you ship. New methods should be declared on a new COM interface. The existing coclass declaration gets amended to support this interface as well.

  2. You are removing methods, changing behavior, or otherwise making a breaking change to shipping software. Change your guid on the CoClass such that it can possibly co-exist side by side with those dependent on the old version. Rename the DLL as well such that it doesn't necessarily overwrite the old one on upgrade.

In your above example, I don't think you need to declare a new coclass - just a new interface. And your new interface doesn't need to implement the methods of the first. Just make mark both interfaces on the coclass.

dispinterface _DTest
{
    properties:
    methods:
        [id(1)] short TestMethod();
};

//  This is the new interface.
[ uuid(D2F8E5A8-8A95-463C-814F-B3CF84286224)]
dispinterface _DTest2
{
    properties:
    methods:
        [id(2)] short TestMethod2();
};


//  Class information for CTestCtrl
[ uuid(1DBD2333-2073-4FB6-89AC-E4B200ADED48), control ]
coclass Test
{
    [default] dispinterface _DTest;
    dispinterface _DTest2;
}

};

1
votes

You can always add interfaces. The new control can simply implement the old and new interfaces at the same time. Inheritance is an easy C++ technique to recycle large parts of the old interface and implementation.