txtsrv.c 39.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
 * Unit test suite for windowless rich edit controls
 *
 * Copyright 2008 Maarten Lankhorst
 * Copyright 2008 Austin Lund
 * Copyright 2008 Dylan Smith
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#define COBJMACROS
24
#define CONST_VTABLE
25 26 27 28 29 30 31

#include <stdio.h>
#include <stdarg.h>
#include <windef.h>
#include <winbase.h>
#include <objbase.h>
#include <richedit.h>
32 33
#include <tom.h>
#include <richole.h>
34
#include <initguid.h>
35
#include <imm.h>
36 37
#include <textserv.h>
#include <wine/test.h>
38
#include <oleauto.h>
39
#include <limits.h>
40 41

static HMODULE hmoduleRichEdit;
42 43 44
static IID *pIID_ITextServices;
static IID *pIID_ITextHost;
static IID *pIID_ITextHost2;
45
static PCreateTextServices pCreateTextServices;
46

47 48 49 50 51
/* Define C Macros for ITextServices calls. */

/* Use a special table for x86 machines to convert the thiscall
 * calling convention.  This isn't needed on other platforms. */
#ifdef __i386__
52
static ITextServicesVtbl itextServicesStdcallVtbl;
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
#define TXTSERV_VTABLE(This) (&itextServicesStdcallVtbl)
#else /* __i386__ */
#define TXTSERV_VTABLE(This) (This)->lpVtbl
#endif /* __i386__ */

#define ITextServices_TxSendMessage(This,a,b,c,d) TXTSERV_VTABLE(This)->TxSendMessage(This,a,b,c,d)
#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)
#define ITextServices_TxGetHScroll(This,a,b,c,d,e) TXTSERV_VTABLE(This)->TxGetHScroll(This,a,b,c,d,e)
#define ITextServices_TxGetVScroll(This,a,b,c,d,e) TXTSERV_VTABLE(This)->TxGetVScroll(This,a,b,c,d,e)
#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)
#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)
#define ITextServices_OnTxInplaceActivate(This,a) TXTSERV_VTABLE(This)->OnTxInplaceActivate(This,a)
#define ITextServices_OnTxInplaceDeactivate(This) TXTSERV_VTABLE(This)->OnTxInplaceDeactivate(This)
#define ITextServices_OnTxUIActivate(This) TXTSERV_VTABLE(This)->OnTxUIActivate(This)
#define ITextServices_OnTxUIDeactivate(This) TXTSERV_VTABLE(This)->OnTxUIDeactivate(This)
#define ITextServices_TxGetText(This,a) TXTSERV_VTABLE(This)->TxGetText(This,a)
#define ITextServices_TxSetText(This,a) TXTSERV_VTABLE(This)->TxSetText(This,a)
70
#define ITextServices_TxGetCurTargetX(This,a) TXTSERV_VTABLE(This)->TxGetCurTargetX(This,a)
71 72 73 74 75 76
#define ITextServices_TxGetBaseLinePos(This,a) TXTSERV_VTABLE(This)->TxGetBaseLinePos(This,a)
#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)
#define ITextServices_TxGetDropTarget(This,a) TXTSERV_VTABLE(This)->TxGetDropTarget(This,a)
#define ITextServices_OnTxPropertyBitsChange(This,a,b) TXTSERV_VTABLE(This)->OnTxPropertyBitsChange(This,a,b)
#define ITextServices_TxGetCachedSize(This,a,b) TXTSERV_VTABLE(This)->TxGetCachedSize(This,a,b)

77 78 79 80 81 82 83 84 85
/* Set the WINETEST_DEBUG environment variable to be greater than 1 for verbose
 * function call traces of ITextHost. */
#define TRACECALL if(winetest_debug > 1) trace

/************************************************************************/
/* ITextHost implementation for conformance testing. */

typedef struct ITextHostTestImpl
{
86
    ITextHost ITextHost_iface;
87
    LONG refCount;
88
    CHARFORMAT2W char_format;
89 90
} ITextHostTestImpl;

91 92 93 94 95
static inline ITextHostTestImpl *impl_from_ITextHost(ITextHost *iface)
{
    return CONTAINING_RECORD(iface, ITextHostTestImpl, ITextHost_iface);
}

96 97 98 99
static HRESULT WINAPI ITextHostImpl_QueryInterface(ITextHost *iface,
                                                   REFIID riid,
                                                   LPVOID *ppvObject)
{
100
    ITextHostTestImpl *This = impl_from_ITextHost(iface);
101

102
    if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, pIID_ITextHost)) {
103
        *ppvObject = &This->ITextHost_iface;
104 105 106 107 108 109 110 111 112
        ITextHost_AddRef((ITextHost *)*ppvObject);
        return S_OK;
    }

    return E_NOINTERFACE;
}

static ULONG WINAPI ITextHostImpl_AddRef(ITextHost *iface)
{
113
    ITextHostTestImpl *This = impl_from_ITextHost(iface);
114 115 116 117 118 119
    ULONG refCount = InterlockedIncrement(&This->refCount);
    return refCount;
}

static ULONG WINAPI ITextHostImpl_Release(ITextHost *iface)
{
120
    ITextHostTestImpl *This = impl_from_ITextHost(iface);
121 122 123 124 125 126 127 128 129 130 131 132 133
    ULONG refCount = InterlockedDecrement(&This->refCount);

    if (!refCount)
    {
        CoTaskMemFree(This);
        return 0;
    } else {
        return refCount;
    }
}

static HDC WINAPI ITextHostImpl_TxGetDC(ITextHost *iface)
{
134
    ITextHostTestImpl *This = impl_from_ITextHost(iface);
135 136 137 138 139
    TRACECALL("Call to TxGetDC(%p)\n", This);
    return NULL;
}

static INT WINAPI ITextHostImpl_TxReleaseDC(ITextHost *iface,
140
                                            HDC hdc)
141
{
142
    ITextHostTestImpl *This = impl_from_ITextHost(iface);
143 144 145 146 147
    TRACECALL("Call to TxReleaseDC(%p)\n", This);
    return 0;
}

static BOOL WINAPI ITextHostImpl_TxShowScrollBar(ITextHost *iface,
148 149
                                                 INT fnBar,
                                                 BOOL fShow)
150
{
151
    ITextHostTestImpl *This = impl_from_ITextHost(iface);
152 153
    TRACECALL("Call to TxShowScrollBar(%p, fnBar=%d, fShow=%d)\n",
                This, fnBar, fShow);
154
    return FALSE;
155 156 157
}

