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