not quite minimalistic enough  

Templates. It's always the templates that get you.

Problem

You are developing a COM object that supports outgoing events (source interfaces). You get a compiler error similar to

C2440: ‘static_cast’ cannot convert from ‘CMyClass::_atl_conn_classtype *’ to ‘ATL::_ICPLocator<IID_IMyClassEvents> *'. Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast.

on a CONNECTION_POINT_ENTRY() line:

class CMyClass
   : public ATL::CComObjectRootEx<ATL::CComSingleThreadModel>
   , public ATL::CComCoClass<CMyClass, &CLSID_MyClass>
   , public ATL::IConnectionPointContainerImpl<CMyClass>
   , public ATL::IConnectionPointImpl<CMyClass, &__uuidof(IMyClassEvents)>
   , public IMyClass
{
public:
   CMyClass();

   DECLARE_PROTECT_FINAL_CONSTRUCT()

   BEGIN_COM_MAP(CMyClass)
      COM_INTERFACE_ENTRY(IMyClass)
      COM_INTERFACE_ENTRY(IConnectionPointContainer)
   END_COM_MAP()

   BEGIN_CONNECTION_POINT_MAP(CMyClass)
      CONNECTION_POINT_ENTRY(IID_IMyClassEvents)    // <-- C2440
   END_CONNECTION_POINT_MAP()
...

Analysis

The IIDs in the IConnectionPointImpl base class and the CONNECTION_POINT_ENTRY are not the same symbol. The cast fails because it attempts to convert to a base-class pointer that is not a base class, but a separate specialization of the same template.

Solution

Use either the predefined constants (IID_IMyClassEvents) or the MIDL metadata based constants (__uuidof(IMyClassEvents)) in both places; do not mix them.

Written on June 28, 2019