/* * TYPELIB Marshaler * * Copyright 2002,2005 Marcus Meissner * * The olerelay debug channel allows you to see calls marshalled by * the typelib marshaller. It is not a generic COM relaying system. * * 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 "config.h" #include "wine/port.h" #include #include #include #include #include #include #define COBJMACROS #define NONAMELESSUNION #define NONAMELESSSTRUCT #include "winerror.h" #include "windef.h" #include "winbase.h" #include "winnls.h" #include "winreg.h" #include "winuser.h" #include "ole2.h" #include "propidl.h" /* for LPSAFEARRAY_User* functions */ #include "typelib.h" #include "variant.h" #include "wine/debug.h" #include "wine/exception.h" static const WCHAR IDispatchW[] = { 'I','D','i','s','p','a','t','c','h',0}; WINE_DEFAULT_DEBUG_CHANNEL(ole); WINE_DECLARE_DEBUG_CHANNEL(olerelay); #define ICOM_THIS_MULTI(impl,field,iface) impl* const This=(impl*)((char*)(iface) - offsetof(impl,field)) static HRESULT TMarshalDispatchChannel_Create( IRpcChannelBuffer *pDelegateChannel, REFIID tmarshal_riid, IRpcChannelBuffer **ppChannel); typedef struct _marshal_state { LPBYTE base; int size; int curoff; } marshal_state; /* used in the olerelay code to avoid having the L"" stuff added by debugstr_w */ static char *relaystr(WCHAR *in) { char *tmp = (char *)debugstr_w(in); tmp += 2; tmp[strlen(tmp)-1] = '\0'; return tmp; } static HRESULT xbuf_resize(marshal_state *buf, DWORD newsize) { if(buf->size >= newsize) return S_FALSE; if(buf->base) { buf->base = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, buf->base, newsize); if(!buf->base) return E_OUTOFMEMORY; } else { buf->base = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, newsize); if(!buf->base) return E_OUTOFMEMORY; } buf->size = newsize; return S_OK; } static HRESULT xbuf_add(marshal_state *buf, const BYTE *stuff, DWORD size) { HRESULT hr; if(buf->size - buf->curoff < size) { hr = xbuf_resize(buf, buf->size + size + 100); if(FAILED(hr)) return hr; } memcpy(buf->base+buf->curoff,stuff,size); buf->curoff += size; return S_OK; } static HRESULT xbuf_get(marshal_state *buf, LPBYTE stuff, DWORD size) { if (buf->size < buf->curoff+size) return E_FAIL; memcpy(stuff,buf->base+buf->curoff,size); buf->curoff += size; return S_OK; } static HRESULT xbuf_skip(marshal_state *buf, DWORD size) { if (buf->size < buf->curoff+size) return E_FAIL; buf->curoff += size; return S_OK; } static HRESULT _unmarshal_interface(marshal_state *buf, REFIID riid, LPUNKNOWN *pUnk) { IStream *pStm; ULARGE_INTEGER newpos; LARGE_INTEGER seekto; ULONG res; HRESULT hres; DWORD xsize; TRACE("...%s...\n",debugstr_guid(riid)); *pUnk = NULL; hres = xbuf_get(buf,(LPBYTE)&xsize,sizeof(xsize)); if (hres) { ERR("xbuf_get failed\n"); return hres; } if (xsize == 0) return S_OK; hres = CreateStreamOnHGlobal(0,TRUE,&pStm); if (hres) { ERR("Stream create failed %x\n",hres); return hres; } hres = IStream_Write(pStm,buf->base+buf->curoff,xsize,&res); if (hres) { ERR("stream write %x\n",hres); return hres; } memset(&seekto,0,sizeof(seekto)); hres = IStream_Seek(pStm,seekto,SEEK_SET,&newpos); if (hres) { ERR("Failed Seek %x\n",hres); return hres; } hres = CoUnmarshalInterface(pStm,riid,(LPVOID*)pUnk); if (hres) { ERR("Unmarshalling interface %s failed with %x\n",debugstr_guid(riid),hres); return hres; } IStream_Release(pStm); return xbuf_skip(buf,xsize); } static HRESULT _marshal_interface(marshal_state *buf, REFIID riid, LPUNKNOWN pUnk) { LPBYTE tempbuf = NULL; IStream *pStm = NULL; STATSTG ststg; ULARGE_INTEGER newpos; LARGE_INTEGER seekto; ULONG res; DWORD xsize; HRESULT hres; if (!pUnk) { /* this is valid, if for instance we serialize * a VT_DISPATCH with NULL ptr which apparently * can happen. S_OK to make sure we continue * serializing. */ WARN("pUnk is NULL\n"); xsize = 0; return xbuf_add(buf,(LPBYTE)&xsize,sizeof(xsize)); } hres = E_FAIL; TRACE("...%s...\n",debugstr_guid(riid)); hres = CreateStreamOnHGlobal(0,TRUE,&pStm); if (hres) { ERR("Stream create failed %x\n",hres); goto fail; } hres = CoMarshalInterface(pStm,riid,pUnk,0,NULL,0); if (hres) { ERR("Marshalling interface %s failed with %x\n", debugstr_guid(riid), hres); goto fail; } hres = IStream_Stat(pStm,&ststg,STATFLAG_NONAME); if (hres) { ERR("Stream stat failed\n"); goto fail; } tempbuf = HeapAlloc(GetProcessHeap(), 0, ststg.cbSize.u.LowPart); memset(&seekto,0,sizeof(seekto)); hres = IStream_Seek(pStm,seekto,SEEK_SET,&newpos); if (hres) { ERR("Failed Seek %x\n",hres); goto fail; } hres = IStream_Read(pStm,tempbuf,ststg.cbSize.u.LowPart,&res); if (hres) { ERR("Failed Read %x\n",hres); goto fail; } xsize = ststg.cbSize.u.LowPart; xbuf_add(buf,(LPBYTE)&xsize,sizeof(xsize)); hres = xbuf_add(buf,tempbuf,ststg.cbSize.u.LowPart); HeapFree(GetProcessHeap(),0,tempbuf); IStream_Release(pStm); return hres; fail: xsize = 0; xbuf_add(buf,(LPBYTE)&xsize,sizeof(xsize)); if (pStm) IUnknown_Release(pStm); HeapFree(GetProcessHeap(), 0, tempbuf); return hres; } /********************* OLE Proxy/Stub Factory ********************************/ static HRESULT WINAPI PSFacBuf_QueryInterface(LPPSFACTORYBUFFER iface, REFIID iid, LPVOID *ppv) { if (IsEqualIID(iid,&IID_IPSFactoryBuffer)||IsEqualIID(iid,&IID_IUnknown)) { *ppv = iface; /* No ref counting, static class */ return S_OK; } FIXME("(%s) unknown IID?\n",debugstr_guid(iid)); return E_NOINTERFACE; } static ULONG WINAPI PSFacBuf_AddRef(LPPSFACTORYBUFFER iface) { return 2; } static ULONG WINAPI PSFacBuf_Release(LPPSFACTORYBUFFER iface) { return 1; } static HRESULT _get_typeinfo_for_iid(REFIID riid, ITypeInfo**ti) { HRESULT hres; HKEY ikey; char tlguid[200],typelibkey[300],interfacekey[300],ver[100]; char tlfn[260]; OLECHAR tlfnW[260]; DWORD tlguidlen, verlen, type; LONG tlfnlen; ITypeLib *tl; sprintf( interfacekey, "Interface\\{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\\Typelib", riid->Data1, riid->Data2, riid->Data3, riid->Data4[0], riid->Data4[1], riid->Data4[2], riid->Data4[3], riid->Data4[4], riid->Data4[5], riid->Data4[6], riid->Data4[7] ); if (RegOpenKeyA(HKEY_CLASSES_ROOT,interfacekey,&ikey)) { ERR("No %s key found.\n",interfacekey); return E_FAIL; } tlguidlen = sizeof(tlguid); if (RegQueryValueExA(ikey,NULL,NULL,&type,(LPBYTE)tlguid,&tlguidlen)) { ERR("Getting typelib guid failed.\n"); RegCloseKey(ikey); return E_FAIL; } verlen = sizeof(ver); if (RegQueryValueExA(ikey,"Version",NULL,&type,(LPBYTE)ver,&verlen)) { ERR("Could not get version value?\n"); RegCloseKey(ikey); return E_FAIL; } RegCloseKey(ikey); sprintf(typelibkey,"Typelib\\%s\\%s\\0\\win%u",tlguid,ver,(sizeof(void*) == 8) ? 64 : 32); tlfnlen = sizeof(tlfn); if (RegQueryValueA(HKEY_CLASSES_ROOT,typelibkey,tlfn,&tlfnlen)) { ERR("Could not get typelib fn?\n"); return E_FAIL; } MultiByteToWideChar(CP_ACP, 0, tlfn, -1, tlfnW, sizeof(tlfnW) / sizeof(tlfnW[0])); hres = LoadTypeLib(tlfnW,&tl); if (hres) { ERR("Failed to load typelib for %s, but it should be there.\n",debugstr_guid(riid)); return hres; } hres = ITypeLib_GetTypeInfoOfGuid(tl,riid,ti); if (hres) { ERR("typelib does not contain info for %s?\n",debugstr_guid(riid)); ITypeLib_Release(tl); return hres; } ITypeLib_Release(tl); return hres; } /* * Determine the number of functions including all inherited functions. * Note for non-dual dispinterfaces we simply return the size of IDispatch. */ static HRESULT num_of_funcs(ITypeInfo *tinfo, unsigned int *num) { HRESULT hres; TYPEATTR *attr; ITypeInfo *tinfo2; *num = 0; hres = ITypeInfo_GetTypeAttr(tinfo, &attr); if (hres) { ERR("GetTypeAttr failed with %x\n",hres); return hres; } if(attr->typekind == TKIND_DISPATCH && (attr->wTypeFlags & TYPEFLAG_FDUAL)) { HREFTYPE href; hres = ITypeInfo_GetRefTypeOfImplType(tinfo, -1, &href); if(FAILED(hres)) { ERR("Unable to get interface href from dual dispinterface\n"); goto end; } hres = ITypeInfo_GetRefTypeInfo(tinfo, href, &tinfo2); if(FAILED(hres)) { ERR("Unable to get interface from dual dispinterface\n"); goto end; } hres = num_of_funcs(tinfo2, num); ITypeInfo_Release(tinfo2); } else { *num = attr->cbSizeVft / 4; } end: ITypeInfo_ReleaseTypeAttr(tinfo, attr); return hres; } #ifdef __i386__ #include "pshpack1.h" typedef struct _TMAsmProxy { BYTE popleax; BYTE pushlval; DWORD nr; BYTE pushleax; BYTE lcall; DWORD xcall; BYTE lret; WORD bytestopop; BYTE nop; } TMAsmProxy; #include "poppack.h" #else /* __i386__ */ # warning You need to implement stubless proxies for your architecture typedef struct _TMAsmProxy { } TMAsmProxy; #endif typedef struct _TMProxyImpl { LPVOID *lpvtbl; const IRpcProxyBufferVtbl *lpvtbl2; LONG ref; TMAsmProxy *asmstubs; ITypeInfo* tinfo; IRpcChannelBuffer* chanbuf; IID iid; CRITICAL_SECTION crit; IUnknown *outerunknown; IDispatch *dispatch; IRpcProxyBuffer *dispatch_proxy; } TMProxyImpl; static HRESULT WINAPI TMProxyImpl_QueryInterface(LPRPCPROXYBUFFER iface, REFIID riid, LPVOID *ppv) { TRACE("()\n"); if (IsEqualIID(riid,&IID_IUnknown)||IsEqualIID(riid,&IID_IRpcProxyBuffer)) { *ppv = iface; IRpcProxyBuffer_AddRef(iface); return S_OK; } FIXME("no interface for %s\n",debugstr_guid(riid)); return E_NOINTERFACE; } static ULONG WINAPI TMProxyImpl_AddRef(LPRPCPROXYBUFFER iface) { ICOM_THIS_MULTI(TMProxyImpl,lpvtbl2,iface); ULONG refCount = InterlockedIncrement(&This->ref); TRACE("(%p)->(ref before=%u)\n",This, refCount - 1); return refCount; } static ULONG WINAPI TMProxyImpl_Release(LPRPCPROXYBUFFER iface) { ICOM_THIS_MULTI(TMProxyImpl,lpvtbl2,iface); ULONG refCount = InterlockedDecrement(&This->ref); TRACE("(%p)->(ref before=%u)\n",This, refCount + 1); if (!refCount) { if (This->dispatch_proxy) IRpcProxyBuffer_Release(This->dispatch_proxy); This->crit.DebugInfo->Spare[0] = 0; DeleteCriticalSection(&This->crit); if (This->chanbuf) IRpcChannelBuffer_Release(This->chanbuf); VirtualFree(This->asmstubs, 0, MEM_RELEASE); HeapFree(GetProcessHeap(), 0, This->lpvtbl); ITypeInfo_Release(This->tinfo); CoTaskMemFree(This); } return refCount; } static HRESULT WINAPI TMProxyImpl_Connect( LPRPCPROXYBUFFER iface,IRpcChannelBuffer* pRpcChannelBuffer) { ICOM_THIS_MULTI(TMProxyImpl, lpvtbl2, iface); TRACE("(%p)\n", pRpcChannelBuffer); EnterCriticalSection(&This->crit); IRpcChannelBuffer_AddRef(pRpcChannelBuffer); This->chanbuf = pRpcChannelBuffer; LeaveCriticalSection(&This->crit); if (This->dispatch_proxy) { IRpcChannelBuffer *pDelegateChannel; HRESULT hr = TMarshalDispatchChannel_Create(pRpcChannelBuffer, &This->iid, &pDelegateChannel); if (FAILED(hr)) return hr; hr = IRpcProxyBuffer_Connect(This->dispatch_proxy, pDelegateChannel); IRpcChannelBuffer_Release(pDelegateChannel); return hr; } return S_OK; } static void WINAPI TMProxyImpl_Disconnect(LPRPCPROXYBUFFER iface) { ICOM_THIS_MULTI(TMProxyImpl, lpvtbl2, iface); TRACE("()\n"); EnterCriticalSection(&This->crit); IRpcChannelBuffer_Release(This->chanbuf); This->chanbuf = NULL; LeaveCriticalSection(&This->crit); if (This->dispatch_proxy) IRpcProxyBuffer_Disconnect(This->dispatch_proxy); } static const IRpcProxyBufferVtbl tmproxyvtable = { TMProxyImpl_QueryInterface, TMProxyImpl_AddRef, TMProxyImpl_Release, TMProxyImpl_Connect, TMProxyImpl_Disconnect }; /* how much space do we use on stack in DWORD steps. */ static int _argsize(TYPEDESC *tdesc, ITypeInfo *tinfo) { switch (tdesc->vt) { case VT_I8: case VT_UI8: return 8/sizeof(DWORD); case VT_R8: return sizeof(double)/sizeof(DWORD); case VT_CY: return sizeof(CY)/sizeof(DWORD); case VT_DATE: return sizeof(DATE)/sizeof(DWORD); case VT_DECIMAL: return (sizeof(DECIMAL)+3)/sizeof(DWORD); case VT_VARIANT: return (sizeof(VARIANT)+3)/sizeof(DWORD); case VT_USERDEFINED: { ITypeInfo *tinfo2; TYPEATTR *tattr; HRESULT hres; DWORD ret; hres = ITypeInfo_GetRefTypeInfo(tinfo,tdesc->u.hreftype,&tinfo2); if (FAILED(hres)) return 0; /* should fail critically in serialize_param */ ITypeInfo_GetTypeAttr(tinfo2,&tattr); ret = (tattr->cbSizeInstance+3)/sizeof(DWORD); ITypeInfo_ReleaseTypeAttr(tinfo2, tattr); ITypeInfo_Release(tinfo2); return ret; } default: return 1; } } /* how much space do we use on the heap (in bytes) */ static int _xsize(const TYPEDESC *td, ITypeInfo *tinfo) { switch (td->vt) { case VT_DATE: return sizeof(DATE); case VT_CY: return sizeof(CY); /* FIXME: VT_BOOL should return 2? */ case VT_VARIANT: return sizeof(VARIANT)+3; /* FIXME: why the +3? */ case VT_CARRAY: { int i, arrsize = 1; const ARRAYDESC *adesc = td->u.lpadesc; for (i=0;icDims;i++) arrsize *= adesc->rgbounds[i].cElements; return arrsize*_xsize(&adesc->tdescElem, tinfo); } case VT_UI8: case VT_I8: case VT_R8: return 8; case VT_UI2: case VT_I2: return 2; case VT_UI1: case VT_I1: return 1; case VT_USERDEFINED: { ITypeInfo *tinfo2; TYPEATTR *tattr; HRESULT hres; DWORD ret; hres = ITypeInfo_GetRefTypeInfo(tinfo,td->u.hreftype,&tinfo2); if (FAILED(hres)) return 0; ITypeInfo_GetTypeAttr(tinfo2,&tattr); ret = tattr->cbSizeInstance; ITypeInfo_ReleaseTypeAttr(tinfo2, tattr); ITypeInfo_Release(tinfo2); return ret; } default: return 4; } } static HRESULT serialize_param( ITypeInfo *tinfo, BOOL writeit, BOOL debugout, BOOL dealloc, TYPEDESC *tdesc, DWORD *arg, marshal_state *buf) { HRESULT hres = S_OK; VARTYPE vartype; TRACE("(tdesc.vt %s)\n",debugstr_vt(tdesc->vt)); vartype = tdesc->vt; if ((vartype & 0xf000) == VT_ARRAY) vartype = VT_SAFEARRAY; switch (vartype) { case VT_I8: case VT_UI8: case VT_R8: case VT_CY: hres = S_OK; if (debugout) TRACE_(olerelay)("%x%x\n",arg[0],arg[1]); if (writeit) hres = xbuf_add(buf,(LPBYTE)arg,8); return hres; case VT_BOOL: case VT_ERROR: case VT_INT: case VT_UINT: case VT_I4: case VT_R4: case VT_UI4: hres = S_OK; if (debugout) TRACE_(olerelay)("%x\n",*arg); if (writeit) hres = xbuf_add(buf,(LPBYTE)arg,sizeof(DWORD)); return hres; case VT_I2: case VT_UI2: hres = S_OK; if (debugout) TRACE_(olerelay)("%04x\n",*arg & 0xffff); if (writeit) hres = xbuf_add(buf,(LPBYTE)arg,sizeof(DWORD)); return hres; case VT_I1: case VT_UI1: hres = S_OK; if (debugout) TRACE_(olerelay)("%02x\n",*arg & 0xff); if (writeit) hres = xbuf_add(buf,(LPBYTE)arg,sizeof(DWORD)); return hres; case VT_VARIANT: { if (debugout) TRACE_(olerelay)("Vt(%s%s)(",debugstr_vt(V_VT((VARIANT *)arg)),debugstr_vf(V_VT((VARIANT *)arg))); if (writeit) { ULONG flags = MAKELONG(MSHCTX_DIFFERENTMACHINE, NDR_LOCAL_DATA_REPRESENTATION); ULONG size = VARIANT_UserSize(&flags, buf->curoff, (VARIANT *)arg); xbuf_resize(buf, size); VARIANT_UserMarshal(&flags, buf->base + buf->curoff, (VARIANT *)arg); buf->curoff = size; } if (dealloc) { ULONG flags = MAKELONG(MSHCTX_DIFFERENTMACHINE, NDR_LOCAL_DATA_REPRESENTATION); VARIANT_UserFree(&flags, (VARIANT *)arg); } return S_OK; } case VT_BSTR: { if (debugout) { if (*arg) TRACE_(olerelay)("%s",relaystr((WCHAR*)*arg)); else TRACE_(olerelay)(""); } if (writeit) { ULONG flags = MAKELONG(MSHCTX_DIFFERENTMACHINE, NDR_LOCAL_DATA_REPRESENTATION); ULONG size = BSTR_UserSize(&flags, buf->curoff, (BSTR *)arg); xbuf_resize(buf, size); BSTR_UserMarshal(&flags, buf->base + buf->curoff, (BSTR *)arg); buf->curoff = size; } if (dealloc) { ULONG flags = MAKELONG(MSHCTX_DIFFERENTMACHINE, NDR_LOCAL_DATA_REPRESENTATION); BSTR_UserFree(&flags, (BSTR *)arg); } return S_OK; } case VT_PTR: { DWORD cookie; BOOL derefhere = TRUE; if (tdesc->u.lptdesc->vt == VT_USERDEFINED) { ITypeInfo *tinfo2; TYPEATTR *tattr; hres = ITypeInfo_GetRefTypeInfo(tinfo,tdesc->u.lptdesc->u.hreftype,&tinfo2); if (hres) { ERR("Could not get typeinfo of hreftype %x for VT_USERDEFINED.\n",tdesc->u.lptdesc->u.hreftype); return hres; } ITypeInfo_GetTypeAttr(tinfo2,&tattr); switch (tattr->typekind) { case TKIND_ALIAS: if (tattr->tdescAlias.vt == VT_USERDEFINED) { DWORD href = tattr->tdescAlias.u.hreftype; ITypeInfo_ReleaseTypeAttr(tinfo, tattr); ITypeInfo_Release(tinfo2); hres = ITypeInfo_GetRefTypeInfo(tinfo,href,&tinfo2); if (hres) { ERR("Could not get typeinfo of hreftype %x for VT_USERDEFINED.\n",tdesc->u.lptdesc->u.hreftype); return hres; } ITypeInfo_GetTypeAttr(tinfo2,&tattr); derefhere = (tattr->typekind != TKIND_DISPATCH && tattr->typekind != TKIND_INTERFACE); } break; case TKIND_ENUM: /* confirmed */ case TKIND_RECORD: /* FIXME: mostly untested */ break; case TKIND_DISPATCH: /* will be done in VT_USERDEFINED case */ case TKIND_INTERFACE: /* will be done in VT_USERDEFINED case */ derefhere=FALSE; break; default: FIXME("unhandled switch cases tattr->typekind %d\n", tattr->typekind); derefhere=FALSE; break; } ITypeInfo_ReleaseTypeAttr(tinfo, tattr); ITypeInfo_Release(tinfo2); } if (debugout) TRACE_(olerelay)("*"); /* Write always, so the other side knows when it gets a NULL pointer. */ cookie = *arg ? 0x42424242 : 0; hres = xbuf_add(buf,(LPBYTE)&cookie,sizeof(cookie)); if (hres) return hres; if (!*arg) { if (debugout) TRACE_(olerelay)("NULL"); return S_OK; } hres = serialize_param(tinfo,writeit,debugout,dealloc,tdesc->u.lptdesc,(DWORD*)*arg,buf); if (derefhere && dealloc) HeapFree(GetProcessHeap(),0,(LPVOID)*arg); return hres; } case VT_UNKNOWN: if (debugout) TRACE_(olerelay)("unk(0x%x)",*arg); if (writeit) hres = _marshal_interface(buf,&IID_IUnknown,(LPUNKNOWN)*arg); if (dealloc && *(IUnknown **)arg) IUnknown_Release((LPUNKNOWN)*arg); return hres; case VT_DISPATCH: if (debugout) TRACE_(olerelay)("idisp(0x%x)",*arg); if (writeit) hres = _marshal_interface(buf,&IID_IDispatch,(LPUNKNOWN)*arg); if (dealloc && *(IUnknown **)arg) IUnknown_Release((LPUNKNOWN)*arg); return hres; case VT_VOID: if (debugout) TRACE_(olerelay)(""); return S_OK; case VT_USERDEFINED: { ITypeInfo *tinfo2; TYPEATTR *tattr; hres = ITypeInfo_GetRefTypeInfo(tinfo,tdesc->u.hreftype,&tinfo2); if (hres) { ERR("Could not get typeinfo of hreftype %x for VT_USERDEFINED.\n",tdesc->u.hreftype); return hres; } ITypeInfo_GetTypeAttr(tinfo2,&tattr); switch (tattr->typekind) { case TKIND_DISPATCH: case TKIND_INTERFACE: if (writeit) hres=_marshal_interface(buf,&(tattr->guid),(LPUNKNOWN)arg); if (dealloc) IUnknown_Release((LPUNKNOWN)arg); break; case TKIND_RECORD: { int i; if (debugout) TRACE_(olerelay)("{"); for (i=0;icVars;i++) { VARDESC *vdesc; ELEMDESC *elem2; TYPEDESC *tdesc2; hres = ITypeInfo2_GetVarDesc(tinfo2, i, &vdesc); if (hres) { ERR("Could not get vardesc of %d\n",i); return hres; } elem2 = &vdesc->elemdescVar; tdesc2 = &elem2->tdesc; hres = serialize_param( tinfo2, writeit, debugout, dealloc, tdesc2, (DWORD*)(((LPBYTE)arg)+vdesc->u.oInst), buf ); ITypeInfo_ReleaseVarDesc(tinfo2, vdesc); if (hres!=S_OK) return hres; if (debugout && (i<(tattr->cVars-1))) TRACE_(olerelay)(","); } if (debugout) TRACE_(olerelay)("}"); break; } case TKIND_ALIAS: hres = serialize_param(tinfo2,writeit,debugout,dealloc,&tattr->tdescAlias,arg,buf); break; case TKIND_ENUM: hres = S_OK; if (debugout) TRACE_(olerelay)("%x",*arg); if (writeit) hres = xbuf_add(buf,(LPBYTE)arg,sizeof(DWORD)); break; default: FIXME("Unhandled typekind %d\n",tattr->typekind); hres = E_FAIL; break; } ITypeInfo_ReleaseTypeAttr(tinfo2, tattr); ITypeInfo_Release(tinfo2); return hres; } case VT_CARRAY: { ARRAYDESC *adesc = tdesc->u.lpadesc; int i, arrsize = 1; if (debugout) TRACE_(olerelay)("carr"); for (i=0;icDims;i++) { if (debugout) TRACE_(olerelay)("[%d]",adesc->rgbounds[i].cElements); arrsize *= adesc->rgbounds[i].cElements; } if (debugout) TRACE_(olerelay)("(vt %s)",debugstr_vt(adesc->tdescElem.vt)); if (debugout) TRACE_(olerelay)("["); for (i=0;itdescElem, (DWORD*)((LPBYTE)(*arg)+i*_xsize(&adesc->tdescElem, tinfo)), buf); if (hres) return hres; if (debugout && (icuroff, (LPSAFEARRAY *)arg); xbuf_resize(buf, size); LPSAFEARRAY_UserMarshal(&flags, buf->base + buf->curoff, (LPSAFEARRAY *)arg); buf->curoff = size; } if (dealloc) { ULONG flags = MAKELONG(MSHCTX_DIFFERENTMACHINE, NDR_LOCAL_DATA_REPRESENTATION); LPSAFEARRAY_UserFree(&flags, (LPSAFEARRAY *)arg); } return S_OK; } default: ERR("Unhandled marshal type %d.\n",tdesc->vt); return S_OK; } } static HRESULT deserialize_param( ITypeInfo *tinfo, BOOL readit, BOOL debugout, BOOL alloc, TYPEDESC *tdesc, DWORD *arg, marshal_state *buf) { HRESULT hres = S_OK; VARTYPE vartype; TRACE("vt %s at %p\n",debugstr_vt(tdesc->vt),arg); vartype = tdesc->vt; if ((vartype & 0xf000) == VT_ARRAY) vartype = VT_SAFEARRAY; while (1) { switch (vartype) { case VT_VARIANT: { if (readit) { ULONG flags = MAKELONG(MSHCTX_DIFFERENTMACHINE, NDR_LOCAL_DATA_REPRESENTATION); unsigned char *buffer; buffer = VARIANT_UserUnmarshal(&flags, buf->base + buf->curoff, (VARIANT *)arg); buf->curoff = buffer - buf->base; } return S_OK; } case VT_I8: case VT_UI8: case VT_R8: case VT_CY: if (readit) { hres = xbuf_get(buf,(LPBYTE)arg,8); if (hres) ERR("Failed to read integer 8 byte\n"); } if (debugout) TRACE_(olerelay)("%x%x",arg[0],arg[1]); return hres; case VT_ERROR: case VT_BOOL: case VT_I4: case VT_INT: case VT_UINT: case VT_R4: case VT_UI4: if (readit) { hres = xbuf_get(buf,(LPBYTE)arg,sizeof(DWORD)); if (hres) ERR("Failed to read integer 4 byte\n"); } if (debugout) TRACE_(olerelay)("%x",*arg); return hres; case VT_I2: case VT_UI2: if (readit) { DWORD x; hres = xbuf_get(buf,(LPBYTE)&x,sizeof(DWORD)); if (hres) ERR("Failed to read integer 4 byte\n"); memcpy(arg,&x,2); } if (debugout) TRACE_(olerelay)("%04x",*arg & 0xffff); return hres; case VT_I1: case VT_UI1: if (readit) { DWORD x; hres = xbuf_get(buf,(LPBYTE)&x,sizeof(DWORD)); if (hres) ERR("Failed to read integer 4 byte\n"); memcpy(arg,&x,1); } if (debugout) TRACE_(olerelay)("%02x",*arg & 0xff); return hres; case VT_BSTR: { if (readit) { ULONG flags = MAKELONG(MSHCTX_DIFFERENTMACHINE, NDR_LOCAL_DATA_REPRESENTATION); unsigned char *buffer; buffer = BSTR_UserUnmarshal(&flags, buf->base + buf->curoff, (BSTR *)arg); buf->curoff = buffer - buf->base; if (debugout) TRACE_(olerelay)("%s",debugstr_w(*(BSTR *)arg)); } return S_OK; } case VT_PTR: { DWORD cookie; BOOL derefhere = TRUE; if (tdesc->u.lptdesc->vt == VT_USERDEFINED) { ITypeInfo *tinfo2; TYPEATTR *tattr; hres = ITypeInfo_GetRefTypeInfo(tinfo,tdesc->u.lptdesc->u.hreftype,&tinfo2); if (hres) { ERR("Could not get typeinfo of hreftype %x for VT_USERDEFINED.\n",tdesc->u.lptdesc->u.hreftype); return hres; } ITypeInfo_GetTypeAttr(tinfo2,&tattr); switch (tattr->typekind) { case TKIND_ALIAS: if (tattr->tdescAlias.vt == VT_USERDEFINED) { DWORD href = tattr->tdescAlias.u.hreftype; ITypeInfo_ReleaseTypeAttr(tinfo, tattr); ITypeInfo_Release(tinfo2); hres = ITypeInfo_GetRefTypeInfo(tinfo,href,&tinfo2); if (hres) { ERR("Could not get typeinfo of hreftype %x for VT_USERDEFINED.\n",tdesc->u.lptdesc->u.hreftype); return hres; } ITypeInfo_GetTypeAttr(tinfo2,&tattr); derefhere = (tattr->typekind != TKIND_DISPATCH && tattr->typekind != TKIND_INTERFACE); } break; case TKIND_ENUM: /* confirmed */ case TKIND_RECORD: /* FIXME: mostly untested */ break; case TKIND_DISPATCH: /* will be done in VT_USERDEFINED case */ case TKIND_INTERFACE: /* will be done in VT_USERDEFINED case */ derefhere=FALSE; break; default: FIXME("unhandled switch cases tattr->typekind %d\n", tattr->typekind); derefhere=FALSE; break; } ITypeInfo_ReleaseTypeAttr(tinfo2, tattr); ITypeInfo_Release(tinfo2); } /* read it in all cases, we need to know if we have * NULL pointer or not. */ hres = xbuf_get(buf,(LPBYTE)&cookie,sizeof(cookie)); if (hres) { ERR("Failed to load pointer cookie.\n"); return hres; } if (cookie != 0x42424242) { /* we read a NULL ptr from the remote side */ if (debugout) TRACE_(olerelay)("NULL"); *arg = 0; return S_OK; } if (debugout) TRACE_(olerelay)("*"); if (alloc) { /* Allocate space for the referenced struct */ if (derefhere) *arg=(DWORD)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,_xsize(tdesc->u.lptdesc, tinfo)); } if (derefhere) return deserialize_param(tinfo, readit, debugout, alloc, tdesc->u.lptdesc, (LPDWORD)*arg, buf); else return deserialize_param(tinfo, readit, debugout, alloc, tdesc->u.lptdesc, arg, buf); } case VT_UNKNOWN: /* FIXME: UNKNOWN is unknown ..., but allocate 4 byte for it */ if (alloc) *arg=(DWORD)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(DWORD)); hres = S_OK; if (readit) hres = _unmarshal_interface(buf,&IID_IUnknown,(LPUNKNOWN*)arg); if (debugout) TRACE_(olerelay)("unk(%p)",arg); return hres; case VT_DISPATCH: hres = S_OK; if (readit) hres = _unmarshal_interface(buf,&IID_IDispatch,(LPUNKNOWN*)arg); if (debugout) TRACE_(olerelay)("idisp(%p)",arg); return hres; case VT_VOID: if (debugout) TRACE_(olerelay)(""); return S_OK; case VT_USERDEFINED: { ITypeInfo *tinfo2; TYPEATTR *tattr; hres = ITypeInfo_GetRefTypeInfo(tinfo,tdesc->u.hreftype,&tinfo2); if (hres) { ERR("Could not get typeinfo of hreftype %x for VT_USERDEFINED.\n",tdesc->u.hreftype); return hres; } hres = ITypeInfo_GetTypeAttr(tinfo2,&tattr); if (hres) { ERR("Could not get typeattr in VT_USERDEFINED.\n"); } else { switch (tattr->typekind) { case TKIND_DISPATCH: case TKIND_INTERFACE: if (readit) hres = _unmarshal_interface(buf,&(tattr->guid),(LPUNKNOWN*)arg); break; case TKIND_RECORD: { int i; if (debugout) TRACE_(olerelay)("{"); for (i=0;icVars;i++) { VARDESC *vdesc; hres = ITypeInfo2_GetVarDesc(tinfo2, i, &vdesc); if (hres) { ERR("Could not get vardesc of %d\n",i); ITypeInfo_ReleaseTypeAttr(tinfo2, tattr); ITypeInfo_Release(tinfo2); return hres; } hres = deserialize_param( tinfo2, readit, debugout, alloc, &vdesc->elemdescVar.tdesc, (DWORD*)(((LPBYTE)arg)+vdesc->u.oInst), buf ); ITypeInfo2_ReleaseVarDesc(tinfo2, vdesc); if (debugout && (icVars-1)) TRACE_(olerelay)(","); } if (debugout) TRACE_(olerelay)("}"); break; } case TKIND_ALIAS: hres = deserialize_param(tinfo2,readit,debugout,alloc,&tattr->tdescAlias,arg,buf); break; case TKIND_ENUM: if (readit) { hres = xbuf_get(buf,(LPBYTE)arg,sizeof(DWORD)); if (hres) ERR("Failed to read enum (4 byte)\n"); } if (debugout) TRACE_(olerelay)("%x",*arg); break; default: ERR("Unhandled typekind %d\n",tattr->typekind); hres = E_FAIL; break; } ITypeInfo_ReleaseTypeAttr(tinfo2, tattr); } if (hres) ERR("failed to stuballoc in TKIND_RECORD.\n"); ITypeInfo_Release(tinfo2); return hres; } case VT_CARRAY: { /* arg is pointing to the start of the array. */ ARRAYDESC *adesc = tdesc->u.lpadesc; int arrsize,i; arrsize = 1; if (adesc->cDims > 1) FIXME("cDims > 1 in VT_CARRAY. Does it work?\n"); for (i=0;icDims;i++) arrsize *= adesc->rgbounds[i].cElements; *arg=(DWORD)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,_xsize(tdesc->u.lptdesc, tinfo) * arrsize); for (i=0;itdescElem, (DWORD*)((LPBYTE)(*arg)+i*_xsize(&adesc->tdescElem, tinfo)), buf ); return S_OK; } case VT_SAFEARRAY: { if (readit) { ULONG flags = MAKELONG(MSHCTX_DIFFERENTMACHINE, NDR_LOCAL_DATA_REPRESENTATION); unsigned char *buffer; buffer = LPSAFEARRAY_UserUnmarshal(&flags, buf->base + buf->curoff, (LPSAFEARRAY *)arg); buf->curoff = buffer - buf->base; } return S_OK; } default: ERR("No handler for VT type %d!\n",tdesc->vt); return S_OK; } } } /* Retrieves a function's funcdesc, searching back into inherited interfaces. */ static HRESULT get_funcdesc(ITypeInfo *tinfo, int iMethod, ITypeInfo **tactual, const FUNCDESC **fdesc, BSTR *iname, BSTR *fname, UINT *num) { HRESULT hr; UINT i, impl_types; UINT inherited_funcs = 0; TYPEATTR *attr; if (fname) *fname = NULL; if (iname) *iname = NULL; if (num) *num = 0; *tactual = NULL; hr = ITypeInfo_GetTypeAttr(tinfo, &attr); if (FAILED(hr)) { ERR("GetTypeAttr failed with %x\n",hr); return hr; } if(attr->typekind == TKIND_DISPATCH) { if(attr->wTypeFlags & TYPEFLAG_FDUAL) { HREFTYPE href; ITypeInfo *tinfo2; hr = ITypeInfo_GetRefTypeOfImplType(tinfo, -1, &href); if(FAILED(hr)) { ERR("Cannot get interface href from dual dispinterface\n"); ITypeInfo_ReleaseTypeAttr(tinfo, attr); return hr; } hr = ITypeInfo_GetRefTypeInfo(tinfo, href, &tinfo2); if(FAILED(hr)) { ERR("Cannot get interface from dual dispinterface\n"); ITypeInfo_ReleaseTypeAttr(tinfo, attr); return hr; } hr = get_funcdesc(tinfo2, iMethod, tactual, fdesc, iname, fname, num); ITypeInfo_Release(tinfo2); ITypeInfo_ReleaseTypeAttr(tinfo, attr); return hr; } ERR("Shouldn't be called with a non-dual dispinterface\n"); return E_FAIL; } impl_types = attr->cImplTypes; ITypeInfo_ReleaseTypeAttr(tinfo, attr); for (i = 0; i < impl_types; i++) { HREFTYPE href; ITypeInfo *pSubTypeInfo; UINT sub_funcs; hr = ITypeInfo_GetRefTypeOfImplType(tinfo, i, &href); if (FAILED(hr)) return hr; hr = ITypeInfo_GetRefTypeInfo(tinfo, href, &pSubTypeInfo); if (FAILED(hr)) return hr; hr = get_funcdesc(pSubTypeInfo, iMethod, tactual, fdesc, iname, fname, &sub_funcs); inherited_funcs += sub_funcs; ITypeInfo_Release(pSubTypeInfo); if(SUCCEEDED(hr)) return hr; } if(iMethod < inherited_funcs) { ERR("shouldn't be here\n"); return E_INVALIDARG; } for(i = inherited_funcs; i <= iMethod; i++) { hr = ITypeInfoImpl_GetInternalFuncDesc(tinfo, i - inherited_funcs, fdesc); if(FAILED(hr)) { if(num) *num = i; return hr; } } /* found it. We don't care about num so zero it */ if(num) *num = 0; *tactual = tinfo; ITypeInfo_AddRef(*tactual); if (fname) ITypeInfo_GetDocumentation(tinfo,(*fdesc)->memid,fname,NULL,NULL,NULL); if (iname) ITypeInfo_GetDocumentation(tinfo,-1,iname,NULL,NULL,NULL); return S_OK; } static inline BOOL is_in_elem(const ELEMDESC *elem) { return (elem->u.paramdesc.wParamFlags & PARAMFLAG_FIN || !elem->u.paramdesc.wParamFlags); } static inline BOOL is_out_elem(const ELEMDESC *elem) { return (elem->u.paramdesc.wParamFlags & PARAMFLAG_FOUT || !elem->u.paramdesc.wParamFlags); } static DWORD xCall(LPVOID retptr, int method, TMProxyImpl *tpinfo /*, args */) { DWORD *args = ((DWORD*)&tpinfo)+1, *xargs; const FUNCDESC *fdesc; HRESULT hres; int i, relaydeb = TRACE_ON(olerelay); marshal_state buf; RPCOLEMESSAGE msg; ULONG status; BSTR fname,iname; BSTR names[10]; UINT nrofnames; DWORD remoteresult = 0; ITypeInfo *tinfo; IRpcChannelBuffer *chanbuf; EnterCriticalSection(&tpinfo->crit); hres = get_funcdesc(tpinfo->tinfo,method,&tinfo,&fdesc,&iname,&fname,NULL); if (hres) { ERR("Did not find typeinfo/funcdesc entry for method %d!\n",method); LeaveCriticalSection(&tpinfo->crit); return E_FAIL; } if (!tpinfo->chanbuf) { WARN("Tried to use disconnected proxy\n"); ITypeInfo_Release(tinfo); LeaveCriticalSection(&tpinfo->crit); return RPC_E_DISCONNECTED; } chanbuf = tpinfo->chanbuf; IRpcChannelBuffer_AddRef(chanbuf); LeaveCriticalSection(&tpinfo->crit); if (relaydeb) { TRACE_(olerelay)("->"); if (iname) TRACE_(olerelay)("%s:",relaystr(iname)); if (fname) TRACE_(olerelay)("%s(%d)",relaystr(fname),method); else TRACE_(olerelay)("%d",method); TRACE_(olerelay)("("); } SysFreeString(iname); SysFreeString(fname); memset(&buf,0,sizeof(buf)); /* normal typelib driven serializing */ /* Need them for hack below */ memset(names,0,sizeof(names)); if (ITypeInfo_GetNames(tinfo,fdesc->memid,names,sizeof(names)/sizeof(names[0]),&nrofnames)) nrofnames = 0; if (nrofnames > sizeof(names)/sizeof(names[0])) ERR("Need more names!\n"); xargs = args; for (i=0;icParams;i++) { ELEMDESC *elem = fdesc->lprgelemdescParam+i; if (relaydeb) { if (i) TRACE_(olerelay)(","); if (i+1tdesc, xargs, &buf ); if (hres) { ERR("Failed to serialize param, hres %x\n",hres); break; } xargs+=_argsize(&elem->tdesc, tinfo); } if (relaydeb) TRACE_(olerelay)(")"); memset(&msg,0,sizeof(msg)); msg.cbBuffer = buf.curoff; msg.iMethod = method; hres = IRpcChannelBuffer_GetBuffer(chanbuf,&msg,&(tpinfo->iid)); if (hres) { ERR("RpcChannelBuffer GetBuffer failed, %x\n",hres); goto exit; } memcpy(msg.Buffer,buf.base,buf.curoff); if (relaydeb) TRACE_(olerelay)("\n"); hres = IRpcChannelBuffer_SendReceive(chanbuf,&msg,&status); if (hres) { ERR("RpcChannelBuffer SendReceive failed, %x\n",hres); goto exit; } if (relaydeb) TRACE_(olerelay)(" status = %08x (",status); if (buf.base) buf.base = HeapReAlloc(GetProcessHeap(),0,buf.base,msg.cbBuffer); else buf.base = HeapAlloc(GetProcessHeap(),0,msg.cbBuffer); buf.size = msg.cbBuffer; memcpy(buf.base,msg.Buffer,buf.size); buf.curoff = 0; /* generic deserializer using typelib description */ xargs = args; status = S_OK; for (i=0;icParams;i++) { ELEMDESC *elem = fdesc->lprgelemdescParam+i; if (relaydeb) { if (i) TRACE_(olerelay)(","); if (i+1tdesc), xargs, &buf ); if (hres) { ERR("Failed to unmarshall param, hres %x\n",hres); status = hres; break; } xargs += _argsize(&elem->tdesc, tinfo); } hres = xbuf_get(&buf, (LPBYTE)&remoteresult, sizeof(DWORD)); if (hres != S_OK) goto exit; if (relaydeb) TRACE_(olerelay)(") = %08x\n", remoteresult); hres = remoteresult; exit: IRpcChannelBuffer_FreeBuffer(chanbuf,&msg); for (i = 0; i < nrofnames; i++) SysFreeString(names[i]); HeapFree(GetProcessHeap(),0,buf.base); IRpcChannelBuffer_Release(chanbuf); ITypeInfo_Release(tinfo); TRACE("-- 0x%08x\n", hres); return hres; } static HRESULT WINAPI ProxyIUnknown_QueryInterface(IUnknown *iface, REFIID riid, void **ppv) { TMProxyImpl *proxy = (TMProxyImpl *)iface; TRACE("(%s, %p)\n", debugstr_guid(riid), ppv); if (proxy->outerunknown) return IUnknown_QueryInterface(proxy->outerunknown, riid, ppv); FIXME("No interface\n"); return E_NOINTERFACE; } static ULONG WINAPI ProxyIUnknown_AddRef(IUnknown *iface) { TMProxyImpl *proxy = (TMProxyImpl *)iface; TRACE("\n"); if (proxy->outerunknown) return IUnknown_AddRef(proxy->outerunknown); return 2; /* FIXME */ } static ULONG WINAPI ProxyIUnknown_Release(IUnknown *iface) { TMProxyImpl *proxy = (TMProxyImpl *)iface; TRACE("\n"); if (proxy->outerunknown) return IUnknown_Release(proxy->outerunknown); return 1; /* FIXME */ } static HRESULT WINAPI ProxyIDispatch_GetTypeInfoCount(LPDISPATCH iface, UINT * pctinfo) { TMProxyImpl *This = (TMProxyImpl *)iface; TRACE("(%p)\n", pctinfo); return IDispatch_GetTypeInfoCount(This->dispatch, pctinfo); } static HRESULT WINAPI ProxyIDispatch_GetTypeInfo(LPDISPATCH iface, UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo) { TMProxyImpl *This = (TMProxyImpl *)iface; TRACE("(%d, %x, %p)\n", iTInfo, lcid, ppTInfo); return IDispatch_GetTypeInfo(This->dispatch, iTInfo, lcid, ppTInfo); } static HRESULT WINAPI ProxyIDispatch_GetIDsOfNames(LPDISPATCH iface, REFIID riid, LPOLESTR * rgszNames, UINT cNames, LCID lcid, DISPID * rgDispId) { TMProxyImpl *This = (TMProxyImpl *)iface; TRACE("(%s, %p, %d, 0x%x, %p)\n", debugstr_guid(riid), rgszNames, cNames, lcid, rgDispId); return IDispatch_GetIDsOfNames(This->dispatch, riid, rgszNames, cNames, lcid, rgDispId); } static HRESULT WINAPI ProxyIDispatch_Invoke(LPDISPATCH iface, DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS * pDispParams, VARIANT * pVarResult, EXCEPINFO * pExcepInfo, UINT * puArgErr) { TMProxyImpl *This = (TMProxyImpl *)iface; TRACE("(%d, %s, 0x%x, 0x%x, %p, %p, %p, %p)\n", dispIdMember, debugstr_guid(riid), lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); return IDispatch_Invoke(This->dispatch, dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); } typedef struct { const IRpcChannelBufferVtbl *lpVtbl; LONG refs; /* the IDispatch-derived interface we are handling */ IID tmarshal_iid; IRpcChannelBuffer *pDelegateChannel; } TMarshalDispatchChannel; static HRESULT WINAPI TMarshalDispatchChannel_QueryInterface(LPRPCCHANNELBUFFER iface, REFIID riid, LPVOID *ppv) { *ppv = NULL; if (IsEqualIID(riid,&IID_IRpcChannelBuffer) || IsEqualIID(riid,&IID_IUnknown)) { *ppv = iface; IUnknown_AddRef(iface); return S_OK; } return E_NOINTERFACE; } static ULONG WINAPI TMarshalDispatchChannel_AddRef(LPRPCCHANNELBUFFER iface) { TMarshalDispatchChannel *This = (TMarshalDispatchChannel *)iface; return InterlockedIncrement(&This->refs); } static ULONG WINAPI TMarshalDispatchChannel_Release(LPRPCCHANNELBUFFER iface) { TMarshalDispatchChannel *This = (TMarshalDispatchChannel *)iface; ULONG ref; ref = InterlockedDecrement(&This->refs); if (ref) return ref; IRpcChannelBuffer_Release(This->pDelegateChannel); HeapFree(GetProcessHeap(), 0, This); return 0; } static HRESULT WINAPI TMarshalDispatchChannel_GetBuffer(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE* olemsg, REFIID riid) { TMarshalDispatchChannel *This = (TMarshalDispatchChannel *)iface; TRACE("(%p, %s)\n", olemsg, debugstr_guid(riid)); /* Note: we are pretending to invoke a method on the interface identified * by tmarshal_iid so that we can re-use the IDispatch proxy/stub code * without the RPC runtime getting confused by not exporting an IDispatch interface */ return IRpcChannelBuffer_GetBuffer(This->pDelegateChannel, olemsg, &This->tmarshal_iid); } static HRESULT WINAPI TMarshalDispatchChannel_SendReceive(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE *olemsg, ULONG *pstatus) { TMarshalDispatchChannel *This = (TMarshalDispatchChannel *)iface; TRACE("(%p, %p)\n", olemsg, pstatus); return IRpcChannelBuffer_SendReceive(This->pDelegateChannel, olemsg, pstatus); } static HRESULT WINAPI TMarshalDispatchChannel_FreeBuffer(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE* olemsg) { TMarshalDispatchChannel *This = (TMarshalDispatchChannel *)iface; TRACE("(%p)\n", olemsg); return IRpcChannelBuffer_FreeBuffer(This->pDelegateChannel, olemsg); } static HRESULT WINAPI TMarshalDispatchChannel_GetDestCtx(LPRPCCHANNELBUFFER iface, DWORD* pdwDestContext, void** ppvDestContext) { TMarshalDispatchChannel *This = (TMarshalDispatchChannel *)iface; TRACE("(%p,%p)\n", pdwDestContext, ppvDestContext); return IRpcChannelBuffer_GetDestCtx(This->pDelegateChannel, pdwDestContext, ppvDestContext); } static HRESULT WINAPI TMarshalDispatchChannel_IsConnected(LPRPCCHANNELBUFFER iface) { TMarshalDispatchChannel *This = (TMarshalDispatchChannel *)iface; TRACE("()\n"); return IRpcChannelBuffer_IsConnected(This->pDelegateChannel); } static const IRpcChannelBufferVtbl TMarshalDispatchChannelVtbl = { TMarshalDispatchChannel_QueryInterface, TMarshalDispatchChannel_AddRef, TMarshalDispatchChannel_Release, TMarshalDispatchChannel_GetBuffer, TMarshalDispatchChannel_SendReceive, TMarshalDispatchChannel_FreeBuffer, TMarshalDispatchChannel_GetDestCtx, TMarshalDispatchChannel_IsConnected }; static HRESULT TMarshalDispatchChannel_Create( IRpcChannelBuffer *pDelegateChannel, REFIID tmarshal_riid, IRpcChannelBuffer **ppChannel) { TMarshalDispatchChannel *This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This)); if (!This) return E_OUTOFMEMORY; This->lpVtbl = &TMarshalDispatchChannelVtbl; This->refs = 1; IRpcChannelBuffer_AddRef(pDelegateChannel); This->pDelegateChannel = pDelegateChannel; This->tmarshal_iid = *tmarshal_riid; *ppChannel = (IRpcChannelBuffer *)&This->lpVtbl; return S_OK; } static inline HRESULT get_facbuf_for_iid(REFIID riid, IPSFactoryBuffer **facbuf) { HRESULT hr; CLSID clsid; if ((hr = CoGetPSClsid(riid, &clsid))) return hr; return CoGetClassObject(&clsid, CLSCTX_INPROC_SERVER, NULL, &IID_IPSFactoryBuffer, (LPVOID*)facbuf); } static HRESULT init_proxy_entry_point(TMProxyImpl *proxy, unsigned int num) { int j; /* nrofargs without This */ int nrofargs; ITypeInfo *tinfo2; TMAsmProxy *xasm = proxy->asmstubs + num; HRESULT hres; const FUNCDESC *fdesc; hres = get_funcdesc(proxy->tinfo, num, &tinfo2, &fdesc, NULL, NULL, NULL); if (hres) { ERR("GetFuncDesc %x should not fail here.\n",hres); return hres; } ITypeInfo_Release(tinfo2); /* some args take more than 4 byte on the stack */ nrofargs = 0; for (j=0;jcParams;j++) nrofargs += _argsize(&fdesc->lprgelemdescParam[j].tdesc, proxy->tinfo); #ifdef __i386__ if (fdesc->callconv != CC_STDCALL) { ERR("calling convention is not stdcall????\n"); return E_FAIL; } /* popl %eax - return ptr * pushl * pushl %eax * call xCall * lret (+4) * * * arg3 arg2 arg1 */ xasm->popleax = 0x58; xasm->pushlval = 0x68; xasm->nr = num; xasm->pushleax = 0x50; xasm->lcall = 0xe8; /* relative jump */ xasm->xcall = (DWORD)xCall; xasm->xcall -= (DWORD)&(xasm->lret); xasm->lret = 0xc2; xasm->bytestopop = (nrofargs+2)*4; /* pop args, This, iMethod */ xasm->nop = 0x90; proxy->lpvtbl[num] = xasm; #else FIXME("not implemented on non i386\n"); return E_FAIL; #endif return S_OK; } static HRESULT WINAPI PSFacBuf_CreateProxy( LPPSFACTORYBUFFER iface, IUnknown* pUnkOuter, REFIID riid, IRpcProxyBuffer **ppProxy, LPVOID *ppv) { HRESULT hres; ITypeInfo *tinfo; unsigned int i, nroffuncs; TMProxyImpl *proxy; TYPEATTR *typeattr; BOOL defer_to_dispatch = FALSE; TRACE("(...%s...)\n",debugstr_guid(riid)); hres = _get_typeinfo_for_iid(riid,&tinfo); if (hres) { ERR("No typeinfo for %s?\n",debugstr_guid(riid)); return hres; } hres = num_of_funcs(tinfo, &nroffuncs); if (FAILED(hres)) { ERR("Cannot get number of functions for typeinfo %s\n",debugstr_guid(riid)); ITypeInfo_Release(tinfo); return hres; } proxy = CoTaskMemAlloc(sizeof(TMProxyImpl)); if (!proxy) return E_OUTOFMEMORY; assert(sizeof(TMAsmProxy) == 16); proxy->dispatch = NULL; proxy->dispatch_proxy = NULL; proxy->outerunknown = pUnkOuter; proxy->asmstubs = VirtualAlloc(NULL, sizeof(TMAsmProxy) * nroffuncs, MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (!proxy->asmstubs) { ERR("Could not commit pages for proxy thunks\n"); CoTaskMemFree(proxy); return E_OUTOFMEMORY; } proxy->lpvtbl2 = &tmproxyvtable; /* one reference for the proxy */ proxy->ref = 1; proxy->tinfo = tinfo; proxy->iid = *riid; proxy->chanbuf = 0; InitializeCriticalSection(&proxy->crit); proxy->crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": TMProxyImpl.crit"); proxy->lpvtbl = HeapAlloc(GetProcessHeap(),0,sizeof(LPBYTE)*nroffuncs); /* if we derive from IDispatch then defer to its proxy for its methods */ hres = ITypeInfo_GetTypeAttr(tinfo, &typeattr); if (hres == S_OK) { if (typeattr->wTypeFlags & TYPEFLAG_FDISPATCHABLE) { IPSFactoryBuffer *factory_buffer; hres = get_facbuf_for_iid(&IID_IDispatch, &factory_buffer); if (hres == S_OK) { hres = IPSFactoryBuffer_CreateProxy(factory_buffer, NULL, &IID_IDispatch, &proxy->dispatch_proxy, (void **)&proxy->dispatch); IPSFactoryBuffer_Release(factory_buffer); } if ((hres == S_OK) && (nroffuncs < 7)) { ERR("nroffuncs calculated incorrectly (%d)\n", nroffuncs); hres = E_UNEXPECTED; } if (hres == S_OK) { defer_to_dispatch = TRUE; } } ITypeInfo_ReleaseTypeAttr(tinfo, typeattr); } for (i=0;ilpvtbl[i] = ProxyIUnknown_QueryInterface; break; case 1: proxy->lpvtbl[i] = ProxyIUnknown_AddRef; break; case 2: proxy->lpvtbl[i] = ProxyIUnknown_Release; break; case 3: if(!defer_to_dispatch) { hres = init_proxy_entry_point(proxy, i); if(FAILED(hres)) return hres; } else proxy->lpvtbl[3] = ProxyIDispatch_GetTypeInfoCount; break; case 4: if(!defer_to_dispatch) { hres = init_proxy_entry_point(proxy, i); if(FAILED(hres)) return hres; } else proxy->lpvtbl[4] = ProxyIDispatch_GetTypeInfo; break; case 5: if(!defer_to_dispatch) { hres = init_proxy_entry_point(proxy, i); if(FAILED(hres)) return hres; } else proxy->lpvtbl[5] = ProxyIDispatch_GetIDsOfNames; break; case 6: if(!defer_to_dispatch) { hres = init_proxy_entry_point(proxy, i); if(FAILED(hres)) return hres; } else proxy->lpvtbl[6] = ProxyIDispatch_Invoke; break; default: hres = init_proxy_entry_point(proxy, i); if(FAILED(hres)) return hres; } } if (hres == S_OK) { *ppv = proxy; *ppProxy = (IRpcProxyBuffer *)&(proxy->lpvtbl2); IUnknown_AddRef((IUnknown *)*ppv); return S_OK; } else TMProxyImpl_Release((IRpcProxyBuffer *)&proxy->lpvtbl2); return hres; } typedef struct _TMStubImpl { const IRpcStubBufferVtbl *lpvtbl; LONG ref; LPUNKNOWN pUnk; ITypeInfo *tinfo; IID iid; IRpcStubBuffer *dispatch_stub; BOOL dispatch_derivative; } TMStubImpl; static HRESULT WINAPI TMStubImpl_QueryInterface(LPRPCSTUBBUFFER iface, REFIID riid, LPVOID *ppv) { if (IsEqualIID(riid,&IID_IRpcStubBuffer)||IsEqualIID(riid,&IID_IUnknown)){ *ppv = iface; IRpcStubBuffer_AddRef(iface); return S_OK; } FIXME("%s, not supported IID.\n",debugstr_guid(riid)); return E_NOINTERFACE; } static ULONG WINAPI TMStubImpl_AddRef(LPRPCSTUBBUFFER iface) { TMStubImpl *This = (TMStubImpl *)iface; ULONG refCount = InterlockedIncrement(&This->ref); TRACE("(%p)->(ref before=%u)\n", This, refCount - 1); return refCount; } static ULONG WINAPI TMStubImpl_Release(LPRPCSTUBBUFFER iface) { TMStubImpl *This = (TMStubImpl *)iface; ULONG refCount = InterlockedDecrement(&This->ref); TRACE("(%p)->(ref before=%u)\n", This, refCount + 1); if (!refCount) { IRpcStubBuffer_Disconnect(iface); ITypeInfo_Release(This->tinfo); if (This->dispatch_stub) IRpcStubBuffer_Release(This->dispatch_stub); CoTaskMemFree(This); } return refCount; } static HRESULT WINAPI TMStubImpl_Connect(LPRPCSTUBBUFFER iface, LPUNKNOWN pUnkServer) { TMStubImpl *This = (TMStubImpl *)iface; TRACE("(%p)->(%p)\n", This, pUnkServer); IUnknown_AddRef(pUnkServer); This->pUnk = pUnkServer; if (This->dispatch_stub) IRpcStubBuffer_Connect(This->dispatch_stub, pUnkServer); return S_OK; } static void WINAPI TMStubImpl_Disconnect(LPRPCSTUBBUFFER iface) { TMStubImpl *This = (TMStubImpl *)iface; TRACE("(%p)->()\n", This); if (This->pUnk) { IUnknown_Release(This->pUnk); This->pUnk = NULL; } if (This->dispatch_stub) IRpcStubBuffer_Disconnect(This->dispatch_stub); } static HRESULT WINAPI TMStubImpl_Invoke( LPRPCSTUBBUFFER iface, RPCOLEMESSAGE* xmsg,IRpcChannelBuffer*rpcchanbuf) { #ifdef __i386__ int i; const FUNCDESC *fdesc; TMStubImpl *This = (TMStubImpl *)iface; HRESULT hres; DWORD *args = NULL, res, *xargs, nrofargs; marshal_state buf; UINT nrofnames = 0; BSTR names[10]; BSTR iname = NULL; ITypeInfo *tinfo = NULL; TRACE("...\n"); if (xmsg->iMethod < 3) { ERR("IUnknown methods cannot be marshaled by the typelib marshaler\n"); return E_UNEXPECTED; } if (This->dispatch_derivative && xmsg->iMethod < sizeof(IDispatchVtbl)/sizeof(void *)) { IPSFactoryBuffer *factory_buffer; hres = get_facbuf_for_iid(&IID_IDispatch, &factory_buffer); if (hres == S_OK) { hres = IPSFactoryBuffer_CreateStub(factory_buffer, &IID_IDispatch, This->pUnk, &This->dispatch_stub); IPSFactoryBuffer_Release(factory_buffer); } if (hres != S_OK) return hres; return IRpcStubBuffer_Invoke(This->dispatch_stub, xmsg, rpcchanbuf); } memset(&buf,0,sizeof(buf)); buf.size = xmsg->cbBuffer; buf.base = HeapAlloc(GetProcessHeap(), 0, xmsg->cbBuffer); memcpy(buf.base, xmsg->Buffer, xmsg->cbBuffer); buf.curoff = 0; hres = get_funcdesc(This->tinfo,xmsg->iMethod,&tinfo,&fdesc,&iname,NULL,NULL); if (hres) { ERR("GetFuncDesc on method %d failed with %x\n",xmsg->iMethod,hres); return hres; } if (iname && !lstrcmpW(iname, IDispatchW)) { ERR("IDispatch cannot be marshaled by the typelib marshaler\n"); hres = E_UNEXPECTED; SysFreeString (iname); goto exit; } SysFreeString (iname); /* Need them for hack below */ memset(names,0,sizeof(names)); ITypeInfo_GetNames(tinfo,fdesc->memid,names,sizeof(names)/sizeof(names[0]),&nrofnames); if (nrofnames > sizeof(names)/sizeof(names[0])) { ERR("Need more names!\n"); } /*dump_FUNCDESC(fdesc);*/ nrofargs = 0; for (i=0;icParams;i++) nrofargs += _argsize(&fdesc->lprgelemdescParam[i].tdesc, tinfo); args = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,(nrofargs+1)*sizeof(DWORD)); if (!args) { hres = E_OUTOFMEMORY; goto exit; } /* Allocate all stuff used by call. */ xargs = args+1; for (i=0;icParams;i++) { ELEMDESC *elem = fdesc->lprgelemdescParam+i; hres = deserialize_param( tinfo, is_in_elem(elem), FALSE, TRUE, &(elem->tdesc), xargs, &buf ); xargs += _argsize(&elem->tdesc, tinfo); if (hres) { ERR("Failed to deserialize param %s, hres %x\n",relaystr(names[i+1]),hres); break; } } args[0] = (DWORD)This->pUnk; __TRY { res = _invoke( (*((FARPROC**)args[0]))[fdesc->oVft/4], fdesc->callconv, (xargs-args), args ); } __EXCEPT_ALL { DWORD dwExceptionCode = GetExceptionCode(); ERR("invoke call failed with exception 0x%08x (%d)\n", dwExceptionCode, dwExceptionCode); if (FAILED(dwExceptionCode)) hres = dwExceptionCode; else hres = HRESULT_FROM_WIN32(dwExceptionCode); } __ENDTRY if (hres != S_OK) goto exit; buf.curoff = 0; xargs = args+1; for (i=0;icParams;i++) { ELEMDESC *elem = fdesc->lprgelemdescParam+i; hres = serialize_param( tinfo, is_out_elem(elem), FALSE, TRUE, &elem->tdesc, xargs, &buf ); xargs += _argsize(&elem->tdesc, tinfo); if (hres) { ERR("Failed to stuballoc param, hres %x\n",hres); break; } } hres = xbuf_add (&buf, (LPBYTE)&res, sizeof(DWORD)); if (hres != S_OK) goto exit; xmsg->cbBuffer = buf.curoff; hres = IRpcChannelBuffer_GetBuffer(rpcchanbuf, xmsg, &This->iid); if (hres != S_OK) ERR("IRpcChannelBuffer_GetBuffer failed with error 0x%08x\n", hres); if (hres == S_OK) memcpy(xmsg->Buffer, buf.base, buf.curoff); exit: for (i = 0; i < nrofnames; i++) SysFreeString(names[i]); ITypeInfo_Release(tinfo); HeapFree(GetProcessHeap(), 0, args); HeapFree(GetProcessHeap(), 0, buf.base); TRACE("returning\n"); return hres; #else FIXME( "not implemented on non-i386\n" ); return E_FAIL; #endif } static LPRPCSTUBBUFFER WINAPI TMStubImpl_IsIIDSupported(LPRPCSTUBBUFFER iface, REFIID riid) { FIXME("Huh (%s)?\n",debugstr_guid(riid)); return NULL; } static ULONG WINAPI TMStubImpl_CountRefs(LPRPCSTUBBUFFER iface) { TMStubImpl *This = (TMStubImpl *)iface; FIXME("()\n"); return This->ref; /*FIXME? */ } static HRESULT WINAPI TMStubImpl_DebugServerQueryInterface(LPRPCSTUBBUFFER iface, LPVOID *ppv) { return E_NOTIMPL; } static void WINAPI TMStubImpl_DebugServerRelease(LPRPCSTUBBUFFER iface, LPVOID ppv) { return; } static const IRpcStubBufferVtbl tmstubvtbl = { TMStubImpl_QueryInterface, TMStubImpl_AddRef, TMStubImpl_Release, TMStubImpl_Connect, TMStubImpl_Disconnect, TMStubImpl_Invoke, TMStubImpl_IsIIDSupported, TMStubImpl_CountRefs, TMStubImpl_DebugServerQueryInterface, TMStubImpl_DebugServerRelease }; static HRESULT WINAPI PSFacBuf_CreateStub( LPPSFACTORYBUFFER iface, REFIID riid,IUnknown *pUnkServer, IRpcStubBuffer** ppStub ) { HRESULT hres; ITypeInfo *tinfo; TMStubImpl *stub; TYPEATTR *typeattr; TRACE("(%s,%p,%p)\n",debugstr_guid(riid),pUnkServer,ppStub); hres = _get_typeinfo_for_iid(riid,&tinfo); if (hres) { ERR("No typeinfo for %s?\n",debugstr_guid(riid)); return hres; } stub = CoTaskMemAlloc(sizeof(TMStubImpl)); if (!stub) return E_OUTOFMEMORY; stub->lpvtbl = &tmstubvtbl; stub->ref = 1; stub->tinfo = tinfo; stub->dispatch_stub = NULL; stub->dispatch_derivative = FALSE; stub->iid = *riid; hres = IRpcStubBuffer_Connect((LPRPCSTUBBUFFER)stub,pUnkServer); *ppStub = (LPRPCSTUBBUFFER)stub; TRACE("IRpcStubBuffer: %p\n", stub); if (hres) ERR("Connect to pUnkServer failed?\n"); /* if we derive from IDispatch then defer to its stub for some of its methods */ hres = ITypeInfo_GetTypeAttr(tinfo, &typeattr); if (hres == S_OK) { if (typeattr->wTypeFlags & TYPEFLAG_FDISPATCHABLE) stub->dispatch_derivative = TRUE; ITypeInfo_ReleaseTypeAttr(tinfo, typeattr); } return hres; } static const IPSFactoryBufferVtbl psfacbufvtbl = { PSFacBuf_QueryInterface, PSFacBuf_AddRef, PSFacBuf_Release, PSFacBuf_CreateProxy, PSFacBuf_CreateStub }; /* This is the whole PSFactoryBuffer object, just the vtableptr */ static const IPSFactoryBufferVtbl *lppsfac = &psfacbufvtbl; /*********************************************************************** * TMARSHAL_DllGetClassObject */ HRESULT TMARSHAL_DllGetClassObject(REFCLSID rclsid, REFIID iid,LPVOID *ppv) { if (IsEqualIID(iid,&IID_IPSFactoryBuffer)) { *ppv = &lppsfac; return S_OK; } return E_NOINTERFACE; }