static BOOL WINAPI ITextHostImpl_TxEnableScrollBar(ITextHost *iface,
158 159
                                                   INT fuSBFlags,
                                                   INT fuArrowflags)
160
{
161
    ITextHostTestImpl *This = impl_from_ITextHost(iface);
162 163
    TRACECALL("Call to TxEnableScrollBar(%p, fuSBFlags=%d, fuArrowflags=%d)\n",
               This, fuSBFlags, fuArrowflags);
164
    return FALSE;
165 166 167
}

static BOOL WINAPI ITextHostImpl_TxSetScrollRange(ITextHost *iface,
168 169 170 171
                                                  INT fnBar,
                                                  LONG nMinPos,
                                                  INT nMaxPos,
                                                  BOOL fRedraw)
172
{
173
    ITextHostTestImpl *This = impl_from_ITextHost(iface);
174 175
    TRACECALL("Call to TxSetScrollRange(%p, fnBar=%d, nMinPos=%d, nMaxPos=%d, fRedraw=%d)\n",
               This, fnBar, nMinPos, nMaxPos, fRedraw);
176
    return FALSE;
177 178 179
}

static BOOL WINAPI ITextHostImpl_TxSetScrollPos(ITextHost *iface,
180 181 182
                                                INT fnBar,
                                                INT nPos,
                                                BOOL fRedraw)
183
{
184
    ITextHostTestImpl *This = impl_from_ITextHost(iface);
185 186
    TRACECALL("Call to TxSetScrollPos(%p, fnBar=%d, nPos=%d, fRedraw=%d)\n",
               This, fnBar, nPos, fRedraw);
187
    return FALSE;
188 189 190
}

static void WINAPI ITextHostImpl_TxInvalidateRect(ITextHost *iface,
191 192
                                                  LPCRECT prc,
                                                  BOOL fMode)
193
{
194
    ITextHostTestImpl *This = impl_from_ITextHost(iface);
195 196 197 198 199 200
    TRACECALL("Call to TxInvalidateRect(%p, prc=%p, fMode=%d)\n",
               This, prc, fMode);
}

static void WINAPI ITextHostImpl_TxViewChange(ITextHost *iface, BOOL fUpdate)
{
201
    ITextHostTestImpl *This = impl_from_ITextHost(iface);
202 203 204 205 206
    TRACECALL("Call to TxViewChange(%p, fUpdate=%d)\n",
               This, fUpdate);
}

static BOOL WINAPI ITextHostImpl_TxCreateCaret(ITextHost *iface,
207 208
                                               HBITMAP hbmp,
                                               INT xWidth, INT yHeight)
209
{
210
    ITextHostTestImpl *This = impl_from_ITextHost(iface);
211 212
    TRACECALL("Call to TxCreateCaret(%p, nbmp=%p, xWidth=%d, yHeight=%d)\n",
               This, hbmp, xWidth, yHeight);
213
    return FALSE;
214 215 216 217
}

static BOOL WINAPI ITextHostImpl_TxShowCaret(ITextHost *iface, BOOL fShow)
{
218
    ITextHostTestImpl *This = impl_from_ITextHost(iface);
219 220
    TRACECALL("Call to TxShowCaret(%p, fShow=%d)\n",
               This, fShow);
221
    return FALSE;
222 223 224
}

static BOOL WINAPI ITextHostImpl_TxSetCaretPos(ITextHost *iface,
225
                                               INT x, INT y)
226
{
227
    ITextHostTestImpl *This = impl_from_ITextHost(iface);
228
    TRACECALL("Call to TxSetCaretPos(%p, x=%d, y=%d)\n", This, x, y);
229
    return FALSE;
230 231 232
}

static BOOL WINAPI ITextHostImpl_TxSetTimer(ITextHost *iface,
233
                                            UINT idTimer, UINT uTimeout)
234
{
235
    ITextHostTestImpl *This = impl_from_ITextHost(iface);
236 237
    TRACECALL("Call to TxSetTimer(%p, idTimer=%u, uTimeout=%u)\n",
              This, idTimer, uTimeout);
238
    return FALSE;
239 240 241 242
}

static void WINAPI ITextHostImpl_TxKillTimer(ITextHost *iface, UINT idTimer)
{
243
    ITextHostTestImpl *This = impl_from_ITextHost(iface);
244 245 246 247
    TRACECALL("Call to TxKillTimer(%p, idTimer=%u)\n", This, idTimer);
}

static void WINAPI ITextHostImpl_TxScrollWindowEx(ITextHost *iface,
248 249 250 251 252 253
                                                  INT dx, INT dy,
                                                  LPCRECT lprcScroll,
                                                  LPCRECT lprcClip,
                                                  HRGN hRgnUpdate,
                                                  LPRECT lprcUpdate,
                                                  UINT fuScroll)
254
{
255
    ITextHostTestImpl *This = impl_from_ITextHost(iface);
256 257 258 259 260 261
    TRACECALL("Call to TxScrollWindowEx(%p, %d, %d, %p, %p, %p, %p, %d)\n",
              This, dx, dy, lprcScroll, lprcClip, hRgnUpdate, lprcUpdate, fuScroll);
}

static void WINAPI ITextHostImpl_TxSetCapture(ITextHost *iface, BOOL fCapture)
{
262
    ITextHostTestImpl *This = impl_from_ITextHost(iface);
263 264 265 266 267
    TRACECALL("Call to TxSetCapture(%p, fCapture=%d)\n", This, fCapture);
}

static void WINAPI ITextHostImpl_TxSetFocus(ITextHost *iface)
{
268
    ITextHostTestImpl *This = impl_from_ITextHost(iface);
269 270 271 272
    TRACECALL("Call to TxSetFocus(%p)\n", This);
}

static void WINAPI ITextHostImpl_TxSetCursor(ITextHost *iface,
273 274
                                             HCURSOR hcur,
                                             BOOL fText)
275
{
276
    ITextHostTestImpl *This = impl_from_ITextHost(iface);
277 278 279 280 281
    TRACECALL("Call to TxSetCursor(%p, hcur=%p, fText=%d)\n",
              This, hcur, fText);
}

static BOOL WINAPI ITextHostImpl_TxScreenToClient(ITextHost *iface,
282
                                                  LPPOINT lppt)
283
{
284
    ITextHostTestImpl *This = impl_from_ITextHost(iface);
285
    TRACECALL("Call to TxScreenToClient(%p, lppt=%p)\n", This, lppt);
286
    return FALSE;
287 288 289
}

