reactos/dll/win32/rpcrt4/ndr_ole.c

485 lines
14 KiB
C
Raw Normal View History

/*
* 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 "ndrtypes.h"
#include "rpcproxy.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] != FC_IP) FIXME("format=%d\n", pFormat[0]);
if (pFormat[1] == 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)
{
IUnknown **unk = (IUnknown **)ppMemory;
LPSTREAM stream;
HRESULT hr;
TRACE("(%p,%p,%p,%d)\n", pStubMsg, ppMemory, pFormat, fMustAlloc);
if (!LoadCOM()) return NULL;
/* Avoid reference leaks for [in, out] pointers. */
if (pStubMsg->IsClient && *unk)
IUnknown_Release(*unk);
*unk = NULL;
Sync to Wine-0_9_3: Robert Shearman <rob@codeweavers.com> - Return the correct error code from NdrProxyErrorHandler. - Add a function to retrieve the MIDL_SERVER_INFO struct from an object. - Make sure to fill out the MIDL_STUB_MESSAGE structure in NdrSendReceive like we do in NdrProxySendReceive. - Fix the overflow check to not depend on pStubMsg->BufferStart and pStubMsg->BufferEnd being valid, because they aren't filled in when using MIDL-generated server stubs. - Don't set the pointer to NULL on unmarshaling because we may want to unmarshal the value to an existing pointer instead of allocating a new one. - Raise exceptions on failures. Replace references of pStubMsg->BufferEnd with RpcMsg->Buffer + pStubMsg->BufferLength. - Fix buffer calculation when no interface data is marshaled to the stream. - Implement conformant varying array functions. - Implement conformant struct functions. - Implement FC_STRUCTPAD2 for complex types. - Add functions for marshaling base types (ints, floats, etc.). - Extend conformance computation function to also compute variances. MSDN suggests that conformance and variance are pretty much the same, but there may be some subtleties to it. - Fix NdrConformantArrayBufferSize to include the size of the conformance value. Make NdrConformantArrayMemorySize do something more useful, like actually return the required memory. Conformance offset can be negative and should only be two bytes. - We should always allocate in NdrConformantStringUnmarshal if the memory pointer is NULL. - The CLSID can be substituted by an IID present in one of the proxy file infos in NdrDllGetClassObject. Ge van Geldorp <gvg@reactos.org> - Match PSDK STATUS_* definitions. svn path=/trunk/; revision=20167
2005-12-14 19:02:42 +00:00
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, (void **)unk);
IStream_Release(stream);
}
if (FAILED(hr))
Sync to Wine-0_9_3: Robert Shearman <rob@codeweavers.com> - Return the correct error code from NdrProxyErrorHandler. - Add a function to retrieve the MIDL_SERVER_INFO struct from an object. - Make sure to fill out the MIDL_STUB_MESSAGE structure in NdrSendReceive like we do in NdrProxySendReceive. - Fix the overflow check to not depend on pStubMsg->BufferStart and pStubMsg->BufferEnd being valid, because they aren't filled in when using MIDL-generated server stubs. - Don't set the pointer to NULL on unmarshaling because we may want to unmarshal the value to an existing pointer instead of allocating a new one. - Raise exceptions on failures. Replace references of pStubMsg->BufferEnd with RpcMsg->Buffer + pStubMsg->BufferLength. - Fix buffer calculation when no interface data is marshaled to the stream. - Implement conformant varying array functions. - Implement conformant struct functions. - Implement FC_STRUCTPAD2 for complex types. - Add functions for marshaling base types (ints, floats, etc.). - Extend conformance computation function to also compute variances. MSDN suggests that conformance and variance are pretty much the same, but there may be some subtleties to it. - Fix NdrConformantArrayBufferSize to include the size of the conformance value. Make NdrConformantArrayMemorySize do something more useful, like actually return the required memory. Conformance offset can be negative and should only be two bytes. - We should always allocate in NdrConformantStringUnmarshal if the memory pointer is NULL. - The CLSID can be substituted by an IID present in one of the proxy file infos in NdrDllGetClassObject. Ge van Geldorp <gvg@reactos.org> - Match PSDK STATUS_* definitions. svn path=/trunk/; revision=20167
2005-12-14 19:02:42 +00:00
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);
Sync to Wine-0_9_3: Robert Shearman <rob@codeweavers.com> - Return the correct error code from NdrProxyErrorHandler. - Add a function to retrieve the MIDL_SERVER_INFO struct from an object. - Make sure to fill out the MIDL_STUB_MESSAGE structure in NdrSendReceive like we do in NdrProxySendReceive. - Fix the overflow check to not depend on pStubMsg->BufferStart and pStubMsg->BufferEnd being valid, because they aren't filled in when using MIDL-generated server stubs. - Don't set the pointer to NULL on unmarshaling because we may want to unmarshal the value to an existing pointer instead of allocating a new one. - Raise exceptions on failures. Replace references of pStubMsg->BufferEnd with RpcMsg->Buffer + pStubMsg->BufferLength. - Fix buffer calculation when no interface data is marshaled to the stream. - Implement conformant varying array functions. - Implement conformant struct functions. - Implement FC_STRUCTPAD2 for complex types. - Add functions for marshaling base types (ints, floats, etc.). - Extend conformance computation function to also compute variances. MSDN suggests that conformance and variance are pretty much the same, but there may be some subtleties to it. - Fix NdrConformantArrayBufferSize to include the size of the conformance value. Make NdrConformantArrayMemorySize do something more useful, like actually return the required memory. Conformance offset can be negative and should only be two bytes. - We should always allocate in NdrConformantStringUnmarshal if the memory pointer is NULL. - The CLSID can be substituted by an IID present in one of the proxy file infos in NdrDllGetClassObject. Ge van Geldorp <gvg@reactos.org> - Match PSDK STATUS_* definitions. svn path=/trunk/; revision=20167
2005-12-14 19:02:42 +00:00
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;
}