wine-wine/dlls/dispex/tests/marshal.c

438 lines
13 KiB
C

/*
* Tests for marshaling IDispatchEx
*
* Copyright 2005-2006 Robert Shearman
* Copyright 2010 Huw Davies
*
* 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
#define CONST_VTABLE
#include <stdarg.h>
#include "initguid.h"
#include "objidl.h"
#include "dispex.h"
#include "wine/test.h"
#define ok_ole_success(hr, func) ok(hr == S_OK, #func " failed with error 0x%08x\n", hr)
#define RELEASEMARSHALDATA WM_USER
struct host_object_data
{
IStream *stream;
IID iid;
IUnknown *object;
MSHLFLAGS marshal_flags;
HANDLE marshal_event;
HANDLE error_event;
IMessageFilter *filter;
};
static DWORD CALLBACK host_object_proc(LPVOID p)
{
struct host_object_data *data = p;
HRESULT hr;
MSG msg;
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
if (data->filter)
{
IMessageFilter * prev_filter = NULL;
hr = CoRegisterMessageFilter(data->filter, &prev_filter);
if (prev_filter) IMessageFilter_Release(prev_filter);
ok_ole_success(hr, CoRegisterMessageFilter);
}
hr = CoMarshalInterface(data->stream, &data->iid, data->object, MSHCTX_INPROC, NULL, data->marshal_flags);
/* force the message queue to be created before signaling parent thread */
PeekMessageA(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
if(hr == S_OK)
SetEvent(data->marshal_event);
else
{
win_skip("IDispatchEx marshaller not available.\n");
SetEvent(data->error_event);
return hr;
}
while (GetMessageA(&msg, NULL, 0, 0))
{
if (msg.hwnd == NULL && msg.message == RELEASEMARSHALDATA)
{
trace("releasing marshal data\n");
CoReleaseMarshalData(data->stream);
SetEvent((HANDLE)msg.lParam);
}
else
DispatchMessageA(&msg);
}
HeapFree(GetProcessHeap(), 0, data);
CoUninitialize();
return hr;
}
static DWORD start_host_object2(IStream *stream, REFIID riid, IUnknown *object, MSHLFLAGS marshal_flags, IMessageFilter *filter, HANDLE *thread)
{
DWORD tid = 0, ret;
HANDLE events[2];
struct host_object_data *data = HeapAlloc(GetProcessHeap(), 0, sizeof(*data));
data->stream = stream;
data->iid = *riid;
data->object = object;
data->marshal_flags = marshal_flags;
data->marshal_event = events[0] = CreateEventW(NULL, FALSE, FALSE, NULL);
data->error_event = events[1] = CreateEventW(NULL, FALSE, FALSE, NULL);
data->filter = filter;
*thread = CreateThread(NULL, 0, host_object_proc, data, 0, &tid);
/* wait for marshaling to complete before returning */
ret = WaitForMultipleObjects(2, events, FALSE, INFINITE);
CloseHandle(events[0]);
CloseHandle(events[1]);
if(ret == WAIT_OBJECT_0) return tid;
WaitForSingleObject(*thread, INFINITE);
CloseHandle(*thread);
*thread = INVALID_HANDLE_VALUE;
return 0;
}
static DWORD start_host_object(IStream *stream, REFIID riid, IUnknown *object, MSHLFLAGS marshal_flags, HANDLE *thread)
{
return start_host_object2(stream, riid, object, marshal_flags, NULL, thread);
}
static void end_host_object(DWORD tid, HANDLE thread)
{
BOOL ret = PostThreadMessageA(tid, WM_QUIT, 0, 0);
ok(ret, "PostThreadMessage failed with error %d\n", GetLastError());
/* be careful of races - don't return until hosting thread has terminated */
WaitForSingleObject(thread, INFINITE);
CloseHandle(thread);
}
typedef struct
{
IDispatchEx IDispatchEx_iface;
LONG refs;
} dispex;
static inline dispex *impl_from_IDispatchEx(IDispatchEx *iface)
{
return CONTAINING_RECORD(iface, dispex, IDispatchEx_iface);
}
static HRESULT WINAPI dispex_QueryInterface(IDispatchEx* iface,
REFIID iid, void **obj)
{
trace("QI {%08x-...}\n", iid->Data1);
if(IsEqualIID(iid, &IID_IUnknown) ||
IsEqualIID(iid, &IID_IDispatchEx))
{
IDispatchEx_AddRef(iface);
*obj = iface;
return S_OK;
}
else
{
*obj = NULL;
return E_NOINTERFACE;
}
}
static ULONG WINAPI dispex_AddRef(IDispatchEx* iface)
{
dispex *This = impl_from_IDispatchEx(iface);
trace("AddRef\n");
return InterlockedIncrement(&This->refs);
}
static ULONG WINAPI dispex_Release(IDispatchEx* iface)
{
dispex *This = impl_from_IDispatchEx(iface);
ULONG refs = InterlockedDecrement(&This->refs);
trace("Release\n");
if(!refs)
{
HeapFree(GetProcessHeap(), 0, This);
}
return refs;
}
static HRESULT WINAPI dispex_GetTypeInfoCount(IDispatchEx* iface,
UINT *pctinfo)
{
trace("\n");
return E_NOTIMPL;
}
static HRESULT WINAPI dispex_GetTypeInfo(IDispatchEx* iface,
UINT iTInfo,
LCID lcid,
ITypeInfo **ppTInfo)
{
trace("\n");
return E_NOTIMPL;
}
static HRESULT WINAPI dispex_GetIDsOfNames(IDispatchEx* iface,
REFIID riid,
LPOLESTR *rgszNames,
UINT cNames,
LCID lcid,
DISPID *rgDispId)
{
trace("\n");
return E_NOTIMPL;
}
static HRESULT WINAPI dispex_Invoke(IDispatchEx* iface,
DISPID dispIdMember,
REFIID riid,
LCID lcid,
WORD wFlags,
DISPPARAMS *pDispParams,
VARIANT *pVarResult,
EXCEPINFO *pExcepInfo,
UINT *puArgErr)
{
trace("\n");
return E_NOTIMPL;
}
static HRESULT WINAPI dispex_GetDispID(IDispatchEx* iface,
BSTR bstrName,
DWORD grfdex,
DISPID *pid)
{
trace("\n");
return E_NOTIMPL;
}
static HRESULT WINAPI defer_fn(EXCEPINFO *except)
{
except->scode = E_OUTOFMEMORY;
return S_OK;
}
static HRESULT WINAPI dispex_InvokeEx(IDispatchEx* iface,
DISPID id,
LCID lcid,
WORD wFlags,
DISPPARAMS *pdp,
VARIANT *pvarRes,
EXCEPINFO *pei,
IServiceProvider *pspCaller)
{
if(id == 1)
{
ok(pdp->cArgs == 0, "got %d\n", pdp->cArgs);
ok(pei == NULL, "got non-NULL excepinfo\n");
ok(pvarRes == NULL, "got non-NULL result\n");
}
else if(id == 2)
{
ok(pdp->cArgs == 2, "got %d\n", pdp->cArgs);
ok(V_VT(&pdp->rgvarg[0]) == VT_INT, "got %04x\n", V_VT(&pdp->rgvarg[0]));
ok(V_VT(&pdp->rgvarg[1]) == (VT_INT | VT_BYREF), "got %04x\n", V_VT(&pdp->rgvarg[1]));
ok(*V_INTREF(&pdp->rgvarg[1]) == 0xbeef, "got %08x\n", *V_INTREF(&pdp->rgvarg[1]));
*V_INTREF(&pdp->rgvarg[1]) = 0xdead;
}
else if(id == 3)
{
ok(pdp->cArgs == 2, "got %d\n", pdp->cArgs);
ok(V_VT(&pdp->rgvarg[0]) == VT_INT, "got %04x\n", V_VT(&pdp->rgvarg[0]));
ok(V_VT(&pdp->rgvarg[1]) == (VT_INT | VT_BYREF), "got %04x\n", V_VT(&pdp->rgvarg[1]));
V_VT(&pdp->rgvarg[0]) = VT_I4;
}
else if(id == 4)
{
ok(wFlags == 0xf, "got %04x\n", wFlags);
}
else if(id == 5)
{
if(pei) pei->pfnDeferredFillIn = defer_fn;
return DISP_E_EXCEPTION;
}
return S_OK;
}
static HRESULT WINAPI dispex_DeleteMemberByName(IDispatchEx* iface,
BSTR bstrName,
DWORD grfdex)
{
trace("\n");
return E_NOTIMPL;
}
static HRESULT WINAPI dispex_DeleteMemberByDispID(IDispatchEx* iface, DISPID id)
{
trace("\n");
return E_NOTIMPL;
}
static HRESULT WINAPI dispex_GetMemberProperties(IDispatchEx* iface, DISPID id,
DWORD grfdexFetch, DWORD *pgrfdex)
{
trace("\n");
return E_NOTIMPL;
}
static HRESULT WINAPI dispex_GetMemberName(IDispatchEx* iface,
DISPID id, BSTR *pbstrName)
{
trace("\n");
return E_NOTIMPL;
}
static HRESULT WINAPI dispex_GetNextDispID(IDispatchEx* iface,
DWORD grfdex,
DISPID id,
DISPID *pid)
{
trace("\n");
return E_NOTIMPL;
}
static HRESULT WINAPI dispex_GetNameSpaceParent(IDispatchEx* iface,
IUnknown **ppunk)
{
trace("\n");
return E_NOTIMPL;
}
static const IDispatchExVtbl dispex_vtable =
{
dispex_QueryInterface,
dispex_AddRef,
dispex_Release,
dispex_GetTypeInfoCount,
dispex_GetTypeInfo,
dispex_GetIDsOfNames,
dispex_Invoke,
dispex_GetDispID,
dispex_InvokeEx,
dispex_DeleteMemberByName,
dispex_DeleteMemberByDispID,
dispex_GetMemberProperties,
dispex_GetMemberName,
dispex_GetNextDispID,
dispex_GetNameSpaceParent
};
static IDispatchEx *dispex_create(void)
{
dispex *This;
This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
if (!This) return NULL;
This->IDispatchEx_iface.lpVtbl = &dispex_vtable;
This->refs = 1;
return &This->IDispatchEx_iface;
}
static void test_dispex(void)
{
HRESULT hr;
IStream *stream;
DWORD tid;
HANDLE thread;
static const LARGE_INTEGER zero;
IDispatchEx *dispex = dispex_create();
DISPPARAMS params;
VARIANTARG args[10];
INT i;
EXCEPINFO excepinfo;
hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
ok(hr == S_OK, "got %08x\n", hr);
tid = start_host_object(stream, &IID_IDispatchEx, (IUnknown *)dispex, MSHLFLAGS_NORMAL, &thread);
IDispatchEx_Release(dispex);
if(tid == 0)
{
IStream_Release(stream);
return;
}
IStream_Seek(stream, zero, STREAM_SEEK_SET, NULL);
hr = CoUnmarshalInterface(stream, &IID_IDispatchEx, (void **)&dispex);
ok(hr == S_OK, "got %08x\n", hr);
IStream_Release(stream);
params.rgvarg = NULL;
params.rgdispidNamedArgs = NULL;
params.cArgs = 0;
params.cNamedArgs = 0;
hr = IDispatchEx_InvokeEx(dispex, 1, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, &params, NULL, NULL, NULL);
ok(hr == S_OK, "got %08x\n", hr);
params.rgvarg = args;
params.rgdispidNamedArgs = NULL;
params.cArgs = 2;
params.cNamedArgs = 0;
V_VT(&args[0]) = VT_INT;
V_INT(&args[0]) = 0xcafe;
V_VT(&args[1]) = VT_INT | VT_BYREF;
V_INTREF(&args[1]) = &i;
i = 0xbeef;
hr = IDispatchEx_InvokeEx(dispex, 2, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, &params, NULL, NULL, NULL);
ok(hr == S_OK, "got %08x\n", hr);
ok(i == 0xdead, "got %08x\n", i);
/* change one of the argument vts */
i = 0xbeef;
hr = IDispatchEx_InvokeEx(dispex, 3, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, &params, NULL, NULL, NULL);
ok(hr == DISP_E_BADCALLEE, "got %08x\n", hr);
hr = IDispatchEx_InvokeEx(dispex, 4, LOCALE_SYSTEM_DEFAULT, 0xffff, &params, NULL, NULL, NULL);
ok(hr == S_OK, "got %08x\n", hr);
params.cArgs = 0;
hr = IDispatchEx_InvokeEx(dispex, 5, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, &params, NULL, NULL, NULL);
ok(hr == DISP_E_EXCEPTION, "got %08x\n", hr);
hr = IDispatchEx_InvokeEx(dispex, 5, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, &params, NULL, &excepinfo, NULL);
ok(hr == DISP_E_EXCEPTION, "got %08x\n", hr);
ok(excepinfo.scode == E_OUTOFMEMORY, "got scode %08x\n", excepinfo.scode);
ok(excepinfo.pfnDeferredFillIn == NULL, "got non-NULL pfnDeferredFillIn\n");
IDispatchEx_Release(dispex);
end_host_object(tid, thread);
}
START_TEST(marshal)
{
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
test_dispex();
CoUninitialize();
}