📄 usrmarshal.c
字号:
/*
* Misc marshalling routines
*
* Copyright 2002 Ove Kaaven
* Copyright 2003 Mike Hearn
*
* 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
#define NONAMELESSUNION
#define NONAMELESSSTRUCT
#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
#include "winuser.h"
#include "winerror.h"
#include "ole2.h"
#include "oleauto.h"
#include "rpcproxy.h"
#include "typelib.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(ole);
#define ALIGNED_LENGTH(_Len, _Align) (((_Len)+(_Align))&~(_Align))
#define ALIGNED_POINTER(_Ptr, _Align) ((LPVOID)ALIGNED_LENGTH((ULONG_PTR)(_Ptr), _Align))
#define ALIGN_LENGTH(_Len, _Align) _Len = ALIGNED_LENGTH(_Len, _Align)
#define ALIGN_POINTER(_Ptr, _Align) _Ptr = ALIGNED_POINTER(_Ptr, _Align)
static CStdPSFactoryBuffer PSFactoryBuffer;
CSTDSTUBBUFFERRELEASE(&PSFactoryBuffer)
extern const ExtendedProxyFileInfo oaidl_ProxyFileInfo;
static const ProxyFileInfo *OLEAUT32_ProxyFileList[] = {
&oaidl_ProxyFileInfo,
NULL
};
HRESULT OLEAUTPS_DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
{
return NdrDllGetClassObject(rclsid, riid, ppv, OLEAUT32_ProxyFileList,
&CLSID_PSDispatch, &PSFactoryBuffer);
}
static void dump_user_flags(ULONG *pFlags)
{
if (HIWORD(*pFlags) == NDR_LOCAL_DATA_REPRESENTATION)
TRACE("MAKELONG(NDR_LOCAL_REPRESENTATION, ");
else
TRACE("MAKELONG(0x%04x, ", HIWORD(*pFlags));
switch (LOWORD(*pFlags))
{
case MSHCTX_LOCAL: TRACE("MSHCTX_LOCAL)"); break;
case MSHCTX_NOSHAREDMEM: TRACE("MSHCTX_NOSHAREDMEM)"); break;
case MSHCTX_DIFFERENTMACHINE: TRACE("MSHCTX_DIFFERENTMACHINE)"); break;
case MSHCTX_INPROC: TRACE("MSHCTX_INPROC)"); break;
default: TRACE("%d)", LOWORD(*pFlags));
}
}
/* CLEANLOCALSTORAGE */
#define CLS_FUNCDESC 'f'
#define CLS_LIBATTR 'l'
#define CLS_TYPEATTR 't'
#define CLS_VARDESC 'v'
ULONG WINAPI CLEANLOCALSTORAGE_UserSize(ULONG *pFlags, ULONG Start, CLEANLOCALSTORAGE *pstg)
{
ALIGN_LENGTH(Start, 3);
return Start + sizeof(DWORD);
}
unsigned char * WINAPI CLEANLOCALSTORAGE_UserMarshal(ULONG *pFlags, unsigned char *Buffer, CLEANLOCALSTORAGE *pstg)
{
ALIGN_POINTER(Buffer, 3);
*(DWORD*)Buffer = pstg->flags;
switch(pstg->flags)
{
case CLS_LIBATTR:
ITypeLib_ReleaseTLibAttr((ITypeLib*)pstg->pInterface, *(TLIBATTR**)pstg->pStorage);
break;
case CLS_TYPEATTR:
ITypeInfo_ReleaseTypeAttr((ITypeInfo*)pstg->pInterface, *(TYPEATTR**)pstg->pStorage);
break;
case CLS_FUNCDESC:
ITypeInfo_ReleaseFuncDesc((ITypeInfo*)pstg->pInterface, *(FUNCDESC**)pstg->pStorage);
break;
case CLS_VARDESC:
ITypeInfo_ReleaseVarDesc((ITypeInfo*)pstg->pInterface, *(VARDESC**)pstg->pStorage);
break;
default:
ERR("Unknown type %x\n", pstg->flags);
}
*(VOID**)pstg->pStorage = NULL;
IUnknown_Release(pstg->pInterface);
pstg->pInterface = NULL;
return Buffer + sizeof(DWORD);
}
unsigned char * WINAPI CLEANLOCALSTORAGE_UserUnmarshal(ULONG *pFlags, unsigned char *Buffer, CLEANLOCALSTORAGE *pstr)
{
ALIGN_POINTER(Buffer, 3);
pstr->flags = *(DWORD*)Buffer;
return Buffer + sizeof(DWORD);
}
void WINAPI CLEANLOCALSTORAGE_UserFree(ULONG *pFlags, CLEANLOCALSTORAGE *pstr)
{
/* Nothing to do */
}
/* BSTR */
typedef struct
{
DWORD len; /* No. of chars not including trailing '\0' */
DWORD byte_len; /* len * 2 or 0xffffffff if len == 0 */
DWORD len2; /* == len */
} bstr_wire_t;
ULONG WINAPI BSTR_UserSize(ULONG *pFlags, ULONG Start, BSTR *pstr)
{
TRACE("(%x,%d,%p) => %p\n", *pFlags, Start, pstr, *pstr);
if (*pstr) TRACE("string=%s\n", debugstr_w(*pstr));
ALIGN_LENGTH(Start, 3);
Start += sizeof(bstr_wire_t) + ((SysStringByteLen(*pstr) + 1) & ~1);
TRACE("returning %d\n", Start);
return Start;
}
unsigned char * WINAPI BSTR_UserMarshal(ULONG *pFlags, unsigned char *Buffer, BSTR *pstr)
{
bstr_wire_t *header;
DWORD len = SysStringByteLen(*pstr);
TRACE("(%x,%p,%p) => %p\n", *pFlags, Buffer, pstr, *pstr);
if (*pstr) TRACE("string=%s\n", debugstr_w(*pstr));
ALIGN_POINTER(Buffer, 3);
header = (bstr_wire_t*)Buffer;
header->len = header->len2 = (len + 1) / 2;
if (*pstr)
{
header->byte_len = len;
memcpy(header + 1, *pstr, header->len * 2);
}
else
header->byte_len = 0xffffffff; /* special case for a null bstr */
return Buffer + sizeof(*header) + sizeof(OLECHAR) * header->len;
}
unsigned char * WINAPI BSTR_UserUnmarshal(ULONG *pFlags, unsigned char *Buffer, BSTR *pstr)
{
bstr_wire_t *header;
TRACE("(%x,%p,%p) => %p\n", *pFlags, Buffer, pstr, *pstr);
ALIGN_POINTER(Buffer, 3);
header = (bstr_wire_t*)Buffer;
if(header->len != header->len2)
FIXME("len %08x != len2 %08x\n", header->len, header->len2);
if(*pstr)
{
SysFreeString(*pstr);
*pstr = NULL;
}
if(header->byte_len != 0xffffffff)
*pstr = SysAllocStringByteLen((char*)(header + 1), header->byte_len);
if (*pstr) TRACE("string=%s\n", debugstr_w(*pstr));
return Buffer + sizeof(*header) + sizeof(OLECHAR) * header->len;
}
void WINAPI BSTR_UserFree(ULONG *pFlags, BSTR *pstr)
{
TRACE("(%x,%p) => %p\n", *pFlags, pstr, *pstr);
if (*pstr)
{
SysFreeString(*pstr);
*pstr = NULL;
}
}
/* VARIANT */
typedef struct
{
DWORD clSize;
DWORD rpcReserverd;
USHORT vt;
USHORT wReserved1;
USHORT wReserved2;
USHORT wReserved3;
DWORD switch_is;
} variant_wire_t;
static unsigned int get_type_size(ULONG *pFlags, VARIANT *pvar)
{
if (V_VT(pvar) & VT_ARRAY) return 4;
switch (V_VT(pvar) & ~VT_BYREF) {
case VT_EMPTY:
case VT_NULL:
return 0;
case VT_I1:
case VT_UI1:
return sizeof(CHAR);
case VT_I2:
case VT_UI2:
return sizeof(SHORT);
case VT_I4:
case VT_UI4:
return sizeof(LONG);
case VT_INT:
case VT_UINT:
return sizeof(INT);
case VT_R4:
return sizeof(FLOAT);
case VT_R8:
return sizeof(DOUBLE);
case VT_BOOL:
return sizeof(VARIANT_BOOL);
case VT_ERROR:
return sizeof(SCODE);
case VT_DATE:
return sizeof(DATE);
case VT_CY:
return sizeof(CY);
case VT_DECIMAL:
return sizeof(DECIMAL);
case VT_BSTR:
return sizeof(BSTR);
case VT_VARIANT:
return sizeof(VARIANT);
case VT_UNKNOWN:
case VT_DISPATCH:
case VT_RECORD:
return 0;
default:
FIXME("unhandled VT %d\n", V_VT(pvar));
return 0;
}
}
static unsigned int get_type_alignment(ULONG *pFlags, VARIANT *pvar)
{
unsigned int size = get_type_size(pFlags, pvar);
if(V_VT(pvar) & VT_BYREF) return 3;
if(size == 0) return 0;
if(size <= 4) return size - 1;
return 7;
}
static unsigned interface_variant_size(ULONG *pFlags, REFIID riid, IUnknown *punk)
{
ULONG size;
HRESULT hr;
/* find the buffer size of the marshalled dispatch interface */
hr = CoGetMarshalSizeMax(&size, riid, punk, LOWORD(*pFlags), NULL, MSHLFLAGS_NORMAL);
if (FAILED(hr)) {
if (!punk)
WARN("NULL dispatch pointer\n");
else
ERR("Dispatch variant buffer size calculation failed, HRESULT=0x%x\n", hr);
return 0;
}
size += sizeof(ULONG); /* we have to store the buffersize in the stream */
TRACE("wire-size extra of dispatch variant is %d\n", size);
return size;
}
static ULONG wire_extra_user_size(ULONG *pFlags, ULONG Start, VARIANT *pvar)
{
if (V_ISARRAY(pvar))
{
if (V_ISBYREF(pvar))
return LPSAFEARRAY_UserSize(pFlags, Start, V_ARRAYREF(pvar));
else
return LPSAFEARRAY_UserSize(pFlags, Start, &V_ARRAY(pvar));
}
switch (V_VT(pvar)) {
case VT_BSTR:
return BSTR_UserSize(pFlags, Start, &V_BSTR(pvar));
case VT_BSTR | VT_BYREF:
return BSTR_UserSize(pFlags, Start, V_BSTRREF(pvar));
case VT_VARIANT | VT_BYREF:
return VARIANT_UserSize(pFlags, Start, V_VARIANTREF(pvar));
case VT_UNKNOWN:
return Start + interface_variant_size(pFlags, &IID_IUnknown, V_UNKNOWN(pvar));
case VT_UNKNOWN | VT_BYREF:
return Start + interface_variant_size(pFlags, &IID_IUnknown, *V_UNKNOWNREF(pvar));
case VT_DISPATCH:
return Start + interface_variant_size(pFlags, &IID_IDispatch, (IUnknown*)V_DISPATCH(pvar));
case VT_DISPATCH | VT_BYREF:
return Start + interface_variant_size(pFlags, &IID_IDispatch, (IUnknown*)*V_DISPATCHREF(pvar));
case VT_RECORD:
FIXME("wire-size record\n");
return Start;
case VT_SAFEARRAY:
case VT_SAFEARRAY | VT_BYREF:
FIXME("wire-size safearray: shouldn't be marshaling this\n");
return Start;
default:
return Start;
}
}
/* helper: called for VT_DISPATCH variants to marshal the IDispatch* into the buffer. returns Buffer on failure, new position otherwise */
static unsigned char* interface_variant_marshal(ULONG *pFlags, unsigned char *Buffer, REFIID riid, IUnknown *punk)
{
IStream *working;
HGLOBAL working_mem;
void *working_memlocked;
unsigned char *oldpos;
ULONG size;
HRESULT hr;
TRACE("pFlags=%d, Buffer=%p, pUnk=%p\n", *pFlags, Buffer, punk);
oldpos = Buffer;
/* CoMarshalInterface needs a stream, whereas at this level we are operating in terms of buffers.
* We create a stream on an HGLOBAL, so we can simply do a memcpy to move it to the buffer.
* in rpcrt4/ndr_ole.c, a simple IStream implementation is wrapped around the buffer object,
* but that would be overkill here, hence this implementation. We save the size because the unmarshal
* code has no way to know how long the marshalled buffer is. */
size = interface_variant_size(pFlags, riid, punk);
working_mem = GlobalAlloc(0, size);
if (!working_mem) return oldpos;
hr = CreateStreamOnHGlobal(working_mem, TRUE, &working);
if (hr != S_OK) {
GlobalFree(working_mem);
return oldpos;
}
hr = CoMarshalInterface(working, riid, punk, LOWORD(*pFlags), NULL, MSHLFLAGS_NORMAL);
if (hr != S_OK) {
IStream_Release(working); /* this also releases the hglobal */
return oldpos;
}
working_memlocked = GlobalLock(working_mem);
memcpy(Buffer, &size, sizeof(ULONG)); /* copy the buffersize */
memcpy(Buffer + sizeof(ULONG), working_memlocked, size - sizeof(ULONG));
GlobalUnlock(working_mem);
IStream_Release(working);
/* size includes the ULONG for the size written above */
TRACE("done, size=%d\n", size);
return Buffer + size;
}
/* helper: called for VT_DISPATCH / VT_UNKNOWN variants to unmarshal the buffer. returns Buffer on failure, new position otherwise */
static unsigned char *interface_variant_unmarshal(ULONG *pFlags, unsigned char *Buffer, REFIID riid, IUnknown **ppunk)
{
IStream *working;
HGLOBAL working_mem;
void *working_memlocked;
unsigned char *oldpos;
ULONG size;
HRESULT hr;
TRACE("pFlags=%d, Buffer=%p, ppUnk=%p\n", *pFlags, Buffer, ppunk);
oldpos = Buffer;
/* get the buffersize */
memcpy(&size, Buffer, sizeof(ULONG));
TRACE("buffersize=%d\n", size);
working_mem = GlobalAlloc(0, size);
if (!working_mem) return oldpos;
hr = CreateStreamOnHGlobal(working_mem, TRUE, &working);
if (hr != S_OK) {
GlobalFree(working_mem);
return oldpos;
}
working_memlocked = GlobalLock(working_mem);
/* now we copy the contents of the marshalling buffer to working_memlocked, unlock it, and demarshal the stream */
memcpy(working_memlocked, Buffer + sizeof(ULONG), size);
GlobalUnlock(working_mem);
hr = CoUnmarshalInterface(working, riid, (void**)ppunk);
if (hr != S_OK) {
IStream_Release(working);
return oldpos;
}
IStream_Release(working); /* this also frees the underlying hglobal */
/* size includes the ULONG for the size written above */
TRACE("done, processed=%d bytes\n", size);
return Buffer + size;
}
ULONG WINAPI VARIANT_UserSize(ULONG *pFlags, ULONG Start, VARIANT *pvar)
{
int align;
TRACE("(%x,%d,%p)\n", *pFlags, Start, pvar);
TRACE("vt=%04x\n", V_VT(pvar));
ALIGN_LENGTH(Start, 7);
Start += sizeof(variant_wire_t);
if(V_VT(pvar) & VT_BYREF)
Start += 4;
align = get_type_alignment(pFlags, pvar);
ALIGN_LENGTH(Start, align);
if(V_VT(pvar) == (VT_VARIANT | VT_BYREF))
Start += 4;
else
Start += get_type_size(pFlags, pvar);
Start = wire_extra_user_size(pFlags, Start, pvar);
TRACE("returning %d\n", Start);
return Start;
}
unsigned char * WINAPI VARIANT_UserMarshal(ULONG *pFlags, unsigned char *Buffer, VARIANT *pvar)
{
variant_wire_t *header;
ULONG type_size;
int align;
unsigned char *Pos;
TRACE("(%x,%p,%p)\n", *pFlags, Buffer, pvar);
TRACE("vt=%04x\n", V_VT(pvar));
ALIGN_POINTER(Buffer, 7);
header = (variant_wire_t *)Buffer;
header->clSize = 0; /* fixed up at the end */
header->rpcReserverd = 0;
header->vt = pvar->n1.n2.vt;
header->wReserved1 = pvar->n1.n2.wReserved1;
header->wReserved2 = pvar->n1.n2.wReserved2;
header->wReserved3 = pvar->n1.n2.wReserved3;
header->switch_is = pvar->n1.n2.vt;
if(header->switch_is & VT_ARRAY)
header->switch_is &= ~VT_TYPEMASK;
Pos = (unsigned char*)(header + 1);
type_size = get_type_size(pFlags, pvar);
align = get_type_alignment(pFlags, pvar);
ALIGN_POINTER(Pos, align);
if(header->vt & VT_BYREF)
{
*(DWORD *)Pos = max(type_size, 4);
Pos += 4;
if((header->vt & VT_TYPEMASK) != VT_VARIANT)
{
memcpy(Pos, pvar->n1.n2.n3.byref, type_size);
Pos += type_size;
}
else
{
*(DWORD*)Pos = 'U' | 's' << 8 | 'e' << 16 | 'r' << 24;
Pos += 4;
}
}
else
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -