Cast time_t to long for printing.
[wine] / documentation / ole.sgml
1   <chapter id="ole">
2     <title>COM/OLE in Wine</title>
3
4     <sect1 id="com-writing">
5       <title>Writing OLE Components for Wine</title>
6
7       <para>
8         This section describes how to create your own natively
9         compiled COM/OLE components.
10       </para>
11
12       <sect2>
13         <title>Macros to define a COM interface</title>
14
15         <para>
16           The goal of the following set of definitions is to provide a
17           way to use the same header file definitions to provide both
18           a C interface and a C++ object oriented interface to COM
19           interfaces. The type of interface is selected automatically
20           depending on the language but it is always possible to get
21           the C interface in C++ by defining CINTERFACE.
22         </para>
23         <para>
24           It is based on the following assumptions:
25         </para>
26         <itemizedlist>
27           <listitem>
28             <para>
29               all COM interfaces derive from IUnknown, this should not
30               be a problem.
31             </para>
32           </listitem>
33           <listitem>
34             <para>
35               the header file only defines the interface, the actual
36               fields are defined separately in the C file implementing
37               the interface.
38             </para>
39           </listitem>
40         </itemizedlist>
41         <para>
42           The natural approach to this problem would be to make sure
43           we get a C++ class and virtual methods in C++ and a
44           structure with a table of pointer to functions in C.
45           Unfortunately the layout of the virtual table is compiler
46           specific, the layout of g++ virtual tables is not the same
47           as that of an egcs virtual table which is not the same as
48           that generated by Visual C+. There are work arounds to make
49           the virtual tables compatible via padding but unfortunately
50           the one which is imposed to the Wine emulator by the Windows
51           binaries, i.e. the Visual C++ one, is the most compact of
52           all.
53         </para>
54         <para>
55           So the solution I finally adopted does not use virtual
56           tables. Instead I use in-line non virtual methods that
57           dereference the method pointer themselves and perform the
58           call.
59         </para>
60         <para>
61           Let's take Direct3D as an example:
62         </para>
63         <programlisting>#define ICOM_INTERFACE IDirect3D
64 #define IDirect3D_METHODS \
65     ICOM_METHOD1(HRESULT,Initialize,    REFIID,) \
66     ICOM_METHOD2(HRESULT,EnumDevices,   LPD3DENUMDEVICESCALLBACK,, LPVOID,) \
67     ICOM_METHOD2(HRESULT,CreateLight,   LPDIRECT3DLIGHT*,, IUnknown*,) \
68     ICOM_METHOD2(HRESULT,CreateMaterial,LPDIRECT3DMATERIAL*,, IUnknown*,) \
69     ICOM_METHOD2(HRESULT,CreateViewport,LPDIRECT3DVIEWPORT*,, IUnknown*,) \
70     ICOM_METHOD2(HRESULT,FindDevice,    LPD3DFINDDEVICESEARCH,, LPD3DFINDDEVICERESULT,)
71 #define IDirect3D_IMETHODS \
72     IUnknown_IMETHODS \
73     IDirect3D_METHODS
74 ICOM_DEFINE(IDirect3D,IUnknown)
75 #undef ICOM_INTERFACE
76
77 #ifdef ICOM_CINTERFACE
78 // *** IUnknown methods *** //
79 #define IDirect3D_QueryInterface(p,a,b) ICOM_CALL2(QueryInterface,p,a,b)
80 #define IDirect3D_AddRef(p)             ICOM_CALL (AddRef,p)
81 #define IDirect3D_Release(p)            ICOM_CALL (Release,p)
82 // *** IDirect3D methods *** //
83 #define IDirect3D_Initialize(p,a)       ICOM_CALL1(Initialize,p,a)
84 #define IDirect3D_EnumDevices(p,a,b)    ICOM_CALL2(EnumDevice,p,a,b)
85 #define IDirect3D_CreateLight(p,a,b)    ICOM_CALL2(CreateLight,p,a,b)
86 #define IDirect3D_CreateMaterial(p,a,b) ICOM_CALL2(CreateMaterial,p,a,b)
87 #define IDirect3D_CreateViewport(p,a,b) ICOM_CALL2(CreateViewport,p,a,b)
88 #define IDirect3D_FindDevice(p,a,b)     ICOM_CALL2(FindDevice,p,a,b)
89 #endif</programlisting>
90         <para>
91           Comments:
92         </para>
93         <para>
94           The ICOM_INTERFACE macro is used in the ICOM_METHOD macros
95           to define the type of the 'this' pointer. Defining this
96           macro here saves us the trouble of having to repeat the
97           interface name everywhere. Note however that because of the
98           way macros work, a macro like ICOM_METHOD1 cannot use
99           'ICOM_INTERFACE##_VTABLE' because this would give
100           'ICOM_INTERFACE_VTABLE' and not 'IDirect3D_VTABLE'.
101         </para>
102         <para>
103           ICOM_METHODS defines the methods specific to this
104           interface. It is then aggregated with the inherited methods
105           to form ICOM_IMETHODS.
106         </para>
107         <para>
108           ICOM_IMETHODS defines the list of methods that are
109           inheritable from this interface. It must be written manually
110           (rather than using a macro to generate the equivalent code)
111           to avoid macro recursion (which compilers don't like).
112         </para>
113         <para>
114           The ICOM_DEFINE finally declares all the structures
115           necessary for the interface. We have to explicitly use the
116           interface name for macro expansion reasons again.  Inherited
117           methods are inherited in C by using the IDirect3D_METHODS
118           macro and the parent's Xxx_IMETHODS macro. In C++ we need
119           only use the IDirect3D_METHODS since method inheritance is
120           taken care of by the language.
121         </para>
122         <para>
123           In C++ the ICOM_METHOD macros generate a function prototype
124           and a call to a function pointer method. This means using
125           once 't1 p1, t2 p2, ...' and once 'p1, p2' without the
126           types. The only way I found to handle this is to have one
127           ICOM_METHOD macro per number of parameters and to have it
128           take only the type information (with const if necessary) as
129           parameters.  The 'undef ICOM_INTERFACE' is here to remind
130           you that using ICOM_INTERFACE in the following macros will
131           not work. This time it's because the ICOM_CALL macro
132           expansion is done only once the 'IDirect3D_Xxx' macro is
133           expanded. And by that time ICOM_INTERFACE will be long gone
134           anyway.
135         </para>
136         <para>
137           You may have noticed the double commas after each parameter
138           type. This allows you to put the name of that parameter
139           which I think is good for documentation. It is not required
140           and since I did not know what to put there for this example
141           (I could only find doc about IDirect3D2), I left them blank.
142         </para>
143         <para>
144           Finally the set of 'IDirect3D_Xxx' macros is a standard set
145           of macros defined to ease access to the interface methods in
146           C. Unfortunately I don't see any way to avoid having to
147           duplicate the inherited method definitions there. This time
148           I could have used a trick to use only one macro whatever the
149           number of parameters but I preferred to have it work the same
150           way as above.
151         </para>
152         <para>
153           You probably have noticed that we don't define the fields we
154           need to actually implement this interface: reference count,
155           pointer to other resources and miscellaneous fields. That's
156           because these interfaces are just that: interfaces. They may
157           be implemented more than once, in different contexts and
158           sometimes not even in Wine. Thus it would not make sense to
159           impose that the interface contains some specific fields.
160         </para>
161       </sect2>
162
163       <sect2>
164         <title>Bindings in C</title>
165
166         <para>
167           In C this gives:
168         </para>
169         <programlisting>typedef struct IDirect3DVtbl IDirect3DVtbl;
170 struct IDirect3D {
171     IDirect3DVtbl* lpVtbl;
172 };
173 struct IDirect3DVtbl {
174     HRESULT (*fnQueryInterface)(IDirect3D* me, REFIID riid, LPVOID* ppvObj);
175     ULONG (*fnAddRef)(IDirect3D* me);
176     ULONG (*fnRelease)(IDirect3D* me);
177     HRESULT (*fnInitialize)(IDirect3D* me, REFIID a);
178     HRESULT (*fnEnumDevices)(IDirect3D* me, LPD3DENUMDEVICESCALLBACK a, LPVOID b);
179     HRESULT (*fnCreateLight)(IDirect3D* me, LPDIRECT3DLIGHT* a, IUnknown* b);
180     HRESULT (*fnCreateMaterial)(IDirect3D* me, LPDIRECT3DMATERIAL* a, IUnknown* b);
181     HRESULT (*fnCreateViewport)(IDirect3D* me, LPDIRECT3DVIEWPORT* a, IUnknown* b);
182     HRESULT (*fnFindDevice)(IDirect3D* me, LPD3DFINDDEVICESEARCH a, LPD3DFINDDEVICERESULT b);
183 }; 
184
185 #ifdef ICOM_CINTERFACE
186 // *** IUnknown methods *** //
187 #define IDirect3D_QueryInterface(p,a,b) (p)->lpVtbl->fnQueryInterface(p,a,b)
188 #define IDirect3D_AddRef(p)             (p)->lpVtbl->fnAddRef(p)
189 #define IDirect3D_Release(p)            (p)->lpVtbl->fnRelease(p)
190 // *** IDirect3D methods *** //
191 #define IDirect3D_Initialize(p,a)       (p)->lpVtbl->fnInitialize(p,a)
192 #define IDirect3D_EnumDevices(p,a,b)    (p)->lpVtbl->fnEnumDevice(p,a,b)
193 #define IDirect3D_CreateLight(p,a,b)    (p)->lpVtbl->fnCreateLight(p,a,b)
194 #define IDirect3D_CreateMaterial(p,a,b) (p)->lpVtbl->fnCreateMaterial(p,a,b)
195 #define IDirect3D_CreateViewport(p,a,b) (p)->lpVtbl->fnCreateViewport(p,a,b)
196 #define IDirect3D_FindDevice(p,a,b)     (p)->lpVtbl->fnFindDevice(p,a,b)
197 #endif</programlisting>
198         <para>
199           Comments:
200         </para>
201         <para>
202           IDirect3D only contains a pointer to the IDirect3D
203           virtual/jump table. This is the only thing the user needs to
204           know to use the interface. Of course the structure we will
205           define to implement this interface will have more fields but
206           the first one will match this pointer.
207         </para>
208         <para>
209           The code generated by ICOM_DEFINE defines both the structure
210           representing the interface and the structure for the jump
211           table. ICOM_DEFINE uses the parent's Xxx_IMETHODS macro to
212           automatically repeat the prototypes of all the inherited
213           methods and then uses IDirect3D_METHODS to define the
214           IDirect3D methods.
215         </para>
216         <para>
217           Each method is declared as a pointer to function field in
218           the jump table. The implementation will fill this jump table
219           with appropriate values, probably using a static variable,
220           and initialize the lpVtbl field to point to this variable.
221         </para>
222         <para>
223           The IDirect3D_Xxx macros then just dereference the lpVtbl
224           pointer and use the function pointer corresponding to the
225           macro name. This emulates the behavior of a virtual table
226           and should be just as fast.
227         </para>
228         <para>
229           This C code should be quite compatible with the Windows
230           headers both for code that uses COM interfaces and for code
231           implementing a COM interface.
232         </para>
233       </sect2>
234
235       <sect2>
236         <title>Bindings in C++</title>
237         <para>
238           And in C++ (with gcc's g++):
239         </para>
240         <programlisting>typedef struct IDirect3D: public IUnknown {
241     private: HRESULT (*fnInitialize)(IDirect3D* me, REFIID a);
242     public: inline HRESULT Initialize(REFIID a) { return ((IDirect3D*)t.lpVtbl)->fnInitialize(this,a); };
243     private: HRESULT (*fnEnumDevices)(IDirect3D* me, LPD3DENUMDEVICESCALLBACK a, LPVOID b);
244     public: inline HRESULT EnumDevices(LPD3DENUMDEVICESCALLBACK a, LPVOID b)
245         { return ((IDirect3D*)t.lpVtbl)->fnEnumDevices(this,a,b); };
246     private: HRESULT (*fnCreateLight)(IDirect3D* me, LPDIRECT3DLIGHT* a, IUnknown* b);
247     public: inline HRESULT CreateLight(LPDIRECT3DLIGHT* a, IUnknown* b)
248         { return ((IDirect3D*)t.lpVtbl)->fnCreateLight(this,a,b); };
249     private: HRESULT (*fnCreateMaterial)(IDirect3D* me, LPDIRECT3DMATERIAL* a, IUnknown* b);
250     public: inline HRESULT CreateMaterial(LPDIRECT3DMATERIAL* a, IUnknown* b)
251         { return ((IDirect3D*)t.lpVtbl)->fnCreateMaterial(this,a,b); };
252     private: HRESULT (*fnCreateViewport)(IDirect3D* me, LPDIRECT3DVIEWPORT* a, IUnknown* b);
253     public: inline HRESULT CreateViewport(LPDIRECT3DVIEWPORT* a, IUnknown* b)
254         { return ((IDirect3D*)t.lpVtbl)->fnCreateViewport(this,a,b); };
255     private:  HRESULT (*fnFindDevice)(IDirect3D* me, LPD3DFINDDEVICESEARCH a, LPD3DFINDDEVICERESULT b);
256     public: inline HRESULT FindDevice(LPD3DFINDDEVICESEARCH a, LPD3DFINDDEVICERESULT b)
257         { return ((IDirect3D*)t.lpVtbl)->fnFindDevice(this,a,b); };
258 };</programlisting>
259         <para>
260           Comments:
261         </para>
262         <para>
263           In C++ IDirect3D does double duty as both the virtual/jump
264           table and as the interface definition. The reason for this
265           is to avoid having to duplicate the method definitions: once
266           to have the function pointers in the jump table and once to
267           have the methods in the interface class. Here one macro can
268           generate both. This means though that the first pointer,
269           t.lpVtbl defined in IUnknown, must be interpreted as the
270           jump table pointer if we interpret the structure as the
271           interface class, and as the function pointer to the
272           QueryInterface method, t.fnQueryInterface, if we interpret
273           the structure as the jump table. Fortunately this gymnastic
274           is entirely taken care of in the header of IUnknown.
275         </para>
276         <para>
277           Of course in C++ we use inheritance so that we don't have to
278           duplicate the method definitions.
279         </para>
280         <para>
281           Since IDirect3D does double duty, each ICOM_METHOD macro
282           defines both a function pointer and a non-virtual inline
283           method which dereferences it and calls it. This way this
284           method behaves just like a virtual method but does not
285           create a true C++ virtual table which would break the
286           structure layout. If you look at the implementation of these
287           methods you'll notice that they would not work for void
288           functions. We have to return something and fortunately this
289           seems to be what all the COM methods do (otherwise we would
290           need another set of macros).
291         </para>
292         <para>
293           Note how the ICOM_METHOD generates both function prototypes
294           mixing types and formal parameter names and the method
295           invocation using only the formal parameter name. This is the
296           reason why we need different macros to handle different
297           numbers of parameters.
298         </para>
299         <para>
300           Finally there is no IDirect3D_Xxx macro. These are not
301           needed in C++ unless the CINTERFACE macro is defined in
302           which case we would not be here.
303         </para>
304         <para>
305           This C++ code works well for code that just uses COM
306           interfaces. But it will not work with C++ code implement a
307           COM interface. That's because such code assumes the
308           interface methods are declared as virtual C++ methods which
309           is not the case here.
310         </para>
311       </sect2>
312
313       <sect2>
314         <title>Implementing a COM interface.</title>
315
316         <para>
317           This continues the above example. This example assumes that
318           the implementation is in C.
319         </para>
320         <programlisting>typedef struct _IDirect3D {
321     void* lpVtbl;
322     // ...
323  } _IDirect3D;
324
325 static ICOM_VTABLE(IDirect3D) d3dvt;
326
327 // implement the IDirect3D methods here
328
329 int IDirect3D_fnQueryInterface(IDirect3D* me)
330 {
331     ICOM_THIS(IDirect3D,me);
332     // ...
333 }
334
335 // ...
336
337 static ICOM_VTABLE(IDirect3D) d3dvt = {
338     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
339     IDirect3D_fnQueryInterface,
340     IDirect3D_fnAdd,
341     IDirect3D_fnAdd2,
342     IDirect3D_fnInitialize,
343     IDirect3D_fnSetWidth
344 };</programlisting>
345         <para>
346           Comments:
347         </para>
348         <para>
349           We first define what the interface really contains. This is
350           the _IDirect3D structure. The first field must of course be
351           the virtual table pointer. Everything else is free.
352         </para>
353         <para>
354           Then we predeclare our static virtual table variable, we
355           will need its address in some methods to initialize the
356           virtual table pointer of the returned interface objects.
357         </para>
358         <para>
359           Then we implement the interface methods. To match what has
360           been declared in the header file they must take a pointer to
361           a IDirect3D structure and we must cast it to an _IDirect3D
362           so that we can manipulate the fields. This is performed by
363           the ICOM_THIS macro.
364         </para>
365         <para>
366           Finally we initialize the virtual table.
367         </para>
368       </sect2>
369     </sect1>
370   </chapter>
371
372 <!-- Keep this comment at the end of the file
373 Local variables:
374 mode: sgml
375 sgml-parent-document:("wine-devel.sgml" "set" "book" "part" "chapter" "")
376 End:
377 -->