|  | <chapter id="ole"> | 
|  | <title>COM/OLE in Wine</title> | 
|  |  | 
|  | <sect1 id="ole-architecture"> | 
|  | <title>COM/OLE Architecture in Wine</title> | 
|  |  | 
|  | <para> | 
|  | The section goes into detail about how COM/OLE2 are | 
|  | implemented in Wine. | 
|  | </para> | 
|  | </sect1> | 
|  |  | 
|  | <sect1 id="ole-binary"> | 
|  | <title>Using Binary OLE components in Wine</title> | 
|  | <para> | 
|  | This section describes how to import pre-compiled COM/OLE | 
|  | components... | 
|  | </para> | 
|  | </sect1> | 
|  |  | 
|  | <sect1 id="com-writing"> | 
|  | <title>Writing OLE Components for Wine</title> | 
|  |  | 
|  | <para> | 
|  | Based on the comments in <filename>wine/include/wine/obj_base.h</filename>. | 
|  | </para> | 
|  | <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 difference 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 differences 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: | 
|  | --> |