I am trying to implement shared logic using COM aggregation with ATL. I've defined a base class, called CameraBase
, that is only available through aggregation. Therefor I've added the aggregateable
annotation to it's coclass
-declaration.
[
uuid(...),
aggregatable
]
coclass CameraBase
{
[default] interface ICamera;
};
I've also added the DECLARE_ONLY_AGGREGATEABLE
macro to the class definition.
class ATL_NO_VTABLE CCameraBase :
public CComObjectRootEx<CComMultiThreadModel>,
public CComCoClass<CCameraBase, &CLSID_CameraBase>,
public ISupportErrorInfo,
public IProvideClassInfoImpl<...>,
public IDispatchImpl<...>
{
public:
CCameraBase()
{
}
DECLARE_REGISTRY_RESOURCEID(IDR_CAMERABASE)
DECLARE_ONLY_AGGREGATABLE(CCameraBase)
BEGIN_COM_MAP(CCameraBase)
...
END_COM_MAP()
DECLARE_PROTECT_FINAL_CONSTRUCT()
...
}
Now I have different classes who are using the logic of CameraBase
somewhere. Therefor I've extented the com map of the parent class (e.g. SampleCamera
):
BEGIN_COM_MAP(CSampleCamera)
COM_INTERFACE_ENTRY_AGGREGATE(IID_ICamera, m_base)
...
END_COM_MAP
DECLARE_GET_CONTROLLING_UNKNOWN()
Since I want to be able to call the members on CameraBase
(through the ICamera
interface) from the parent class, I do not want to use COM_INTERFACE_ENTRY_AUTOAGGREGATE
, which stores the inner object's pointer as a reference of IUnknown
. Therefor I am creating it on my own from the FinalConstruct
-method:
HRESULT FinalConstruct()
{
HRESULT hr;
if (FAILED(hr = m_camera.CoCreateInstance(CLSID_CameraBase, this->GetControllingUnknown(), CLSCTX_INPROC_SERVER)))
return hr;
}
Where m_camera
is defined as CComPtr<ICamera>
. However, this does result in a error CLASS_E_NOAGGREGATION
(HRESULT 0x80040110
). My current workaround is to store two references, IUnknown
and ICamera
, and query for the later one.
if (FAILED(hr = m_base.CoCreateInstance(CLSID_CameraBase, this->GetControllingUnknown(), CLSCTX_INPROC_SERVER)) ||
FAILED(hr = m_base->QueryInterface(&m_camera)))
return hr;
This works, but it feels kinda strange, since the class (CameraBase
) that get's instanciated is the same in both cases. Am I missing something? Am I using the right way to aggregate the inner object? Why does the returned pointer of CoCreateInstance
need to be of type IUnknown
, if an outer unknown is passed?
Thanks in advance! :)
An aggregatable COM object provides two distinct implementations of IUnknown
- non-delegating and delegating.
The non-delegating implementation is the "normal" one - its QueryInterface
hands out interfaces implemented by the aggregatable object, and its AddRef
and Release
control the lifetime of that object.
The delegating implementation, as the name suggests, delegates all three method calls to the controlling IUnknown
of the outer object. All the other interfaces implemented by the object have their three IUnknown
methods backed by this delegating implementation. This is how the aggregation can maintain an illusion for the client that it's dealing with a single COM object - it allows the client to query from an interface implemented by the outer to one implemented by the inner, and (more interestingly) vice versa. Recall that it's a requirement for IUnknown
that QueryInterface
implementation be symmetrical and transitive.
When CoCreateInstance
is called with non-NULL controlling unknown parameter, it must request IUnknown
from the inner object - that is the outer's one and only chance to obtain a non-delegating implementation. You cannot use any other interface pointer from the inner in the outer's interface map - again, all other interfaces are backed by a delegating unknown, so forwarding QueryInterface
call to them would end up calling QueryInterface
on the outer, and end up right back in the interface map, leading to an infinite recursion.
User contributions licensed under CC BY-SA 3.0