| /* |
| * OLE32 callouts, COM interface marshalling |
| * |
| * Copyright 2001 Ove Kåven, TransGaming Technologies |
| * |
| * 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 |
| * |
| * TODO: |
| * - fix the wire-protocol to match MS/RPC |
| */ |
| |
| #include <stdarg.h> |
| #include <stdio.h> |
| #include <string.h> |
| |
| #define COBJMACROS |
| #define NONAMELESSUNION |
| |
| #include "windef.h" |
| #include "winbase.h" |
| #include "winerror.h" |
| |
| #include "objbase.h" |
| |
| #include "ndr_misc.h" |
| #include "rpcndr.h" |
| #include "rpcproxy.h" |
| #include "wine/rpcfc.h" |
| #include "cpsf.h" |
| |
| #include "wine/debug.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(ole); |
| |
| static HMODULE hOLE; |
| |
| static HRESULT (WINAPI *COM_GetMarshalSizeMax)(ULONG *,REFIID,LPUNKNOWN,DWORD,LPVOID,DWORD); |
| static HRESULT (WINAPI *COM_MarshalInterface)(LPSTREAM,REFIID,LPUNKNOWN,DWORD,LPVOID,DWORD); |
| static HRESULT (WINAPI *COM_UnmarshalInterface)(LPSTREAM,REFIID,LPVOID*); |
| static HRESULT (WINAPI *COM_ReleaseMarshalData)(LPSTREAM); |
| static HRESULT (WINAPI *COM_GetClassObject)(REFCLSID,DWORD,COSERVERINFO *,REFIID,LPVOID *); |
| static HRESULT (WINAPI *COM_GetPSClsid)(REFIID,CLSID *); |
| static LPVOID (WINAPI *COM_MemAlloc)(ULONG); |
| static void (WINAPI *COM_MemFree)(LPVOID); |
| |
| static HMODULE LoadCOM(void) |
| { |
| if (hOLE) return hOLE; |
| hOLE = LoadLibraryA("OLE32.DLL"); |
| if (!hOLE) return 0; |
| COM_GetMarshalSizeMax = (LPVOID)GetProcAddress(hOLE, "CoGetMarshalSizeMax"); |
| COM_MarshalInterface = (LPVOID)GetProcAddress(hOLE, "CoMarshalInterface"); |
| COM_UnmarshalInterface = (LPVOID)GetProcAddress(hOLE, "CoUnmarshalInterface"); |
| COM_ReleaseMarshalData = (LPVOID)GetProcAddress(hOLE, "CoReleaseMarshalData"); |
| COM_GetClassObject = (LPVOID)GetProcAddress(hOLE, "CoGetClassObject"); |
| COM_GetPSClsid = (LPVOID)GetProcAddress(hOLE, "CoGetPSClsid"); |
| COM_MemAlloc = (LPVOID)GetProcAddress(hOLE, "CoTaskMemAlloc"); |
| COM_MemFree = (LPVOID)GetProcAddress(hOLE, "CoTaskMemFree"); |
| return hOLE; |
| } |
| |
| /* CoMarshalInterface/CoUnmarshalInterface works on streams, |
| * so implement a simple stream on top of the RPC buffer |
| * (which also implements the MInterfacePointer structure) */ |
| typedef struct RpcStreamImpl |
| { |
| IStream IStream_iface; |
| LONG RefCount; |
| PMIDL_STUB_MESSAGE pMsg; |
| LPDWORD size; |
| unsigned char *data; |
| DWORD pos; |
| } RpcStreamImpl; |
| |
| static inline RpcStreamImpl *impl_from_IStream(IStream *iface) |
| { |
| return CONTAINING_RECORD(iface, RpcStreamImpl, IStream_iface); |
| } |
| |
| static HRESULT WINAPI RpcStream_QueryInterface(LPSTREAM iface, |
| REFIID riid, |
| LPVOID *obj) |
| { |
| if (IsEqualGUID(&IID_IUnknown, riid) || |
| IsEqualGUID(&IID_ISequentialStream, riid) || |
| IsEqualGUID(&IID_IStream, riid)) { |
| *obj = iface; |
| IStream_AddRef(iface); |
| return S_OK; |
| } |
| |
| *obj = NULL; |
| return E_NOINTERFACE; |
| } |
| |
| static ULONG WINAPI RpcStream_AddRef(LPSTREAM iface) |
| { |
| RpcStreamImpl *This = impl_from_IStream(iface); |
| return InterlockedIncrement( &This->RefCount ); |
| } |
| |
| static ULONG WINAPI RpcStream_Release(LPSTREAM iface) |
| { |
| RpcStreamImpl *This = impl_from_IStream(iface); |
| ULONG ref = InterlockedDecrement( &This->RefCount ); |
| if (!ref) { |
| TRACE("size=%d\n", *This->size); |
| This->pMsg->Buffer = This->data + *This->size; |
| HeapFree(GetProcessHeap(),0,This); |
| } |
| return ref; |
| } |
| |
| static HRESULT WINAPI RpcStream_Read(LPSTREAM iface, |
| void *pv, |
| ULONG cb, |
| ULONG *pcbRead) |
| { |
| RpcStreamImpl *This = impl_from_IStream(iface); |
| HRESULT hr = S_OK; |
| if (This->pos + cb > *This->size) |
| { |
| cb = *This->size - This->pos; |
| hr = S_FALSE; |
| } |
| if (cb) { |
| memcpy(pv, This->data + This->pos, cb); |
| This->pos += cb; |
| } |
| if (pcbRead) *pcbRead = cb; |
| return hr; |
| } |
| |
| static HRESULT WINAPI RpcStream_Write(LPSTREAM iface, |
| const void *pv, |
| ULONG cb, |
| ULONG *pcbWritten) |
| { |
| RpcStreamImpl *This = impl_from_IStream(iface); |
| if (This->data + cb > (unsigned char *)This->pMsg->RpcMsg->Buffer + This->pMsg->BufferLength) |
| return STG_E_MEDIUMFULL; |
| memcpy(This->data + This->pos, pv, cb); |
| This->pos += cb; |
| if (This->pos > *This->size) *This->size = This->pos; |
| if (pcbWritten) *pcbWritten = cb; |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI RpcStream_Seek(LPSTREAM iface, |
| LARGE_INTEGER move, |
| DWORD origin, |
| ULARGE_INTEGER *newPos) |
| { |
| RpcStreamImpl *This = impl_from_IStream(iface); |
| switch (origin) { |
| case STREAM_SEEK_SET: |
| This->pos = move.u.LowPart; |
| break; |
| case STREAM_SEEK_CUR: |
| This->pos = This->pos + move.u.LowPart; |
| break; |
| case STREAM_SEEK_END: |
| This->pos = *This->size + move.u.LowPart; |
| break; |
| default: |
| return STG_E_INVALIDFUNCTION; |
| } |
| if (newPos) { |
| newPos->u.LowPart = This->pos; |
| newPos->u.HighPart = 0; |
| } |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI RpcStream_SetSize(LPSTREAM iface, |
| ULARGE_INTEGER newSize) |
| { |
| RpcStreamImpl *This = impl_from_IStream(iface); |
| *This->size = newSize.u.LowPart; |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI RpcStream_CopyTo(IStream *iface, IStream *dest, |
| ULARGE_INTEGER len, ULARGE_INTEGER *read, ULARGE_INTEGER *written) |
| { |
| RpcStreamImpl *This = impl_from_IStream(iface); |
| FIXME("(%p): stub\n", This); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI RpcStream_Commit(IStream *iface, DWORD flags) |
| { |
| RpcStreamImpl *This = impl_from_IStream(iface); |
| FIXME("(%p)->(0x%08x): stub\n", This, flags); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI RpcStream_Revert(IStream *iface) |
| { |
| RpcStreamImpl *This = impl_from_IStream(iface); |
| FIXME("(%p): stub\n", This); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI RpcStream_LockRegion(IStream *iface, |
| ULARGE_INTEGER offset, ULARGE_INTEGER len, DWORD locktype) |
| { |
| RpcStreamImpl *This = impl_from_IStream(iface); |
| FIXME("(%p): stub\n", This); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI RpcStream_UnlockRegion(IStream *iface, |
| ULARGE_INTEGER offset, ULARGE_INTEGER len, DWORD locktype) |
| { |
| RpcStreamImpl *This = impl_from_IStream(iface); |
| FIXME("(%p): stub\n", This); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI RpcStream_Stat(IStream *iface, STATSTG *stat, DWORD flag) |
| { |
| RpcStreamImpl *This = impl_from_IStream(iface); |
| FIXME("(%p): stub\n", This); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI RpcStream_Clone(IStream *iface, IStream **cloned) |
| { |
| RpcStreamImpl *This = impl_from_IStream(iface); |
| FIXME("(%p): stub\n", This); |
| return E_NOTIMPL; |
| } |
| |
| static const IStreamVtbl RpcStream_Vtbl = |
| { |
| RpcStream_QueryInterface, |
| RpcStream_AddRef, |
| RpcStream_Release, |
| RpcStream_Read, |
| RpcStream_Write, |
| RpcStream_Seek, |
| RpcStream_SetSize, |
| RpcStream_CopyTo, |
| RpcStream_Commit, |
| RpcStream_Revert, |
| RpcStream_LockRegion, |
| RpcStream_UnlockRegion, |
| RpcStream_Stat, |
| RpcStream_Clone |
| }; |
| |
| static HRESULT RpcStream_Create(PMIDL_STUB_MESSAGE pStubMsg, BOOL init, ULONG *size, IStream **stream) |
| { |
| RpcStreamImpl *This; |
| |
| *stream = NULL; |
| This = HeapAlloc(GetProcessHeap(), 0, sizeof(RpcStreamImpl)); |
| if (!This) return E_OUTOFMEMORY; |
| This->IStream_iface.lpVtbl = &RpcStream_Vtbl; |
| This->RefCount = 1; |
| This->pMsg = pStubMsg; |
| This->size = (LPDWORD)pStubMsg->Buffer; |
| This->data = pStubMsg->Buffer + sizeof(DWORD); |
| This->pos = 0; |
| if (init) *This->size = 0; |
| TRACE("init size=%d\n", *This->size); |
| |
| if (size) *size = *This->size; |
| *stream = &This->IStream_iface; |
| return S_OK; |
| } |
| |
| static const IID* get_ip_iid(PMIDL_STUB_MESSAGE pStubMsg, unsigned char *pMemory, PFORMAT_STRING pFormat) |
| { |
| const IID *riid; |
| if (!pFormat) return &IID_IUnknown; |
| TRACE("format=%02x %02x\n", pFormat[0], pFormat[1]); |
| if (pFormat[0] != RPC_FC_IP) FIXME("format=%d\n", pFormat[0]); |
| if (pFormat[1] == RPC_FC_CONSTANT_IID) { |
| riid = (const IID *)&pFormat[2]; |
| } else { |
| ComputeConformance(pStubMsg, pMemory, pFormat+2, 0); |
| riid = (const IID *)pStubMsg->MaxCount; |
| } |
| if (!riid) riid = &IID_IUnknown; |
| TRACE("got %s\n", debugstr_guid(riid)); |
| return riid; |
| } |
| |
| /*********************************************************************** |
| * NdrInterfacePointerMarshall [RPCRT4.@] |
| */ |
| unsigned char * WINAPI NdrInterfacePointerMarshall(PMIDL_STUB_MESSAGE pStubMsg, |
| unsigned char *pMemory, |
| PFORMAT_STRING pFormat) |
| { |
| const IID *riid = get_ip_iid(pStubMsg, pMemory, pFormat); |
| LPSTREAM stream; |
| HRESULT hr; |
| |
| TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat); |
| pStubMsg->MaxCount = 0; |
| if (!LoadCOM()) return NULL; |
| if (pStubMsg->Buffer + sizeof(DWORD) <= (unsigned char *)pStubMsg->RpcMsg->Buffer + pStubMsg->BufferLength) { |
| hr = RpcStream_Create(pStubMsg, TRUE, NULL, &stream); |
| if (hr == S_OK) { |
| if (pMemory) |
| hr = COM_MarshalInterface(stream, riid, (LPUNKNOWN)pMemory, |
| pStubMsg->dwDestContext, pStubMsg->pvDestContext, |
| MSHLFLAGS_NORMAL); |
| IStream_Release(stream); |
| } |
| |
| if (FAILED(hr)) |
| RpcRaiseException(hr); |
| } |
| return NULL; |
| } |
| |
| /*********************************************************************** |
| * NdrInterfacePointerUnmarshall [RPCRT4.@] |
| */ |
| unsigned char * WINAPI NdrInterfacePointerUnmarshall(PMIDL_STUB_MESSAGE pStubMsg, |
| unsigned char **ppMemory, |
| PFORMAT_STRING pFormat, |
| unsigned char fMustAlloc) |
| { |
| LPSTREAM stream; |
| HRESULT hr; |
| |
| TRACE("(%p,%p,%p,%d)\n", pStubMsg, ppMemory, pFormat, fMustAlloc); |
| if (!LoadCOM()) return NULL; |
| *(LPVOID*)ppMemory = NULL; |
| if (pStubMsg->Buffer + sizeof(DWORD) < (unsigned char *)pStubMsg->RpcMsg->Buffer + pStubMsg->BufferLength) { |
| ULONG size; |
| |
| hr = RpcStream_Create(pStubMsg, FALSE, &size, &stream); |
| if (hr == S_OK) { |
| if (size != 0) |
| hr = COM_UnmarshalInterface(stream, &IID_NULL, (LPVOID*)ppMemory); |
| |
| IStream_Release(stream); |
| } |
| |
| if (FAILED(hr)) |
| RpcRaiseException(hr); |
| } |
| return NULL; |
| } |
| |
| /*********************************************************************** |
| * NdrInterfacePointerBufferSize [RPCRT4.@] |
| */ |
| void WINAPI NdrInterfacePointerBufferSize(PMIDL_STUB_MESSAGE pStubMsg, |
| unsigned char *pMemory, |
| PFORMAT_STRING pFormat) |
| { |
| const IID *riid = get_ip_iid(pStubMsg, pMemory, pFormat); |
| ULONG size = 0; |
| |
| TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat); |
| if (!LoadCOM()) return; |
| COM_GetMarshalSizeMax(&size, riid, (LPUNKNOWN)pMemory, |
| pStubMsg->dwDestContext, pStubMsg->pvDestContext, |
| MSHLFLAGS_NORMAL); |
| TRACE("size=%d\n", size); |
| pStubMsg->BufferLength += sizeof(DWORD) + size; |
| } |
| |
| /*********************************************************************** |
| * NdrInterfacePointerMemorySize [RPCRT4.@] |
| */ |
| ULONG WINAPI NdrInterfacePointerMemorySize(PMIDL_STUB_MESSAGE pStubMsg, |
| PFORMAT_STRING pFormat) |
| { |
| ULONG size; |
| |
| TRACE("(%p,%p)\n", pStubMsg, pFormat); |
| |
| size = *(ULONG *)pStubMsg->Buffer; |
| pStubMsg->Buffer += 4; |
| pStubMsg->MemorySize += 4; |
| |
| pStubMsg->Buffer += size; |
| |
| return pStubMsg->MemorySize; |
| } |
| |
| /*********************************************************************** |
| * NdrInterfacePointerFree [RPCRT4.@] |
| */ |
| void WINAPI NdrInterfacePointerFree(PMIDL_STUB_MESSAGE pStubMsg, |
| unsigned char *pMemory, |
| PFORMAT_STRING pFormat) |
| { |
| LPUNKNOWN pUnk = (LPUNKNOWN)pMemory; |
| TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat); |
| if (pUnk) IUnknown_Release(pUnk); |
| } |
| |
| /*********************************************************************** |
| * NdrOleAllocate [RPCRT4.@] |
| */ |
| void * WINAPI NdrOleAllocate(SIZE_T Size) |
| { |
| if (!LoadCOM()) return NULL; |
| return COM_MemAlloc(Size); |
| } |
| |
| /*********************************************************************** |
| * NdrOleFree [RPCRT4.@] |
| */ |
| void WINAPI NdrOleFree(void *NodeToFree) |
| { |
| if (!LoadCOM()) return; |
| COM_MemFree(NodeToFree); |
| } |
| |
| /*********************************************************************** |
| * Helper function to create a proxy. |
| * Probably similar to NdrpCreateProxy. |
| */ |
| HRESULT create_proxy(REFIID iid, IUnknown *pUnkOuter, IRpcProxyBuffer **pproxy, void **ppv) |
| { |
| CLSID clsid; |
| IPSFactoryBuffer *psfac; |
| HRESULT r; |
| |
| if(!LoadCOM()) return E_FAIL; |
| |
| r = COM_GetPSClsid( iid, &clsid ); |
| if(FAILED(r)) return r; |
| |
| r = COM_GetClassObject( &clsid, CLSCTX_INPROC_SERVER, NULL, &IID_IPSFactoryBuffer, (void**)&psfac ); |
| if(FAILED(r)) return r; |
| |
| r = IPSFactoryBuffer_CreateProxy(psfac, pUnkOuter, iid, pproxy, ppv); |
| |
| IPSFactoryBuffer_Release(psfac); |
| return r; |
| } |
| |
| /*********************************************************************** |
| * Helper function to create a stub. |
| * This probably looks very much like NdrpCreateStub. |
| */ |
| HRESULT create_stub(REFIID iid, IUnknown *pUnk, IRpcStubBuffer **ppstub) |
| { |
| CLSID clsid; |
| IPSFactoryBuffer *psfac; |
| HRESULT r; |
| |
| if(!LoadCOM()) return E_FAIL; |
| |
| r = COM_GetPSClsid( iid, &clsid ); |
| if(FAILED(r)) return r; |
| |
| r = COM_GetClassObject( &clsid, CLSCTX_INPROC_SERVER, NULL, &IID_IPSFactoryBuffer, (void**)&psfac ); |
| if(FAILED(r)) return r; |
| |
| r = IPSFactoryBuffer_CreateStub(psfac, iid, pUnk, ppstub); |
| |
| IPSFactoryBuffer_Release(psfac); |
| return r; |
| } |