Module: wine Branch: master Commit: bf859fb09bba29ac1fbc9c8d92377399573afc50 URL: https://gitlab.winehq.org/wine/wine/-/commit/bf859fb09bba29ac1fbc9c8d9237739...
Author: Zebediah Figura zfigura@codeweavers.com Date: Mon Dec 12 16:02:57 2022 -0600
oleaut32: Manually construct a stub buffer for dispinterfaces.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=53926
---
dlls/oleaut32/oleaut.c | 114 ++++++++++++++++++++++++++++++++++++++++- dlls/oleaut32/tests/tmarshal.c | 17 +++++- 2 files changed, 127 insertions(+), 4 deletions(-)
diff --git a/dlls/oleaut32/oleaut.c b/dlls/oleaut32/oleaut.c index 858622c833c..92a9f12661b 100644 --- a/dlls/oleaut32/oleaut.c +++ b/dlls/oleaut32/oleaut.c @@ -18,6 +18,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#include <assert.h> #include <stdarg.h> #include <stdio.h> #include <string.h> @@ -34,6 +35,7 @@ #include "ole2.h" #include "olectl.h" #include "oleauto.h" +#include "rpcproxy.h" #include "initguid.h" #include "typelib.h" #include "oleaut32_oaidl.h" @@ -999,6 +1001,114 @@ static HRESULT dispatch_create_stub(IUnknown *server, IRpcStubBuffer **stub) return hr; }
+struct dispinterface_stub +{ + CInterfaceStubVtbl stub_vtbl; + CStdStubBuffer stub_buffer; +}; + +static struct dispinterface_stub *impl_from_IRpcStubBuffer(IRpcStubBuffer *iface) +{ + return CONTAINING_RECORD(&iface->lpVtbl, struct dispinterface_stub, stub_buffer.lpVtbl); +} + +static ULONG WINAPI dispinterface_stub_Release(IRpcStubBuffer *iface) +{ + struct dispinterface_stub *stub = impl_from_IRpcStubBuffer(iface); + unsigned int refcount = InterlockedDecrement(&stub->stub_buffer.RefCount); + + TRACE("%p decreasing refcount to %u.\n", stub, refcount); + + if (!refcount) + { + /* Copied from NdrCStdStubBuffer_Release(), but supposedly incorrect + * according to the comment there. */ + IRpcStubBuffer_Disconnect(iface); + + free(stub); + } + return refcount; +} + +extern const ExtendedProxyFileInfo oleaut32_oaidl_ProxyFileInfo; + +static const CInterfaceStubVtbl *find_idispatch_stub_vtbl(void) +{ + CInterfaceStubVtbl *const *vtbl; + + for (vtbl = oleaut32_oaidl_ProxyFileInfo.pStubVtblList; *vtbl; ++vtbl) + { + if (IsEqualGUID((*vtbl)->header.piid, &IID_IDispatch)) + return *vtbl; + } + + assert(0); + return NULL; +} + +/* Normal dispinterfaces have an IID specified by the IDL compiler as DIID_*, + * but are otherwise identical to IDispatch. Unfortunately, such interfaces may + * not actually support IDispatch in QueryInterface. + * + * This becomes a problem, since CreateStub() was designed such that, for some + * reason, the caller need not actually pass the interface matching "iid". As + * such the standard rpcrt4 implementation will query the server for the + * relevant IID. + * + * This means that we cannot just pass IID_IDispatch with the object, even + * though it is in theory an IDispatch. However, while the standard stub + * constructor is not exported from rpcrt4, all of the vtbl methods are, and + * the type is public, so we *can* manually create it ourselves, bypassing the + * QueryInterface check. + * + * This relies on some rpcrt4 implementation details. + */ +static HRESULT dispinterface_create_stub(IUnknown *server, const GUID *iid, IRpcStubBuffer **stub) +{ + const CInterfaceStubVtbl *stub_vtbl = find_idispatch_stub_vtbl(); + struct dispinterface_stub *object; + void *dispatch; + HRESULT hr; + + if (!(object = calloc(1, sizeof(*object)))) + return E_OUTOFMEMORY; + + /* It's possible we can just assume that "server" is already the + * dispinterface type—we don't have tests for this—but since rpcrt4 queries + * (which we do have tests for) it makes sense for us to match that + * behaviour. */ + if (FAILED(hr = IUnknown_QueryInterface(server, iid, &dispatch))) + { + ERR("Object does not support interface %s.\n", debugstr_guid(iid)); + free(object); + return hr; + } + + object->stub_vtbl.header = stub_vtbl->header; + object->stub_vtbl.Vtbl.QueryInterface = CStdStubBuffer_QueryInterface; + object->stub_vtbl.Vtbl.AddRef = CStdStubBuffer_AddRef; + object->stub_vtbl.Vtbl.Release = dispinterface_stub_Release; + object->stub_vtbl.Vtbl.Connect = CStdStubBuffer_Connect; + object->stub_vtbl.Vtbl.Disconnect = CStdStubBuffer_Disconnect; + object->stub_vtbl.Vtbl.Invoke = CStdStubBuffer_Invoke; + object->stub_vtbl.Vtbl.IsIIDSupported = CStdStubBuffer_IsIIDSupported; + object->stub_vtbl.Vtbl.CountRefs = CStdStubBuffer_CountRefs; + object->stub_vtbl.Vtbl.DebugServerQueryInterface = CStdStubBuffer_DebugServerQueryInterface; + object->stub_vtbl.Vtbl.DebugServerRelease = CStdStubBuffer_DebugServerRelease; + object->stub_buffer.lpVtbl = &object->stub_vtbl.Vtbl; + object->stub_buffer.RefCount = 1; + object->stub_buffer.pvServerObject = dispatch; + /* rpcrt4 will also fill pPSFactory, but it never uses it except in the + * Release method (which we reimplement). It's only to keep a reference to + * the module to implement NdrDllCanUnloadNow(). We use the default + * DllCanUnloadNow() from winecrt0, which always returns S_FALSE, so don't + * bother filling pPSFactory. */ + + TRACE("Created stub %p.\n", object); + *stub = (IRpcStubBuffer *)&object->stub_buffer.lpVtbl; + return S_OK; +} + static HRESULT WINAPI dispatch_typelib_ps_CreateStub(IPSFactoryBuffer *iface, REFIID iid, IUnknown *server, IRpcStubBuffer **stub) { @@ -1022,10 +1132,10 @@ static HRESULT WINAPI dispatch_typelib_ps_CreateStub(IPSFactoryBuffer *iface, if (attr->typekind == TKIND_INTERFACE || (attr->wTypeFlags & TYPEFLAG_FDUAL)) hr = CreateStubFromTypeInfo(typeinfo, iid, server, stub); else - hr = dispatch_create_stub(server, stub); + hr = dispinterface_create_stub(server, iid, stub);
if (FAILED(hr)) - ERR("Failed to create proxy, hr %#lx.\n", hr); + ERR("Failed to create stub, hr %#lx.\n", hr);
ITypeInfo_ReleaseTypeAttr(typeinfo, attr); ITypeInfo_Release(typeinfo); diff --git a/dlls/oleaut32/tests/tmarshal.c b/dlls/oleaut32/tests/tmarshal.c index 53861870e78..5ffc103c7eb 100644 --- a/dlls/oleaut32/tests/tmarshal.c +++ b/dlls/oleaut32/tests/tmarshal.c @@ -17,6 +17,7 @@ * */
+#include <stdbool.h> #include <math.h>
#define COBJMACROS @@ -394,6 +395,7 @@ struct disp_obj { ISomethingFromDispatch ISomethingFromDispatch_iface; LONG ref; + bool support_idispatch; };
static inline struct disp_obj *impl_from_ISomethingFromDispatch(ISomethingFromDispatch *iface) @@ -403,6 +405,11 @@ static inline struct disp_obj *impl_from_ISomethingFromDispatch(ISomethingFromDi
static HRESULT WINAPI disp_obj_QueryInterface(ISomethingFromDispatch *iface, REFIID iid, void **out) { + struct disp_obj *obj = impl_from_ISomethingFromDispatch(iface); + + if (!obj->support_idispatch) + ok(!IsEqualGUID(iid, &IID_IDispatch), "Expected no query for IDispatch.\n"); + if (IsEqualGUID(iid, &IID_IUnknown) || IsEqualGUID(iid, &IID_IDispatch) || IsEqualGUID(iid, &IID_ISomethingFromDispatch) || IsEqualGUID(iid, &DIID_ItestIF4)) @@ -475,14 +482,20 @@ static const ISomethingFromDispatchVtbl disp_obj_vtbl = disp_obj_anotherfn, };
-static ISomethingFromDispatch *create_disp_obj(void) +static ISomethingFromDispatch *create_disp_obj2(bool support_idispatch) { struct disp_obj *obj = CoTaskMemAlloc(sizeof(*obj)); obj->ISomethingFromDispatch_iface.lpVtbl = &disp_obj_vtbl; obj->ref = 1; + obj->support_idispatch = support_idispatch; return &obj->ISomethingFromDispatch_iface; }
+static ISomethingFromDispatch *create_disp_obj(void) +{ + return create_disp_obj2(true); +} + struct coclass_obj { ICoclass1 ICoclass1_iface; @@ -3710,7 +3723,7 @@ static void test_marshal_dispinterface(void) { static const LARGE_INTEGER zero;
- ISomethingFromDispatch *disp_obj = create_disp_obj(); + ISomethingFromDispatch *disp_obj = create_disp_obj2(false); ITypeInfo *typeinfo = NULL; IDispatch *proxy_disp; IStream *stream;