riched20/tests: Get the CreateTextServices() pointer only once.
[wine] / dlls / riched20 / tests / txtsrv.c
1 /*
2  * Unit test suite for windowless rich edit controls
3  *
4  * Copyright 2008 Maarten Lankhorst
5  * Copyright 2008 Austin Lund
6  * Copyright 2008 Dylan Smith
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22
23 #define COBJMACROS
24
25 #include <stdio.h>
26 #include <stdarg.h>
27 #include <windef.h>
28 #include <winbase.h>
29 #include <objbase.h>
30 #include <richedit.h>
31 #include <initguid.h>
32 #include <textserv.h>
33 #include <wine/test.h>
34 #include <oleauto.h>
35 #include <limits.h>
36
37 static HMODULE hmoduleRichEdit;
38 static IID *pIID_ITextServices;
39 static IID *pIID_ITextHost;
40 static IID *pIID_ITextHost2;
41 static PCreateTextServices pCreateTextServices;
42
43 static const char *debugstr_guid(REFIID riid)
44 {
45     static char buf[50];
46
47     if(!riid)
48         return "(null)";
49
50     sprintf(buf, "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
51             riid->Data1, riid->Data2, riid->Data3, riid->Data4[0],
52             riid->Data4[1], riid->Data4[2], riid->Data4[3], riid->Data4[4],
53             riid->Data4[5], riid->Data4[6], riid->Data4[7]);
54
55     return buf;
56 }
57
58 /* Define C Macros for ITextServices calls. */
59
60 /* Use a special table for x86 machines to convert the thiscall
61  * calling convention.  This isn't needed on other platforms. */
62 #ifdef __i386__
63 static ITextServicesVtbl itextServicesStdcallVtbl;
64 #define TXTSERV_VTABLE(This) (&itextServicesStdcallVtbl)
65 #else /* __i386__ */
66 #define TXTSERV_VTABLE(This) (This)->lpVtbl
67 #endif /* __i386__ */
68
69 #define ITextServices_TxSendMessage(This,a,b,c,d) TXTSERV_VTABLE(This)->TxSendMessage(This,a,b,c,d)
70 #define ITextServices_TxDraw(This,a,b,c,d,e,f,g,h,i,j,k,l) TXTSERV_VTABLE(This)->TxDraw(This,a,b,c,d,e,f,g,h,i,j,k,l)
71 #define ITextServices_TxGetHScroll(This,a,b,c,d,e) TXTSERV_VTABLE(This)->TxGetHScroll(This,a,b,c,d,e)
72 #define ITextServices_TxGetVScroll(This,a,b,c,d,e) TXTSERV_VTABLE(This)->TxGetVScroll(This,a,b,c,d,e)
73 #define ITextServices_OnTxSetCursor(This,a,b,c,d,e,f,g,h,i) TXTSERV_VTABLE(This)->OnTxSetCursor(This,a,b,c,d,e,f,g,h,i)
74 #define ITextServices_TxQueryHitPoint(This,a,b,c,d,e,f,g,h,i,j) TXTSERV_VTABLE(This)->TxQueryHitPoint(This,a,b,c,d,e,f,g,h,i,j)
75 #define ITextServices_OnTxInplaceActivate(This,a) TXTSERV_VTABLE(This)->OnTxInplaceActivate(This,a)
76 #define ITextServices_OnTxInplaceDeactivate(This) TXTSERV_VTABLE(This)->OnTxInplaceDeactivate(This)
77 #define ITextServices_OnTxUIActivate(This) TXTSERV_VTABLE(This)->OnTxUIActivate(This)
78 #define ITextServices_OnTxUIDeactivate(This) TXTSERV_VTABLE(This)->OnTxUIDeactivate(This)
79 #define ITextServices_TxGetText(This,a) TXTSERV_VTABLE(This)->TxGetText(This,a)
80 #define ITextServices_TxSetText(This,a) TXTSERV_VTABLE(This)->TxSetText(This,a)
81 #define ITextServices_TxGetCurrentTargetX(This,a) TXTSERV_VTABLE(This)->TxGetCurrentTargetX(This,a)
82 #define ITextServices_TxGetBaseLinePos(This,a) TXTSERV_VTABLE(This)->TxGetBaseLinePos(This,a)
83 #define ITextServices_TxGetNaturalSize(This,a,b,c,d,e,f,g,h) TXTSERV_VTABLE(This)->TxGetNaturalSize(This,a,b,c,d,e,f,g,h)
84 #define ITextServices_TxGetDropTarget(This,a) TXTSERV_VTABLE(This)->TxGetDropTarget(This,a)
85 #define ITextServices_OnTxPropertyBitsChange(This,a,b) TXTSERV_VTABLE(This)->OnTxPropertyBitsChange(This,a,b)
86 #define ITextServices_TxGetCachedSize(This,a,b) TXTSERV_VTABLE(This)->TxGetCachedSize(This,a,b)
87
88 /* Set the WINETEST_DEBUG environment variable to be greater than 1 for verbose
89  * function call traces of ITextHost. */
90 #define TRACECALL if(winetest_debug > 1) trace
91
92 /************************************************************************/
93 /* ITextHost implementation for conformance testing. */
94
95 typedef struct ITextHostTestImpl
96 {
97     ITextHost ITextHost_iface;
98     LONG refCount;
99 } ITextHostTestImpl;
100
101 static inline ITextHostTestImpl *impl_from_ITextHost(ITextHost *iface)
102 {
103     return CONTAINING_RECORD(iface, ITextHostTestImpl, ITextHost_iface);
104 }
105
106 static HRESULT WINAPI ITextHostImpl_QueryInterface(ITextHost *iface,
107                                                    REFIID riid,
108                                                    LPVOID *ppvObject)
109 {
110     ITextHostTestImpl *This = impl_from_ITextHost(iface);
111
112     if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, pIID_ITextHost)) {
113         *ppvObject = This;
114         ITextHost_AddRef((ITextHost *)*ppvObject);
115         return S_OK;
116     }
117
118     return E_NOINTERFACE;
119 }
120
121 static ULONG WINAPI ITextHostImpl_AddRef(ITextHost *iface)
122 {
123     ITextHostTestImpl *This = impl_from_ITextHost(iface);
124     ULONG refCount = InterlockedIncrement(&This->refCount);
125     return refCount;
126 }
127
128 static ULONG WINAPI ITextHostImpl_Release(ITextHost *iface)
129 {
130     ITextHostTestImpl *This = impl_from_ITextHost(iface);
131     ULONG refCount = InterlockedDecrement(&This->refCount);
132
133     if (!refCount)
134     {
135         CoTaskMemFree(This);
136         return 0;
137     } else {
138         return refCount;
139     }
140 }
141
142 static HDC WINAPI ITextHostImpl_TxGetDC(ITextHost *iface)
143 {
144     ITextHostTestImpl *This = impl_from_ITextHost(iface);
145     TRACECALL("Call to TxGetDC(%p)\n", This);
146     return NULL;
147 }
148
149 static INT WINAPI ITextHostImpl_TxReleaseDC(ITextHost *iface,
150                                             HDC hdc)
151 {
152     ITextHostTestImpl *This = impl_from_ITextHost(iface);
153     TRACECALL("Call to TxReleaseDC(%p)\n", This);
154     return 0;
155 }
156
157 static BOOL WINAPI ITextHostImpl_TxShowScrollBar(ITextHost *iface,
158                                                  INT fnBar,
159                                                  BOOL fShow)
160 {
161     ITextHostTestImpl *This = impl_from_ITextHost(iface);
162     TRACECALL("Call to TxShowScrollBar(%p, fnBar=%d, fShow=%d)\n",
163                 This, fnBar, fShow);
164     return FALSE;
165 }
166
167 static BOOL WINAPI ITextHostImpl_TxEnableScrollBar(ITextHost *iface,
168                                                    INT fuSBFlags,
169                                                    INT fuArrowflags)
170 {
171     ITextHostTestImpl *This = impl_from_ITextHost(iface);
172     TRACECALL("Call to TxEnableScrollBar(%p, fuSBFlags=%d, fuArrowflags=%d)\n",
173                This, fuSBFlags, fuArrowflags);
174     return FALSE;
175 }
176
177 static BOOL WINAPI ITextHostImpl_TxSetScrollRange(ITextHost *iface,
178                                                   INT fnBar,
179                                                   LONG nMinPos,
180                                                   INT nMaxPos,
181                                                   BOOL fRedraw)
182 {
183     ITextHostTestImpl *This = impl_from_ITextHost(iface);
184     TRACECALL("Call to TxSetScrollRange(%p, fnBar=%d, nMinPos=%d, nMaxPos=%d, fRedraw=%d)\n",
185                This, fnBar, nMinPos, nMaxPos, fRedraw);
186     return FALSE;
187 }
188
189 static BOOL WINAPI ITextHostImpl_TxSetScrollPos(ITextHost *iface,
190                                                 INT fnBar,
191                                                 INT nPos,
192                                                 BOOL fRedraw)
193 {
194     ITextHostTestImpl *This = impl_from_ITextHost(iface);
195     TRACECALL("Call to TxSetScrollPos(%p, fnBar=%d, nPos=%d, fRedraw=%d)\n",
196                This, fnBar, nPos, fRedraw);
197     return FALSE;
198 }
199
200 static void WINAPI ITextHostImpl_TxInvalidateRect(ITextHost *iface,
201                                                   LPCRECT prc,
202                                                   BOOL fMode)
203 {
204     ITextHostTestImpl *This = impl_from_ITextHost(iface);
205     TRACECALL("Call to TxInvalidateRect(%p, prc=%p, fMode=%d)\n",
206                This, prc, fMode);
207 }
208
209 static void WINAPI ITextHostImpl_TxViewChange(ITextHost *iface, BOOL fUpdate)
210 {
211     ITextHostTestImpl *This = impl_from_ITextHost(iface);
212     TRACECALL("Call to TxViewChange(%p, fUpdate=%d)\n",
213                This, fUpdate);
214 }
215
216 static BOOL WINAPI ITextHostImpl_TxCreateCaret(ITextHost *iface,
217                                                HBITMAP hbmp,
218                                                INT xWidth, INT yHeight)
219 {
220     ITextHostTestImpl *This = impl_from_ITextHost(iface);
221     TRACECALL("Call to TxCreateCaret(%p, nbmp=%p, xWidth=%d, yHeight=%d)\n",
222                This, hbmp, xWidth, yHeight);
223     return FALSE;
224 }
225
226 static BOOL WINAPI ITextHostImpl_TxShowCaret(ITextHost *iface, BOOL fShow)
227 {
228     ITextHostTestImpl *This = impl_from_ITextHost(iface);
229     TRACECALL("Call to TxShowCaret(%p, fShow=%d)\n",
230                This, fShow);
231     return FALSE;
232 }
233
234 static BOOL WINAPI ITextHostImpl_TxSetCaretPos(ITextHost *iface,
235                                                INT x, INT y)
236 {
237     ITextHostTestImpl *This = impl_from_ITextHost(iface);
238     TRACECALL("Call to TxSetCaretPos(%p, x=%d, y=%d)\n", This, x, y);
239     return FALSE;
240 }
241
242 static BOOL WINAPI ITextHostImpl_TxSetTimer(ITextHost *iface,
243                                             UINT idTimer, UINT uTimeout)
244 {
245     ITextHostTestImpl *This = impl_from_ITextHost(iface);
246     TRACECALL("Call to TxSetTimer(%p, idTimer=%u, uTimeout=%u)\n",
247               This, idTimer, uTimeout);
248     return FALSE;
249 }
250
251 static void WINAPI ITextHostImpl_TxKillTimer(ITextHost *iface, UINT idTimer)
252 {
253     ITextHostTestImpl *This = impl_from_ITextHost(iface);
254     TRACECALL("Call to TxKillTimer(%p, idTimer=%u)\n", This, idTimer);
255 }
256
257 static void WINAPI ITextHostImpl_TxScrollWindowEx(ITextHost *iface,
258                                                   INT dx, INT dy,
259                                                   LPCRECT lprcScroll,
260                                                   LPCRECT lprcClip,
261                                                   HRGN hRgnUpdate,
262                                                   LPRECT lprcUpdate,
263                                                   UINT fuScroll)
264 {
265     ITextHostTestImpl *This = impl_from_ITextHost(iface);
266     TRACECALL("Call to TxScrollWindowEx(%p, %d, %d, %p, %p, %p, %p, %d)\n",
267               This, dx, dy, lprcScroll, lprcClip, hRgnUpdate, lprcUpdate, fuScroll);
268 }
269
270 static void WINAPI ITextHostImpl_TxSetCapture(ITextHost *iface, BOOL fCapture)
271 {
272     ITextHostTestImpl *This = impl_from_ITextHost(iface);
273     TRACECALL("Call to TxSetCapture(%p, fCapture=%d)\n", This, fCapture);
274 }
275
276 static void WINAPI ITextHostImpl_TxSetFocus(ITextHost *iface)
277 {
278     ITextHostTestImpl *This = impl_from_ITextHost(iface);
279     TRACECALL("Call to TxSetFocus(%p)\n", This);
280 }
281
282 static void WINAPI ITextHostImpl_TxSetCursor(ITextHost *iface,
283                                              HCURSOR hcur,
284                                              BOOL fText)
285 {
286     ITextHostTestImpl *This = impl_from_ITextHost(iface);
287     TRACECALL("Call to TxSetCursor(%p, hcur=%p, fText=%d)\n",
288               This, hcur, fText);
289 }
290
291 static BOOL WINAPI ITextHostImpl_TxScreenToClient(ITextHost *iface,
292                                                   LPPOINT lppt)
293 {
294     ITextHostTestImpl *This = impl_from_ITextHost(iface);
295     TRACECALL("Call to TxScreenToClient(%p, lppt=%p)\n", This, lppt);
296     return FALSE;
297 }
298
299 static BOOL WINAPI ITextHostImpl_TxClientToScreen(ITextHost *iface,
300                                                   LPPOINT lppt)
301 {
302     ITextHostTestImpl *This = impl_from_ITextHost(iface);
303     TRACECALL("Call to TxClientToScreen(%p, lppt=%p)\n", This, lppt);
304     return FALSE;
305 }
306
307 static HRESULT WINAPI ITextHostImpl_TxActivate(ITextHost *iface,
308                                                LONG *plOldState)
309 {
310     ITextHostTestImpl *This = impl_from_ITextHost(iface);
311     TRACECALL("Call to TxActivate(%p, plOldState=%p)\n", This, plOldState);
312     return E_NOTIMPL;
313 }
314
315 static HRESULT WINAPI ITextHostImpl_TxDeactivate(ITextHost *iface,
316                                                  LONG lNewState)
317 {
318     ITextHostTestImpl *This = impl_from_ITextHost(iface);
319     TRACECALL("Call to TxDeactivate(%p, lNewState=%d)\n", This, lNewState);
320     return E_NOTIMPL;
321 }
322
323 static HRESULT WINAPI ITextHostImpl_TxGetClientRect(ITextHost *iface,
324                                                     LPRECT prc)
325 {
326     ITextHostTestImpl *This = impl_from_ITextHost(iface);
327     TRACECALL("Call to TxGetClientRect(%p, prc=%p)\n", This, prc);
328     return E_NOTIMPL;
329 }
330
331 static HRESULT WINAPI ITextHostImpl_TxGetViewInset(ITextHost *iface,
332                                                    LPRECT prc)
333 {
334     ITextHostTestImpl *This = impl_from_ITextHost(iface);
335     TRACECALL("Call to TxGetViewInset(%p, prc=%p)\n", This, prc);
336     return E_NOTIMPL;
337 }
338
339 static HRESULT WINAPI ITextHostImpl_TxGetCharFormat(ITextHost *iface,
340                                                     const CHARFORMATW **ppCF)
341 {
342     ITextHostTestImpl *This = impl_from_ITextHost(iface);
343     TRACECALL("Call to TxGetCharFormat(%p, ppCF=%p)\n", This, ppCF);
344     return E_NOTIMPL;
345 }
346
347 static HRESULT WINAPI ITextHostImpl_TxGetParaFormat(ITextHost *iface,
348                                                     const PARAFORMAT **ppPF)
349 {
350     ITextHostTestImpl *This = impl_from_ITextHost(iface);
351     TRACECALL("Call to TxGetParaFormat(%p, ppPF=%p)\n", This, ppPF);
352     return E_NOTIMPL;
353 }
354
355 static COLORREF WINAPI ITextHostImpl_TxGetSysColor(ITextHost *iface,
356                                                    int nIndex)
357 {
358     ITextHostTestImpl *This = impl_from_ITextHost(iface);
359     TRACECALL("Call to TxGetSysColor(%p, nIndex=%d)\n", This, nIndex);
360     return E_NOTIMPL;
361 }
362
363 static HRESULT WINAPI ITextHostImpl_TxGetBackStyle(ITextHost *iface,
364                                                    TXTBACKSTYLE *pStyle)
365 {
366     ITextHostTestImpl *This = impl_from_ITextHost(iface);
367     TRACECALL("Call to TxGetBackStyle(%p, pStyle=%p)\n", This, pStyle);
368     return E_NOTIMPL;
369 }
370
371 static HRESULT WINAPI ITextHostImpl_TxGetMaxLength(ITextHost *iface,
372                                                    DWORD *pLength)
373 {
374     ITextHostTestImpl *This = impl_from_ITextHost(iface);
375     TRACECALL("Call to TxGetMaxLength(%p, pLength=%p)\n", This, pLength);
376     return E_NOTIMPL;
377 }
378
379 static HRESULT WINAPI ITextHostImpl_TxGetScrollBars(ITextHost *iface,
380                                                     DWORD *pdwScrollBar)
381 {
382     ITextHostTestImpl *This = impl_from_ITextHost(iface);
383     TRACECALL("Call to TxGetScrollBars(%p, pdwScrollBar=%p)\n",
384                This, pdwScrollBar);
385     return E_NOTIMPL;
386 }
387
388 static HRESULT WINAPI ITextHostImpl_TxGetPasswordChar(ITextHost *iface,
389                                                       WCHAR *pch)
390 {
391     ITextHostTestImpl *This = impl_from_ITextHost(iface);
392     TRACECALL("Call to TxGetPasswordChar(%p, pch=%p)\n", This, pch);
393     return E_NOTIMPL;
394 }
395
396 static HRESULT WINAPI ITextHostImpl_TxGetAcceleratorPos(ITextHost *iface,
397                                                         LONG *pch)
398 {
399     ITextHostTestImpl *This = impl_from_ITextHost(iface);
400     TRACECALL("Call to TxGetAcceleratorPos(%p, pch=%p)\n", This, pch);
401     return E_NOTIMPL;
402 }
403
404 static HRESULT WINAPI ITextHostImpl_TxGetExtent(ITextHost *iface,
405                                                 LPSIZEL lpExtent)
406 {
407     ITextHostTestImpl *This = impl_from_ITextHost(iface);
408     TRACECALL("Call to TxGetExtent(%p, lpExtent=%p)\n", This, lpExtent);
409     return E_NOTIMPL;
410 }
411
412 static HRESULT WINAPI ITextHostImpl_OnTxCharFormatChange(ITextHost *iface,
413                                                          const CHARFORMATW *pcf)
414 {
415     ITextHostTestImpl *This = impl_from_ITextHost(iface);
416     TRACECALL("Call to OnTxCharFormatChange(%p, pcf=%p)\n", This, pcf);
417     return E_NOTIMPL;
418 }
419
420 static HRESULT WINAPI ITextHostImpl_OnTxParaFormatChange(ITextHost *iface,
421                                                          const PARAFORMAT *ppf)
422 {
423     ITextHostTestImpl *This = impl_from_ITextHost(iface);
424     TRACECALL("Call to OnTxParaFormatChange(%p, ppf=%p)\n", This, ppf);
425     return E_NOTIMPL;
426 }
427
428 /* This must return S_OK for the native ITextServices object to
429    initialize. */
430 static HRESULT WINAPI ITextHostImpl_TxGetPropertyBits(ITextHost *iface,
431                                                       DWORD dwMask,
432                                                       DWORD *pdwBits)
433 {
434     ITextHostTestImpl *This = impl_from_ITextHost(iface);
435     TRACECALL("Call to TxGetPropertyBits(%p, dwMask=0x%08x, pdwBits=%p)\n",
436               This, dwMask, pdwBits);
437     *pdwBits = 0;
438     return S_OK;
439 }
440
441 static HRESULT WINAPI ITextHostImpl_TxNotify(ITextHost *iface, DWORD iNotify,
442                                              void *pv)
443 {
444     ITextHostTestImpl *This = impl_from_ITextHost(iface);
445     TRACECALL("Call to TxNotify(%p, iNotify=%d, pv=%p)\n", This, iNotify, pv);
446     return E_NOTIMPL;
447 }
448
449 static HIMC WINAPI ITextHostImpl_TxImmGetContext(ITextHost *iface)
450 {
451     ITextHostTestImpl *This = impl_from_ITextHost(iface);
452     TRACECALL("Call to TxImmGetContext(%p)\n", This);
453     return 0;
454 }
455
456 static void WINAPI ITextHostImpl_TxImmReleaseContext(ITextHost *iface, HIMC himc)
457 {
458     ITextHostTestImpl *This = impl_from_ITextHost(iface);
459     TRACECALL("Call to TxImmReleaseContext(%p, himc=%p)\n", This, himc);
460 }
461
462 /* This function must set the variable pointed to by *lSelBarWidth.
463    Otherwise an uninitialized value will be used to calculate
464    positions and sizes even if E_NOTIMPL is returned. */
465 static HRESULT WINAPI ITextHostImpl_TxGetSelectionBarWidth(ITextHost *iface,
466                                                            LONG *lSelBarWidth)
467 {
468     ITextHostTestImpl *This = impl_from_ITextHost(iface);
469     TRACECALL("Call to TxGetSelectionBarWidth(%p, lSelBarWidth=%p)\n",
470                 This, lSelBarWidth);
471     *lSelBarWidth = 0;
472     return E_NOTIMPL;
473 }
474
475 static ITextHostVtbl itextHostVtbl = {
476     ITextHostImpl_QueryInterface,
477     ITextHostImpl_AddRef,
478     ITextHostImpl_Release,
479     ITextHostImpl_TxGetDC,
480     ITextHostImpl_TxReleaseDC,
481     ITextHostImpl_TxShowScrollBar,
482     ITextHostImpl_TxEnableScrollBar,
483     ITextHostImpl_TxSetScrollRange,
484     ITextHostImpl_TxSetScrollPos,
485     ITextHostImpl_TxInvalidateRect,
486     ITextHostImpl_TxViewChange,
487     ITextHostImpl_TxCreateCaret,
488     ITextHostImpl_TxShowCaret,
489     ITextHostImpl_TxSetCaretPos,
490     ITextHostImpl_TxSetTimer,
491     ITextHostImpl_TxKillTimer,
492     ITextHostImpl_TxScrollWindowEx,
493     ITextHostImpl_TxSetCapture,
494     ITextHostImpl_TxSetFocus,
495     ITextHostImpl_TxSetCursor,
496     ITextHostImpl_TxScreenToClient,
497     ITextHostImpl_TxClientToScreen,
498     ITextHostImpl_TxActivate,
499     ITextHostImpl_TxDeactivate,
500     ITextHostImpl_TxGetClientRect,
501     ITextHostImpl_TxGetViewInset,
502     ITextHostImpl_TxGetCharFormat,
503     ITextHostImpl_TxGetParaFormat,
504     ITextHostImpl_TxGetSysColor,
505     ITextHostImpl_TxGetBackStyle,
506     ITextHostImpl_TxGetMaxLength,
507     ITextHostImpl_TxGetScrollBars,
508     ITextHostImpl_TxGetPasswordChar,
509     ITextHostImpl_TxGetAcceleratorPos,
510     ITextHostImpl_TxGetExtent,
511     ITextHostImpl_OnTxCharFormatChange,
512     ITextHostImpl_OnTxParaFormatChange,
513     ITextHostImpl_TxGetPropertyBits,
514     ITextHostImpl_TxNotify,
515     ITextHostImpl_TxImmGetContext,
516     ITextHostImpl_TxImmReleaseContext,
517     ITextHostImpl_TxGetSelectionBarWidth
518 };
519
520 static ITextServices *txtserv = NULL;
521 static ITextHostTestImpl *dummyTextHost;
522 static void *wrapperCodeMem = NULL;
523
524 #include "pshpack1.h"
525
526 /* Code structure for x86 byte code */
527 typedef struct
528 {
529     BYTE pop_eax;  /* popl  %eax  */
530     BYTE push_ecx; /* pushl %ecx  */
531     BYTE push_eax; /* pushl %eax  */
532     BYTE jmp_func; /* jmp   $func */
533     DWORD func;
534 } THISCALL_TO_STDCALL_THUNK;
535
536 typedef struct
537 {
538     BYTE pop_eax;               /* popl  %eax */
539     BYTE pop_ecx;               /* popl  %ecx */
540     BYTE push_eax;              /* pushl %eax */
541     BYTE mov_vtable_eax[2];     /* movl (%ecx), %eax */
542     BYTE jmp_eax[2];            /* jmp *$vtablefunc_offset(%eax) */
543     int  vtablefunc_offset;
544 } STDCALL_TO_THISCALL_THUNK;
545
546 #include "poppack.h"
547
548 static void setup_thiscall_wrappers(void)
549 {
550 #ifdef __i386__
551     void** pVtable;
552     void** pVtableEnd;
553     THISCALL_TO_STDCALL_THUNK *thunk;
554     STDCALL_TO_THISCALL_THUNK *thunk2;
555
556     wrapperCodeMem = VirtualAlloc(NULL,
557                                   (sizeof(ITextHostVtbl)/sizeof(void*) - 3)
558                                     * sizeof(THISCALL_TO_STDCALL_THUNK)
559                                   +(sizeof(ITextServicesVtbl)/sizeof(void*) - 3)
560                                     * sizeof(STDCALL_TO_THISCALL_THUNK),
561                                   MEM_COMMIT, PAGE_EXECUTE_READWRITE);
562     thunk = wrapperCodeMem;
563
564     /* Wrap all ITextHostImpl methods with code to perform a thiscall to
565      * stdcall conversion. The thiscall calling convention places the This
566      * pointer in ecx on the x86 platform, and the stdcall calling convention
567      * pushes the This pointer on the stack as the first argument.
568      *
569      * The byte code does the conversion then jumps to the real function.
570      *
571      * Each wrapper needs to be modified so that the function to jump to is
572      * modified in the byte code. */
573
574     /* Skip QueryInterface, AddRef, and Release native actually
575      * defined them with the stdcall calling convention. */
576     pVtable = (void**)&itextHostVtbl + 3;
577     pVtableEnd = (void**)(&itextHostVtbl + 1);
578     while (pVtable != pVtableEnd) {
579         /* write byte code to executable memory */
580         thunk->pop_eax = 0x58;  /* popl  %eax  */
581         thunk->push_ecx = 0x51; /* pushl %ecx  */
582         thunk->push_eax = 0x50; /* pushl %eax  */
583         thunk->jmp_func = 0xe9; /* jmp   $func */
584         /* The address needs to be relative to the end of the jump instructions. */
585         thunk->func = (char*)*pVtable - (char*)(&thunk->func + 1);
586         *pVtable = thunk;
587         pVtable++;
588         thunk++;
589     }
590
591     /* Setup an ITextServices standard call vtable that will call the
592      * native thiscall vtable when the methods are called. */
593
594     /* QueryInterface, AddRef, and Release should be called directly on the
595      * real vtable since they use the stdcall calling convention. */
596     thunk2 = (STDCALL_TO_THISCALL_THUNK *)thunk;
597     pVtable = (void**)&itextServicesStdcallVtbl + 3;
598     pVtableEnd = (void**)(&itextServicesStdcallVtbl + 1);
599     while (pVtable != pVtableEnd) {
600         /* write byte code to executable memory */
601         thunk2->pop_eax = 0x58;               /* popl  %eax */
602         thunk2->pop_ecx = 0x59;               /* popl  %ecx */
603         thunk2->push_eax = 0x50;              /* pushl %eax */
604         thunk2->mov_vtable_eax[0] = 0x8b;     /* movl (%ecx), %eax */
605         thunk2->mov_vtable_eax[1] = 0x01;
606         thunk2->jmp_eax[0] = 0xff;            /* jmp *$vtablefunc_offset(%eax) */
607         thunk2->jmp_eax[1] = 0xa0;
608         thunk2->vtablefunc_offset = (char*)pVtable - (char*)&itextServicesStdcallVtbl;
609         *pVtable = thunk2;
610         pVtable++;
611         thunk2++;
612     }
613 #endif /* __i386__ */
614 }
615
616 /*************************************************************************/
617 /* Conformance test functions. */
618
619 /* Initialize the test texthost structure */
620 static BOOL init_texthost(void)
621 {
622     IUnknown *init;
623     HRESULT result;
624
625     dummyTextHost = CoTaskMemAlloc(sizeof(*dummyTextHost));
626     if (dummyTextHost == NULL) {
627         skip("Insufficient memory to create ITextHost interface\n");
628         return FALSE;
629     }
630     dummyTextHost->ITextHost_iface.lpVtbl = &itextHostVtbl;
631     dummyTextHost->refCount = 1;
632
633     /* MSDN states that an IUnknown object is returned by
634        CreateTextServices which is then queried to obtain a
635        ITextServices object. */
636     result = (*pCreateTextServices)(NULL, &dummyTextHost->ITextHost_iface, &init);
637     ok(result == S_OK, "Did not return S_OK when created (result =  %x)\n", result);
638     if (result != S_OK) {
639         CoTaskMemFree(dummyTextHost);
640         skip("CreateTextServices failed.\n");
641         return FALSE;
642     }
643
644     result = IUnknown_QueryInterface(init, pIID_ITextServices,
645                                      (void **)&txtserv);
646     ok((result == S_OK) && (txtserv != NULL), "Querying interface failed (result = %x, txtserv = %p)\n", result, txtserv);
647     IUnknown_Release(init);
648     if (!((result == S_OK) && (txtserv != NULL))) {
649         CoTaskMemFree(dummyTextHost);
650         skip("Could not retrieve ITextServices interface\n");
651         return FALSE;
652     }
653
654     return TRUE;
655 }
656
657 static void free_texthost(void)
658 {
659     IUnknown_Release(txtserv);
660     CoTaskMemFree(dummyTextHost);
661 }
662
663 static void test_TxGetText(void)
664 {
665     HRESULT hres;
666     BSTR rettext;
667
668     if (!init_texthost())
669         return;
670
671     hres = ITextServices_TxGetText(txtserv, &rettext);
672     ok(hres == S_OK, "ITextServices_TxGetText failed (result = %x)\n", hres);
673
674     free_texthost();
675 }
676
677 static void test_TxSetText(void)
678 {
679     HRESULT hres;
680     BSTR rettext;
681     WCHAR settext[] = {'T','e','s','t',0};
682
683     if (!init_texthost())
684         return;
685
686     hres = ITextServices_TxSetText(txtserv, settext);
687     ok(hres == S_OK, "ITextServices_TxSetText failed (result = %x)\n", hres);
688
689     hres = ITextServices_TxGetText(txtserv, &rettext);
690     ok(hres == S_OK, "ITextServices_TxGetText failed (result = %x)\n", hres);
691
692     ok(SysStringLen(rettext) == 4,
693                  "String returned of wrong length (expected 4, got %d)\n", SysStringLen(rettext));
694     ok(memcmp(rettext,settext,SysStringByteLen(rettext)) == 0,
695                  "String returned differs\n");
696
697     SysFreeString(rettext);
698     free_texthost();
699 }
700
701 static void test_TxGetNaturalSize(void) {
702     HRESULT result;
703     BOOL ret;
704
705     /* This value is used when calling TxGetNaturalSize.  MSDN says
706        that this is not supported however a null pointer cannot be
707        used as it will cause a segmentation violation.  The values in
708        the structure being pointed to are required to be INT_MAX
709        otherwise calculations can give wrong values. */
710     const SIZEL psizelExtent = {INT_MAX,INT_MAX};
711
712     static const WCHAR oneA[] = {'A',0};
713
714     /* Results of measurements */
715     LONG xdim, ydim;
716
717     /* The device context to do the tests in */
718     HDC hdcDraw;
719
720     /* Variables with the text metric information */
721     INT charwidth_caps_text[26];
722     TEXTMETRIC tmInfo_text;
723
724     if (!init_texthost())
725         return;
726
727     hdcDraw = GetDC(NULL);
728     SaveDC(hdcDraw);
729
730     /* Populate the metric strucs */
731     SetMapMode(hdcDraw,MM_TEXT);
732     GetTextMetrics(hdcDraw, &tmInfo_text);
733     SetLastError(0xdeadbeef);
734     ret = GetCharWidth32(hdcDraw,'A','Z',charwidth_caps_text);
735     if (!ret && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) {
736         win_skip("GetCharWidth32 is not available\n");
737         goto cleanup;
738     }
739
740     /* Make measurements in MM_TEXT */
741     SetMapMode(hdcDraw,MM_TEXT);
742     xdim = 0; ydim = 0;
743
744     result = ITextServices_TxSetText(txtserv, oneA);
745     ok(result == S_OK, "ITextServices_TxSetText failed (result = %x)\n", result);
746     if (result != S_OK) {
747         skip("Could not set text\n");
748         goto cleanup;
749     }
750
751     SetLastError(0xdeadbeef);
752     result = ITextServices_TxGetNaturalSize(txtserv, DVASPECT_CONTENT,
753                                             hdcDraw, NULL, NULL,
754                                             TXTNS_FITTOCONTENT, &psizelExtent,
755                                             &xdim, &ydim);
756     todo_wine ok(result == S_OK || broken(result == E_FAIL), /* WINXP Arabic Language */
757         "TxGetNaturalSize gave unexpected return value (result = %x)\n", result);
758     if (result == S_OK) {
759     todo_wine ok(ydim == tmInfo_text.tmHeight,
760                  "Height calculated incorrectly (expected %d, got %d)\n",
761                  tmInfo_text.tmHeight, ydim);
762     /* The native DLL adds one pixel extra when calculating widths. */
763     todo_wine ok(xdim >= charwidth_caps_text[0] && xdim <= charwidth_caps_text[0] + 1,
764                  "Width calculated incorrectly (expected %d {+1}, got %d)\n",
765                  charwidth_caps_text[0], xdim);
766     } else
767         skip("TxGetNaturalSize measurements not performed (xdim = %d, ydim = %d, result = %x, error = %x)\n",
768              xdim, ydim, result, GetLastError());
769
770 cleanup:
771     RestoreDC(hdcDraw,1);
772     ReleaseDC(NULL,hdcDraw);
773     free_texthost();
774 }
775
776 static void test_TxDraw(void)
777 {
778     HDC tmphdc = GetDC(NULL);
779     DWORD dwAspect = DVASPECT_CONTENT;
780     HDC hicTargetDev = NULL; /* Means "default" device */
781     DVTARGETDEVICE *ptd = NULL;
782     void *pvAspect = NULL;
783     HRESULT result;
784     RECTL client = {0,0,100,100};
785
786     if (!init_texthost())
787         return;
788
789     todo_wine {
790         result = ITextServices_TxDraw(txtserv, dwAspect, 0, pvAspect, ptd,
791                                       tmphdc, hicTargetDev, &client, NULL,
792                                       NULL, NULL, 0, 0);
793         ok(result == S_OK, "TxDraw failed (result = %x)\n", result);
794     }
795
796     free_texthost();
797
798 }
799
800 DEFINE_GUID(expected_iid_itextservices, 0x8d33f740, 0xcf58, 0x11ce, 0xa8, 0x9d, 0x00, 0xaa, 0x00, 0x6c, 0xad, 0xc5);
801 DEFINE_GUID(expected_iid_itexthost, 0x13e670f4,0x1a5a,0x11cf,0xab,0xeb,0x00,0xaa,0x00,0xb6,0x5e,0xa1);
802 DEFINE_GUID(expected_iid_itexthost2, 0x13e670f5,0x1a5a,0x11cf,0xab,0xeb,0x00,0xaa,0x00,0xb6,0x5e,0xa1);
803
804 static void test_IIDs(void)
805 {
806     ok(IsEqualIID(pIID_ITextServices, &expected_iid_itextservices),
807        "unexpected value for IID_ITextServices: %s\n", debugstr_guid(pIID_ITextServices));
808     ok(IsEqualIID(pIID_ITextHost, &expected_iid_itexthost),
809        "unexpected value for IID_ITextHost: %s\n", debugstr_guid(pIID_ITextHost));
810     ok(IsEqualIID(pIID_ITextHost2, &expected_iid_itexthost2),
811        "unexpected value for IID_ITextHost2: %s\n", debugstr_guid(pIID_ITextHost2));
812 }
813
814
815 START_TEST( txtsrv )
816 {
817     setup_thiscall_wrappers();
818
819     /* Must explicitly LoadLibrary(). The test has no references to functions in
820      * RICHED20.DLL, so the linker doesn't actually link to it. */
821     hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
822     ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
823
824     pIID_ITextServices = (IID*)GetProcAddress(hmoduleRichEdit, "IID_ITextServices");
825     pIID_ITextHost = (IID*)GetProcAddress(hmoduleRichEdit, "IID_ITextHost");
826     pIID_ITextHost2 = (IID*)GetProcAddress(hmoduleRichEdit, "IID_ITextHost2");
827     pCreateTextServices = (void*)GetProcAddress(hmoduleRichEdit, "CreateTextServices");
828
829     test_IIDs();
830
831     if (init_texthost())
832     {
833         free_texthost();
834
835         test_TxGetText();
836         test_TxSetText();
837         test_TxGetNaturalSize();
838         test_TxDraw();
839     }
840     if (wrapperCodeMem) VirtualFree(wrapperCodeMem, 0, MEM_RELEASE);
841 }