From f9e9012e75afe4418f4ebe652fa99a1fa2348a71 Mon Sep 17 00:00:00 2001 From: Tor Lillqvist Date: Mon, 11 Jun 2018 19:53:06 +0300 Subject: Support named arguments in InterfaceOleWrapper::Invoke() Convert a DISPPARAMS with named arguments into one with just positional ones. If there are gaps, use markers for them (VARIANTs with VT_ERROR:DISP_E_PARAMNOTFOUND). Those are then passed on as empty UNO Anys. Change-Id: Iad1197ba2559567a9c0eca4524e76389c2048fec Reviewed-on: https://gerrit.libreoffice.org/55646 Tested-by: Jenkins Reviewed-by: Tor Lillqvist --- extensions/source/ole/unoobjw.cxx | 97 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 91 insertions(+), 6 deletions(-) (limited to 'extensions') diff --git a/extensions/source/ole/unoobjw.cxx b/extensions/source/ole/unoobjw.cxx index f04254ed0edf..123a4a914412 100644 --- a/extensions/source/ole/unoobjw.cxx +++ b/extensions/source/ole/unoobjw.cxx @@ -1225,6 +1225,10 @@ STDMETHODIMP InterfaceOleWrapper::GetIDsOfNames(REFIID /*riid*/, return ret; } +// Note: What the comments here say about JScript possibly holds for Automation clients in general, +// like VBScript ones, too. Or not. Hard to say. What is the relevance of JScript nowadays anyway, +// and can LO really be used from JScript code on web pages any longer? + // "convertDispparamsArgs" converts VARIANTS to their respecting Any counterparts // The parameters "id", "wFlags" and "pdispparams" equal those as used in // IDispatch::Invoke. The function handles special JavaScript @@ -1331,6 +1335,16 @@ void InterfaceOleWrapper::convertDispparamsArgs(DISPID id, if (i < countIncomingArgs) { + // A missing (and hopefully optional) arg (in the middle of the argument list) is passed + // as an empty Any. + if (pdispparams->rgvarg[i].vt == VT_ERROR && pdispparams->rgvarg[i].scode == DISP_E_PARAMNOTFOUND) + { + Any aEmpty; + pParams[ outgoingArgIndex ] = aEmpty; + outgoingArgIndex++; + continue; + } + if(convertValueObject( & pdispparams->rgvarg[i], anyParam)) { //a param is a ValueObject and could be converted pParams[ outgoingArgIndex ] = anyParam; @@ -1825,17 +1839,88 @@ STDMETHODIMP InterfaceOleWrapper::Invoke(DISPID dispidMember, { if ((flags & DISPATCH_METHOD) != 0) { + std::unique_ptr pNewDispParams; + std::vector vNewArgs; + if (pdispparams->cNamedArgs > 0) - ret = DISP_E_NONAMEDARGS; - else { - Sequence params; + // Convert named arguments to positional ones. + + // An example: + // + // Function declaration (in pseudo-code): + // int foo(int A, int B, optional int C, optional int D, optional int E, optional int F, optional int G) + // + // Corresponding parameter numbers (DISPIDs): + // 0 1 2 3 4 5 6 + // + // Actual call: + // foo(10, 20, E:=50, D:=40, F:=60) + // + // That is, A and B are pased positionally, D, E, and F as named arguments, + // and the optional C and G parameters are left out. + // + // Incoming DISPPARAMS: + // cArgs=5, cNamedArgs=3 + // rgvarg: [60, 40, 50, 20, 10] + // rgdispidNamedArgs: [5, 3, 4] + // + // We calculate nLowestNamedArgDispid = 3 and nHighestNamedArgDispid = 5. + // + // Result of conversion, no named args: + // cArgs=6, cNamedArgs=0 + // rgvarg: [60, 50, 40, DISP_E_PARAMNOTFOUND, 20, 10] + + // First find the lowest and highest DISPID of the named arguments. + DISPID nLowestNamedArgDispid = 1000000; + DISPID nHighestNamedArgDispid = -1; + for (unsigned int i = 0; i < pdispparams->cNamedArgs; ++i) + { + if (pdispparams->rgdispidNamedArgs[i] < nLowestNamedArgDispid) + nLowestNamedArgDispid = pdispparams->rgdispidNamedArgs[i]; + if (pdispparams->rgdispidNamedArgs[i] > nHighestNamedArgDispid) + nHighestNamedArgDispid = pdispparams->rgdispidNamedArgs[i]; + } + + // Make sure named arguments don't overlap with positional ones. The lowest + // DISPID of the named arguments should be >= the number of positional + // arguments. + if (nLowestNamedArgDispid < static_cast(pdispparams->cArgs - pdispparams->cNamedArgs)) + return DISP_E_NONAMEDARGS; + + // Do the actual conversion. + pNewDispParams.reset(new DISPPARAMS); + vNewArgs.resize(nHighestNamedArgDispid + 1); + pNewDispParams->rgvarg = vNewArgs.data(); + pNewDispParams->rgdispidNamedArgs = nullptr; + pNewDispParams->cArgs = nHighestNamedArgDispid + 1; + pNewDispParams->cNamedArgs = 0; + + // Initialise all parameter slots as missing + for (int i = 0; i < nHighestNamedArgDispid; ++i) + { + pNewDispParams->rgvarg[i].vt = VT_ERROR; + pNewDispParams->rgvarg[i].scode = DISP_E_PARAMNOTFOUND; + } + + // Then set the value of those actually present. + for (unsigned int i = 0; i < pdispparams->cNamedArgs; ++i) + pNewDispParams->rgvarg[nHighestNamedArgDispid - pdispparams->rgdispidNamedArgs[i]] = pdispparams->rgvarg[i]; - convertDispparamsArgs(dispidMember, wFlags, pdispparams , params ); + const int nFirstUnnamedArg = pdispparams->cNamedArgs + (nLowestNamedArgDispid-(pdispparams->cArgs - pdispparams->cNamedArgs)); - ret= doInvoke(pdispparams, pvarResult, - pexcepinfo, puArgErr, d.name, params); + for (unsigned int i = pdispparams->cNamedArgs; i < pdispparams->cArgs; ++i) + pNewDispParams->rgvarg[nFirstUnnamedArg + (i-pdispparams->cNamedArgs)] = pdispparams->rgvarg[i]; + + pdispparams = pNewDispParams.get(); } + + Sequence params; + + convertDispparamsArgs(dispidMember, wFlags, pdispparams , params ); + + ret= doInvoke(pdispparams, pvarResult, + pexcepinfo, puArgErr, d.name, params); } else if ((flags & DISPATCH_PROPERTYGET) != 0) { -- cgit