| /* |
| * Misc marshaling routines |
| * |
| * 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 |
| */ |
| #include <stdarg.h> |
| #include <string.h> |
| |
| #define COBJMACROS |
| |
| #include "windef.h" |
| #include "winbase.h" |
| #include "wingdi.h" |
| #include "winuser.h" |
| #include "winerror.h" |
| #include "objbase.h" |
| #include "oleauto.h" |
| #include "dispex.h" |
| |
| #include "wine/debug.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(ole); |
| |
| #define NULL_RESULT 0x20000 |
| #define NULL_EXCEPINFO 0x40000 |
| |
| HRESULT CALLBACK IDispatchEx_InvokeEx_Proxy(IDispatchEx* This, DISPID id, LCID lcid, WORD wFlags, |
| DISPPARAMS *pdp, VARIANT *pvarRes, EXCEPINFO *pei, |
| IServiceProvider *pspCaller) |
| { |
| HRESULT hr; |
| VARIANT result; |
| EXCEPINFO excep_info; |
| UINT byref_args, arg, dummy_idx; |
| VARIANT dummy_arg, *ref_arg = &dummy_arg, *copy_arg, *orig_arg = NULL; |
| UINT *ref_idx = &dummy_idx; |
| DWORD dword_flags = wFlags & 0xf; |
| |
| TRACE("(%p)->(%08x, %04x, %04x, %p, %p, %p, %p)\n", This, id, lcid, wFlags, |
| pdp, pvarRes, pei, pspCaller); |
| |
| if(!pvarRes) |
| { |
| pvarRes = &result; |
| dword_flags |= NULL_RESULT; |
| } |
| |
| if(!pei) |
| { |
| pei = &excep_info; |
| dword_flags |= NULL_EXCEPINFO; |
| } |
| |
| for(arg = 0, byref_args = 0; arg < pdp->cArgs; arg++) |
| if(V_ISBYREF(pdp->rgvarg + arg)) byref_args++; |
| |
| if(byref_args) |
| { |
| DWORD size = pdp->cArgs * sizeof(VARIANT) + |
| byref_args * (sizeof(VARIANT) + sizeof(UINT)); |
| |
| copy_arg = CoTaskMemAlloc(size); |
| if(!copy_arg) return E_OUTOFMEMORY; |
| |
| ref_arg = copy_arg + pdp->cArgs; |
| ref_idx = (UINT*)(ref_arg + byref_args); |
| |
| /* copy the byref args to ref_arg[], the others go to copy_arg[] */ |
| for(arg = 0, byref_args = 0; arg < pdp->cArgs; arg++) |
| { |
| if(V_ISBYREF(pdp->rgvarg + arg)) |
| { |
| ref_arg[byref_args] = pdp->rgvarg[arg]; |
| ref_idx[byref_args] = arg; |
| VariantInit(copy_arg + arg); |
| byref_args++; |
| } |
| else |
| copy_arg[arg] = pdp->rgvarg[arg]; |
| } |
| |
| orig_arg = pdp->rgvarg; |
| pdp->rgvarg = copy_arg; |
| } |
| |
| hr = IDispatchEx_RemoteInvokeEx_Proxy(This, id, lcid, dword_flags, pdp, pvarRes, pei, pspCaller, |
| byref_args, ref_idx, ref_arg); |
| |
| if(byref_args) |
| { |
| CoTaskMemFree(pdp->rgvarg); |
| pdp->rgvarg = orig_arg; |
| } |
| |
| return hr; |
| } |
| |
| HRESULT __RPC_STUB IDispatchEx_InvokeEx_Stub(IDispatchEx* This, DISPID id, LCID lcid, DWORD dwFlags, |
| DISPPARAMS *pdp, VARIANT *result, EXCEPINFO *pei, |
| IServiceProvider *pspCaller, UINT byref_args, |
| UINT *ref_idx, VARIANT *ref_arg) |
| { |
| HRESULT hr; |
| UINT arg; |
| VARTYPE *vt_list = NULL; |
| |
| TRACE("(%p)->(%08x, %04x, %08x, %p, %p, %p, %p, %d, %p, %p)\n", This, id, lcid, dwFlags, |
| pdp, result, pei, pspCaller, byref_args, ref_idx, ref_arg); |
| |
| VariantInit(result); |
| memset(pei, 0, sizeof(*pei)); |
| |
| for(arg = 0; arg < byref_args; arg++) |
| pdp->rgvarg[ref_idx[arg]] = ref_arg[arg]; |
| |
| if(dwFlags & NULL_RESULT) result = NULL; |
| if(dwFlags & NULL_EXCEPINFO) pei = NULL; |
| |
| /* Create an array of the original VTs to check that the function doesn't change |
| any on return. */ |
| if(byref_args) |
| { |
| vt_list = HeapAlloc(GetProcessHeap(), 0, pdp->cArgs * sizeof(vt_list[0])); |
| if(!vt_list) return E_OUTOFMEMORY; |
| for(arg = 0; arg < pdp->cArgs; arg++) |
| vt_list[arg] = V_VT(pdp->rgvarg + arg); |
| } |
| |
| hr = IDispatchEx_InvokeEx(This, id, lcid, dwFlags & 0xffff, pdp, result, pei, pspCaller); |
| |
| if(SUCCEEDED(hr) && byref_args) |
| { |
| for(arg = 0; arg < pdp->cArgs; arg++) |
| { |
| if(vt_list[arg] != V_VT(pdp->rgvarg + arg)) |
| { |
| hr = DISP_E_BADCALLEE; |
| break; |
| } |
| } |
| } |
| |
| if(hr == DISP_E_EXCEPTION) |
| { |
| if(pei && pei->pfnDeferredFillIn) |
| { |
| pei->pfnDeferredFillIn(pei); |
| pei->pfnDeferredFillIn = NULL; |
| } |
| } |
| |
| for(arg = 0; arg < byref_args; arg++) |
| VariantInit(pdp->rgvarg + ref_idx[arg]); |
| |
| HeapFree(GetProcessHeap(), 0, vt_list); |
| return hr; |
| } |