Sahil said:
So a COM component has a lot to be told about - more than I can
possibly type here. I'd recommend Inside COM by Don Box.
Hmmm, Inside COM was written by Dale Rogerson, and IMO not particularly
relevant here. Don's book is Essential COM. ;-)
First, its worth pointing out that a COM interface is very much like a C++
v-table. Imagine COM marshalling as being a mechanism to transport a
v-table from one COM object in one memory space and make it available in
another memory space.
COM itself is essentially a mechanism to manage DLLs (we'll ignore 'local
servers'). Imagine that you want to create an instance of a class class
and you know the name of the DLL. The problem in Win32 is that you first
have to locate the DLL and load it - this is the DLL hell problem, because
LoadLibrary will look in various places for the DLL and may pick up a
different version of the DLL or even a totally different DLL that has the
same name. COM manages this through values in the registry, when you
install a COM server the classes it contains will be registered in the
registry and these entries will contain the full path to the DLL. ***It is
vitally important that the location of a COM server DLL does not
change***. Each class has a unique name called a GUID (a 128 bit integer)
and so whenever you create an object you create it via its GUID. Languages
do have mappings to human readable names, but it is important to realise
that the real name of a class is the GUID. COM provides APIs that when
given a GUID will locate and load the DLL containing the class.
Next you need to create an instance of the class, which means finding some
code that will create it for you. Since you don't want a memory leak, you
also have to have a mechanism to remove the instance. COM objects are
usually created by class factory objects, a class factory is intimately
intwined with an object because the class factory calls new with whatever
memory manager it wants to use, and hence when the object is destroyed it
must call the appropriate delete.
So you need to get access to a class factory object in the DLL. However,
DLLs can only export C functions. So all COM server DLLs have to export a
function called DllGetClassObject. In effect you pass the name (GUID) of a
class to this function and it will return the class factory interface of
the class factory object (remember an interface is just a v-table pointer,
so all you get back is a pointer to a table of pointers). You can then
call the IClassFactory::CreateInstance to create an instance of the class.
Note that *all* access to COM objects are via interfaces so you need to
provide an identifier about what interface you want to have on the new
object.
All COM objects must implement IUnknown which has three methods:
QueryInterface allows you to ask for a specific interface, it's equivalent
to the C++ dynamic_cast<>. Note that there is no mechanism to get a list
of the interfaces on an object, instead, you have to call QueryInterface
for the interfaces that you want to use. The other two methods are AddRef
and Release. These maintain a reference count (usually on the object) and
when you make a copy of an interface pointer you have to call AddRef, when
you no longer need the pointer you have to call Release. Once the
reference count falls to zero the object can delete itself. (What this
means is up to the object, but it usually means calling the delete on its
this pointer that corresponds to the new that the class factory called.)
The beauty of interfaces is that it allows polymorphism, because a method
that takes an interface pointer does not care what object the pointer
points to, it just cares about the behaviour of the interface.
Non straightforward way - do what CoCreateInstance does -
You pass the class's GUID and the indentifier (another GUID) of the
interface you want on the object to CCI, which will locate the DLL, call
the class factory, create the object and then query for the specified
interface.
When you create a COM interface you write the interface in IDL and compile
it with MIDL. MIDL will create a header file with a C++ and a C mapping
for the interface. The platform SDK contains MIDL generated header files
created for the standard interfaces. For example, here's IUnknown C
mapping from unknwn.h:
typedef struct IUnknownVtbl
{
BEGIN_INTERFACE
HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
IUnknown * This,
/* [in] */ REFIID riid,
/* [iid_is][out] */ void **ppvObject);
ULONG ( STDMETHODCALLTYPE *AddRef )(
IUnknown * This);
ULONG ( STDMETHODCALLTYPE *Release )(
IUnknown * This);
END_INTERFACE
} IUnknownVtbl;
interface IUnknown
{
CONST_VTBL struct IUnknownVtbl *lpVtbl;
};
So when you call CoCreateInstance in C you will get back an IUnknown* to
call a method you need to derefernce it to get the lpVtbl, and then
dereference that to get to the function pointer that you want to call. In
C++ the interface is treated as a C++ class v-table (the language mapping
does this) so you only need to dereference it once. Also, the first,
implicit parameter of a v-table method is a this pointer. The C++ mapping
masks this, but in C you have to pass it explicitly.
// C code
IUnknown* pUnk = NULL;
HRESULT hr;
CoInitialize(); // must initialize COM, this creates a single threaded
apartment
hr = CoCreateInstance(&CLSID_MyClass, NULL, CLSCTX_INPROC_SERVER,
&IID_IUnknown, &pUnk);
if (SUCCEEDED(hr)) // always check return values
{
// use the interface here...
// finished with the interface so call Release
pUnk->lpVtbl->Release(pUnk);
// in C++ this would be pUnk->Release();
}
CoUninitialize(); // must uninitialise COM
As you can see it is much easier in C++ so usually people don't bother
with C. The only book that I know off hand that shows how to program COM
in C is Brockschmidt's Inside OLE. However, I doubt if this is still in
print. Anyway if you are a proficient C programmer it should not be too
difficult to write C to access COM objects using the headers created by
MIDL.
Richard