This is a resend. From now on, no more unbuildable patches. Promise :) ChangeLog: Implement support for dispatch variant marshalling Index: include/oaidl.h =================================================================== RCS file: /home/wine/wine/include/oaidl.h,v retrieving revision 1.17 diff -u -r1.17 oaidl.h --- include/oaidl.h 18 Aug 2003 19:59:47 -0000 1.17 +++ include/oaidl.h 28 Aug 2003 12:54:15 -0000 @@ -775,7 +775,7 @@ DISPID dispIdMember, REFIID riid, LCID lcid, - DWORD dwFlags, + WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult, EXCEPINFO* pExcepInfo, Index: dlls/oleaut32/usrmarshal.c =================================================================== RCS file: /home/wine/wine/dlls/oleaut32/usrmarshal.c,v retrieving revision 1.3 diff -u -r1.3 usrmarshal.c --- dlls/oleaut32/usrmarshal.c 13 May 2003 00:41:58 -0000 1.3 +++ dlls/oleaut32/usrmarshal.c 28 Aug 2003 12:54:19 -0000 @@ -2,6 +2,7 @@ * Misc marshalling routines * * Copyright 2002 Ove Kaaven + * 2003 Mike Hearn * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,6 +20,7 @@ */ #include <string.h> +#include <assert.h> #define NONAMELESSUNION #define NONAMELESSSTRUCT @@ -183,6 +185,9 @@ static unsigned wire_extra(unsigned long *pFlags, VARIANT *pvar) { + ULONG size; + HRESULT hr; + if (V_VT(pvar) & VT_ARRAY) { FIXME("wire-size safearray\n"); return 0; @@ -200,8 +205,15 @@ return VARIANT_UserSize(pFlags, 0, V_VARIANTREF(pvar)); case VT_UNKNOWN: case VT_DISPATCH: - FIXME("wire-size interfaces\n"); - return 0; + /* find the buffer size of the marshalled dispatch interface */ + hr = CoGetMarshalSizeMax(&size, &IID_IDispatch, (IUnknown*)V_DISPATCH(pvar), LOWORD(*pFlags), NULL, MSHLFLAGS_NORMAL); + if (FAILED(hr)) { + ERR("Dispatch variant buffer size calculation failed, HRESULT=0x%lx\n", hr); + return 0; + } + size += sizeof(ULONG); /* we have to store the buffersize in the stream */ + TRACE("wire-size extra of dispatch variant is %ld\n", size); + return size; case VT_RECORD: FIXME("wire-size record\n"); return 0; @@ -210,6 +222,83 @@ } } +/* helper: called for VT_DISPATCH variants to marshal the IDispatch* into the buffer */ +static unsigned char* dispatch_variant_marshal(unsigned long *pFlags, unsigned char *Buffer, VARIANT *pvar) { + IStream *working; + HGLOBAL working_mem; + void *working_memlocked; + ULONG size; + HRESULT hr; + + TRACE("pFlags=%ld, Buffer=%p, pvar=%p\n", *pFlags, Buffer, pvar); + + /* CoMarshalInterface needs a stream, whereas at this level we are operating in terms of buffers. + * We create a stream on an HGLOBAL, so we can simply do a memcpy to move it to the buffer. + * in rpcrt4/ndr_ole.c, a simple IStream implementation is wrapped around the buffer object, + * but that would be overkill here, hence this implementation. We save the size because the unmarshal + * code has no way to know how long the marshalled buffer is. */ + + size = wire_extra(pFlags, pvar); + + working_mem = GlobalAlloc(0, size); + assert( working_mem != NULL ); + + hr = CreateStreamOnHGlobal(working_mem, FALSE, &working); + assert( hr == S_OK ); + + hr = CoMarshalInterface(working, &IID_IDispatch, (IUnknown*)V_DISPATCH(pvar), LOWORD(*pFlags), NULL, MSHLFLAGS_NORMAL); + assert( hr == S_OK ); + + IStream_Release(working); + + working_memlocked = GlobalLock(working_mem); + memcpy(Buffer, &size, sizeof(ULONG)); /* copy the buffersize */ + Buffer += sizeof(ULONG); + memcpy(Buffer, working_memlocked, size); + + GlobalUnlock(working_mem); + GlobalFree(working_mem); + TRACE("done, size=%ld\n", sizeof(ULONG) + size); + return Buffer + sizeof(ULONG) + size; +} + +/* helper: called for VT_DISPATCH variants to unmarshal the buffer back into a dispatch variant */ +static unsigned char* dispatch_variant_unmarshal(unsigned long *pFlags, unsigned char *Buffer, VARIANT *pvar) { + IStream *working; + HGLOBAL working_mem; + void *working_memlocked; + ULONG size; + HRESULT hr; + + TRACE("pFlags=%ld, Buffer=%p, pvar=%p\n", *pFlags, Buffer, pvar); + + /* get the buffersize */ + memcpy(&size, Buffer, sizeof(ULONG)); + TRACE("buffersize=%ld\n", size); + Buffer += sizeof(ULONG); + + working_mem = GlobalAlloc(0, size); + assert( working_mem != NULL ); + + hr = CreateStreamOnHGlobal(working_mem, TRUE, &working); + assert( hr == S_OK ); + + working_memlocked = GlobalLock(working_mem); + + /* now we copy the contents of the marshalling buffer to working_memlocked, unlock it, and demarshal the stream */ + memcpy(working_memlocked, Buffer, size); + GlobalUnlock(working_mem); + + hr = CoUnmarshalInterface(working, &IID_IDispatch, (void**)&V_DISPATCH(pvar)); + assert( hr == S_OK ); + + IStream_Release(working); /* this also frees the underlying hglobal */ + + TRACE("done, processed=%ld bytes\n", sizeof(ULONG) + size); + return Buffer + sizeof(ULONG) + size; +} + + unsigned long WINAPI VARIANT_UserSize(unsigned long *pFlags, unsigned long Start, VARIANT *pvar) { TRACE("(%lx,%ld,%p)\n", *pFlags, Start, pvar); @@ -266,6 +355,13 @@ case VT_VARIANT | VT_BYREF: Pos = VARIANT_UserMarshal(pFlags, Pos, V_VARIANTREF(pvar)); break; + case VT_DISPATCH | VT_BYREF: + FIXME("handle DISPATCH by ref\n"); + break; + case VT_DISPATCH: + /* this should probably call WdtpInterfacePointer_UserMarshal in ole32.dll */ + Pos = dispatch_variant_marshal(pFlags, Pos, pvar); + break; case VT_RECORD: FIXME("handle BRECORD by val\n"); break; @@ -334,6 +430,9 @@ case VT_RECORD | VT_BYREF: FIXME("handle BRECORD by ref\n"); break; + case VT_DISPATCH: + Pos = dispatch_variant_unmarshal(pFlags, Pos, pvar); + break; default: FIXME("handle unknown complex type\n"); break; @@ -401,6 +500,7 @@ lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); + TRACE("riid=%p\n", riid); /* [out] args can't be null, use dummy vars if needed */ if (!pVarResult) pVarResult = &VarResult; @@ -461,11 +561,11 @@ DISPID dispIdMember, REFIID riid, LCID lcid, - DWORD dwFlags, + WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult, EXCEPINFO* pExcepInfo, - UINT* pArgErr, + UINT* puArgErr, UINT cVarRef, UINT* rgVarRefIdx, VARIANTARG* rgVarRef) @@ -474,32 +574,41 @@ VARIANTARG *rgvarg, *arg; UINT u; + TRACE("(%p)->(%ld,%s,%lx,%x,%p,%p,%p,%p)\n", This, + dispIdMember, debugstr_guid(riid), + lcid, wFlags, pDispParams, pVarResult, + pExcepInfo, puArgErr); + /* let the real Invoke operate on a copy of the in parameters, * so we don't risk losing pointers to allocated memory */ rgvarg = pDispParams->rgvarg; arg = CoTaskMemAlloc(sizeof(VARIANTARG)*pDispParams->cArgs); for (u=0; u<pDispParams->cArgs; u++) { + TRACE("marshalling\n"); VariantInit(&arg[u]); VariantCopy(&arg[u], &rgvarg[u]); } + TRACE("done\n"); pDispParams->rgvarg = arg; /* initialize out parameters, so that they can be marshalled * in case the real Invoke doesn't initialize them */ VariantInit(pVarResult); memset(pExcepInfo, 0, sizeof(*pExcepInfo)); - *pArgErr = 0; + *puArgErr = 0; + TRACE("calling invoke\n"); hr = IDispatch_Invoke(This, dispIdMember, riid, lcid, - dwFlags, + wFlags, pDispParams, pVarResult, pExcepInfo, - pArgErr); + puArgErr); + TRACE("returned\n"); /* copy ref args to out list */ for (u=0; u<cVarRef; u++) { unsigned i = rgVarRefIdx[u];