static BOOL WINAPI ITextHostImpl_TxClientToScreen(ITextHost *iface,
290
                                                  LPPOINT lppt)
291
{
292
    ITextHostTestImpl *This = impl_from_ITextHost(iface);
293
    TRACECALL("Call to TxClientToScreen(%p, lppt=%p)\n", This, lppt);
294
    return FALSE;
295 296 297
}

static HRESULT WINAPI ITextHostImpl_TxActivate(ITextHost *iface,
298
                                               LONG *plOldState)
299
{
300
    ITextHostTestImpl *This = impl_from_ITextHost(iface);
301 302 303 304 305
    TRACECALL("Call to TxActivate(%p, plOldState=%p)\n", This, plOldState);
    return E_NOTIMPL;
}

static HRESULT WINAPI ITextHostImpl_TxDeactivate(ITextHost *iface,
306
                                                 LONG lNewState)
307
{
308
    ITextHostTestImpl *This = impl_from_ITextHost(iface);
309 310 311 312 313
    TRACECALL("Call to TxDeactivate(%p, lNewState=%d)\n", This, lNewState);
    return E_NOTIMPL;
}

static HRESULT WINAPI ITextHostImpl_TxGetClientRect(ITextHost *iface,
314
                                                    LPRECT prc)
315
{
316
    ITextHostTestImpl *This = impl_from_ITextHost(iface);
317 318 319 320 321
    TRACECALL("Call to TxGetClientRect(%p, prc=%p)\n", This, prc);
    return E_NOTIMPL;
}

static HRESULT WINAPI ITextHostImpl_TxGetViewInset(ITextHost *iface,
322
                                                   LPRECT prc)
323
{
324
    ITextHostTestImpl *This = impl_from_ITextHost(iface);
325 326 327 328 329
    TRACECALL("Call to TxGetViewInset(%p, prc=%p)\n", This, prc);
    return E_NOTIMPL;
}

static HRESULT WINAPI ITextHostImpl_TxGetCharFormat(ITextHost *iface,
330
                                                    const CHARFORMATW **ppCF)
331
{
332
    ITextHostTestImpl *This = impl_from_ITextHost(iface);
333
    TRACECALL("Call to TxGetCharFormat(%p, ppCF=%p)\n", This, ppCF);
334 335
    *ppCF = (CHARFORMATW *)&This->char_format;
    return S_OK;
336 337 338
}

static HRESULT WINAPI ITextHostImpl_TxGetParaFormat(ITextHost *iface,
339
                                                    const PARAFORMAT **ppPF)
340
{
341
    ITextHostTestImpl *This = impl_from_ITextHost(iface);
342 343 344 345 346
    TRACECALL("Call to TxGetParaFormat(%p, ppPF=%p)\n", This, ppPF);
    return E_NOTIMPL;
}

static COLORREF WINAPI ITextHostImpl_TxGetSysColor(ITextHost *iface,
347
                                                   int nIndex)
348
{
349
    ITextHostTestImpl *This = impl_from_ITextHost(iface);
350 351 352 353 354
    TRACECALL("Call to TxGetSysColor(%p, nIndex=%d)\n", This, nIndex);
    return E_NOTIMPL;
}

static HRESULT WINAPI ITextHostImpl_TxGetBackStyle(ITextHost *iface,
355
                                                   TXTBACKSTYLE *pStyle)
356
{
357
    ITextHostTestImpl *This = impl_from_ITextHost(iface);
358 359 360 361 362
    TRACECALL("Call to TxGetBackStyle(%p, pStyle=%p)\n", This, pStyle);
    return E_NOTIMPL;
}

static HRESULT WINAPI ITextHostImpl_TxGetMaxLength(ITextHost *iface,
363
                                                   DWORD *pLength)
364
{
365
    ITextHostTestImpl *This = impl_from_ITextHost(iface);
366 367 368 369
    TRACECALL("Call to TxGetMaxLength(%p, pLength=%p)\n", This, pLength);
    return E_NOTIMPL;
}

370
static HRESULT WINAPI ITextHostImpl_TxGetScrollBars(ITextHost *iface,
371
                                                    DWORD *pdwScrollBar)
372
{
373
    ITextHostTestImpl *This = impl_from_ITextHost(iface);
374
    TRACECALL("Call to TxGetScrollBars(%p, pdwScrollBar=%p)\n",
375 376 377 378 379
               This, pdwScrollBar);
    return E_NOTIMPL;
}

static HRESULT WINAPI ITextHostImpl_TxGetPasswordChar(ITextHost *iface,
380
                                                      WCHAR *pch)
381
{
382
    ITextHostTestImpl *This = impl_from_ITextHost(iface);
383 384 385 386 387
    TRACECALL("Call to TxGetPasswordChar(%p, pch=%p)\n", This, pch);
    return E_NOTIMPL;
}

static HRESULT WINAPI ITextHostImpl_TxGetAcceleratorPos(ITextHost *iface,
388
                                                        LONG *pch)
389
{
390
    ITextHostTestImpl *This = impl_from_ITextHost(iface);
391 392 393 394 395
    TRACECALL("Call to TxGetAcceleratorPos(%p, pch=%p)\n", This, pch);
    return E_NOTIMPL;
}

static HRESULT WINAPI ITextHostImpl_TxGetExtent(ITextHost *iface,
396
                                                LPSIZEL lpExtent)
397
{
398
    ITextHostTestImpl *This = impl_from_ITextHost(iface);
399 400 401 402 403
    TRACECALL("Call to TxGetExtent(%p, lpExtent=%p)\n", This, lpExtent);
    return E_NOTIMPL;
}

static HRESULT WINAPI ITextHostImpl_OnTxCharFormatChange(ITextHost *iface,
404
                                                         const CHARFORMATW *pcf)
405
{
406
    ITextHostTestImpl *This = impl_from_ITextHost(iface);
407 408 409 410 411
    TRACECALL("Call to OnTxCharFormatChange(%p, pcf=%p)\n", This, pcf);
    return E_NOTIMPL;
}

static HRESULT WINAPI ITextHostImpl_OnTxParaFormatChange(ITextHost *iface,
412
                                                         const PARAFORMAT *ppf)
413
{
414
    ITextHostTestImpl *This = impl_from_ITextHost(iface);
415 416 417 418 419 420 421
    TRACECALL("Call to OnTxParaFormatChange(%p, ppf=%p)\n", This, ppf);
    return E_NOTIMPL;
}

/* This must return S_OK for the native ITextServices object to
   initialize. */
