📄 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(ole);
/* FIXME: not supposed to be here */
const CLSID CLSID_PSDispatch = {
0x20420, 0, 0, {0xC0, 0, 0, 0, 0, 0, 0, 0x46}
};
static CStdPSFactoryBuffer PSFactoryBuffer;
CSTDSTUBBUFFERRELEASE(&PSFactoryBuffer)
extern const ExtendedProxyFileInfo oaidl_ProxyFileInfo;
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(unsigned long *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 */
/* I'm not sure how this is supposed to work yet */
unsigned long WINAPI CLEANLOCALSTORAGE_UserSize(unsigned long *pFlags, unsigned long Start, CLEANLOCALSTORAGE *pstg)
{
return Start + sizeof(DWORD);
}
unsigned char * WINAPI CLEANLOCALSTORAGE_UserMarshal(unsigned long *pFlags, unsigned char *Buffer, CLEANLOCALSTORAGE *pstg)
{
*(DWORD*)Buffer = 0;
return Buffer + sizeof(DWORD);
}
unsigned char * WINAPI CLEANLOCALSTORAGE_UserUnmarshal(unsigned long *pFlags, unsigned char *Buffer, CLEANLOCALSTORAGE *pstr)
{
return Buffer + sizeof(DWORD);
}
void WINAPI CLEANLOCALSTORAGE_UserFree(unsigned long *pFlags, CLEANLOCALSTORAGE *pstr)
{
}
/* BSTR */
unsigned long WINAPI BSTR_UserSize(unsigned long *pFlags, unsigned long Start, BSTR *pstr)
{
TRACE("(%lx,%ld,%p) => %p\n", *pFlags, Start, pstr, *pstr);
if (*pstr) TRACE("string=%s\n", debugstr_w(*pstr));
Start += sizeof(FLAGGED_WORD_BLOB) + sizeof(OLECHAR) * (SysStringLen(*pstr) - 1);
TRACE("returning %ld\n", Start);
return Start;
}
unsigned char * WINAPI BSTR_UserMarshal(unsigned long *pFlags, unsigned char *Buffer, BSTR *pstr)
{
wireBSTR str = (wireBSTR)Buffer;
TRACE("(%lx,%p,%p) => %p\n", *pFlags, Buffer, pstr, *pstr);
if (*pstr) TRACE("string=%s\n", debugstr_w(*pstr));
str->fFlags = 0;
str->clSize = SysStringLen(*pstr);
if (str->clSize)
memcpy(&str->asData, *pstr, sizeof(OLECHAR) * str->clSize);
return Buffer + sizeof(FLAGGED_WORD_BLOB) + sizeof(OLECHAR) * (str->clSize - 1);
}
unsigned char * WINAPI BSTR_UserUnmarshal(unsigned long *pFlags, unsigned char *Buffer, BSTR *pstr)
{
wireBSTR str = (wireBSTR)Buffer;
TRACE("(%lx,%p,%p) => %p\n", *pFlags, Buffer, pstr, *pstr);
if (str->clSize) {
SysReAllocStringLen(pstr, (OLECHAR*)&str->asData, str->clSize);
}
else if (*pstr) {
SysFreeString(*pstr);
*pstr = NULL;
}
if (*pstr) TRACE("string=%s\n", debugstr_w(*pstr));
return Buffer + sizeof(FLAGGED_WORD_BLOB) + sizeof(OLECHAR) * (str->clSize - 1);
}
void WINAPI BSTR_UserFree(unsigned long *pFlags, BSTR *pstr)
{
TRACE("(%lx,%p) => %p\n", *pFlags, pstr, *pstr);
if (*pstr) {
SysFreeString(*pstr);
*pstr = NULL;
}
}
/* VARIANT */
/* I'm not too sure how to do this yet */
#define VARIANT_wiresize sizeof(struct _wireVARIANT)
static unsigned wire_size(VARTYPE vt)
{
if (vt & VT_ARRAY) return 0;
switch (vt & ~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:
case VT_VARIANT:
case VT_UNKNOWN:
case VT_DISPATCH:
case VT_SAFEARRAY:
case VT_RECORD:
return 0;
default:
FIXME("unhandled VT %d\n", vt);
return 0;
}
}
static unsigned interface_variant_size(unsigned long *pFlags, REFIID riid, VARIANT *pvar)
{
ULONG size;
HRESULT hr;
/* find the buffer size of the marshalled dispatch interface */
hr = CoGetMarshalSizeMax(&size, riid, V_UNKNOWN(pvar), LOWORD(*pFlags), NULL, MSHLFLAGS_NORMAL);
if (FAILED(hr)) {
if (!V_DISPATCH(pvar))
WARN("NULL dispatch pointer\n");
else
ERR("Dispatch variant buffer size calculation failed, HRESULT=0x%lx\n", hr);
return 0;
}
size += sizeof(ULONG); /* we have to store the buffersize in the stream */
TRACE("wire-size extra of dispatch variant is %ld\n", size);
return size;
}
static unsigned wire_extra(unsigned long *pFlags, VARIANT *pvar)
{
if (V_ISARRAY(pvar)) {
FIXME("wire-size safearray\n");
return 0;
}
switch (V_VT(pvar)) {
case VT_BSTR:
return BSTR_UserSize(pFlags, 0, &V_BSTR(pvar));
case VT_BSTR | VT_BYREF:
return BSTR_UserSize(pFlags, 0, V_BSTRREF(pvar));
case VT_SAFEARRAY:
case VT_SAFEARRAY | VT_BYREF:
FIXME("wire-size safearray\n");
return 0;
case VT_VARIANT | VT_BYREF:
return VARIANT_UserSize(pFlags, 0, V_VARIANTREF(pvar));
case VT_UNKNOWN:
return interface_variant_size(pFlags, &IID_IUnknown, pvar);
case VT_DISPATCH:
return interface_variant_size(pFlags, &IID_IDispatch, pvar);
case VT_RECORD:
FIXME("wire-size record\n");
return 0;
default:
return 0;
}
}
/* 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(unsigned long *pFlags, unsigned char *Buffer, REFIID riid, VARIANT *pvar)
{
IStream *working;
HGLOBAL working_mem;
void *working_memlocked;
unsigned char *oldpos;
ULONG size;
HRESULT hr;
TRACE("pFlags=%ld, Buffer=%p, pvar=%p\n", *pFlags, Buffer, pvar);
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 = wire_extra(pFlags, pvar);
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, V_UNKNOWN(pvar), 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 */
Buffer += sizeof(ULONG);
memcpy(Buffer, working_memlocked, size);
GlobalUnlock(working_mem);
IStream_Release(working);
TRACE("done, size=%ld\n", sizeof(ULONG) + size);
return Buffer + sizeof(ULONG) + 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(unsigned long *pFlags, unsigned char *Buffer, REFIID riid, VARIANT *pvar)
{
IStream *working;
HGLOBAL working_mem;
void *working_memlocked;
unsigned char *oldpos;
ULONG size;
HRESULT hr;
TRACE("pFlags=%ld, Buffer=%p, pvar=%p\n", *pFlags, Buffer, pvar);
oldpos = Buffer;
/* get the buffersize */
memcpy(&size, Buffer, sizeof(ULONG));
TRACE("buffersize=%ld\n", size);
Buffer += sizeof(ULONG);
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, size);
GlobalUnlock(working_mem);
hr = CoUnmarshalInterface(working, riid, (void**)&V_UNKNOWN(pvar));
if (hr != S_OK) {
IStream_Release(working);
return oldpos;
}
IStream_Release(working); /* this also frees the underlying hglobal */
TRACE("done, processed=%ld bytes\n", sizeof(ULONG) + size);
return Buffer + sizeof(ULONG) + size;
}
unsigned long WINAPI VARIANT_UserSize(unsigned long *pFlags, unsigned long Start, VARIANT *pvar)
{
TRACE("(%lx,%ld,%p)\n", *pFlags, Start, pvar);
TRACE("vt=%04x\n", V_VT(pvar));
Start += VARIANT_wiresize + wire_extra(pFlags, pvar);
TRACE("returning %ld\n", Start);
return Start;
}
unsigned char * WINAPI VARIANT_UserMarshal(unsigned long *pFlags, unsigned char *Buffer, VARIANT *pvar)
{
wireVARIANT var = (wireVARIANT)Buffer;
unsigned size, extra;
unsigned char *Pos = Buffer + VARIANT_wiresize;
TRACE("(%lx,%p,%p)\n", *pFlags, Buffer, pvar);
TRACE("vt=%04x\n", V_VT(pvar));
memset(var, 0, sizeof(*var));
var->clSize = sizeof(*var);
var->vt = pvar->n1.n2.vt;
var->rpcReserved = var->vt;
if ((var->vt & VT_ARRAY) ||
((var->vt & VT_TYPEMASK) == VT_SAFEARRAY))
var->vt = VT_ARRAY | (var->vt & VT_BYREF);
if (var->vt == VT_DECIMAL) {
/* special case because decVal is on a different level */
var->u.decVal = pvar->n1.decVal;
return Pos;
}
size = wire_size(V_VT(pvar));
extra = wire_extra(pFlags, pvar);
var->wReserved1 = pvar->n1.n2.wReserved1;
var->wReserved2 = pvar->n1.n2.wReserved2;
var->wReserved3 = pvar->n1.n2.wReserved3;
if (size) {
if (var->vt & VT_BYREF)
memcpy(&var->u.cVal, pvar->n1.n2.n3.byref, size);
else
memcpy(&var->u.cVal, &pvar->n1.n2.n3, size);
}
if (!extra) return Pos;
switch (var->vt) {
case VT_BSTR:
Pos = BSTR_UserMarshal(pFlags, Pos, &V_BSTR(pvar));
break;
case VT_BSTR | VT_BYREF:
Pos = BSTR_UserMarshal(pFlags, Pos, V_BSTRREF(pvar));
break;
case VT_VARIANT | VT_BYREF:
Pos = VARIANT_UserMarshal(pFlags, Pos, V_VARIANTREF(pvar));
break;
case VT_DISPATCH | VT_BYREF:
FIXME("handle DISPATCH by ref\n");
break;
case VT_UNKNOWN:
/* this should probably call WdtpInterfacePointer_UserMarshal in ole32.dll */
Pos = interface_variant_marshal(pFlags, Pos, &IID_IUnknown, pvar);
break;
case VT_DISPATCH:
/* this should probably call WdtpInterfacePointer_UserMarshal in ole32.dll */
Pos = interface_variant_marshal(pFlags, Pos, &IID_IDispatch, pvar);
break;
case VT_RECORD:
FIXME("handle BRECORD by val\n");
break;
case VT_RECORD | VT_BYREF:
FIXME("handle BRECORD by ref\n");
break;
default:
FIXME("handle unknown complex type\n");
break;
}
var->clSize = Pos - Buffer;
TRACE("marshalled size=%ld\n", var->clSize);
return Pos;
}
unsigned char * WINAPI VARIANT_UserUnmarshal(unsigned long *pFlags, unsigned char *Buffer, VARIANT *pvar)
{
wireVARIANT var = (wireVARIANT)Buffer;
unsigned size;
unsigned char *Pos = Buffer + VARIANT_wiresize;
TRACE("(%lx,%p,%p)\n", *pFlags, Buffer, pvar);
VariantInit(pvar);
pvar->n1.n2.vt = var->rpcReserved;
TRACE("marshalled: clSize=%ld, vt=%04x\n", var->clSize, var->vt);
TRACE("vt=%04x\n", V_VT(pvar));
TRACE("reserved: %d, %d, %d\n", var->wReserved1, var->wReserved2, var->wReserved3);
TRACE("val: %ld\n", var->u.lVal);
if (var->vt == VT_DECIMAL) {
/* special case because decVal is on a different level */
pvar->n1.decVal = var->u.decVal;
return Pos;
}
size = wire_size(V_VT(pvar));
pvar->n1.n2.wReserved1 = var->wReserved1;
pvar->n1.n2.wReserved2 = var->wReserved2;
pvar->n1.n2.wReserved3 = var->wReserved3;
if (size) {
if (var->vt & VT_BYREF) {
pvar->n1.n2.n3.byref = CoTaskMemAlloc(size);
memcpy(pvar->n1.n2.n3.byref, &var->u.cVal, size);
}
else
memcpy(&pvar->n1.n2.n3, &var->u.cVal, size);
}
if (var->clSize <= VARIANT_wiresize) return Pos;
switch (var->vt) {
case VT_BSTR:
Pos = BSTR_UserUnmarshal(pFlags, Pos, &V_BSTR(pvar));
break;
case VT_BSTR | VT_BYREF:
pvar->n1.n2.n3.byref = CoTaskMemAlloc(sizeof(BSTR));
*(BSTR*)pvar->n1.n2.n3.byref = NULL;
Pos = BSTR_UserUnmarshal(pFlags, Pos, V_BSTRREF(pvar));
break;
case VT_VARIANT | VT_BYREF:
pvar->n1.n2.n3.byref = CoTaskMemAlloc(sizeof(VARIANT));
Pos = VARIANT_UserUnmarshal(pFlags, Pos, V_VARIANTREF(pvar));
break;
case VT_RECORD:
FIXME("handle BRECORD by val\n");
break;
case VT_RECORD | VT_BYREF:
FIXME("handle BRECORD by ref\n");
break;
case VT_UNKNOWN:
Pos = interface_variant_unmarshal(pFlags, Pos, &IID_IUnknown, pvar);
break;
case VT_DISPATCH:
Pos = interface_variant_unmarshal(pFlags, Pos, &IID_IDispatch, pvar);
break;
case VT_DISPATCH | VT_BYREF:
FIXME("handle DISPATCH by ref\n");
default:
FIXME("handle unknown complex type\n");
break;
}
if (Pos != Buffer + var->clSize) {
ERR("size difference during unmarshal\n");
}
return Buffer + var->clSize;
}
void WINAPI VARIANT_UserFree(unsigned long *pFlags, VARIANT *pvar)
{
VARTYPE vt = V_VT(pvar);
PVOID ref = NULL;
TRACE("(%lx,%p)\n", *pFlags, pvar);
TRACE("vt=%04x\n", V_VT(pvar));
if (vt & VT_BYREF) ref = pvar->n1.n2.n3.byref;
VariantClear(pvar);
if (!ref) return;
switch (vt) {
case VT_BSTR | VT_BYREF:
BSTR_UserFree(pFlags, ref);
break;
case VT_VARIANT | VT_BYREF:
VARIANT_UserFree(pFlags, ref);
break;
case VT_RECORD | VT_BYREF:
FIXME("handle BRECORD by ref\n");
break;
case VT_UNKNOWN:
case VT_DISPATCH:
IUnknown_Release(V_UNKNOWN(pvar));
break;
default:
FIXME("handle unknown complex type\n");
break;
}
CoTaskMemFree(ref);
}
/* LPSAFEARRAY */
/* Get the number of cells in a SafeArray */
static ULONG SAFEARRAY_GetCellCount(const SAFEARRAY *psa)
{
const SAFEARRAYBOUND* psab = psa->rgsabound;
USHORT cCount = psa->cDims;
ULONG ulNumCells = 1;
while (cCount--)
{
/* This is a valid bordercase. See testcases. -Marcus */
if (!psab->cElements)
return 0;
ulNumCells *= psab->cElements;
psab++;
}
return ulNumCells;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -