Ambiguities within Interface Hierarchies

 < Free Open Study > 



Back in Chapter 2, you were introduced to the idea of interface hierarchies. Let's extend the example briefly mentioned at that point, and create a set of related interfaces modeling automobile behaviors:

click to expand
Figure 8-3: A hierarchy of related interfaces.

Now that we are knee-deep in COM, we know that ICar must derive from IUnknown directly (as it is the topmost node). The IDL code to capture this interface hierarchy would be as so:

// ICar is the master interface. [ object, uuid(8945C9D5-2C00-11D3-B901-0020781238D4)] interface ICar : IUnknown {      // Behavior for any car. }; // ISportsCar is derived from ICar. [ object, uuid(2B622560-2C42-11d3-B901-0020781238D4)] interface ISportsCar : ICar {      // Behavior for a standard sports car. }; // IReallyFastSportsCar is derived from ISportsCar. [ object, uuid(2B622561-2C42-11d3-B901-0020781238D4)] interface IReallyFastSportsCar : ISportsCar {      // Turbo boost support, water-cooled engine. }; // IFamilyCar is derived from ICar. [ object, uuid(2B622562-2C42-11d3-B901-0020781238D4)] interface IFamilyCar : ICar {      // Practical and clean. }; // IBoringFamilyCar is derived from IFamilyCar. [ object, uuid(2B622563-2C42-11d3-B901-0020781238D4)] interface IBoringFamilyCar : IFamilyCar {      // Practical, clean and at least one sliding door. }; 

Again, I will allow you to imagine the properties and methods that populate these interfaces. Assume we design an ATL coclass named CoSportsSedan that implements the ICar, IFamilyCar, and ISportsCar interfaces:

click to expand
Figure 8-4: CoSportsSedan supports two interfaces derived from ICar.

Recall that objects only directly derive from the nth most interfaces in an interface hierarchy. The pure virtual methods of the base interfaces are inherited automatically. Therefore, our ATL Simple Object would derive directly from IFamilyCar and ISportsCar, but not directly from ICar (or IUnknown), as these interfaces are base interfaces of each:

// ATL CoSportsSedan class ATL_NO_VTABLE CCoSportsSedan :      public CComObjectRootEx<CComSingleThreadModel>,      public CComCoClass<CCoSportsSedan, &CLSID_CoSportsSedan>,      public IFamilyCar,      public ISportsCar { public: ... // ICar methods. // IFamilyCar methods. // ISportsCar methods. };

As we wish to allow COM clients to ask for ICar, IFamilyCar, and ISportsCar interfaces, we update our COM map as so:

// CoSportsSedan's COM map. BEGIN_COM_MAP(CCoSportsSedan)      COM_INTERFACE_ENTRY(ICar)      COM_INTERFACE_ENTRY(IFamilyCar)      COM_INTERFACE_ENTRY(ISportsCar) END_COM_MAP()

Sadly, once we compile, we are issued the following error:

error C2594: 'static_cast' : ambiguous conversions from 'class CCoSportsSedan *' to 'struct ICar *' 

The problem is that we have two custom interfaces (IFamilyCar and ISportsCar) with the same base interface (ICar). Ambiguity arises as a vPtr for ICar can be obtained from two subinterfaces, and we need to pick which branch the compiler should follow.

We have seen this same problem before. Recall that when we had at least two custom interfaces on a coclass we also had to tell the compiler how to cast for the IUnknown pointer:

// Resolving IUnknown ambiguities. if(riid == IID_IUnknown)      *ppv = (IUnknown*)(ISomeInterface*)this;

ATL provides three COM map macros beyond COM_INTERFACE_ENTRY to resolve this sort of base interface ambiguity:

  • COM_INTERFACE_ENTRY_IID

  • COM_INTERFACE_ENTRY2

  • COM_INTERFACE_ENTRY2_IID

The COM_INTERFACE_ENTRY_IID Macro

