  <chapter id="ole">
    <title>COM/OLE in Wine</title>

    <sect1 id="com-writing">
      <title>Writing OLE Components for Wine</title>

      <para>
        This section describes how to create your own natively
        compiled COM/OLE components.
      </para>

      <sect2>
        <title>Macros to define a COM interface</title>

        <para>
          The goal of the following set of definitions is to provide a
          way to use the same header file definitions to provide both
          a C interface and a C++ object oriented interface to COM
          interfaces. The type of interface is selected automatically
          depending on the language but it is always possible to get
          the C interface in C++ by defining CINTERFACE.
        </para>
        <para>
          It is based on the following assumptions:
        </para>
        <itemizedlist>
          <listitem>
            <para>
              all COM interfaces derive from IUnknown, this should not
              be a problem.
            </para>
          </listitem>
          <listitem>
            <para>
              the header file only defines the interface, the actual
              fields are defined separately in the C file implementing
              the interface.
            </para>
          </listitem>
        </itemizedlist>
        <para>
          The natural approach to this problem would be to make sure
          we get a C++ class and virtual methods in C++ and a
          structure with a table of pointer to functions in C.
          Unfortunately the layout of the virtual table is compiler
          specific, the layout of g++ virtual tables is not the same
          as that of an egcs virtual table which is not the same as
          that generated by Visual C+. There are work arounds to make
          the virtual tables compatible via padding but unfortunately
          the one which is imposed to the Wine emulator by the Windows
          binaries, i.e. the Visual C++ one, is the most compact of
          all.
        </para>
        <para>
          So the solution I finally adopted does not use virtual
          tables. Instead I use in-line non virtual methods that
          dereference the method pointer themselves and perform the
          call.
        </para>
        <para>
          Let's take Direct3D as an example:
        </para>
        <programlisting>#define ICOM_INTERFACE IDirect3D
#define IDirect3D_METHODS \
    ICOM_METHOD1(HRESULT,Initialize,    REFIID,) \
    ICOM_METHOD2(HRESULT,EnumDevices,   LPD3DENUMDEVICESCALLBACK,, LPVOID,) \
    ICOM_METHOD2(HRESULT,CreateLight,   LPDIRECT3DLIGHT*,, IUnknown*,) \
    ICOM_METHOD2(HRESULT,CreateMaterial,LPDIRECT3DMATERIAL*,, IUnknown*,) \
    ICOM_METHOD2(HRESULT,CreateViewport,LPDIRECT3DVIEWPORT*,, IUnknown*,) \
    ICOM_METHOD2(HRESULT,FindDevice,    LPD3DFINDDEVICESEARCH,, LPD3DFINDDEVICERESULT,)
#define IDirect3D_IMETHODS \
    IUnknown_IMETHODS \
    IDirect3D_METHODS
ICOM_DEFINE(IDirect3D,IUnknown)
#undef ICOM_INTERFACE

