Search code examples
windowscombackwards-compatibility

Is it must to change COM interface UID when adding new functions


I have a COM interface exposed from my application which is used by the third party plugins. Now, I need to add a new method to this interface but can not change the GUID of the interface as it will break all the existing plugins. I was told that if I add the new methods at the end of the interface it will work without issues as finally COM interface is a table of function pointers. These new methods will only be used by newly written plugins. I read this post and the first comment in Raymond Chen's blog: http://blogs.msdn.com/b/oldnewthing/archive/2005/11/01/487658.aspx but the situation mentioned in comment won't happen in my case as it is Windows only application. I know theoretically I should change the interface GUID. What would be the correct solution in this case? or will this approach work?


Solution

  • You can usually get away with adding new methods to the end of an existing interface without breaking compatibility. But as you have been reading, there are subtle cases where this can break. Especially when multiple inheritance is already being used.

    The correct solution is to simply declare a new interface for the new methods. Leave your existing interfaces alone. Then have your existing objects implement both interfaces or use inheritance to have the new interface inherit from the old.

    For example, if this is our original code. (I'll pretend this is done without IDL files for brevity).

    Original code:

    class IPublicInterface : public IUnknown
    {
     public:
        virtual void M1() = 0;
        virtual void M2() = 0;
    }
    
    class MyPublicClass : IPublicInterface
    {
     public:
        // IPublicInterface
        void M1();
        void M2();
    
        // IUnknown
        HRESULT QueryInterface(...);
        ULONG AddRef();
        ULONG Release();
    };
    

    Now let's say we want to add a new method to this object called M3 without breaking users of the existing interface and object. The correct solution would be to add a new interface. For convenience, it can just inherit from the original interface.

    class IPublicInterface2 : public IPublicInterface
    {
     public:
        virtual void M3() = 0;
    };
    

    Now modify the class to inherit from both this new derived interface:

    class MyPublicClass : public IPublicInterface2
    {
     public:
        // IPublicInterface
        void M1();
        void M2();
    
        // IPublicInterface2
        void M3();
    
        // IUnknown
        HRESULT QueryInterface(...);
        ULONG AddRef();
        ULONG Release();
    };
    

    Update QueryInterface to support both calls for both the original UUID of IPublicInterface as well as IPublicInterface2.

    HRESULT MyPublicClass::QueryInterface(GUID& iid, void** ppv)
    {
        // QI request for original interface
        if ((iid == uuidof(IPublicInterface) || (iid == uuidof(IUnknown))
        {
            *ppv = (IPublicInterface*)this;
            AddRef();
            return S_OK;
        }
        else if (iid == uuidof(IPublicInterface2)
        {
            *ppv = (IPublicInterface2*)this;
            AddRef();
            return S_OK;
        }
        return E_NOINTERFACE;
    }
    

    Alternatively, IPublicInterface2 does not need to inherit from the original IPublicInterface. In that case, the implementing class inherits from both interfaces. In the QueryInterface implementation, you will need to be consistent about how you handle a possible ambiguous cast to IUnknown.