Re: hosting the runtime



Sahil Malik [MVP] wrote:
> 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.

>> on the other hand I never used COM interface, I have no idea on how
>> they work....
>> care writing me a veyr simple C:main() which load an exe?

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
--
www.richardgrimes.com
my email evpuneqt@xxxxxxxx is encrypted with ROT13 (www.rot13.org)


.



Relevant Pages

  • Re: hosting the runtime
    ... >> and you know the name of the DLL. ... >> usually created by class factory objects, ... >> class to this function and it will return the class factory interface of ... >> so all you get back is a pointer to a table of pointers). ...
    (microsoft.public.dotnet.framework)
  • Re: Passing pointers using a VARIANT
    ... You don't pass classes in COM - you pass interface pointers. ... Why do you believe it needs to be a COM DLL? ... the only way I can get that to work is if I cast the pointer ... What's the threading model of the component? ...
    (microsoft.public.vc.atl)
  • Re: hosting the runtime
    ... its worth pointing out that a COM interface is very much like a C++ ... > and you know the name of the DLL. ... > usually created by class factory objects, ... > so all you get back is a pointer to a table of pointers). ...
    (microsoft.public.dotnet.framework)
  • Re: Why we must use class factory to create some objects in COM?
    ... A dll can expose more than one component.Do u agree with this?If so, ... Instead of calling CoCreateInstancewhich just gives only one interface ... pointer of a component u can use ... You say it can create any number of components using class factory, ...
    (microsoft.public.vc.mfc)
  • Re: Properties Shared Amongst Objects
    ... The host can query my application for certain ... In that context one usually strives to provide a subsystem interface that reflects the invariants of the DLL subject matter. ... A some point the conversion may become so complex that one wants to deal with it explicitly within the DLL subject matter. ...
    (comp.object)