[RICHED20_WINETEST]
[reactos.git] / rostests / winetests / riched20 / 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 IUnknown_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 }