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?

5
  • 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). Commented Jan 31, 2016 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. Commented Jan 31, 2016 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. Commented Feb 1, 2016 at 6:14
  • Yes, sorry, I forgot about ATL's two-phase implementation of IUnknown. The wrapper approach looks more attractive, then. Commented Feb 1, 2016 at 16:23
  • No worries. The details of ATL on top of the complexity of COM are just inhumane. Commented Feb 1, 2016 at 19:17

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.