0
votes

I have added a new interface IAEx which is extended from the existing interface IA (derived from IDispatch).

What are the things to change in idl? I have changed the coclass definitions to inherit from the new one. I changed the coclass entry in idl which was like this earlier.

(I need the deafult interface as new one)

coclass CAx
    {

        [default] interface IA
        [default, source] dispinterface IAEvents;
    };

and changed to

coclass CAx
    {

        [default] interface IAEx
        [default, source] dispinterface IAEvents;
    };

can I change the deafualt interafce?

coclass definition change. old one

class ATL_NO_VTABLE CAx: 
...
public CCIDispatchImpl<IA, &IID_IA, &LIBID_CCALib>,

new one.

class ATL_NO_VTABLE CAx: 
...
public CCIDispatchImpl<IAEx, &IID_IAEx, &LIBID_CCALib>,

Is this fine?

COM MAP entries modification: Old one:

COM_INTERFACE_ENTRY(IA)
COM_INTERFACE_ENTRY2(IDispatch,IA)

new one:

COM_INTERFACE_ENTRY(IAEx)
COM_INTERFACE_ENTRY2(IDispatch,IAEx)

Do I need to add old interface also in COM MAP?

3
"Is this fine?" Well, does it work as intended?πάντα ῥεῖ
it works, but my worry is if it would affect the existing client?Sana
Does IAEx inherit IA?πάντα ῥεῖ

3 Answers

2
votes

No, this is a drastically breaking change to client programs. Golden rule #1 to keep in mind is that names are quite immaterial in COM, only uuids matter. Rule #2 is that COM components have machine scope, modifying a component affects every program on the machine that uses the component. Another way to say that is that COM has a strong DLL Hell problem.

So the first thing that will happens when you install your component on a machine is that every program that uses it will stop to work. They are still looking for the "IA" interface uuid and it is not there anymore. They fail with E_NOINTERFACE. Only way to avoid that is to get the client programs recompiled with your new type library and deploy them at the exact same time your updated COM component is deployed. This is often very hard to arrange since they don't have programmers or companies in common. Usually only the user can do this, they very rarely know how to do this correctly or know how to troubleshoot failure.

If you want your update to be backwards compatible then you must add a new interface to your coclass. It cannot be the [default] interface since existing client programs expect the legacy interface as the default. That however causes a new problem, client runtimes that use IDispatch often don't support anything but a single default interface. Usually because they don't have a notion of interfaces as a primary language construct. In other words, your client programmer has no way to call IUnknown::QueryInterface() and therefore cannot use your new interface at all. So not a general solution.

It is technically possible to violate the interfaces are immutable rule in COM. You can add new methods to the end of the IDispatch interface. Existing client code is unaware of them so will never call them and continue to operate correctly both with the old and the new version of your component. Assuming that you know how to maintain the legacy methods without causing a breaking behavior change, often harder than it looks. There is still a DLL Hell problem though, the world implodes when an updated version of the client code meets the old version of your component. This might seem unlikely at first sight but this tends to go wrong much later, when the machine is replaced or re-imaged. Very ugly scenario, the runtime failure is impossible to diagnose and anybody originally involved is not around anymore or doesn't remember the details.

Only truly safe way to do this is to create a new version. Modify all the uuids (LIBID, CLSID and IID) and change the DLL filename. Now the old and new version can co-exist and the client programmer can use your new version at his leisure. There might still be a deployment problem but the failure is easy to diagnose, the client program fails with "Class not registered".

0
votes

You should include the old interface in the COM MAP as well as if a client attempts to QueryInterface for the old interface it should receive a useful result and not an error. Add COM_INTERFACE_ENTRY2(IA, IAEx).

Otherwise it looks like you have everything. We add both interfaces into the coclass entry in the IDL file but I don't thing anything really uses that. ie:

coclass Ax
{
    [default] interface IAEx;
    interface IA;
};
0
votes

Whether it is fine or not depends on what is the goal exactly. Replacing interface is fine on its own, if you are concerned with not breaking compatibility with existing client, the most important thing is that your server still implements old interface IA. You should list the interface on the COM MAP (since you mentioned that IAEx is inherited from IA, you might need a COM_INTERFACE_ENTRY_IID macro):

COM_INTERFACE_ENTRY(IAEx)
COM_INTERFACE_ENTRY_IID(__uuidof(IA), IAEx) //COM_INTERFACE_ENTRY(IA)
COM_INTERFACE_ENTRY2(IDispatch, IAEx)

This way your server implements both IAEx and IA. Inheriting interfaces one from another is the thing I would rather suggest against, but you have what you have.

Updating coclass (as suggested in answer by patthoyts) makes update cleaner and is worth doing as well, however it is more important for rebuilding the clients rather than keeping compatibility with already existing built code: tools importing type library information will be able to see both interfaces if they at all care and are capable to handle non-default interfaces there.