Hi all,
I've sent a patch for ddraw COM management, I did not have any comments
and the patch has been rejected.
Could someone tell me if something is wrong or lacking?
Christian.
PS: I've joined a small doc that explains how to use objects with
multiple interfaces. This docs is based on what
has been done before with the ddraw code plus the enhancement I did.
---------------------------------------------------------------------------------
- COM OBJECTS MANAGEMENT -
---------------------------------------------------------------------------------
1) Definitions:
---------------
a) Object classes
An object class is a structure wich defines the data of an object and
all the interfaces that act on the object data
Example :
struct ObjectClass {
INTERFACE 1;
INTERFACE 2;
............
INTERFACE N;
DATA;
} ;
typedef struct ObjectClass ObjectClass;
b) Objects
An object is a instance of an object class.
Example :
An object is created as follows :
ObjectClass* object = malloc(sizeof(ObjectClass),...)
c) Interfaces
An interface is a way for an application to interact with an object.
It contains a pointer to a virtual table plus some other data than can help
objects management.
An interface is a structure, which is used internally, that can be defined with
the INTERFACE macro :
#define INTERFACE(iface) \
struct { \
iface interface; \ /* VTABLE */
LPVOID object; \ /* pointer to the object the interface
belongs to */
}
To add a field to the interface, just add it in this macro.
For example, we could embed the version number inside an interface or a magic number
to check if the interface's pointer points to a real interface. And, off course, modify
some macros or create new ones to make use of them.
2) Implementing COM objects:
---------------------------
a) Declaring a COM object with multiple interfaces
To declare interfaces in a COM object the ICOM_VFIELD_MULTI must be
used for each interface as shown below:
struct IDirect3DImpl
{
/* IUnknown fields */
ICOM_VFIELD_MULTI(IDirect3D);
ICOM_VFIELD_MULTI(IDirect3D2);
ICOM_VFIELD_MULTI(IDirect3D3);
ICOM_VFIELD_MULTI(IDirect3D7);
DWORD ref;
/* IDirect3D fields */
IDirectDrawImpl* ddraw;
LPVOID private;
};
b) Creating an object
Just declare an pointer to an object and allocate the memory for it.
Example:
IDirect3DImpl* object = malloc(sizeof(IDirect3DImpl),...)
c) Initializing interfaces
It is done with the ICOM_INIT_INTERFACE(object_pointer, interface_name, virtual_table)
ICOM_INIT_INTERFACE(object,IDirect3D, VTABLE_IDirect3D);
ICOM_INIT_INTERFACE(object,IDirect3D2,VTABLE_IDirect3D2);
ICOM_INIT_INTERFACE(object,IDirect3D3,VTABLE_IDirect3D3);
ICOM_INIT_INTERFACE(object,IDirect3D7,VTABLE_IDirect3D4);
d) Retreive an object from within an interface's function
If we consider the function :
IDirect3D_AddRef(IDirect3D iface,.......)
To retrieve a pointer to an object within the function, both followings macro are used :
1) ICOM_OBJECT(object_class,iface)
This macro return an pointer to the object, the value is of object_class* type
Example:
IDirect3DImpl* d3d = ICOM_OBJECT(IDirect3DImpl,iface);
2) ICOM_THIS_OBJECT(object class,iface)
It simply declares a This pointer and initialize it with the object address.
IDirect3DImpl* This = ICOM_OBJECT(IDirect3DImpl,iface);
e) Sharing functions between multiple interfaces
In the case where a function does no have to do specific actions depending on the interface,
you can share directly a function.
Example :
If we consider the function :
IDirect3D_AddRef(IDirect3D iface,.......)
The different virtual tables would be :
VTABLE_IDirect3D = {......,IDirect3D_AddRef,....);
VTABLE_IDirect3D2 = {......,XCAST()IDirect3D_AddRef,....);
VTABLE_IDirect3D3 = {......,XCAST()IDirect3D_AddRef,....);
VTABLE_IDirect3D7 = {......,XCAST()IDirect3D_AddRef,....);
Note that this is possible because the ICOM_OBJECT can retreive the object's pointer without
knowing the interface involved.
f) Thunking interface
Interface thunking enable changing from one interface to another.
The following macro COM_INTERFACE_CAST(impltype, ifnamefrom, ifnameto, ifaceptr) performs
this task.
impltype = object class
ifnamefrom = interface type we have
ifnamefrom = interface type we want
ifaceptr = pointer to the interface to cast
return = pointer to the casted interface
Interface thunking is a another way to shared a single function across different interfaces but as
shown below this is required only when structures must be translated, interfaces returned casted or
to handle prototype change :
Example:
You want to share the IDirect3D_CreateDevice across all IDirect3DX interfaces (with X above 1 because a
device in Direct3D is created in a different way).
IDirect3D2_CreateDevice(IDirect3D2* iface,.....) is the only one implementation of the function.
The others are thunks to the IDirect3D2_CreateDevice.
IDirect3D3_CreateDevice(IDirect3D3* iface, .....,LPDIRECT3DDEVICE3 *lpdevice, LPUNKNOWN lpUnk)
{
IDirect3DDevice3 *dev;
IDirect3D_CreateDevice(ICOM_INTERFACE_CAST(IDirect3DImpl,IDirect3D2,IDirect3D3,iface),.....,&dev);
*lpdevice = ICOM_INTERFACE_CAST((IDirect3DImpl,IDirect3D3,IDirect3D2,device)
}
IDirect3D7_CreateDevice(IDirect3D7* iface, .....,LPDIRECT3DDEVICE7 *lpdevice)
{
IDirect3DDevice2 *dev;
IDirect3D_CreateDevice(ICOM_INTERFACE_CAST(IDirect3DImpl,IDirect3D2,IDirect3D7,iface),.....,&dev);
*lpdevice = ICOM_INTERFACE_CAST((IDirect3DImpl,IDirect3D7,IDirect3D2,dev)
}
Here, we have to deal with a returned interface and a prototype change.
The VTABLE should be:
VTABLE_IDirect3D2 = {......,IDirect3D2_CreateDevice,....);
VTABLE_IDirect3D3 = {......,IDirect3D3_CreateDevice,....);
VTABLE_IDirect3D7 = {......,IDirect3D7_CreateDevice,....);
Note that there are few functions that need this, for the others, thunks are not necessary and the way
described in e) must be preferred.
g) Getting an specific interface of an object
With the ICOM_INTERFACE(implobj, ifacename) macro, any interfaces can be retreive from
an object.
implobj = pointer to the object
ifacename = interface name we want
return = pointer to an interface
Example:
IDirect3DImpl* object;
............
IDirect3D3* iface_d3d3 = ICOM_INTERFACE(object,IDirect3D3)
h) Calling an COM object functions from another
If a function is shared across multiple interfaces, you should write:
IDirect3D3_CreateDevice(IDirect3D* iface,.....)
{
ICOM_THIS_OBJECT(iface);
.................
IDirect3D_AddRef(ICOM_INTERFACE(This,IDirect3D),.....);
.................
}
If you know the nature of iface (i.e. thunking is used or the function is used in only
one interface), you can write :
IDirect3D3_CreateDevice(IDirect3D* iface,.....)
{
ICOM_THIS_OBJECT(iface);
.................
IDirect3D3_AddRef(iface,.....);
.................
}