From 09076372eb62f56b90ba6b2f6984883816d9ef67 Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Mon, 29 Oct 2018 23:20:23 -0500 Subject: [PATCH] oleaut32/tests: Add some tests for marshalling interfaces. Signed-off-by: Zebediah Figura Signed-off-by: Alexandre Julliard --- dlls/oleaut32/tests/tmarshal.c | 428 +++++++++++++++++++++++++++++++ dlls/oleaut32/tests/tmarshal.idl | 13 + 2 files changed, 441 insertions(+) diff --git a/dlls/oleaut32/tests/tmarshal.c b/dlls/oleaut32/tests/tmarshal.c index b21b021ab60..0b9dc67918d 100644 --- a/dlls/oleaut32/tests/tmarshal.c +++ b/dlls/oleaut32/tests/tmarshal.c @@ -34,6 +34,12 @@ static HRESULT (WINAPI *pVarAdd)(LPVARIANT,LPVARIANT,LPVARIANT); #define ok_ole_success(hr, func) ok(hr == S_OK, #func " failed with error 0x%08x\n", hr) +static inline void release_iface_(unsigned int line, void *iface) +{ + ULONG ref = IUnknown_Release((IUnknown *)iface); + ok_(__FILE__, line)(!ref, "Got outstanding refcount %d.\n", ref); +} +#define release_iface(a) release_iface_(__LINE__, a) /* ULL suffix is not portable */ #define ULL_CONST(dw1, dw2) ((((ULONGLONG)dw1) << 32) | (ULONGLONG)dw2) @@ -370,6 +376,98 @@ static ItestDualVtbl TestDualVtbl = { static ItestDual TestDual = { &TestDualVtbl }; static ItestDual TestDualDisp = { &TestDualVtbl }; +struct disp_obj +{ + ISomethingFromDispatch ISomethingFromDispatch_iface; + LONG ref; +}; + +static inline struct disp_obj *impl_from_ISomethingFromDispatch(ISomethingFromDispatch *iface) +{ + return CONTAINING_RECORD(iface, struct disp_obj, ISomethingFromDispatch_iface); +} + +static HRESULT WINAPI disp_obj_QueryInterface(ISomethingFromDispatch *iface, REFIID iid, void **out) +{ + if (IsEqualGUID(iid, &IID_IUnknown) || IsEqualGUID(iid, &IID_IDispatch) + || IsEqualGUID(iid, &IID_ISomethingFromDispatch)) + { + *out = iface; + ISomethingFromDispatch_AddRef(iface); + return S_OK; + } + + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI disp_obj_AddRef(ISomethingFromDispatch *iface) +{ + struct disp_obj *obj = impl_from_ISomethingFromDispatch(iface); + return ++obj->ref; +} + +static ULONG WINAPI disp_obj_Release(ISomethingFromDispatch *iface) +{ + struct disp_obj *obj = impl_from_ISomethingFromDispatch(iface); + LONG ref = --obj->ref; + if (!ref) + CoTaskMemFree(obj); + return ref; +} + +static HRESULT WINAPI disp_obj_GetTypeInfoCount(ISomethingFromDispatch *iface, UINT *count) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI disp_obj_GetTypeInfo(ISomethingFromDispatch *iface, + UINT index, LCID lcid, ITypeInfo **typeinfo) +{ + ok(index == 0xdeadbeef, "Got unexpected index %#x.\n", index); + return 0xbeefdead; +} + +static HRESULT WINAPI disp_obj_GetIDsOfNames(ISomethingFromDispatch *iface, + REFIID iid, LPOLESTR *names, UINT count, LCID lcid, DISPID *ids) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI disp_obj_Invoke(ISomethingFromDispatch *iface, DISPID id, REFIID iid, LCID lcid, + WORD flags, DISPPARAMS *dispparams, VARIANT *result, EXCEPINFO *excepinfo, UINT *errarg) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI disp_obj_anotherfn(ISomethingFromDispatch *iface) +{ + return 0x01234567; +} + +static const ISomethingFromDispatchVtbl disp_obj_vtbl = +{ + disp_obj_QueryInterface, + disp_obj_AddRef, + disp_obj_Release, + disp_obj_GetTypeInfoCount, + disp_obj_GetTypeInfo, + disp_obj_GetIDsOfNames, + disp_obj_Invoke, + disp_obj_anotherfn, +}; + +static ISomethingFromDispatch *create_disp_obj(void) +{ + struct disp_obj *obj = CoTaskMemAlloc(sizeof(*obj)); + obj->ISomethingFromDispatch_iface.lpVtbl = &disp_obj_vtbl; + obj->ref = 1; + return &obj->ISomethingFromDispatch_iface; +} + static int testmode; typedef struct Widget @@ -1011,6 +1109,90 @@ todo_wine_if(testmode == 2) return S_OK; } +/* Call methods to check that we have valid proxies to each interface. */ +static void check_iface_marshal(IUnknown *unk, IDispatch *disp, ISomethingFromDispatch *sfd) +{ + ISomethingFromDispatch *sfd2; + ITypeInfo *typeinfo; + HRESULT hr; + + hr = IUnknown_QueryInterface(unk, &IID_ISomethingFromDispatch, (void **)&sfd2); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ISomethingFromDispatch_Release(sfd2); + + hr = IDispatch_GetTypeInfo(disp, 0xdeadbeef, 0, &typeinfo); + ok(hr == 0xbeefdead, "Got hr %#x.\n", hr); + + hr = ISomethingFromDispatch_anotherfn(sfd); + ok(hr == 0x01234567, "Got hr %#x.\n", hr); +} + +static HRESULT WINAPI Widget_iface_in(IWidget *iface, IUnknown *unk, IDispatch *disp, ISomethingFromDispatch *sfd) +{ + if (testmode == 0) + check_iface_marshal(unk, disp, sfd); + else if (testmode == 1) + { + ok(!unk, "Got iface %p.\n", unk); + ok(!disp, "Got iface %p.\n", disp); + ok(!sfd, "Got iface %p.\n", sfd); + } + return S_OK; +} + +static HRESULT WINAPI Widget_iface_out(IWidget *iface, IUnknown **unk, IDispatch **disp, ISomethingFromDispatch **sfd) +{ +todo_wine + ok(!*unk, "Got iface %p.\n", *unk); + ok(!*disp, "Got iface %p.\n", *disp); + ok(!*sfd, "Got iface %p.\n", *sfd); + + if (testmode == 0) + { + *unk = (IUnknown *)create_disp_obj(); + *disp = (IDispatch *)create_disp_obj(); + *sfd = create_disp_obj(); + } + return S_OK; +} + +static HRESULT WINAPI Widget_iface_ptr(IWidget *iface, ISomethingFromDispatch **in, + ISomethingFromDispatch **out, ISomethingFromDispatch **in_out) +{ + HRESULT hr; + + ok(!*out, "Got [out] %p.\n", *out); + if (testmode == 0 || testmode == 1) + { + hr = ISomethingFromDispatch_anotherfn(*in); + ok(hr == 0x01234567, "Got hr %#x.\n", hr); + hr = ISomethingFromDispatch_anotherfn(*in_out); + ok(hr == 0x01234567, "Got hr %#x.\n", hr); + } + + if (testmode == 1) + { + *out = create_disp_obj(); + ISomethingFromDispatch_Release(*in_out); + *in_out = create_disp_obj(); + } + else if (testmode == 2) + { + ok(!*in, "Got [in] %p.\n", *in); + ok(!*in_out, "Got [in, out] %p.\n", *in_out); + *in_out = create_disp_obj(); + } + else if (testmode == 3) + { + hr = ISomethingFromDispatch_anotherfn(*in_out); + ok(hr == 0x01234567, "Got hr %#x.\n", hr); + ISomethingFromDispatch_Release(*in_out); + *in_out = NULL; + } + + return S_OK; +} + static const struct IWidgetVtbl Widget_VTable = { Widget_QueryInterface, @@ -1056,6 +1238,9 @@ static const struct IWidgetVtbl Widget_VTable = Widget_basetypes_out, Widget_int_ptr, Widget_int_ptr_ptr, + Widget_iface_in, + Widget_iface_out, + Widget_iface_ptr, }; static HRESULT WINAPI StaticWidget_QueryInterface(IStaticWidget *iface, REFIID riid, void **ppvObject) @@ -1573,6 +1758,248 @@ if (0) { } } +static void test_marshal_iface(IWidget *widget, IDispatch *disp) +{ + VARIANTARG arg[3]; + DISPPARAMS dispparams = {arg, NULL, ARRAY_SIZE(arg), 0}; + ISomethingFromDispatch *sfd1, *sfd2, *sfd3, *proxy_sfd, *sfd_in, *sfd_out, *sfd_in_out; + IUnknown *proxy_unk, *proxy_unk2, *unk_in, *unk_out, *unk_in_out; + IDispatch *proxy_disp; + HRESULT hr; + + testmode = 0; + sfd1 = create_disp_obj(); + sfd2 = create_disp_obj(); + sfd3 = create_disp_obj(); + hr = IWidget_iface_in(widget, (IUnknown *)create_disp_obj(), + (IDispatch *)create_disp_obj(), create_disp_obj()); + ok(hr == S_OK, "Got hr %#x.\n", hr); + release_iface(sfd1); + release_iface(sfd2); + release_iface(sfd3); + + testmode = 1; + hr = IWidget_iface_in(widget, NULL, NULL, NULL); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + testmode = 0; + proxy_unk = (IUnknown *)0xdeadbeef; + proxy_disp = (IDispatch *)0xdeadbeef; + proxy_sfd = (ISomethingFromDispatch *)0xdeadbeef; + hr = IWidget_iface_out(widget, &proxy_unk, &proxy_disp, &proxy_sfd); + ok(hr == S_OK, "Got hr %#x.\n", hr); + check_iface_marshal(proxy_unk, proxy_disp, proxy_sfd); + release_iface(proxy_unk); + release_iface(proxy_disp); + release_iface(proxy_sfd); + +if (0) { + testmode = 1; + hr = IWidget_iface_out(widget, &proxy_unk, &proxy_disp, &proxy_sfd); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(!proxy_unk, "Got unexpected proxy %p.\n", proxy_unk); + ok(!proxy_disp, "Got unexpected proxy %p.\n", proxy_disp); + ok(!proxy_sfd, "Got unexpected proxy %p.\n", proxy_sfd); +} + + testmode = 0; + sfd_in = sfd1 = create_disp_obj(); + sfd_out = sfd2 = create_disp_obj(); + sfd_in_out = sfd3 = create_disp_obj(); + hr = IWidget_iface_ptr(widget, &sfd_in, &sfd_out, &sfd_in_out); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(sfd_in == sfd1, "[in] parameter should not have changed.\n"); + ok(!sfd_out, "[out] parameter should have been cleared.\n"); + ok(sfd_in_out == sfd3, "[in, out] parameter should not have changed.\n"); + release_iface(sfd1); + release_iface(sfd2); +todo_wine + release_iface(sfd3); + + testmode = 1; + sfd_in = sfd1 = create_disp_obj(); + sfd_in_out = sfd3 = create_disp_obj(); + ISomethingFromDispatch_AddRef(sfd_in_out); + hr = IWidget_iface_ptr(widget, &sfd_in, &sfd_out, &sfd_in_out); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = ISomethingFromDispatch_anotherfn(sfd_out); + ok(hr == 0x01234567, "Got hr %#x.\n", hr); + ok(sfd_in_out != sfd3, "[in, out] parameter should have changed.\n"); + hr = ISomethingFromDispatch_anotherfn(sfd_in_out); + ok(hr == 0x01234567, "Got hr %#x.\n", hr); + release_iface(sfd_out); + release_iface(sfd_in_out); + release_iface(sfd1); +todo_wine + release_iface(sfd3); + + testmode = 2; + sfd_in = sfd_out = sfd_in_out = NULL; + hr = IWidget_iface_ptr(widget, &sfd_in, &sfd_out, &sfd_in_out); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(!sfd_out, "[out] parameter should not have been set.\n"); + hr = ISomethingFromDispatch_anotherfn(sfd_in_out); + ok(hr == 0x01234567, "Got hr %#x.\n", hr); + release_iface(sfd_in_out); + + testmode = 3; + sfd_in = sfd_out = NULL; + sfd_in_out = sfd3 = create_disp_obj(); + ISomethingFromDispatch_AddRef(sfd_in_out); + hr = IWidget_iface_ptr(widget, &sfd_in, &sfd_out, &sfd_in_out); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(!sfd_in_out, "Got [in, out] %p.\n", sfd_in_out); +todo_wine + release_iface(sfd3); + + /* Test with Invoke(). Note that since we pass VT_UNKNOWN, we don't get our + * interface back, but rather an IUnknown. */ + + testmode = 0; + sfd1 = create_disp_obj(); + sfd2 = create_disp_obj(); + sfd3 = create_disp_obj(); + + V_VT(&arg[2]) = VT_UNKNOWN; V_UNKNOWN(&arg[2]) = (IUnknown *)sfd1; + V_VT(&arg[1]) = VT_UNKNOWN; V_UNKNOWN(&arg[1]) = (IUnknown *)sfd2; + V_VT(&arg[0]) = VT_UNKNOWN; V_UNKNOWN(&arg[0]) = (IUnknown *)sfd3; + hr = IDispatch_Invoke(disp, DISPID_TM_IFACE_IN, &IID_NULL, LOCALE_NEUTRAL, + DISPATCH_METHOD, &dispparams, NULL, NULL, NULL); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + V_VT(&arg[2]) = VT_DISPATCH; V_DISPATCH(&arg[2]) = (IDispatch *)sfd1; + V_VT(&arg[1]) = VT_DISPATCH; V_DISPATCH(&arg[1]) = (IDispatch *)sfd2; + V_VT(&arg[0]) = VT_DISPATCH; V_DISPATCH(&arg[0]) = (IDispatch *)sfd3; + hr = IDispatch_Invoke(disp, DISPID_TM_IFACE_IN, &IID_NULL, LOCALE_NEUTRAL, + DISPATCH_METHOD, &dispparams, NULL, NULL, NULL); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + release_iface(sfd1); + release_iface(sfd2); + release_iface(sfd3); + + testmode = 1; + V_VT(&arg[2]) = VT_UNKNOWN; V_UNKNOWN(&arg[2]) = NULL; + V_VT(&arg[1]) = VT_UNKNOWN; V_UNKNOWN(&arg[1]) = NULL; + V_VT(&arg[0]) = VT_UNKNOWN; V_UNKNOWN(&arg[0]) = NULL; + hr = IDispatch_Invoke(disp, DISPID_TM_IFACE_IN, &IID_NULL, LOCALE_NEUTRAL, + DISPATCH_METHOD, &dispparams, NULL, NULL, NULL); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + testmode = 0; + proxy_unk = proxy_unk2 = NULL; + proxy_disp = NULL; + V_VT(&arg[2]) = VT_UNKNOWN|VT_BYREF; V_UNKNOWNREF(&arg[2]) = &proxy_unk; + V_VT(&arg[1]) = VT_DISPATCH|VT_BYREF; V_DISPATCHREF(&arg[1]) = &proxy_disp; + V_VT(&arg[0]) = VT_UNKNOWN|VT_BYREF; V_UNKNOWNREF(&arg[0]) = &proxy_unk2; + hr = IDispatch_Invoke(disp, DISPID_TM_IFACE_OUT, &IID_NULL, LOCALE_NEUTRAL, + DISPATCH_METHOD, &dispparams, NULL, NULL, NULL); +todo_wine + ok(hr == S_OK, "Got hr %#x.\n", hr); +if (hr == S_OK) { + hr = IUnknown_QueryInterface(proxy_unk2, &IID_ISomethingFromDispatch, (void **)&proxy_sfd); + ok(hr == S_OK, "Got hr %#x.\n", hr); + check_iface_marshal(proxy_unk, proxy_disp, proxy_sfd); + ISomethingFromDispatch_Release(proxy_sfd); + release_iface(proxy_unk); + release_iface(proxy_disp); + release_iface(proxy_unk2); +} + + testmode = 1; + proxy_unk = proxy_unk2 = NULL; + proxy_disp = NULL; + hr = IDispatch_Invoke(disp, DISPID_TM_IFACE_OUT, &IID_NULL, LOCALE_NEUTRAL, + DISPATCH_METHOD, &dispparams, NULL, NULL, NULL); +todo_wine + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(!proxy_unk, "Got unexpected proxy %p.\n", proxy_unk); + ok(!proxy_disp, "Got unexpected proxy %p.\n", proxy_disp); + ok(!proxy_unk2, "Got unexpected proxy %p.\n", proxy_unk2); + + testmode = 0; + sfd1 = create_disp_obj(); + sfd3 = create_disp_obj(); + unk_in = (IUnknown *)sfd1; + unk_out = NULL; + unk_in_out = (IUnknown *)sfd3; + V_VT(&arg[2]) = VT_UNKNOWN|VT_BYREF; V_UNKNOWNREF(&arg[2]) = &unk_in; + V_VT(&arg[1]) = VT_UNKNOWN|VT_BYREF; V_UNKNOWNREF(&arg[1]) = &unk_out; + V_VT(&arg[0]) = VT_UNKNOWN|VT_BYREF; V_UNKNOWNREF(&arg[0]) = &unk_in_out; + hr = IDispatch_Invoke(disp, DISPID_TM_IFACE_PTR, &IID_NULL, LOCALE_NEUTRAL, + DISPATCH_METHOD, &dispparams, NULL, NULL, NULL); +todo_wine + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(unk_in == (IUnknown *)sfd1, "[in] parameter should not have changed.\n"); + ok(!unk_out, "[out] parameter should have been cleared.\n"); + ok(unk_in_out == (IUnknown *)sfd3, "[in, out] parameter should not have changed.\n"); + release_iface(sfd1); + release_iface(sfd3); + + testmode = 1; + sfd1 = create_disp_obj(); + sfd3 = create_disp_obj(); + unk_in = (IUnknown *)sfd1; + unk_out = NULL; + unk_in_out = (IUnknown *)sfd3; + IUnknown_AddRef(unk_in_out); + hr = IDispatch_Invoke(disp, DISPID_TM_IFACE_PTR, &IID_NULL, LOCALE_NEUTRAL, + DISPATCH_METHOD, &dispparams, NULL, NULL, NULL); +todo_wine + ok(hr == S_OK, "Got hr %#x.\n", hr); + +if (hr == S_OK) { + hr = IUnknown_QueryInterface(unk_out, &IID_ISomethingFromDispatch, (void **)&sfd_out); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = ISomethingFromDispatch_anotherfn(sfd_out); + ok(hr == 0x01234567, "Got hr %#x.\n", hr); + ISomethingFromDispatch_Release(sfd_out); + + ok(unk_in_out != (IUnknown *)sfd3, "[in, out] parameter should have changed.\n"); + hr = IUnknown_QueryInterface(unk_in_out, &IID_ISomethingFromDispatch, (void **)&sfd_in_out); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = ISomethingFromDispatch_anotherfn(sfd_in_out); + ok(hr == 0x01234567, "Got hr %#x.\n", hr); + ISomethingFromDispatch_Release(sfd_in_out); + + release_iface(unk_out); + release_iface(unk_in_out); + release_iface(sfd1); + release_iface(sfd3); +} + + testmode = 2; + unk_in = unk_out = unk_in_out = NULL; + hr = IDispatch_Invoke(disp, DISPID_TM_IFACE_PTR, &IID_NULL, LOCALE_NEUTRAL, + DISPATCH_METHOD, &dispparams, NULL, NULL, NULL); +todo_wine + ok(hr == S_OK, "Got hr %#x.\n", hr); + + ok(!unk_out, "[out] parameter should not have been set.\n"); +if (hr == S_OK) { + hr = IUnknown_QueryInterface(unk_in_out, &IID_ISomethingFromDispatch, (void **)&sfd_in_out); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = ISomethingFromDispatch_anotherfn(sfd_in_out); + ok(hr == 0x01234567, "Got hr %#x.\n", hr); + ISomethingFromDispatch_Release(sfd_in_out); + + release_iface(unk_in_out); +} + + testmode = 3; + unk_in = unk_out = NULL; + sfd3 = create_disp_obj(); + unk_in_out = (IUnknown *)sfd3; + IUnknown_AddRef(unk_in_out); + hr = IDispatch_Invoke(disp, DISPID_TM_IFACE_PTR, &IID_NULL, LOCALE_NEUTRAL, + DISPATCH_METHOD, &dispparams, NULL, NULL, NULL); +todo_wine { + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(!unk_in_out, "[in, out] parameter should have been cleared.\n"); + release_iface(sfd3); +} +} + static void test_typelibmarshal(void) { static const WCHAR szCat[] = { 'C','a','t',0 }; @@ -2184,6 +2611,7 @@ todo_wine test_marshal_basetypes(pWidget, pDispatch); test_marshal_pointer(pWidget, pDispatch); + test_marshal_iface(pWidget, pDispatch); IDispatch_Release(pDispatch); IWidget_Release(pWidget); diff --git a/dlls/oleaut32/tests/tmarshal.idl b/dlls/oleaut32/tests/tmarshal.idl index 3cbad570850..7e02453c419 100644 --- a/dlls/oleaut32/tests/tmarshal.idl +++ b/dlls/oleaut32/tests/tmarshal.idl @@ -59,6 +59,9 @@ enum IWidget_dispids DISPID_TM_BASETYPES_OUT, DISPID_TM_INT_PTR, DISPID_TM_INT_PTR_PTR, + DISPID_TM_IFACE_IN, + DISPID_TM_IFACE_OUT, + DISPID_TM_IFACE_PTR, }; static const int DISPID_TM_NEG_RESTRICTED = -26; @@ -95,6 +98,7 @@ library TestTypelib [ odl, + oleautomation, uuid(12345678-1234-4321-1234-121212121212) ] interface ISomethingFromDispatch : IDispatch @@ -250,6 +254,15 @@ library TestTypelib [id(DISPID_TM_INT_PTR_PTR)] HRESULT int_ptr_ptr([in] int **in, [out] int **out, [in, out] int **in_out); + + [id(DISPID_TM_IFACE_IN)] + HRESULT iface_in([in] IUnknown *unk, [in] IDispatch *disp, [in] ISomethingFromDispatch *sfd); + + [id(DISPID_TM_IFACE_OUT)] + HRESULT iface_out([out] IUnknown **unk, [out] IDispatch **disp, [out] ISomethingFromDispatch **sfd); + + [id(DISPID_TM_IFACE_PTR)] + HRESULT iface_ptr([in] ISomethingFromDispatch **in, [out] ISomethingFromDispatch **out, [in, out] ISomethingFromDispatch **in_out); } [