Consider the following ATL class declaration:
class Widget :
public CComObjectRootEx<CComMultiThreadModel>,
public CComCoClass<Widget>,
public IAgileObject
{
public :
DECLARE_NOT_AGGREGATABLE(Widget)
BEGIN_COM_MAP(Widget)
COM_INTERFACE_ENTRY(IAgileObject)
END_COM_MAP()
⟦ ... ⟧
};
This class says that it is not aggregatable.
Hold my beer.
HRESULT CreateAggregatedWidget( IUnknown* punkOuter, REFIID riid, void** ppv) { return CComCreator<CComAggObject<Widget>>:: CreateInstance(punkOuter, riid, pppv); }
There, I aggregated your allegedly non-aggregatable object.
The DECLARE_
macro is an instruction to the class factory to disallow aggregation, but if you create the object by means other than the class factory, then the macro has no effect. Creating the object without using the class factory is common for objects created internally.
So how do you mark your class so that any attempt to aggregate the object encounters a compiler error?
I found a sneaky trick.
The CComAggObject
template sets up aggregation by creating the inner object and overriding its IUnknown
methods to forward to the controlling unknown, kept in the member variable m_pOuterUnknown
. The connection is made here:
template <class Base>
class CComContainedObject :
public Base
{
public:
typedef Base _BaseBlass;
CComContainedObject(void* pv)
{
this->m_pOuterUnknown = (IUnknown*)pv;
}
If we can make this line of code produce an error, then we can prevent people from instantiating CComContainedObject<
and thereby prevent them from putting it in a CComAggObject
.
I had multiple ideas for how to accomplish this, but the simplest was this:
class Widget :
public CComObjectRootEx<CComMultiThreadModel>,
public CComCoClass<Widget>,
public IAgileObject
{
public :
DECLARE_NOT_AGGREGATABLE(Widget)
static constexpr void* m_pOuterUnknown = nullptr;
BEGIN_COM_MAP(Widget)
COM_INTERFACE_ENTRY(IAgileObject)
END_COM_MAP()
⟦ ... ⟧
};
This shadows the m_pOuterUnknown
member from CComObjectRootEx
, so when CComContainedObject
tries to do a this->m_pOuterUnknown
, it gets the Widget
one. And since the Widget
one is marked constexpr
, it cannot be modified.
error C3892: 'm_pOuterUnknown': you cannot assign to a variable that is const
If you are compiling with a version of C++ that doesn’t support constexpr
(which is not entirely out of the question given that you’re using ATL, and ATL was written in 1996, over a decade before constexpr
was a twinkle in Gabriel and Bjarne’s eyes), you can use this alternate shadowing definition:
static void m_pOuterUnknown() {}
This makes m_pOuterUnknown
a function, and you cannot assign to a function.
error C2659: '=': function as left operand
0 comments