COM Enhancement patch

Christian Costa titan.costa at wanadoo.fr
Wed Nov 13 13:22:25 CST 2002


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.

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


More information about the wine-devel mailing list