When you have a coclass supporting interfaces descending from a common base interface, you will need to pick a specific subinterface the compiler can cast against. For example, as both IFamilyCar and ISportsCar derive from ICar, the compiler is confused as it sees two different definitions for ICar. We must tell the compiler to pick either IFamilyCar or ISportsCar as the class to cast against when fetching the ICar interface for a COM client. The COM_INTERFACE_ENTRY_IID macro is used for this very purpose:

// One of three COM map macros used to resolve method ambiguities. #define COM_INTERFACE_ENTRY_IID(iid, x)\      {&iid,\      offsetofclass(x, _ComMapClass),\      _ATL_SIMPLEMAPENTRY},

The COM_INTERFACE_ENTRY_IID macro takes two parameters: the GUID of the common base interface (in this case ICar) and the name of a derived interface used to resolve the ambiguity (which can be any interface deriving from ICar). If we update our COM map to use COM_INTERFACE_ENTRY_IID, we will compile clean as we have specified which subinterface will be used to determine the ICar pointer:

// This COM map resolves the ambiguity of a common base interface // by using COM_INTERFACE_ENTRY_IID BEGIN_COM_MAP(CCoSportsSedan)      COM_INTERFACE_ENTRY(IFamilyCar)      COM_INTERFACE_ENTRY(ISportsCar)      COM_INTERFACE_ENTRY_IID(IID_ICar, ISportsCar) END_COM_MAP()

The COM_INTERFACE_ENTRY2 Macro

This ATL COM map macro is also useful when dealing with base interface ambiguities. Functionally it serves the same purpose as COM_INTERFACE_ENTRY_IID, but this time you send in the IDL name of each interface (the common base interface and a derived interface):

// Another COM map macro used to resolve method ambiguities. #define COM_INTERFACE_ENTRY2(x, x2)\      {&_ATL_IIDOF(x),\      (DWORD)((x*)(x2*)((_ComMapClass*)8))-8,\      _ATL_SIMPLEMAPENTRY},

We could use this COM map macro in our CoSportsSedan as follows:

// This COM map resolves the ambiguity of a common base interface // by using COM_INTERFACE_ENTRY2 BEGIN_COM_MAP(CCoSportsSedan)      COM_INTERFACE_ENTRY(IFamilyCar)      COM_INTERFACE_ENTRY(ISportsCar)      // Use ISportsCar to fetch ICar.      COM_INTERFACE_ENTRY2(ICar, ISportsCar) END_COM_MAP()

The COM_INTERFACE_ENTRY2_IID Macro

The final macro ATL provides to resolve common base interface ambiguities is COM_INTERFACE_ENTRY2_IID. This macro takes three parameters: the GUID of the common base interface, the name of the common base interface, and the name of the subinterface used to resolve the ambiguity:

// The final ATL ambiguity macro. #define COM_INTERFACE_ENTRY2_IID(iid, x, x2)\      {&iid,\      (DWORD)((x*)(x2*)((_ComMapClass*)8))-8,\      _ATL_SIMPLEMAPENTRY},

We could again rewrite our COM map as follows:

// This COM map resolves the ambiguity of a common base interface // by using COM_INTERFACE_ENTRY2_IID BEGIN_COM_MAP(CCoSportsSedan)      COM_INTERFACE_ENTRY(IFamilyCar)      COM_INTERFACE_ENTRY(ISportsCar)      COM_INTERFACE_ENTRY2_IID(IID_ICar, ICar, ISportsCar) END_COM_MAP()

Functionally, COM_INTERFACE_ENTRY_IID, COM_INTERFACE_ENTRY2, and COM_INTERFACE_ENTRY2_IID are exactly the same. These COM map macros are used to help resolve the ambiguities arising from two (or more) interfaces with a common base interface.

Note 

In order for the ATL framework to calculate the offset to your class's IUnknown interface, ensure the very first listing of your COM map is COM_INTERFACE_ENTRY, COM_INTERFACE_ENTRY_IID, COM_INTERFACE_ENTRY2, or COM_INTERFACE_ENTRY2_IID. These macros are often called the simple entries.



 < Free Open Study > 



Developer's Workshop to COM and ATL 3.0
Developers Workshop to COM and ATL 3.0
ISBN: 1556227043
EAN: 2147483647
Year: 2000
Pages: 171

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net