diff --git a/dlls/riched20/tests/Makefile.in b/dlls/riched20/tests/Makefile.in index 0993609..9690e5e 100644 --- a/dlls/riched20/tests/Makefile.in +++ b/dlls/riched20/tests/Makefile.in @@ -7,7 +7,8 @@ IMPORTS = ole32 user32 gdi32 kernel32 CTESTS = \ editor.c \ - richole.c + richole.c \ + txtsrv.c @MAKE_TEST_RULES@ diff --git a/dlls/riched20/tests/txtsrv.c b/dlls/riched20/tests/txtsrv.c new file mode 100644 index 0000000..da8c81d --- /dev/null +++ b/dlls/riched20/tests/txtsrv.c @@ -0,0 +1,594 @@ +/* + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static HMODULE hmoduleRichEdit; + +/* 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 +{ + ITextHostVtbl *lpVtbl; + LONG refCount; +} ITextHostTestImpl; + +static HRESULT WINAPI ITextHostImpl_QueryInterface(ITextHost *iface, + REFIID riid, + LPVOID *ppvObject) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + + if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_ITextHost)) { + *ppvObject = This; + ITextHost_AddRef((ITextHost *)*ppvObject); + return S_OK; + } + + return E_NOINTERFACE; +} + +static ULONG WINAPI ITextHostImpl_AddRef(ITextHost *iface) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + ULONG refCount = InterlockedIncrement(&This->refCount); + return refCount; +} + +static ULONG WINAPI ITextHostImpl_Release(ITextHost *iface) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + ULONG refCount = InterlockedDecrement(&This->refCount); + + if (!refCount) + { + CoTaskMemFree(This); + return 0; + } else { + return refCount; + } +} + +static HDC WINAPI ITextHostImpl_TxGetDC(ITextHost *iface) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxGetDC(%p)\n", This); + return NULL; +} + +static INT WINAPI ITextHostImpl_TxReleaseDC(ITextHost *iface, + HDC hdc) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxReleaseDC(%p)\n", This); + return 0; +} + +static BOOL WINAPI ITextHostImpl_TxShowScrollBar(ITextHost *iface, + INT fnBar, + BOOL fShow) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxShowScrollBar(%p, fnBar=%d, fShow=%d)\n", + This, fnBar, fShow); + return E_NOTIMPL; +} + +static BOOL WINAPI ITextHostImpl_TxEnableScrollBar(ITextHost *iface, + INT fuSBFlags, + INT fuArrowflags) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxEnableScrollBar(%p, fuSBFlags=%d, fuArrowflags=%d)\n", + This, fuSBFlags, fuArrowflags); + return E_NOTIMPL; +} + +static BOOL WINAPI ITextHostImpl_TxSetScrollRange(ITextHost *iface, + INT fnBar, + LONG nMinPos, + INT nMaxPos, + BOOL fRedraw) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxSetScrollRange(%p, fnBar=%d, nMinPos=%d, nMaxPos=%d, fRedraw=%d)\n", + This, fnBar, nMinPos, nMaxPos, fRedraw); + return E_NOTIMPL; +} + +static BOOL WINAPI ITextHostImpl_TxSetScrollPos(ITextHost *iface, + INT fnBar, + INT nPos, + BOOL fRedraw) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxSetScrollPos(%p, fnBar=%d, nPos=%d, fRedraw=%d)\n", + This, fnBar, nPos, fRedraw); + return E_NOTIMPL; +} + +static void WINAPI ITextHostImpl_TxInvalidateRect(ITextHost *iface, + LPCRECT prc, + BOOL fMode) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxInvalidateRect(%p, prc=%p, fMode=%d)\n", + This, prc, fMode); +} + +static void WINAPI ITextHostImpl_TxViewChange(ITextHost *iface, BOOL fUpdate) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxViewChange(%p, fUpdate=%d)\n", + This, fUpdate); +} + +static BOOL WINAPI ITextHostImpl_TxCreateCaret(ITextHost *iface, + HBITMAP hbmp, + INT xWidth, INT yHeight) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxCreateCaret(%p, nbmp=%p, xWidth=%d, yHeight=%d)\n", + This, hbmp, xWidth, yHeight); + return E_NOTIMPL; +} + +static BOOL WINAPI ITextHostImpl_TxShowCaret(ITextHost *iface, BOOL fShow) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxShowCaret(%p, fShow=%d)\n", + This, fShow); + return E_NOTIMPL; +} + +static BOOL WINAPI ITextHostImpl_TxSetCaretPos(ITextHost *iface, + INT x, INT y) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxSetCaretPos(%p, x=%d, y=%d)\n", This, x, y); + return E_NOTIMPL; +} + +static BOOL WINAPI ITextHostImpl_TxSetTimer(ITextHost *iface, + UINT idTimer, UINT uTimeout) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxSetTimer(%p, idTimer=%u, uTimeout=%u)\n", + This, idTimer, uTimeout); + return E_NOTIMPL; +} + +static void WINAPI ITextHostImpl_TxKillTimer(ITextHost *iface, UINT idTimer) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxKillTimer(%p, idTimer=%u)\n", This, idTimer); +} + +static void WINAPI ITextHostImpl_TxScrollWindowEx(ITextHost *iface, + INT dx, INT dy, + LPCRECT lprcScroll, + LPCRECT lprcClip, + HRGN hRgnUpdate, + LPRECT lprcUpdate, + UINT fuScroll) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + 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) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxSetCapture(%p, fCapture=%d)\n", This, fCapture); +} + +static void WINAPI ITextHostImpl_TxSetFocus(ITextHost *iface) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxSetFocus(%p)\n", This); +} + +static void WINAPI ITextHostImpl_TxSetCursor(ITextHost *iface, + HCURSOR hcur, + BOOL fText) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxSetCursor(%p, hcur=%p, fText=%d)\n", + This, hcur, fText); +} + +static BOOL WINAPI ITextHostImpl_TxScreenToClient(ITextHost *iface, + LPPOINT lppt) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxScreenToClient(%p, lppt=%p)\n", This, lppt); + return E_NOTIMPL; +} + +static BOOL WINAPI ITextHostImpl_TxClientToScreen(ITextHost *iface, + LPPOINT lppt) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxClientToScreen(%p, lppt=%p)\n", This, lppt); + return E_NOTIMPL; +} + +static HRESULT WINAPI ITextHostImpl_TxActivate(ITextHost *iface, + LONG *plOldState) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxActivate(%p, plOldState=%p)\n", This, plOldState); + return E_NOTIMPL; +} + +static HRESULT WINAPI ITextHostImpl_TxDeactivate(ITextHost *iface, + LONG lNewState) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxDeactivate(%p, lNewState=%d)\n", This, lNewState); + return E_NOTIMPL; +} + +static HRESULT WINAPI ITextHostImpl_TxGetClientRect(ITextHost *iface, + LPRECT prc) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxGetClientRect(%p, prc=%p)\n", This, prc); + return E_NOTIMPL; +} + +static HRESULT WINAPI ITextHostImpl_TxGetViewInset(ITextHost *iface, + LPRECT prc) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxGetViewInset(%p, prc=%p)\n", This, prc); + return E_NOTIMPL; +} + +static HRESULT WINAPI ITextHostImpl_TxGetCharFormat(ITextHost *iface, + const CHARFORMATW **ppCF) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxGetCharFormat(%p, ppCF=%p)\n", This, ppCF); + return E_NOTIMPL; +} + +static HRESULT WINAPI ITextHostImpl_TxGetParaFormat(ITextHost *iface, + const PARAFORMAT **ppPF) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxGetParaFormat(%p, ppPF=%p)\n", This, ppPF); + return E_NOTIMPL; +} + +static COLORREF WINAPI ITextHostImpl_TxGetSysColor(ITextHost *iface, + int nIndex) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxGetSysColor(%p, nIndex=%d)\n", This, nIndex); + return E_NOTIMPL; +} + +static HRESULT WINAPI ITextHostImpl_TxGetBackStyle(ITextHost *iface, + TXTBACKSTYLE *pStyle) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxGetBackStyle(%p, pStyle=%p)\n", This, pStyle); + return E_NOTIMPL; +} + +static HRESULT WINAPI ITextHostImpl_TxGetMaxLength(ITextHost *iface, + DWORD *pLength) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxGetMaxLength(%p, pLength=%p)\n", This, pLength); + return E_NOTIMPL; +} + +static HRESULT WINAPI ITextHostImpl_TxGetScrollbars(ITextHost *iface, + DWORD *pdwScrollBar) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxGetScrollbars(%p, pdwScrollBar=%p)\n", + This, pdwScrollBar); + return E_NOTIMPL; +} + +static HRESULT WINAPI ITextHostImpl_TxGetPasswordChar(ITextHost *iface, + WCHAR *pch) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxGetPasswordChar(%p, pch=%p)\n", This, pch); + return E_NOTIMPL; +} + +static HRESULT WINAPI ITextHostImpl_TxGetAcceleratorPos(ITextHost *iface, + LONG *pch) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxGetAcceleratorPos(%p, pch=%p)\n", This, pch); + return E_NOTIMPL; +} + +static HRESULT WINAPI ITextHostImpl_TxGetExtent(ITextHost *iface, + LPSIZEL lpExtent) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxGetExtent(%p, lpExtent=%p)\n", This, lpExtent); + return E_NOTIMPL; +} + +static HRESULT WINAPI ITextHostImpl_OnTxCharFormatChange(ITextHost *iface, + const CHARFORMATW *pcf) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to OnTxCharFormatChange(%p, pcf=%p)\n", This, pcf); + return E_NOTIMPL; +} + +static HRESULT WINAPI ITextHostImpl_OnTxParaFormatChange(ITextHost *iface, + const PARAFORMAT *ppf) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + 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, + DWORD dwMask, + DWORD *pdwBits) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + 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, + void *pv) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxNotify(%p, iNotify=%d, pv=%p)\n", This, iNotify, pv); + return E_NOTIMPL; +} + +static HIMC WINAPI ITextHostImpl_TxImmGetContext(ITextHost *iface) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxImmGetContext(%p)\n", This); + return 0; +} + +static void WINAPI ITextHostImpl_TxImmReleaseContext(ITextHost *iface, HIMC himc) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxImmReleaseContext(%p, himc=%p)\n", This, himc); +} + +static HRESULT WINAPI ITextHostImpl_TxGetSelectionBarWidth(ITextHost *iface, + LONG *lSelBarWidth) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxGetSelectionBarWidth(%p, lSelBarWidth=%p)\n", + This, lSelBarWidth); + 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, + ITextHostImpl_TxGetScrollbars, + ITextHostImpl_TxGetPasswordChar, + ITextHostImpl_TxGetAcceleratorPos, + ITextHostImpl_TxGetExtent, + ITextHostImpl_OnTxCharFormatChange, + ITextHostImpl_OnTxParaFormatChange, + ITextHostImpl_TxGetPropertyBits, + ITextHostImpl_TxNotify, + ITextHostImpl_TxImmGetContext, + ITextHostImpl_TxImmReleaseContext, + ITextHostImpl_TxGetSelectionBarWidth +}; + +ITextServices *txtserv = NULL; +ITextHostTestImpl *dummyTextHost; +void *wrapperCodeMem = NULL; + +/* The following x86 byte code converts between the thiscall and stdcall + * calling convetions. 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 finishes by jumping to the real function. + * + * Byte codes are used so that a copy of it can be modified to use + * for each method using the thiscall calling convention. */ + +char thiscall_to_stdcall_x86_code[] = { + 0x58, /* pop %eax */ + 0x51, /* push %ecx */ + 0x50, /* push %eax */ + /* The 2nd to 5th byte of this opcode is the address of the + * function, so the value of 0 should be replaced with the + * wrapped stdcall function to call. */ + 0xe9, 0x00, 0x00, 0x00, 0x00, /* jmp 0x00000000 */ +}; +#define STDCALL_FUNC_ADDRESS_BYTE_POSITION 4 + + +static void setup_thiscall_wrappers() +{ +#ifdef __i386__ + void** pVtable; + void** pVtableEnd; + char* wrapper_code; + size_t code_size; + DWORD oldProtectFlags; + + code_size = ((sizeof(ITextHostVtbl) / sizeof(void*)) - 3) + * sizeof(thiscall_to_stdcall_x86_code); + wrapperCodeMem = VirtualAlloc(NULL, code_size, + MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); + wrapper_code = wrapperCodeMem; + + /* Wrap all ITextHostImpl methods with code to perform a + * thiscall to stdcall conversion. */ + + /* Skip QueryInterface, AddRef, and Release native actually + * defined them with the stdcall calling convention. */ + pVtable = ((void**)&itextHostVtbl) + 3; + pVtableEnd = (void**)(((char*)&itextHostVtbl) + sizeof(ITextHostVtbl)); + while (pVtable != pVtableEnd) { + void **placeholder; + + CopyMemory(wrapper_code, thiscall_to_stdcall_x86_code, + sizeof(thiscall_to_stdcall_x86_code)); + /* Modify the 4 byte placeholder in the code for the function address. */ + placeholder = (void**)(wrapper_code + STDCALL_FUNC_ADDRESS_BYTE_POSITION); + /* The address needs to be relative to the end of the jump instructions. */ + *placeholder = (void*)(((char*)*pVtable) - ((char*)placeholder) - 4); + *pVtable = wrapper_code; + pVtable++; + wrapper_code += sizeof(thiscall_to_stdcall_x86_code); + } + VirtualProtect(wrapperCodeMem, code_size, PAGE_EXECUTE, &oldProtectFlags); +#endif /* __i386__ */ +} + +void destroy_thiscall_wrappers() +{ +#ifdef __i386__ + /* Release allocated memory for wrappers. */ + VirtualFree(wrapperCodeMem, 0, MEM_RELEASE); +#endif /* __i386__ */ +} + +/*************************************************************************/ +/* Conformance test functions. */ + +/* Initialize the test texthost structure */ +BOOL init_texthost() +{ + IUnknown *init; + HRESULT result; + PCreateTextServices pCreateTextServices; + + dummyTextHost = CoTaskMemAlloc(sizeof(*dummyTextHost)); + if (dummyTextHost == NULL) { + skip("Insufficient memory to create ITextHost interface\n"); + return FALSE; + } + dummyTextHost->lpVtbl = &itextHostVtbl; + dummyTextHost->refCount = 1; + + /* MSDN states that an IUnknown object is returned by + CreateTextServices which is then queried to obtain a + ITextServices object. */ + pCreateTextServices = (void*)GetProcAddress(hmoduleRichEdit, "CreateTextServices"); + result = (*pCreateTextServices)(NULL,(ITextHost*)dummyTextHost, &init); + ok(result == S_OK, "Did not return OK when created. Returned %x\n", result); + if (result != S_OK) { + CoTaskMemFree(dummyTextHost); + skip("CreateTextServices failed.\n"); + return FALSE; + } + + result = IUnknown_QueryInterface(init, &IID_ITextServices, + (void **)&txtserv); + ok((result == S_OK) && (txtserv != NULL), "Querying interface failed\n"); + IUnknown_Release(init); + if (!((result == S_OK) && (txtserv != NULL))) { + CoTaskMemFree(dummyTextHost); + skip("Could not retrieve ITextServices interface\n"); + return FALSE; + } + + return TRUE; +} + +START_TEST( txtsrv ) +{ + 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. */ + hmoduleRichEdit = LoadLibrary("RICHED20.DLL"); + ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError()); + + if (!init_texthost()) { + destroy_thiscall_wrappers(); + return; + } + + IUnknown_Release(txtserv); + CoTaskMemFree(dummyTextHost); + + destroy_thiscall_wrappers(); +}