0

I have a COM object, CProvider, implemented using ATL. This class encloses another class, CProviderInfo, and maintains a static vector of objects of this inner class type.

Here's how it looks like:

//-------------
// CProvider.h
//-------------

//
// COM object class
//
class ATL_NO_VTABLE CProvider :
    public CComObjectRootEx<CComMultiThreadModel>,
    public CComCoClass<CProvider, &CLSID_Provider>,
    public Interface1,
    public Interface2
{
public:
    BEGIN_COM_MAP(CProvider)
       COM_INTERFACE_ENTRY(Interface1)
       COM_INTERFACE_ENTRY(Interface2)
    END_COM_MAP()

    //
    // The inner class
    //
    class CProviderInfo
    {
    public:
        CProviderInfo();
        CComBSTR m_strName;
        GUID m_guidRegistration;
    };

private:
    //
    // static vector of inner class type
    //
    static vector<CProviderInfo> m_vProviderInfo;
};

What I would like to do is introduce a method on CProvider that returns a copy of the static vector m_vProviderInfo. Trying to play by COM rules, I introduced a new IDL interface IProviderInfoRetriever for this purpose:

//---------
// IDL file
//---------

//
// IProviderInfoRetriever interface
//
[
    // uuid, version ... etc.
]
interface IProviderInfoRetriever : IUnknown
{
    HRESULT GetProviderInfo(
        [out, retval] SAFEARRAY(IProviderInfo*) *ppProviderInfo);
}

//
// The interface of the class holding the info
//
[
    // uuid, version ... etc.
]
interface IProviderInfo : IUnknown
{
    [propget] 
    HRESULT Name(
        [out, retval] BSTR *pbstrName);

    [propget]
    HRESULT Registration(
        [out, retval] GUID *pguidRegistration);
}

My plan is to have CProvider implement IProviderInfoRetriever effectively copying the contents of the static vector m_vProviderInfo into the output SAFEARRAY of IProviderInfoRetriever::GetProviderInfo().

My question is: is it possible to have the inner class CProviderInfo implement IProviderInfo? Would that break existing code that creates local variables of type CProviderInfo?

Ahmed A.Hamid
  • 258
  • 1
  • 2
  • 12
  • What do you mean by "COM class" if not "a class that implements one or more COM interfaces"? And no, nothing prevents you from creating, say, an instance of `CProvider` on the stack (`CProvider` apparently being a "COM class" by whatever definition you have in mind). – Igor Tandetnik Jan 31 '16 at 13:52
  • Another alternative, if touching `CProviderInfo` concerns you, would be to write a wrapper class that implements `IProviderInfo` and holds `CProviderInfo` as a member (or even just a pointer to an element of, or an index into, the original vector). This would nicely isolate COM-related parts and minimize footprint on the rest of the codebase. – Igor Tandetnik Jan 31 '16 at 13:55
  • @IgorTandetnik That's what I thought, too. After doing some research, I found out that inheriting IUnknown will break existing code because it will turn **CProviderInfo** into an abstract class. It won't be possible to instantiate **CProviderInfo** except through CComObject, CComObjectStack and this family of classes because it provides implementations of IUnknown members. – Ahmed A.Hamid Feb 01 '16 at 06:14
  • Yes, sorry, I forgot about ATL's two-phase implementation of `IUnknown`. The wrapper approach looks more attractive, then. – Igor Tandetnik Feb 01 '16 at 16:23
  • No worries. The details of ATL on top of the complexity of COM are just inhumane. – Ahmed A.Hamid Feb 01 '16 at 19:17

1 Answers1

1

I have done some extensive research and the answer is simply: yes, but it's not easy. It's highly unlikely that you'll just have an ordinary C++ class inherit/implement an interface defined in IDL without breaking existing code that relies on said C++ class.

First and foremost, if you use the facilities of ATL to convert your C++ class into a COM class, you'll suddenly discover that this C++ class has turned abstract due to all the pure virtual functions ATL macros introduce. So, at the very least, you'll have IUnknown's AddRef(), Release() and QueryInterface() added to your C++ class by ATL macros as pure virtual functions, e.g.

class ATL_NO_VTABLE CProviderInfo:
    public CComObjectRootEx<CComMultiThreadModel>,
    public IProviderInfo
{
public:
    BEGIN_COM_MAP(CProviderInfo)
        COM_INTERFACE_ENTRY(IProviderInfo)
    END_COM_MAP() // This line adds IUnknown's AddRef(), Release(),
                  // and QueryInterface() as pure virtual functions.

    // ...        
};

This alone will break any existing code that used to create instances of your C++ class, be they on the stack or the heap (using new operator). So, conclusively you have 2 options:

  1. Use ATL's facilities to turn your C++ class into a COM class.

    This will turn your C++ class into an abstract class. You'll have to modify all locations in source code creating objects of your C++ class to use ATL's classes instead, i.e. CComObject, CComObjectStack ... etc.

  2. Inherit/Implement the interface defined in IDL directly and manually.

    This will entail providing your own implementations of IUnknown, IDispatch or both depending on your interface, as well as implementations of any methods defined by your interface itself. This option has the advantage that it is less likely to break any existing uses of your C++ class in your codebase.

    However, rolling your own implementations of COM interfaces without ATL is not always going to be easy, especially if your C++ class ever gets involved in complex scenarios, e.g. interop.

Ahmed A.Hamid
  • 258
  • 1
  • 2
  • 12