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,.....); ................. }