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