#ifdef ICOM_CINTERFACE
// *** IUnknown methods *** //
#define IDirect3D_QueryInterface(p,a,b) ICOM_CALL2(QueryInterface,p,a,b)
#define IDirect3D_AddRef(p)             ICOM_CALL (AddRef,p)
#define IDirect3D_Release(p)            ICOM_CALL (Release,p)
// *** IDirect3D methods *** //
#define IDirect3D_Initialize(p,a)       ICOM_CALL1(Initialize,p,a)
#define IDirect3D_EnumDevices(p,a,b)    ICOM_CALL2(EnumDevice,p,a,b)
#define IDirect3D_CreateLight(p,a,b)    ICOM_CALL2(CreateLight,p,a,b)
#define IDirect3D_CreateMaterial(p,a,b) ICOM_CALL2(CreateMaterial,p,a,b)
#define IDirect3D_CreateViewport(p,a,b) ICOM_CALL2(CreateViewport,p,a,b)
#define IDirect3D_FindDevice(p,a,b)     ICOM_CALL2(FindDevice,p,a,b)
#endif</programlisting>
        <para>
          Comments:
        </para>
        <para>
          The ICOM_INTERFACE macro is used in the ICOM_METHOD macros
          to define the type of the 'this' pointer. Defining this
          macro here saves us the trouble of having to repeat the
          interface name everywhere. Note however that because of the
          way macros work, a macro like ICOM_METHOD1 cannot use
          'ICOM_INTERFACE##_VTABLE' because this would give
          'ICOM_INTERFACE_VTABLE' and not 'IDirect3D_VTABLE'.
        </para>
        <para>
          ICOM_METHODS defines the methods specific to this
          interface. It is then aggregated with the inherited methods
          to form ICOM_IMETHODS.
        </para>
        <para>
          ICOM_IMETHODS defines the list of methods that are
          inheritable from this interface. It must be written manually
          (rather than using a macro to generate the equivalent code)
          to avoid macro recursion (which compilers don't like).
        </para>
        <para>
          The ICOM_DEFINE finally declares all the structures
          necessary for the interface. We have to explicitly use the
          interface name for macro expansion reasons again.  Inherited
          methods are inherited in C by using the IDirect3D_METHODS
          macro and the parent's Xxx_IMETHODS macro. In C++ we need
          only use the IDirect3D_METHODS since method inheritance is
          taken care of by the language.
        </para>
        <para>
          In C++ the ICOM_METHOD macros generate a function prototype
          and a call to a function pointer method. This means using
          once 't1 p1, t2 p2, ...' and once 'p1, p2' without the
          types. The only way I found to handle this is to have one
          ICOM_METHOD macro per number of parameters and to have it
          take only the type information (with const if necessary) as
          parameters.  The 'undef ICOM_INTERFACE' is here to remind
          you that using ICOM_INTERFACE in the following macros will
          not work. This time it's because the ICOM_CALL macro
          expansion is done only once the 'IDirect3D_Xxx' macro is
          expanded. And by that time ICOM_INTERFACE will be long gone
          anyway.
        </para>
        <para>
          You may have noticed the double commas after each parameter
          type. This allows you to put the name of that parameter
          which I think is good for documentation. It is not required
          and since I did not know what to put there for this example
          (I could only find doc about IDirect3D2), I left them blank.
        </para>
        <para>
          Finally the set of 'IDirect3D_Xxx' macros is a standard set
          of macros defined to ease access to the interface methods in
          C. Unfortunately I don't see any way to avoid having to
          duplicate the inherited method definitions there. This time
          I could have used a trick to use only one macro whatever the
          number of parameters but I preferred to have it work the same
          way as above.
        </para>
        <para>
          You probably have noticed that we don't define the fields we
          need to actually implement this interface: reference count,
          pointer to other resources and miscellaneous fields. That's
          because these interfaces are just that: interfaces. They may
          be implemented more than once, in different contexts and
          sometimes not even in Wine. Thus it would not make sense to
          impose that the interface contains some specific fields.
        </para>
      </sect2>

      <sect2>
        <title>Bindings in C</title>

        <para>
          In C this gives:
        </para>
        <programlisting>typedef struct IDirect3DVtbl IDirect3DVtbl;
struct IDirect3D {
    IDirect3DVtbl* lpVtbl;
};
struct IDirect3DVtbl {
    HRESULT (*fnQueryInterface)(IDirect3D* me, REFIID riid, LPVOID* ppvObj);
    ULONG (*fnAddRef)(IDirect3D* me);
    ULONG (*fnRelease)(IDirect3D* me);
    HRESULT (*fnInitialize)(IDirect3D* me, REFIID a);
    HRESULT (*fnEnumDevices)(IDirect3D* me, LPD3DENUMDEVICESCALLBACK a, LPVOID b);
    HRESULT (*fnCreateLight)(IDirect3D* me, LPDIRECT3DLIGHT* a, IUnknown* b);
    HRESULT (*fnCreateMaterial)(IDirect3D* me, LPDIRECT3DMATERIAL* a, IUnknown* b);
    HRESULT (*fnCreateViewport)(IDirect3D* me, LPDIRECT3DVIEWPORT* a, IUnknown* b);
    HRESULT (*fnFindDevice)(IDirect3D* me, LPD3DFINDDEVICESEARCH a, LPD3DFINDDEVICERESULT b);
}; 

#ifdef ICOM_CINTERFACE
// *** IUnknown methods *** //
#define IDirect3D_QueryInterface(p,a,b) (p)->lpVtbl->fnQueryInterface(p,a,b)
#define IDirect3D_AddRef(p)             (p)->lpVtbl->fnAddRef(p)
#define IDirect3D_Release(p)            (p)->lpVtbl->fnRelease(p)
// *** IDirect3D methods *** //
#define IDirect3D_Initialize(p,a)       (p)->lpVtbl->fnInitialize(p,a)
#define IDirect3D_EnumDevices(p,a,b)    (p)->lpVtbl->fnEnumDevice(p,a,b)
#define IDirect3D_CreateLight(p,a,b)    (p)->lpVtbl->fnCreateLight(p,a,b)
#define IDirect3D_CreateMaterial(p,a,b) (p)->lpVtbl->fnCreateMaterial(p,a,b)
#define IDirect3D_CreateViewport(p,a,b) (p)->lpVtbl->fnCreateViewport(p,a,b)
#define IDirect3D_FindDevice(p,a,b)     (p)->lpVtbl->fnFindDevice(p,a,b)
#endif</programlisting>
        <para>
          Comments:
        </para>
        <para>
          IDirect3D only contains a pointer to the IDirect3D
          virtual/jump table. This is the only thing the user needs to
          know to use the interface. Of course the structure we will
          define to implement this interface will have more fields but
          the first one will match this pointer.
        </para>
        <para>
          The code generated by ICOM_DEFINE defines both the structure
          representing the interface and the structure for the jump
          table. ICOM_DEFINE uses the parent's Xxx_IMETHODS macro to
          automatically repeat the prototypes of all the inherited
          methods and then uses IDirect3D_METHODS to define the
          IDirect3D methods.
        </para>
        <para>
          Each method is declared as a pointer to function field in
          the jump table. The implementation will fill this jump table
          with appropriate values, probably using a static variable,
          and initialize the lpVtbl field to point to this variable.
        </para>
        <para>
          The IDirect3D_Xxx macros then just dereference the lpVtbl
          pointer and use the function pointer corresponding to the
          macro name. This emulates the behavior of a virtual table
          and should be just as fast.
        </para>
        <para>
          This C code should be quite compatible with the Windows
          headers both for code that uses COM interfaces and for code
          implementing a COM interface.
        </para>
      </sect2>

      <sect2>
        <title>Bindings in C++</title>
        <para>
          And in C++ (with gcc's g++):
        </para>
        <programlisting>typedef struct IDirect3D: public IUnknown {
    private: HRESULT (*fnInitialize)(IDirect3D* me, REFIID a);
    public: inline HRESULT Initialize(REFIID a) { return ((IDirect3D*)t.lpVtbl)->fnInitialize(this,a); };
    private: HRESULT (*fnEnumDevices)(IDirect3D* me, LPD3DENUMDEVICESCALLBACK a, LPVOID b);
    public: inline HRESULT EnumDevices(LPD3DENUMDEVICESCALLBACK a, LPVOID b)
        { return ((IDirect3D*)t.lpVtbl)->fnEnumDevices(this,a,b); };
    private: HRESULT (*fnCreateLight)(IDirect3D* me, LPDIRECT3DLIGHT* a, IUnknown* b);
    public: inline HRESULT CreateLight(LPDIRECT3DLIGHT* a, IUnknown* b)
        { return ((IDirect3D*)t.lpVtbl)->fnCreateLight(this,a,b); };
    private: HRESULT (*fnCreateMaterial)(IDirect3D* me, LPDIRECT3DMATERIAL* a, IUnknown* b);
    public: inline HRESULT CreateMaterial(LPDIRECT3DMATERIAL* a, IUnknown* b)
        { return ((IDirect3D*)t.lpVtbl)->fnCreateMaterial(this,a,b); };
    private: HRESULT (*fnCreateViewport)(IDirect3D* me, LPDIRECT3DVIEWPORT* a, IUnknown* b);
    public: inline HRESULT CreateViewport(LPDIRECT3DVIEWPORT* a, IUnknown* b)
        { return ((IDirect3D*)t.lpVtbl)->fnCreateViewport(this,a,b); };
    private:  HRESULT (*fnFindDevice)(IDirect3D* me, LPD3DFINDDEVICESEARCH a, LPD3DFINDDEVICERESULT b);
    public: inline HRESULT FindDevice(LPD3DFINDDEVICESEARCH a, LPD3DFINDDEVICERESULT b)
        { return ((IDirect3D*)t.lpVtbl)->fnFindDevice(this,a,b); };
};</programlisting>
        <para>
          Comments:
        </para>
        <para>
          In C++ IDirect3D does double duty as both the virtual/jump
          table and as the interface definition. The reason for this
          is to avoid having to duplicate the method definitions: once
          to have the function pointers in the jump table and once to
          have the methods in the interface class. Here one macro can
          generate both. This means though that the first pointer,
          t.lpVtbl defined in IUnknown, must be interpreted as the
          jump table pointer if we interpret the structure as the
          interface class, and as the function pointer to the
          QueryInterface method, t.fnQueryInterface, if we interpret
          the structure as the jump table. Fortunately this gymnastic
          is entirely taken care of in the header of IUnknown.
        </para>
        <para>
          Of course in C++ we use inheritance so that we don't have to
          duplicate the method definitions.
        </para>
        <para>
          Since IDirect3D does double duty, each ICOM_METHOD macro
          defines both a function pointer and a non-virtual inline
          method which dereferences it and calls it. This way this
          method behaves just like a virtual method but does not
          create a true C++ virtual table which would break the
          structure layout. If you look at the implementation of these
          methods you'll notice that they would not work for void
          functions. We have to return something and fortunately this
          seems to be what all the COM methods do (otherwise we would
          need another set of macros).
        </para>
        <para>
          Note how the ICOM_METHOD generates both function prototypes
          mixing types and formal parameter names and the method
          invocation using only the formal parameter name. This is the
          reason why we need different macros to handle different
          numbers of parameters.
        </para>
        <para>
          Finally there is no IDirect3D_Xxx macro. These are not
          needed in C++ unless the CINTERFACE macro is defined in
          which case we would not be here.
        </para>
        <para>
          This C++ code works well for code that just uses COM
          interfaces. But it will not work with C++ code implement a
          COM interface. That's because such code assumes the
          interface methods are declared as virtual C++ methods which
          is not the case here.
        </para>
      </sect2>

      <sect2>
        <title>Implementing a COM interface.</title>

        <para>
          This continues the above example. This example assumes that
          the implementation is in C.
        </para>
        <programlisting>typedef struct _IDirect3D {
    void* lpVtbl;
    // ...
 } _IDirect3D;

static ICOM_VTABLE(IDirect3D) d3dvt;

// implement the IDirect3D methods here

int IDirect3D_fnQueryInterface(IDirect3D* me)
{
    ICOM_THIS(IDirect3D,me);
    // ...
}

// ...

static ICOM_VTABLE(IDirect3D) d3dvt = {
    ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
    IDirect3D_fnQueryInterface,
    IDirect3D_fnAdd,
    IDirect3D_fnAdd2,
    IDirect3D_fnInitialize,
    IDirect3D_fnSetWidth
};</programlisting>
        <para>
          Comments:
        </para>
        <para>
          We first define what the interface really contains. This is
          the _IDirect3D structure. The first field must of course be
          the virtual table pointer. Everything else is free.
        </para>
        <para>
          Then we predeclare our static virtual table variable, we
          will need its address in some methods to initialize the
          virtual table pointer of the returned interface objects.
        </para>
        <para>
          Then we implement the interface methods. To match what has
          been declared in the header file they must take a pointer to
          a IDirect3D structure and we must cast it to an _IDirect3D
          so that we can manipulate the fields. This is performed by
          the ICOM_THIS macro.
        </para>
        <para>
          Finally we initialize the virtual table.
        </para>
      </sect2>
    </sect1>
  </chapter>

<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-parent-document:("wine-devel.sgml" "set" "book" "part" "chapter" "")
End:
-->