static HRESULT WINAPI ITextHostImpl_TxGetPropertyBits(ITextHost *iface,
422 423
                                                      DWORD dwMask,
                                                      DWORD *pdwBits)
424
{
425
    ITextHostTestImpl *This = impl_from_ITextHost(iface);
426 427 428 429 430 431 432
    TRACECALL("Call to TxGetPropertyBits(%p, dwMask=0x%08x, pdwBits=%p)\n",
              This, dwMask, pdwBits);
    *pdwBits = 0;
    return S_OK;
}

static HRESULT WINAPI ITextHostImpl_TxNotify(ITextHost *iface, DWORD iNotify,
433
                                             void *pv)
434
{
435
    ITextHostTestImpl *This = impl_from_ITextHost(iface);
436 437 438 439 440 441
    TRACECALL("Call to TxNotify(%p, iNotify=%d, pv=%p)\n", This, iNotify, pv);
    return E_NOTIMPL;
}

static HIMC WINAPI ITextHostImpl_TxImmGetContext(ITextHost *iface)
{
442
    ITextHostTestImpl *This = impl_from_ITextHost(iface);
443 444 445 446 447 448
    TRACECALL("Call to TxImmGetContext(%p)\n", This);
    return 0;
}

static void WINAPI ITextHostImpl_TxImmReleaseContext(ITextHost *iface, HIMC himc)
{
449
    ITextHostTestImpl *This = impl_from_ITextHost(iface);
450 451 452
    TRACECALL("Call to TxImmReleaseContext(%p, himc=%p)\n", This, himc);
}

453
/* This function must set the variable pointed to by *lSelBarWidth.
454
   Otherwise an uninitialized value will be used to calculate
455
   positions and sizes even if E_NOTIMPL is returned. */
456
static HRESULT WINAPI ITextHostImpl_TxGetSelectionBarWidth(ITextHost *iface,
457
                                                           LONG *lSelBarWidth)
458
{
459
    ITextHostTestImpl *This = impl_from_ITextHost(iface);
460 461
    TRACECALL("Call to TxGetSelectionBarWidth(%p, lSelBarWidth=%p)\n",
                This, lSelBarWidth);
462
    *lSelBarWidth = 0;
463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497
    return E_NOTIMPL;
}

static ITextHostVtbl itextHostVtbl = {
    ITextHostImpl_QueryInterface,
    ITextHostImpl_AddRef,
    ITextHostImpl_Release,
    ITextHostImpl_TxGetDC,
    ITextHostImpl_TxReleaseDC,
    ITextHostImpl_TxShowScrollBar,
    ITextHostImpl_TxEnableScrollBar,
    ITextHostImpl_TxSetScrollRange,
    ITextHostImpl_TxSetScrollPos,
    ITextHostImpl_TxInvalidateRect,
    ITextHostImpl_TxViewChange,
    ITextHostImpl_TxCreateCaret,
    ITextHostImpl_TxShowCaret,
    ITextHostImpl_TxSetCaretPos,
    ITextHostImpl_TxSetTimer,
    ITextHostImpl_TxKillTimer,
    ITextHostImpl_TxScrollWindowEx,
    ITextHostImpl_TxSetCapture,
    ITextHostImpl_TxSetFocus,
    ITextHostImpl_TxSetCursor,
    ITextHostImpl_TxScreenToClient,
    ITextHostImpl_TxClientToScreen,
    ITextHostImpl_TxActivate,
    ITextHostImpl_TxDeactivate,
    ITextHostImpl_TxGetClientRect,
    ITextHostImpl_TxGetViewInset,
    ITextHostImpl_TxGetCharFormat,
    ITextHostImpl_TxGetParaFormat,
    ITextHostImpl_TxGetSysColor,
    ITextHostImpl_TxGetBackStyle,
    ITextHostImpl_TxGetMaxLength,
498
    ITextHostImpl_TxGetScrollBars,
499 500 501 502 503 504 505 506 507 508 509 510 511 512
    ITextHostImpl_TxGetPasswordChar,
    ITextHostImpl_TxGetAcceleratorPos,
    ITextHostImpl_TxGetExtent,
    ITextHostImpl_OnTxCharFormatChange,
    ITextHostImpl_OnTxParaFormatChange,
    ITextHostImpl_TxGetPropertyBits,
    ITextHostImpl_TxNotify,
    ITextHostImpl_TxImmGetContext,
    ITextHostImpl_TxImmReleaseContext,
    ITextHostImpl_TxGetSelectionBarWidth
};

static void *wrapperCodeMem = NULL;

513 514
#include "pshpack1.h"

515 516 517 518 519 520 521 522 523 524
/* Code structure for x86 byte code */
typedef struct
{
    BYTE pop_eax;  /* popl  %eax  */
    BYTE push_ecx; /* pushl %ecx  */
    BYTE push_eax; /* pushl %eax  */
    BYTE jmp_func; /* jmp   $func */
    DWORD func;
} THISCALL_TO_STDCALL_THUNK;

525 526 527 528 529 530 531 532 533 534 535
typedef struct
{
    BYTE pop_eax;               /* popl  %eax */
    BYTE pop_ecx;               /* popl  %ecx */
    BYTE push_eax;              /* pushl %eax */
    BYTE mov_vtable_eax[2];     /* movl (%ecx), %eax */
    BYTE jmp_eax[2];            /* jmp *$vtablefunc_offset(%eax) */
    int  vtablefunc_offset;
} STDCALL_TO_THISCALL_THUNK;

#include "poppack.h"
536 537 538 539 540 541 542

