Are aggregated objects forced to be an IUnknown reference? - c++

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.

Related

Understanding inheritance in Google's V8 C++ code base

I cannot understand the implementation of inheritance in Google's V8 JavaScript engine. It clearly (?) implements an inheritance hierarchy, but seems to completely do away with virtual functions.
This is the inheritance hierarchy as detailed in the objects.h header file:
// Inheritance hierarchy:
// - Object
// - Smi (immediate small integer)
// - HeapObject (superclass for everything allocated in the heap)
// - JSReceiver (suitable for property access)
// - JSObject
// - JSArray
// ... and many more entries
Most object types are derived from Object, which is declared as follows:
// Object is the abstract superclass for all classes in the
// object hierarchy.
// Object does not use any virtual functions to avoid the
// allocation of the C++ vtable.
// Since both Smi and HeapObject are subclasses of Object no
// data members can be present in Object.
class Object {
// ... bunch of method declarations and definitions
};
The relatively simple Smi class is declared next:
class Smi: public Object {
public:
// methods declarations and static member definitions
};
and so on.
For the life of me, I cannot understand how can, say, an instance of Smi can be used as an Object; there are no virtual functions and I cannot find overrides in the the implementation file, objects.cc. At 17,290 lines, though, trying to understand what is going on is proving a difficult task.
As another difficulty, I found an ObjectVisitor class in the same header file (this one is more classical; it consists of virtual methods). But I could not find the equivalent Accept(Visitor*) (or similar) method in the Object base class.
What I am asking in concrete is for a minimal example that illustrates how does this inheritance pattern works.
The classes in objects.h do not actually define real C++ classes. They do not have any fields. The classes are merely facades to objects managed on the V8 JavaScript heap. Hence they cannot have any virtual functions either, because that would require putting vtable pointers into the JS heap. Instead, all dispatch is done manually, via explicit type checks and down casts.
The this pointer inside methods isn't real either. For smis, this is simply an integer. For everything else it is a pointer into the V8 heap, off by one for tagging. Any actual accessor method masks this pointer and adds an offset to access the appropriate address in the heap. The offsets of each field is also defined manually in the classes.
Take a look at Object::IsPromise() for a perfect example of how it works:
bool Object::IsPromise(Handle<Object> object) {
if (!object->IsJSObject()) return false;
auto js_object = Handle<JSObject>::cast(object);
// Promises can't have access checks.
if (js_object->map()->is_access_check_needed()) return false;
auto isolate = js_object->GetIsolate();
// TODO(dcarney): this should just be read from the symbol registry so as not
// to be context dependent.
auto key = isolate->promise_status();
// Shouldn't be possible to throw here.
return JSObject::HasRealNamedProperty(js_object, key).FromJust();
}
The way inheritance is used here is static. That is, type queries are done by a proxy or container (using some hidden magic, that, at a glance looks like they're using references to query a tag), and conversions from Object to a derived class is done by static_cast<>(). In that way, the member functions of the derived class can be called.
Note that in the above function, the type query and cast is indirectly performed by the Handle<> class, not by Object or any of its derived classes.
Note also that the functions which accept ObjectVisitor as a parameter are rather uniformly called Iterate, and that these functions all appear on proxies or handles.

Using coclass that implements multiple interfaces

I am writing a C++/CLI application that makes use of a COM dll that provides a number of classes. Most of them implement a number of interfaces. I was wondering how I can access the methods of each of the various interfaces. For instance when I look at the type library one of the classes is defined as:
coclass FWFile {
[default] interface IFWFile;
interface _IFWFileInternal;
[default, source] interface _FWFileEvents;
interface CStatistics;
interface IFWFile2;
interface IFWFile3;
interface IFWFile4;
};
When I create an object of this type it appears to implement the IFWFile interface. However, I want to make use of the methods in IFWFile2. Can I simply create an object of type IFWFile2 and cast it?
IFWFile2 file2 = (IFWFile2)file1;
When using CoCreateInstance() you can specify which interface to retrieve from the newly created object. If you want more than one interface - retrieve one when calling CoCreateInstance() and use QueryInterface() to retrieve the other interfaces. Don't forget to have Release() called for each successful interface retrieval.
Just don't C-style cast COM pointers - interfaces are not guaranteed to be in the order specified in the type library and the actual class is not guaranteed to actually have the interface implemented. Always use QueryInterface() to retrieve interface pointers from COM objects.

COM: Getting GUID of coclass object using pointer to interface it implements

Having pointer to COM interface that are implemented by some concrete component class object is it possible to get a GUID of the underlying object that implements this interface (CLSID)?
Update 1
More generally, I have a function like SetFont(ICanvasFont* font) and I need a way to determine if the underlying object that implements the ICanvasFont interface is of a certain class (say MCanvasFont).
IUnknown::QueryInterface on this interface pointer to obtain one of the following: IPersist, IPersistStream, IPersistStreamInit or other IPersist* interfaces. If you are lucky to get one, then GetClassID method will get you the CLSID class identifier (alternate option is IProvideClassInfo and IProvideClassInfo::GetClassInfo).
Note that this kind of information does not have to exist. An interface pointer can be valid without having CLSID on the class implementing it.
UPD. If the main goal is to recognize your own implementation on the provided interface ("Is the provided ICanvasFont the instance of my own MCanvasFont class, or it is something different?"), then the easiest yet efficient way is to implement some extra private interface on the class. If your querying it succeeds, then you recognize the instance. Provided no marshaling takes place, you can possibly even static_cast back to original C++ pointer.

C++ DispInvoke fails to find member

The scenario is this:
I have a COM object to ask questions. Name it ICom.
The COM object requires me to implement an IDispatch , descendant of , say, IComEvents, that notifies me for events.
I implement an IDispatch and connect it to the COM interface.
So far so good. My IComEvents descentant's Invoke() is called when the events occur.
The point is now that I must manually parse Invoke() parameters. For example, if a notification function is HRESULT OnMouseHit(int x), I have to detect this function from the DispID, then call it manually, for example
if (dispIdMember == 0xfa)
{
OnMouseHit(pDispParams->rgvarg[0].pIntVal);
}
I would have to do it for all the functions I want to implement. However I saw the DispInvoke() function which presumably will automatically do this for me and call the appropriate overloaded method for the dispId, with the correct parameters:
DispInvoke(this,m_ptinfo,dispIdMember,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr);
How do I generate m_ptinfo? By calling LoadRegTypeLib with the libid of the COM object, and then ITypeLib::GetTypeInfoOfGuid() with the IID of IComEvents. However, DispInvoke() always returns "member not found".
What would be wrong? I expect DispInvoke to parse the type information, find the member function name from the DispID and then use the "this" pointer to get the function address from the vtbl.
What am I doing wrong?
Thanks a lot.
Michael.
Is IComEvents a dual interface or a dispinterface? If it is a pure dispinterface it doesn't have a vtable. DispInvoke requires the interface to have a vtable (ie that it is a dual interface).

Inheriting from a COM class

I'm working in Visual Studio 2005 with a pure unmanaged project (C++). I have a COM class, from a third party, in a OCX file. This class is a control ("widget"). I've been using it through a IDispatch wrapper class generated using the Add Class from Typelib Wizard.
I would like to extend this class in a few ways and public inheritance would be way more practical than compositing (I want the derived object to expose every single method that the parent class does). The derived class should also be available as a COM component.
Can I do this? If positive, how can I accomplish this?
It is not possible to inherit from COM classes as you can in C++. There are workarounds though:
COM Aggregates
Forwarding
COM aggregates is only useful if you want to add an interface (with implementation) to an existing COM class. You cannot intercept calls to the aggregated object.
Forwarding means that if you have an interface IExistingInterface, you implement your own class that implements IExistingInterface. In your class you keep a reference to an instance of the object you want to "inherit" from. In your implementation of IExistingInterface, you forward calls as appropriate to the "inherited" object. This method gives you total control.
Example: (pseudo-code!)
class MyClass : IExistingInterface {
IExistingInterface* m_pInherited;
public:
MyClass() {
::CoCreateInstance(CLSID_OtherImplementation, ..., ..., IID_IExistingInterface, (void**)&m_pInherited);
}
// IExistingInterface methods
HRESULT MethodX() {
// Do some pre processing
HRESULT hr = m_pInherited->MethodX();
if(FAILED(hr))
return hr;
// Do some post processing
return S_OK;
}
};
Edit:
I really recommend that you use ATL to create your COM component. In that case, construct the "inherited" object in FinalConstruct() rather than the C++ constructor.
You can create a new interface that derives from the first. Your QueryInterface function will need to respond to both GUIDs and deliver the proper pointer. Once you've done that, have your concrete class implement the superset of the functions (i.e. all of the second interface, including everything inherited from the first.)
If your concrete class will also inherit from a concrete class in the library, you're going to have a diamond inheritance pattern. You can search for solutions to that, I'd start here: Diamond inheritance (C++)