/* * 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 #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, ¶ms, 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, ¶ms, 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, ¶ms, NULL, NULL, NULL); ok(hr == DISP_E_BADCALLEE, "got %08x\n", hr); hr = IDispatchEx_InvokeEx(dispex, 4, LOCALE_SYSTEM_DEFAULT, 0xffff, ¶ms, NULL, NULL, NULL); ok(hr == S_OK, "got %08x\n", hr); params.cArgs = 0; hr = IDispatchEx_InvokeEx(dispex, 5, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, ¶ms, NULL, NULL, NULL); ok(hr == DISP_E_EXCEPTION, "got %08x\n", hr); hr = IDispatchEx_InvokeEx(dispex, 5, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, ¶ms, 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(); }