static void setup_thiscall_wrappers(void)
{
#ifdef __i386__
    void** pVtable;
    void** pVtableEnd;
    THISCALL_TO_STDCALL_THUNK *thunk;
543
    STDCALL_TO_THISCALL_THUNK *thunk2;
544 545 546

    wrapperCodeMem = VirtualAlloc(NULL,
                                  (sizeof(ITextHostVtbl)/sizeof(void*) - 3)
547 548 549
                                    * sizeof(THISCALL_TO_STDCALL_THUNK)
                                  +(sizeof(ITextServicesVtbl)/sizeof(void*) - 3)
                                    * sizeof(STDCALL_TO_THISCALL_THUNK),
550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565
                                  MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    thunk = wrapperCodeMem;

    /* Wrap all ITextHostImpl methods with code to perform a thiscall to
     * stdcall conversion. The thiscall calling convention places the This
     * pointer in ecx on the x86 platform, and the stdcall calling convention
     * pushes the This pointer on the stack as the first argument.
     *
     * The byte code does the conversion then jumps to the real function.
     *
     * Each wrapper needs to be modified so that the function to jump to is
     * modified in the byte code. */

    /* Skip QueryInterface, AddRef, and Release native actually
     * defined them with the stdcall calling convention. */
    pVtable = (void**)&itextHostVtbl + 3;
566
    pVtableEnd = (void**)(&itextHostVtbl + 1);
567 568 569 570 571 572 573 574 575 576 577 578
    while (pVtable != pVtableEnd) {
        /* write byte code to executable memory */
        thunk->pop_eax = 0x58;  /* popl  %eax  */
        thunk->push_ecx = 0x51; /* pushl %ecx  */
        thunk->push_eax = 0x50; /* pushl %eax  */
        thunk->jmp_func = 0xe9; /* jmp   $func */
        /* The address needs to be relative to the end of the jump instructions. */
        thunk->func = (char*)*pVtable - (char*)(&thunk->func + 1);
        *pVtable = thunk;
        pVtable++;
        thunk++;
    }
579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601

    /* Setup an ITextServices standard call vtable that will call the
     * native thiscall vtable when the methods are called. */

    /* QueryInterface, AddRef, and Release should be called directly on the
     * real vtable since they use the stdcall calling convention. */
    thunk2 = (STDCALL_TO_THISCALL_THUNK *)thunk;
    pVtable = (void**)&itextServicesStdcallVtbl + 3;
    pVtableEnd = (void**)(&itextServicesStdcallVtbl + 1);
    while (pVtable != pVtableEnd) {
        /* write byte code to executable memory */
        thunk2->pop_eax = 0x58;               /* popl  %eax */
        thunk2->pop_ecx = 0x59;               /* popl  %ecx */
        thunk2->push_eax = 0x50;              /* pushl %eax */
        thunk2->mov_vtable_eax[0] = 0x8b;     /* movl (%ecx), %eax */
        thunk2->mov_vtable_eax[1] = 0x01;
        thunk2->jmp_eax[0] = 0xff;            /* jmp *$vtablefunc_offset(%eax) */
        thunk2->jmp_eax[1] = 0xa0;
        thunk2->vtablefunc_offset = (char*)pVtable - (char*)&itextServicesStdcallVtbl;
        *pVtable = thunk2;
        pVtable++;
        thunk2++;
    }
602 603 604
#endif /* __i386__ */
}

605 606 607 608 609 610 611 612 613 614 615 616 617 618 619
static void hf_to_cf(HFONT hf, CHARFORMAT2W *cf)
{
    LOGFONTW lf;

    GetObjectW(hf, sizeof(lf), &lf);
    lstrcpyW(cf->szFaceName, lf.lfFaceName);
    cf->yHeight = MulDiv(abs(lf.lfHeight), 1440, GetDeviceCaps(GetDC(NULL), LOGPIXELSY));
    if (lf.lfWeight > FW_NORMAL) cf->dwEffects |= CFE_BOLD;
    if (lf.lfItalic) cf->dwEffects |= CFE_ITALIC;
    if (lf.lfUnderline) cf->dwEffects |= CFE_UNDERLINE;
    if (lf.lfStrikeOut) cf->dwEffects |= CFE_SUBSCRIPT;
    cf->bPitchAndFamily = lf.lfPitchAndFamily;
    cf->bCharSet = lf.lfCharSet;
}

620 621 622 623
/*************************************************************************/
/* Conformance test functions. */

/* Initialize the test texthost structure */
624
static BOOL init_texthost(ITextServices **txtserv, ITextHost **ret)
625
{
626
    ITextHostTestImpl *dummyTextHost;
627 628
    IUnknown *init;
    HRESULT result;
629
    HFONT hf;
630 631 632

    dummyTextHost = CoTaskMemAlloc(sizeof(*dummyTextHost));
    if (dummyTextHost == NULL) {
633
        win_skip("Insufficient memory to create ITextHost interface\n");
634 635
        return FALSE;
    }
636
    dummyTextHost->ITextHost_iface.lpVtbl = &itextHostVtbl;
637
    dummyTextHost->refCount = 1;
638 639 640 641 642
    memset(&dummyTextHost->char_format, 0, sizeof(dummyTextHost->char_format));
    dummyTextHost->char_format.cbSize = sizeof(dummyTextHost->char_format);
    dummyTextHost->char_format.dwMask = CFM_ALL2;
    hf = GetStockObject(DEFAULT_GUI_FONT);
    hf_to_cf(hf, &dummyTextHost->char_format);
643 644 645 646

    /* MSDN states that an IUnknown object is returned by
       CreateTextServices which is then queried to obtain a
       ITextServices object. */
647
    result = pCreateTextServices(NULL, &dummyTextHost->ITextHost_iface, &init);
648
    ok(result == S_OK, "Did not return S_OK when created (result =  %x)\n", result);
649 650
    if (result != S_OK) {
        CoTaskMemFree(dummyTextHost);
651
        win_skip("CreateTextServices failed.\n");
652 653 654
        return FALSE;
    }

655 656
    result = IUnknown_QueryInterface(init, pIID_ITextServices, (void**)txtserv);
    ok((result == S_OK) && (*txtserv != NULL), "Querying interface failed (result = %x, txtserv = %p)\n", result, *txtserv);
657
    IUnknown_Release(init);
658
    if (!((result == S_OK) && (*txtserv != NULL))) {
659
        CoTaskMemFree(dummyTextHost);
660
        win_skip("Could not retrieve ITextServices interface\n");
661 662 663
        return FALSE;
    }

664
    *ret = &dummyTextHost->ITextHost_iface;
665 666 667
    return TRUE;
}

668 669
static void test_TxGetText(void)
{
670 671
    ITextServices *txtserv;
    ITextHost *host;
672 673 674
    HRESULT hres;
    BSTR rettext;

675
    if (!init_texthost(&txtserv, &host))
676 677 678
        return;

    hres = ITextServices_TxGetText(txtserv, &rettext);
679
    ok(hres == S_OK, "ITextServices_TxGetText failed (result = %x)\n", hres);
680
    SysFreeString(rettext);
681

682 683
    ITextServices_Release(txtserv);
    ITextHost_Release(host);
684 685
}

686 687
static void test_TxSetText(void)
{
688 689
    ITextServices *txtserv;
    ITextHost *host;
690 691 692 693
    HRESULT hres;
    BSTR rettext;
    WCHAR settext[] = {'T','e','s','t',0};

694
    if (!init_texthost(&txtserv, &host))
695 696 697
        return;

    hres = ITextServices_TxSetText(txtserv, settext);
698
    ok(hres == S_OK, "ITextServices_TxSetText failed (result = %x)\n", hres);
699 700

    hres = ITextServices_TxGetText(txtserv, &rettext);
701
    ok(hres == S_OK, "ITextServices_TxGetText failed (result = %x)\n", hres);
702

703
    ok(SysStringLen(rettext) == 4,
704
                 "String returned of wrong length (expected 4, got %d)\n", SysStringLen(rettext));
705
    ok(memcmp(rettext,settext,SysStringByteLen(rettext)) == 0,
706 707
                 "String returned differs\n");

708 709 710 711 712 713 714 715 716 717
    /* Null-pointer should behave the same as empty-string */

    hres = ITextServices_TxSetText(txtserv, 0);
    ok(hres == S_OK, "ITextServices_TxSetText failed (result = %x)\n", hres);

    hres = ITextServices_TxGetText(txtserv, &rettext);
    ok(hres == S_OK, "ITextServices_TxGetText failed (result = %x)\n", hres);
    ok(SysStringLen(rettext) == 0,
                 "String returned of wrong length (expected 0, got %d)\n", SysStringLen(rettext));

718
    SysFreeString(rettext);
719 720
    ITextServices_Release(txtserv);
    ITextHost_Release(host);
721 722
}

723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739
#define CHECK_TXGETNATURALSIZE(res,width,height,hdc,rect,string) \
    _check_txgetnaturalsize(res, width, height, hdc, rect, string, __LINE__)
static void _check_txgetnaturalsize(HRESULT res, LONG width, LONG height, HDC hdc, RECT rect, LPCWSTR string, int line)
{
    RECT expected_rect = rect;
    LONG expected_width, expected_height;

    DrawTextW(hdc, string, -1, &expected_rect, DT_LEFT | DT_CALCRECT | DT_NOCLIP | DT_EDITCONTROL | DT_WORDBREAK);
    expected_width = expected_rect.right - expected_rect.left;
    expected_height = expected_rect.bottom - expected_rect.top;
    ok_(__FILE__,line)(res == S_OK, "ITextServices_TxGetNaturalSize failed: 0x%08x.\n", res);
    ok_(__FILE__,line)(width >= expected_width && width <= expected_width + 1,
                       "got wrong width: %d, expected: %d {+1}.\n", width, expected_width);
    ok_(__FILE__,line)(height == expected_height, "got wrong height: %d, expected: %d.\n",
                       height, expected_height);
}

740 741 742 743
static void test_TxGetNaturalSize(void)
{
    ITextServices *txtserv;
    ITextHost *host;
744
    HRESULT result;
745 746 747
    SIZEL extent;
    static const WCHAR test_text[] = {'T','e','s','t','S','o','m','e','T','e','x','t',0};
    LONG width, height;
748
    HDC hdcDraw;
749 750 751 752 753
    HWND hwnd;
    RECT rect;
    CHARFORMAT2W cf;
    LRESULT lresult;
    HFONT hf;
754

755
    if (!init_texthost(&txtserv, &host))
756 757
        return;

758 759 760
    hwnd = CreateWindowExA(0, "static", NULL, WS_POPUP | WS_VISIBLE,
                           0, 0, 100, 100, 0, 0, 0, NULL);
    hdcDraw = GetDC(hwnd);
761
    SetMapMode(hdcDraw,MM_TEXT);
762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784
    GetClientRect(hwnd, &rect);

    memset(&cf, 0, sizeof(cf));
    cf.cbSize = sizeof(cf);
    cf.dwMask = CFM_ALL2;
    hf = GetStockObject(DEFAULT_GUI_FONT);
    hf_to_cf(hf, &cf);
    result = ITextServices_TxSendMessage(txtserv, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf, &lresult);
    ok(result == S_OK, "ITextServices_TxSendMessage failed: 0x%08x.\n", result);
    SelectObject(hdcDraw, hf);

    result = ITextServices_TxSetText(txtserv, test_text);
    ok(result == S_OK, "ITextServices_TxSetText failed: 0x%08x.\n", result);

    extent.cx = -1; extent.cy = -1;
    width = rect.right - rect.left;
    height = 0;
    result = ITextServices_TxGetNaturalSize(txtserv, DVASPECT_CONTENT, hdcDraw, NULL, NULL,
                                            TXTNS_FITTOCONTENT, &extent, &width, &height);
    todo_wine CHECK_TXGETNATURALSIZE(result, width, height, hdcDraw, rect, test_text);

    ReleaseDC(hwnd, hdcDraw);
    DestroyWindow(hwnd);
785 786
    ITextServices_Release(txtserv);
    ITextHost_Release(host);
787 788
}

789 790
static void test_TxDraw(void)
{
791 792
    ITextServices *txtserv;
    ITextHost *host;
793 794 795 796 797 798 799 800
    HDC tmphdc = GetDC(NULL);
    DWORD dwAspect = DVASPECT_CONTENT;
    HDC hicTargetDev = NULL; /* Means "default" device */
    DVTARGETDEVICE *ptd = NULL;
    void *pvAspect = NULL;
    HRESULT result;
    RECTL client = {0,0,100,100};

801 802

    if (!init_texthost(&txtserv, &host))
803 804 805 806 807 808
        return;

    todo_wine {
        result = ITextServices_TxDraw(txtserv, dwAspect, 0, pvAspect, ptd,
                                      tmphdc, hicTargetDev, &client, NULL,
                                      NULL, NULL, 0, 0);
809
        ok(result == S_OK, "TxDraw failed (result = %x)\n", result);
810 811
    }

812 813
    ITextServices_Release(txtserv);
    ITextHost_Release(host);
814 815
}

816 817 818 819 820 821 822
DEFINE_GUID(expected_iid_itextservices, 0x8d33f740, 0xcf58, 0x11ce, 0xa8, 0x9d, 0x00, 0xaa, 0x00, 0x6c, 0xad, 0xc5);
DEFINE_GUID(expected_iid_itexthost, 0x13e670f4,0x1a5a,0x11cf,0xab,0xeb,0x00,0xaa,0x00,0xb6,0x5e,0xa1);
DEFINE_GUID(expected_iid_itexthost2, 0x13e670f5,0x1a5a,0x11cf,0xab,0xeb,0x00,0xaa,0x00,0xb6,0x5e,0xa1);

static void test_IIDs(void)
{
    ok(IsEqualIID(pIID_ITextServices, &expected_iid_itextservices),
823
       "unexpected value for IID_ITextServices: %s\n", wine_dbgstr_guid(pIID_ITextServices));
824
    ok(IsEqualIID(pIID_ITextHost, &expected_iid_itexthost),
825
       "unexpected value for IID_ITextHost: %s\n", wine_dbgstr_guid(pIID_ITextHost));
826
    ok(IsEqualIID(pIID_ITextHost2, &expected_iid_itexthost2),
827
       "unexpected value for IID_ITextHost2: %s\n", wine_dbgstr_guid(pIID_ITextHost2));
828 829
}

830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892
/* Outer IUnknown for COM aggregation tests */
struct unk_impl {
    IUnknown IUnknown_iface;
    LONG ref;
    IUnknown *inner_unk;
};

static inline struct unk_impl *impl_from_IUnknown(IUnknown *iface)
{
    return CONTAINING_RECORD(iface, struct unk_impl, IUnknown_iface);
}

static HRESULT WINAPI unk_QueryInterface(IUnknown *iface, REFIID riid, void **ppv)
{
    struct unk_impl *This = impl_from_IUnknown(iface);

    return IUnknown_QueryInterface(This->inner_unk, riid, ppv);
}

static ULONG WINAPI unk_AddRef(IUnknown *iface)
{
    struct unk_impl *This = impl_from_IUnknown(iface);

    return InterlockedIncrement(&This->ref);
}

static ULONG WINAPI unk_Release(IUnknown *iface)
{
    struct unk_impl *This = impl_from_IUnknown(iface);

    return InterlockedDecrement(&This->ref);
}

static const IUnknownVtbl unk_vtbl =
{
    unk_QueryInterface,
    unk_AddRef,
    unk_Release
};

static void test_COM(void)
{
    struct unk_impl unk_obj = {{&unk_vtbl}, 19, NULL};
    struct ITextHostTestImpl texthost = {{&itextHostVtbl}, 1};
    ITextServices *textsrv;
    ULONG refcount;
    HRESULT hr;

    /* COM aggregation */
    hr = pCreateTextServices(&unk_obj.IUnknown_iface, &texthost.ITextHost_iface,
                             &unk_obj.inner_unk);
    ok(hr == S_OK, "CreateTextServices failed: %08x\n", hr);
    hr = IUnknown_QueryInterface(unk_obj.inner_unk, pIID_ITextServices, (void**)&textsrv);
    ok(hr == S_OK, "QueryInterface for IID_ITextServices failed: %08x\n", hr);
    refcount = ITextServices_AddRef(textsrv);
    ok(refcount == unk_obj.ref, "CreateTextServices just pretends to support COM aggregation\n");
    refcount = ITextServices_Release(textsrv);
    ok(refcount == unk_obj.ref, "CreateTextServices just pretends to support COM aggregation\n");
    refcount = ITextServices_Release(textsrv);
    ok(refcount == 19, "Refcount should be back at 19 but is %u\n", refcount);

    IUnknown_Release(unk_obj.inner_unk);
}
893

894 895 896 897 898 899 900 901
static ULONG get_refcount(IUnknown *iface)
{
    IUnknown_AddRef(iface);
    return IUnknown_Release(iface);
}

static void test_QueryInterface(void)
{
902 903
    ITextServices *txtserv;
    ITextHost *host;
904 905 906
    HRESULT hres;
    IRichEditOle *reole, *txtsrv_reole;
    ITextDocument *txtdoc, *txtsrv_txtdoc;
907
    ITextDocument2Old *txtdoc2old, *txtsrv_txtdoc2old;
908 909
    ULONG refcount;

910
    if(!init_texthost(&txtserv, &host))
911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933
        return;

    refcount = get_refcount((IUnknown *)txtserv);
    ok(refcount == 1, "got wrong ref count: %d\n", refcount);

    /* IID_IRichEditOle */
    hres = ITextServices_QueryInterface(txtserv, &IID_IRichEditOle, (void **)&txtsrv_reole);
    ok(hres == S_OK, "ITextServices_QueryInterface: 0x%08x\n", hres);
    refcount = get_refcount((IUnknown *)txtserv);
    ok(refcount == 2, "got wrong ref count: %d\n", refcount);
    refcount = get_refcount((IUnknown *)txtsrv_reole);
    ok(refcount == 2, "got wrong ref count: %d\n", refcount);

    hres = IRichEditOle_QueryInterface(txtsrv_reole, &IID_ITextDocument, (void **)&txtdoc);
    ok(hres == S_OK, "IRichEditOle_QueryInterface: 0x%08x\n", hres);
    refcount = get_refcount((IUnknown *)txtserv);
    ok(refcount == 3, "got wrong ref count: %d\n", refcount);
    refcount = get_refcount((IUnknown *)txtsrv_reole);
    ok(refcount == 3, "got wrong ref count: %d\n", refcount);

    ITextDocument_Release(txtdoc);
    refcount = get_refcount((IUnknown *)txtserv);
    ok(refcount == 2, "got wrong ref count: %d\n", refcount);
934 935 936 937 938 939 940 941 942 943 944

    hres = IRichEditOle_QueryInterface(txtsrv_reole, &IID_ITextDocument2Old, (void **)&txtdoc2old);
    ok(hres == S_OK, "IRichEditOle_QueryInterface: 0x%08x\n", hres);
    refcount = get_refcount((IUnknown *)txtserv);
    ok(refcount == 3, "got wrong ref count: %d\n", refcount);
    refcount = get_refcount((IUnknown *)txtsrv_reole);
    ok(refcount == 3, "got wrong ref count: %d\n", refcount);

    ITextDocument2Old_Release(txtdoc2old);
    refcount = get_refcount((IUnknown *)txtserv);
    ok(refcount == 2, "got wrong ref count: %d\n", refcount);
945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969
    IRichEditOle_Release(txtsrv_reole);
    refcount = get_refcount((IUnknown *)txtserv);
    ok(refcount == 1, "got wrong ref count: %d\n", refcount);

    /* IID_ITextDocument */
    hres = ITextServices_QueryInterface(txtserv, &IID_ITextDocument, (void **)&txtsrv_txtdoc);
    ok(hres == S_OK, "ITextServices_QueryInterface: 0x%08x\n", hres);
    refcount = get_refcount((IUnknown *)txtserv);
    ok(refcount == 2, "got wrong ref count: %d\n", refcount);
    refcount = get_refcount((IUnknown *)txtsrv_txtdoc);
    ok(refcount == 2, "got wrong ref count: %d\n", refcount);

    hres = ITextDocument_QueryInterface(txtsrv_txtdoc, &IID_IRichEditOle, (void **)&reole);
    ok(hres == S_OK, "ITextDocument_QueryInterface: 0x%08x\n", hres);
    refcount = get_refcount((IUnknown *)txtserv);
    ok(refcount == 3, "got wrong ref count: %d\n", refcount);
    refcount = get_refcount((IUnknown *)txtsrv_txtdoc);
    ok(refcount == 3, "got wrong ref count: %d\n", refcount);

    IRichEditOle_Release(reole);
    refcount = get_refcount((IUnknown *)txtserv);
    ok(refcount == 2, "got wrong ref count: %d\n", refcount);
    ITextDocument_Release(txtsrv_txtdoc);
    refcount = get_refcount((IUnknown *)txtserv);
    ok(refcount == 1, "got wrong ref count: %d\n", refcount);
970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991

    /* ITextDocument2Old */
    hres = ITextServices_QueryInterface(txtserv, &IID_ITextDocument2Old, (void **)&txtsrv_txtdoc2old);
    ok(hres == S_OK, "ITextServices_QueryInterface: 0x%08x\n", hres);
    refcount = get_refcount((IUnknown *)txtserv);
    ok(refcount == 2, "got wrong ref count: %d\n", refcount);
    refcount = get_refcount((IUnknown *)txtsrv_txtdoc2old);
    ok(refcount == 2, "got wrong ref count: %d\n", refcount);

    hres = ITextDocument2Old_QueryInterface(txtsrv_txtdoc2old, &IID_IRichEditOle, (void **)&reole);
    ok(hres == S_OK, "ITextDocument2Old_QueryInterface: 0x%08x\n", hres);
    refcount = get_refcount((IUnknown *)txtserv);
    ok(refcount == 3, "got wrong ref count: %d\n", refcount);
    refcount = get_refcount((IUnknown *)txtsrv_txtdoc2old);
    ok(refcount == 3, "got wrong ref count: %d\n", refcount);

    IRichEditOle_Release(reole);
    refcount = get_refcount((IUnknown *)txtserv);
    ok(refcount == 2, "got wrong ref count: %d\n", refcount);
    ITextDocument2Old_Release(txtsrv_txtdoc2old);
    refcount = get_refcount((IUnknown *)txtserv);
    ok(refcount == 1, "got wrong ref count: %d\n", refcount);
992

993 994
    ITextServices_Release(txtserv);
    ITextHost_Release(host);
995 996
}

997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026
static void test_default_format(void)
{
    ITextServices *txtserv;
    ITextHost *host;
    HRESULT result;
    LRESULT lresult;
    CHARFORMAT2W cf2;
    const CHARFORMATW *host_cf;
    DWORD expected_effects;

    if (!init_texthost(&txtserv, &host))
        return;

    cf2.cbSize = sizeof(CHARFORMAT2W);
    result = ITextServices_TxSendMessage(txtserv, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2, &lresult);
    ok(result == S_OK, "ITextServices_TxSendMessage failed: 0x%08x.\n", result);

    ITextHostImpl_TxGetCharFormat(host, &host_cf);
    ok(!lstrcmpW(host_cf->szFaceName, cf2.szFaceName), "got wrong font name: %s.\n", wine_dbgstr_w(cf2.szFaceName));
    ok(cf2.yHeight == host_cf->yHeight, "got wrong yHeight: %d, expetced %d.\n", cf2.yHeight, host_cf->yHeight);
    expected_effects = (cf2.dwEffects & ~(CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR));
    ok(host_cf->dwEffects == expected_effects , "got wrong dwEffects: %x, expetced %x.\n", cf2.dwEffects, expected_effects);
    ok(cf2.bPitchAndFamily == host_cf->bPitchAndFamily, "got wrong bPitchAndFamily: %x, expected %x.\n",
       cf2.bPitchAndFamily, host_cf->bPitchAndFamily);
    ok(cf2.bCharSet == host_cf->bCharSet, "got wrong bCharSet: %x, expected %x.\n", cf2.bCharSet, host_cf->bCharSet);

    ITextServices_Release(txtserv);
    ITextHost_Release(host);
}

1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045
static void test_TxGetScroll(void)
{
    ITextServices *txtserv;
    ITextHost *host;
    HRESULT ret;

    if (!init_texthost(&txtserv, &host))
        return;

    ret = ITextServices_TxGetHScroll(txtserv, NULL, NULL, NULL, NULL, NULL);
    ok(ret == S_OK, "ITextSerHices_GetVScroll failed: 0x%08x.\n", ret);

    ret = ITextServices_TxGetVScroll(txtserv, NULL, NULL, NULL, NULL, NULL);
    ok(ret == S_OK, "ITextServices_GetVScroll failed: 0x%08x.\n", ret);

    ITextServices_Release(txtserv);
    ITextHost_Release(host);
}

1046 1047
START_TEST( txtsrv )
{
1048 1049 1050
    ITextServices *txtserv;
    ITextHost *host;

1051 1052 1053 1054
    setup_thiscall_wrappers();

    /* Must explicitly LoadLibrary(). The test has no references to functions in
     * RICHED20.DLL, so the linker doesn't actually link to it. */
1055
    hmoduleRichEdit = LoadLibraryA("riched20.dll");
1056 1057
    ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());

1058 1059 1060
    pIID_ITextServices = (IID*)GetProcAddress(hmoduleRichEdit, "IID_ITextServices");
    pIID_ITextHost = (IID*)GetProcAddress(hmoduleRichEdit, "IID_ITextHost");
    pIID_ITextHost2 = (IID*)GetProcAddress(hmoduleRichEdit, "IID_ITextHost2");
1061 1062
    pCreateTextServices = (void*)GetProcAddress(hmoduleRichEdit, "CreateTextServices");

1063
    test_IIDs();
1064
    test_COM();
1065

1066
    if (init_texthost(&txtserv, &host))
1067
    {
1068 1069
        ITextServices_Release(txtserv);
        ITextHost_Release(host);
1070 1071

        test_TxGetText();
1072
        test_TxSetText();
1073
        test_TxGetNaturalSize();
1074
        test_TxDraw();
1075
        test_QueryInterface();
1076
        test_default_format();
1077
        test_TxGetScroll();
1078 1079 1080
    }
    if (wrapperCodeMem) VirtualFree(wrapperCodeMem, 0, MEM_RELEASE);
}