I’ve imported a type library using c++Builder 2010 (Component/Import Component/Type Library wizard) and am trying to understand the classes and types defined in the generated xxx_TLB.b and xxx_OCX.h files. (Some background to the question – I’m trying to get my COM objects and methods bound by name, rather than by DISPID – see this question)
Say the library contains a class Foo, I’ll get the following GUIDs
const GUID IID_IFoo = {0xF1EC45FE, 0x2540, 0x4188,{ 0xAE, 0x14, 0xD8,0x4F, 0x65, 0x6F,0x7A, 0x00} };
const GUID CLSID_Foo = {0xDD1C416D, 0xD8A2, 0x4BBC,{ 0x8E, 0xA8, 0x1A,0x10, 0x77, 0xA4,0x30, 0x0C} };
That makes sense to me. There’s also FooEvents generated, but I’ll ignore that for now. I also get the following interface and typedef, with two versions of each method – a ‘raw’ one and one with a more friendly wrapper hiding HRESULTS.
interface DECLSPEC_UUID("{F1EC45FE-2540-4188-AE14-D84F656F7A00}") IFoo;
typedef TComInterface<IFoo, &IID_IFoo> IFooPtr;
interface IFoo : public IDispatch
{
virtual HRESULT STDMETHODCALLTYPE get_Bar(BSTR* Value/*[out,retval]*/) = 0; // [201]
...
BSTR __fastcall get_Bar(void)
{
BSTR Value = 0;
OLECHECK(this->get_Bar((BSTR*)&Value));
return Value;
}
}
Now we come to the stuff that’s beyond me: Two additional classes that are basically exposing the same functionality, but differently.
template <class T /* IFoo*/ >
class TCOMIFooT : public TComInterface<IFoo>, public TComInterfaceBase<IUnknown>
{
... again, two versions of each method are provided.
}
typedef TCOMIFooT<IFoo> TCOMIFoo;
template<class T>
class IFooDispT : public TAutoDriver<IFoo>
{
... again, two more versions of each method are provided.
}
typedef IFooDispT<IFoo> IFooDisp;
And it gets worse : In the xxx_OCX.h file we find this additional class:-
class PACKAGE TFoo : public Oleserver::TOleServer
{
IFooPtr m_DefaultIntf;
_di_IUnknown __fastcall GetDunk();
public:
... this tim, just one version of each method
}
So, for an object of type Foo, I seem to have four different classes to represent it – which one(s) should I be using, and when?
IFooPtr f;
IFooDisp f;
TCOMIFoo f;
TFoo *f;
And finally, for IFooDisp f; it seems I can call Bar() via either f->Bar() and f.Bar() – Will they work identically, or is there some subtle difference?
TComInterfaceis a wrapper class for managing a interface pointer’s reference count for you, much like ATL’sCComPtrandCComQIPtrclasses. This way, you don’t have to callAddRef()andRelease()manually.The
TCOMIFoowrapper is basically just a typedef forTComInterface<IFoo>, though it is also used by the createdTCoClassCreator-derived class in that same file.TAutoDriveris a wrapper class for handlingIDispatchand late-binding (which is needed for binding members by name).TOleServeris a wrapper class for exposing a COM object as a VCL component, especially so it can be placed onTForm,TFrame, andTDataModuleobjects at design-time.I have never used
TAutoDriverwrappers in my code, and do not use COM objects at design-time (as they don’t allow you to do run-time error handling if the COM object is not installed). For most code, I would just stick withIFooPtrorTCOMIFoo, they are basically the same thing.There is a subtle difference.
f->Bar()directly calls theIFoo::Bar()method, and as such you have to do your ownHRESULTchecking, which allows you to decide what to do if an error occurs.f.Bar()calls the wrapperIFooDisp::Bar()method, which calls theIFoo::Bar()method and doesHRESULTchecking for you, raising an exception if an error occurs.In general, any method invoked via the
->operator will call into the raw interface directly, and any method invoked via the.operator will call into a wrapper method.Another subtle difference would be if
Bar()returned an interface pointer. Using the->operator, you would have to do your own reference counting on the resulting pointer, eg:Unless you use a
TComInterfacevariable to receive it:Using the
.operator instead, the wrapper will usually return aTComInterfaceinstead of a raw interface pointer. This is especially useful when interfaces are chained together, where you want to get an interface that returns an interface that returns …, and so on. Using the->operator is more flexible but not as clean as using the.operator, eg:Versus: