| /* | |
| * 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, ¶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(); | |
| } |