/*
 * 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

#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
#include "winuser.h"
#include "winerror.h"

#include "ole2.h"
#include "oleauto.h"
#include "typelib.h"
#include "ocidl.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)

/* ole32 exports those, not defined in public headers */
ULONG __RPC_USER WdtpInterfacePointer_UserSize(ULONG*, ULONG, ULONG, IUnknown*, REFIID);
unsigned char * __RPC_USER WdtpInterfacePointer_UserMarshal(ULONG*, ULONG, unsigned char*, IUnknown*, REFIID);
unsigned char * __RPC_USER WdtpInterfacePointer_UserUnmarshal(ULONG*, unsigned char*, IUnknown**, REFIID);

static void dump_user_flags(const 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;

    if (!pstg->pInterface)
        return Buffer + sizeof(DWORD);

    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 (header->byte_len == 0xffffffff)
    {
        SysFreeString(*pstr);
        *pstr = NULL;
    }
    else SysReAllocStringLen( pstr, (OLECHAR *)(header + 1), header->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);
    SysFreeString(*pstr);
    *pstr = NULL;
}

/* VARIANT */

typedef struct
{
    DWORD clSize;
    DWORD rpcReserved;
    USHORT vt;
    USHORT wReserved1;
    USHORT wReserved2;
    USHORT wReserved3;
    DWORD switch_is;
} variant_wire_t;

unsigned int get_type_size(ULONG *pFlags, VARTYPE vt)
{
    if (vt & VT_ARRAY) return 4;

    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:
    case VT_HRESULT:
        return sizeof(LONG);
    case VT_INT:
    case VT_UINT:
        return sizeof(INT);
    case VT_I8:
    case VT_UI8:
        return sizeof(LONGLONG);
    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(ULONG);
    case VT_VARIANT:
        return sizeof(VARIANT);
    case VT_UNKNOWN:
    case VT_DISPATCH:
    case VT_RECORD:
        return 0;
    default:
        FIXME("unhandled VT %d\n", vt);
        return 0;
    }
}

static unsigned int get_type_alignment(ULONG *pFlags, VARTYPE vt)
{
    unsigned int size = get_type_size(pFlags, vt);
    if(vt & VT_BYREF) return 3;
    if(size == 0) return 0;
    if(size <= 4) return size - 1;
    return 7;
}

/* WdtpInterfacePointer_UserSize takes care of 2 additional DWORDs to store marshalling buffer size */
static unsigned interface_variant_size(ULONG *pFlags, REFIID riid, IUnknown *punk)
{
    ULONG size = 0;

    if (punk)
    {
        size = WdtpInterfacePointer_UserSize(pFlags, LOWORD(*pFlags), 0, punk, riid);
        if (!size)
        {
            ERR("interface variant buffer size calculation failed\n");
            return 0;
        }
    }
    size += sizeof(ULONG);
    TRACE("wire-size extra of interface 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 */
static unsigned char* interface_variant_marshal(ULONG *pFlags, unsigned char *Buffer,
                                                REFIID riid, IUnknown *punk)
{
  TRACE("pFlags=%d, Buffer=%p, pUnk=%p\n", *pFlags, Buffer, punk);

  /* first DWORD is used to store pointer itself, truncated on win64 */
  if(!punk)
  {
      memset(Buffer, 0, sizeof(ULONG));
      return Buffer + sizeof(ULONG);
  }
  else
  {
      *(DWORD*)Buffer = (DWORD_PTR)punk;
      Buffer += sizeof(DWORD);
  }

  return WdtpInterfacePointer_UserMarshal(pFlags, LOWORD(*pFlags), Buffer, punk, riid);
}

/* helper: called for VT_DISPATCH / VT_UNKNOWN variants to unmarshal the buffer */
static unsigned char *interface_variant_unmarshal(ULONG *pFlags, unsigned char *Buffer,
                                                  REFIID riid, IUnknown **ppunk)
{
  DWORD ptr;
  
  TRACE("pFlags=%d, Buffer=%p, ppUnk=%p\n", *pFlags, Buffer, ppunk);

  /* skip pointer part itself */
  ptr = *(DWORD*)Buffer;
  Buffer += sizeof(DWORD);

  if(!ptr)
      return Buffer;

  return WdtpInterfacePointer_UserUnmarshal(pFlags, Buffer, ppunk, riid);
}

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, V_VT(pvar));
    ALIGN_LENGTH(Start, align);
    if(V_VT(pvar) == (VT_VARIANT | VT_BYREF))
        Start += 4;
    else
        Start += get_type_size(pFlags, V_VT(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->rpcReserved = 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, V_VT(pvar));
    align = get_type_alignment(pFlags, V_VT(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
    {
        if((header->vt & VT_TYPEMASK) == VT_DECIMAL)
            memcpy(Pos, pvar, type_size);
        else
            memcpy(Pos, &pvar->n1.n2.n3, type_size);
        Pos += type_size;
    }

    if(header->vt & VT_ARRAY)
    {
        if(header->vt & VT_BYREF)
            Pos = LPSAFEARRAY_UserMarshal(pFlags, Pos, V_ARRAYREF(pvar));
        else
            Pos = LPSAFEARRAY_UserMarshal(pFlags, Pos, &V_ARRAY(pvar));
    }
    else
    {
        switch (header->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_UNKNOWN:
            Pos = interface_variant_marshal(pFlags, Pos, &IID_IUnknown, V_UNKNOWN(pvar));
            break;
        case VT_UNKNOWN | VT_BYREF:
            Pos = interface_variant_marshal(pFlags, Pos, &IID_IUnknown, *V_UNKNOWNREF(pvar));
            break;
        case VT_DISPATCH:
            Pos = interface_variant_marshal(pFlags, Pos, &IID_IDispatch, (IUnknown*)V_DISPATCH(pvar));
            break;
        case VT_DISPATCH | VT_BYREF:
            Pos = interface_variant_marshal(pFlags, Pos, &IID_IDispatch, (IUnknown*)*V_DISPATCHREF(pvar));
            break;
        case VT_RECORD:
            FIXME("handle BRECORD by val\n");
            break;
        case VT_RECORD | VT_BYREF:
            FIXME("handle BRECORD by ref\n");
            break;
        }
    }
    header->clSize = ((Pos - Buffer) + 7) >> 3;
    TRACE("marshalled size=%d\n", header->clSize);
    return Pos;
}

unsigned char * WINAPI VARIANT_UserUnmarshal(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);

    ALIGN_POINTER(Buffer, 7);

    header = (variant_wire_t *)Buffer; 
    
    Pos = (unsigned char*)(header + 1);
    type_size = get_type_size(pFlags, header->vt);
    align = get_type_alignment(pFlags, header->vt);
    ALIGN_POINTER(Pos, align);

    if(header->vt & VT_BYREF)
    {
        ULONG mem_size;
        Pos += 4;

        switch (header->vt & ~VT_BYREF)
        {
        /* these types have a different memory size compared to wire size */
        case VT_UNKNOWN:
        case VT_DISPATCH:
        case VT_BSTR:
            mem_size = sizeof(void *);
            break;
        default:
            mem_size = type_size;
            break;
        }

        if (V_VT(pvar) != header->vt)
        {
            VariantClear(pvar);
            V_BYREF(pvar) = CoTaskMemAlloc(mem_size);
            memset(V_BYREF(pvar), 0, mem_size);
        }
        else if (!V_BYREF(pvar))
        {
            V_BYREF(pvar) = CoTaskMemAlloc(mem_size);
            memset(V_BYREF(pvar), 0, mem_size);
        }

        if(!(header->vt & VT_ARRAY)
                && (header->vt & VT_TYPEMASK) != VT_BSTR
                && (header->vt & VT_TYPEMASK) != VT_VARIANT
                && (header->vt & VT_TYPEMASK) != VT_UNKNOWN
                && (header->vt & VT_TYPEMASK) != VT_DISPATCH
                && (header->vt & VT_TYPEMASK) != VT_RECORD)
            memcpy(V_BYREF(pvar), Pos, type_size);

        if((header->vt & VT_TYPEMASK) != VT_VARIANT)
            Pos += type_size;
        else
            Pos += 4;
    }
    else
    {
        VariantClear(pvar);
        if(header->vt & VT_ARRAY)
            V_ARRAY(pvar) = NULL;
        else if((header->vt & VT_TYPEMASK) == VT_BSTR)
            V_BSTR(pvar) = NULL;
        else if((header->vt & VT_TYPEMASK) == VT_UNKNOWN)
            V_UNKNOWN(pvar) = NULL;
        else if((header->vt & VT_TYPEMASK) == VT_DISPATCH)
            V_DISPATCH(pvar) = NULL;
        else if((header->vt & VT_TYPEMASK) == VT_RECORD)
            V_RECORD(pvar) = NULL;
        else if((header->vt & VT_TYPEMASK) == VT_DECIMAL)
            memcpy(pvar, Pos, type_size);
        else
            memcpy(&pvar->n1.n2.n3, Pos, type_size);
        Pos += type_size;
    }

    pvar->n1.n2.vt = header->vt;
    pvar->n1.n2.wReserved1 = header->wReserved1;
    pvar->n1.n2.wReserved2 = header->wReserved2;
    pvar->n1.n2.wReserved3 = header->wReserved3;

    if(header->vt & VT_ARRAY)
    {
        if(header->vt & VT_BYREF)
            Pos = LPSAFEARRAY_UserUnmarshal(pFlags, Pos, V_ARRAYREF(pvar));
        else
            Pos = LPSAFEARRAY_UserUnmarshal(pFlags, Pos, &V_ARRAY(pvar));
    }
    else
    {
        switch (header->vt)
        {
        case VT_BSTR:
            Pos = BSTR_UserUnmarshal(pFlags, Pos, &V_BSTR(pvar));
            break;
        case VT_BSTR | VT_BYREF:
            Pos = BSTR_UserUnmarshal(pFlags, Pos, V_BSTRREF(pvar));
            break;
        case VT_VARIANT | VT_BYREF:
            Pos = VARIANT_UserUnmarshal(pFlags, Pos, V_VARIANTREF(pvar));
            break;
        case VT_UNKNOWN:
            Pos = interface_variant_unmarshal(pFlags, Pos, &IID_IUnknown, &V_UNKNOWN(pvar));
            break;
        case VT_UNKNOWN | VT_BYREF:
            Pos = interface_variant_unmarshal(pFlags, Pos, &IID_IUnknown, V_UNKNOWNREF(pvar));
            break;
        case VT_DISPATCH:
            Pos = interface_variant_unmarshal(pFlags, Pos, &IID_IDispatch, (IUnknown**)&V_DISPATCH(pvar));
            break;
        case VT_DISPATCH | VT_BYREF:
            Pos = interface_variant_unmarshal(pFlags, Pos, &IID_IDispatch, (IUnknown**)V_DISPATCHREF(pvar));
            break;
        case VT_RECORD:
            FIXME("handle BRECORD by val\n");
            break;
        case VT_RECORD | VT_BYREF:
            FIXME("handle BRECORD by ref\n");
            break;
        }
    }
    return Pos;
}

void WINAPI VARIANT_UserFree(ULONG *pFlags, VARIANT *pvar)
{
  VARTYPE vt = V_VT(pvar);
  PVOID ref = NULL;

  TRACE("(%x,%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;

  if(vt & VT_ARRAY)
  {
    if (vt & VT_BYREF)
      LPSAFEARRAY_UserFree(pFlags, V_ARRAYREF(pvar));
    else
      LPSAFEARRAY_UserFree(pFlags, &V_ARRAY(pvar));
  }
  else
  {
    switch (vt)
    {
    case VT_BSTR | VT_BYREF:
      BSTR_UserFree(pFlags, V_BSTRREF(pvar));
      break;
    case VT_VARIANT | VT_BYREF:
      VARIANT_UserFree(pFlags, V_VARIANTREF(pvar));
      break;
    case VT_RECORD | VT_BYREF:
      FIXME("handle BRECORD by ref\n");
      break;
    case VT_UNKNOWN | VT_BYREF:
    case VT_DISPATCH | VT_BYREF:
      if (*V_UNKNOWNREF(pvar))
        IUnknown_Release(*V_UNKNOWNREF(pvar));
      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;
}

static inline SF_TYPE SAFEARRAY_GetUnionType(SAFEARRAY *psa)
{
    VARTYPE vt;
    HRESULT hr;

    hr = SafeArrayGetVartype(psa, &vt);
    if (FAILED(hr))
    {
        if(psa->fFeatures & FADF_VARIANT) return SF_VARIANT;

        switch(psa->cbElements)
        {
        case 1: vt = VT_I1; break;
        case 2: vt = VT_I2; break;
        case 4: vt = VT_I4; break;
        case 8: vt = VT_I8; break;
        default:
            RpcRaiseException(hr);
        }
    }

    if (psa->fFeatures & FADF_HAVEIID)
        return SF_HAVEIID;

    switch (vt)
    {
    case VT_I1:
    case VT_UI1:      return SF_I1;
    case VT_BOOL:
    case VT_I2:
    case VT_UI2:      return SF_I2;
    case VT_INT:
    case VT_UINT:
    case VT_I4:
    case VT_UI4:
    case VT_R4:       return SF_I4;
    case VT_DATE:
    case VT_CY:
    case VT_R8:
    case VT_I8:
    case VT_UI8:      return SF_I8;
    case VT_INT_PTR:
    case VT_UINT_PTR: return (sizeof(UINT_PTR) == 4 ? SF_I4 : SF_I8);
    case VT_BSTR:     return SF_BSTR;
    case VT_DISPATCH: return SF_DISPATCH;
    case VT_VARIANT:  return SF_VARIANT;
    case VT_UNKNOWN:  return SF_UNKNOWN;
    /* Note: Return a non-zero size to indicate vt is valid. The actual size
     * of a UDT is taken from the result of IRecordInfo_GetSize().
     */
    case VT_RECORD:   return SF_RECORD;
    default:          return SF_ERROR;
    }
}

static DWORD elem_wire_size(LPSAFEARRAY lpsa, SF_TYPE sftype)
{
    if (sftype == SF_BSTR)
        return sizeof(DWORD);
    else if (sftype == SF_VARIANT)
        return sizeof(variant_wire_t) - sizeof(DWORD);
    else
        return lpsa->cbElements;
}

static DWORD elem_mem_size(wireSAFEARRAY wiresa, SF_TYPE sftype)
{
    if (sftype == SF_BSTR)
        return sizeof(BSTR);
    else if (sftype == SF_VARIANT)
        return sizeof(VARIANT);
    else
        return wiresa->cbElements;
}

ULONG WINAPI LPSAFEARRAY_UserSize(ULONG *pFlags, ULONG StartingSize, LPSAFEARRAY *ppsa)
{
    ULONG size = StartingSize;

    TRACE("("); dump_user_flags(pFlags); TRACE(", %d, %p\n", StartingSize, *ppsa);

    ALIGN_LENGTH(size, 3);
    size += sizeof(ULONG);
    if (*ppsa)
    {
        SAFEARRAY *psa = *ppsa;
        ULONG ulCellCount = SAFEARRAY_GetCellCount(psa);
        SF_TYPE sftype;
        HRESULT hr;

        size += sizeof(ULONG);
        size += 2 * sizeof(USHORT) + 2 * sizeof(ULONG);

        sftype = SAFEARRAY_GetUnionType(psa);
        size += sizeof(ULONG);

        size += sizeof(ULONG);
        size += sizeof(ULONG);
        if (sftype == SF_HAVEIID)
            size += sizeof(IID);

        size += sizeof(psa->rgsabound[0]) * psa->cDims;

        size += sizeof(ULONG);

        switch (sftype)
        {
            case SF_BSTR:
            {
                BSTR* lpBstr;

                for (lpBstr = psa->pvData; ulCellCount; ulCellCount--, lpBstr++)
                    size = BSTR_UserSize(pFlags, size, lpBstr);

                break;
            }
            case SF_DISPATCH:
            case SF_UNKNOWN:
            case SF_HAVEIID:
                FIXME("size interfaces\n");
                break;
            case SF_VARIANT:
            {
                VARIANT* lpVariant;

                for (lpVariant = psa->pvData; ulCellCount; ulCellCount--, lpVariant++)
                    size = VARIANT_UserSize(pFlags, size, lpVariant);

                break;
            }
            case SF_RECORD:
            {
                IRecordInfo* pRecInfo = NULL;

                hr = SafeArrayGetRecordInfo(psa, &pRecInfo);
                if (FAILED(hr))
                    RpcRaiseException(hr);

                if (pRecInfo)
                {
                    FIXME("size record info %p\n", pRecInfo);

                    IRecordInfo_Release(pRecInfo);
                }
                break;
            }
            case SF_I8:
                ALIGN_LENGTH(size, 7);
                /* fallthrough */
            case SF_I1:
            case SF_I2:
            case SF_I4:
                size += ulCellCount * psa->cbElements;
                break;
            default:
                break;
        }

    }

    return size;
}

unsigned char * WINAPI LPSAFEARRAY_UserMarshal(ULONG *pFlags, unsigned char *Buffer, LPSAFEARRAY *ppsa)
{
    HRESULT hr;

    TRACE("("); dump_user_flags(pFlags); TRACE(", %p, &%p\n", Buffer, *ppsa);

    ALIGN_POINTER(Buffer, 3);
    *(ULONG *)Buffer = *ppsa ? 0x1 : 0x0;
    Buffer += sizeof(ULONG);
    if (*ppsa)
    {
        VARTYPE vt;
        SAFEARRAY *psa = *ppsa;
        ULONG ulCellCount = SAFEARRAY_GetCellCount(psa);
        SAFEARRAYBOUND *bound;
        SF_TYPE sftype;
        GUID guid;
        INT i;

        sftype = SAFEARRAY_GetUnionType(psa);

        *(ULONG *)Buffer = psa->cDims;
        Buffer += sizeof(ULONG);
        *(USHORT *)Buffer = psa->cDims;
        Buffer += sizeof(USHORT);
        *(USHORT *)Buffer = psa->fFeatures;
        Buffer += sizeof(USHORT);
        *(ULONG *)Buffer = elem_wire_size(psa, sftype);
        Buffer += sizeof(ULONG);

        hr = SafeArrayGetVartype(psa, &vt);
        if (FAILED(hr)) vt = 0;

        *(ULONG *)Buffer = (USHORT)psa->cLocks | (vt << 16);
        Buffer += sizeof(ULONG);

        *(ULONG *)Buffer = sftype;
        Buffer += sizeof(ULONG);

        *(ULONG *)Buffer = ulCellCount;
        Buffer += sizeof(ULONG);
        *(ULONG *)Buffer = psa->pvData ? 0x2 : 0x0;
        Buffer += sizeof(ULONG);
        if (sftype == SF_HAVEIID)
        {
            SafeArrayGetIID(psa, &guid);
            memcpy(Buffer, &guid, sizeof(guid));
            Buffer += sizeof(guid);
        }

        /* bounds are marshaled in opposite order comparing to storage layout */
        bound = (SAFEARRAYBOUND*)Buffer;
        for (i = 0; i < psa->cDims; i++)
        {
            memcpy(bound++, &psa->rgsabound[psa->cDims-i-1], sizeof(psa->rgsabound[0]));
        }
        Buffer += sizeof(psa->rgsabound[0]) * psa->cDims;

        *(ULONG *)Buffer = ulCellCount;
        Buffer += sizeof(ULONG);

        if (psa->pvData)
        {
            switch (sftype)
            {
                case SF_BSTR:
                {
                    BSTR* lpBstr;

                    for (lpBstr = psa->pvData; ulCellCount; ulCellCount--, lpBstr++)
                        Buffer = BSTR_UserMarshal(pFlags, Buffer, lpBstr);

                    break;
                }
                case SF_DISPATCH:
                case SF_UNKNOWN:
                case SF_HAVEIID:
                    FIXME("marshal interfaces\n");
                    break;
                case SF_VARIANT:
                {
                    VARIANT* lpVariant;

                    for (lpVariant = psa->pvData; ulCellCount; ulCellCount--, lpVariant++)
                        Buffer = VARIANT_UserMarshal(pFlags, Buffer, lpVariant);

                    break;
                }
                case SF_RECORD:
                {
                    IRecordInfo* pRecInfo = NULL;

                    hr = SafeArrayGetRecordInfo(psa, &pRecInfo);
                    if (FAILED(hr))
                        RpcRaiseException(hr);

                    if (pRecInfo)
                    {
                        FIXME("write record info %p\n", pRecInfo);

                        IRecordInfo_Release(pRecInfo);
                    }
                    break;
                }

                case SF_I8:
                    ALIGN_POINTER(Buffer, 7);
                    /* fallthrough */
                case SF_I1:
                case SF_I2:
                case SF_I4:
                    /* Just copy the data over */
                    memcpy(Buffer, psa->pvData, ulCellCount * psa->cbElements);
                    Buffer += ulCellCount * psa->cbElements;
                    break;
                default:
                    break;
            }
        }

    }
    return Buffer;
}

#define FADF_AUTOSETFLAGS (FADF_HAVEIID | FADF_RECORD | FADF_HAVEVARTYPE | \
                           FADF_BSTR | FADF_UNKNOWN | FADF_DISPATCH | \
                           FADF_VARIANT | FADF_CREATEVECTOR)

unsigned char * WINAPI LPSAFEARRAY_UserUnmarshal(ULONG *pFlags, unsigned char *Buffer, LPSAFEARRAY *ppsa)
{
    ULONG ptr;
    wireSAFEARRAY wiresa;
    ULONG cDims;
    HRESULT hr;
    SF_TYPE sftype;
    ULONG cell_count;
    GUID guid;
    VARTYPE vt;
    SAFEARRAYBOUND *wiresab;

    TRACE("("); dump_user_flags(pFlags); TRACE(", %p, %p\n", Buffer, ppsa);

    ALIGN_POINTER(Buffer, 3);
    ptr = *(ULONG *)Buffer;
    Buffer += sizeof(ULONG);

    if (!ptr)
    {
        SafeArrayDestroy(*ppsa);
        *ppsa = NULL;

        TRACE("NULL safe array unmarshaled\n");

        return Buffer;
    }

    cDims = *(ULONG *)Buffer;
    Buffer += sizeof(ULONG);

    wiresa = (wireSAFEARRAY)Buffer;
    Buffer += 2 * sizeof(USHORT) + 2 * sizeof(ULONG);

    if (cDims != wiresa->cDims)
        RpcRaiseException(RPC_S_INVALID_BOUND);

    /* FIXME: there should be a limit on how large cDims can be */

    vt = HIWORD(wiresa->cLocks);

    sftype = *(ULONG *)Buffer;
    Buffer += sizeof(ULONG);

    cell_count = *(ULONG *)Buffer;
    Buffer += sizeof(ULONG);
    ptr = *(ULONG *)Buffer;
    Buffer += sizeof(ULONG);
    if (sftype == SF_HAVEIID)
    {
        memcpy(&guid, Buffer, sizeof(guid));
        Buffer += sizeof(guid);
    }

    wiresab = (SAFEARRAYBOUND *)Buffer;
    Buffer += sizeof(wiresab[0]) * wiresa->cDims;

    if(*ppsa && (*ppsa)->cDims==wiresa->cDims)
    {
        if(((*ppsa)->fFeatures & ~FADF_AUTOSETFLAGS) != (wiresa->fFeatures & ~FADF_AUTOSETFLAGS))
            RpcRaiseException(DISP_E_BADCALLEE);

        if(SAFEARRAY_GetCellCount(*ppsa)*(*ppsa)->cbElements != cell_count*elem_mem_size(wiresa, sftype))
        {
            if((*ppsa)->fFeatures & (FADF_AUTO|FADF_STATIC|FADF_EMBEDDED|FADF_FIXEDSIZE))
                RpcRaiseException(DISP_E_BADCALLEE);

            hr = SafeArrayDestroyData(*ppsa);
            if(FAILED(hr))
                RpcRaiseException(hr);
        }
        memcpy((*ppsa)->rgsabound, wiresab, sizeof(*wiresab)*wiresa->cDims);

        if((*ppsa)->fFeatures & FADF_HAVEVARTYPE)
            ((DWORD*)(*ppsa))[-1] = vt;
    }
    else if(vt)
    {
        SafeArrayDestroy(*ppsa);
        *ppsa = SafeArrayCreateEx(vt, wiresa->cDims, wiresab, NULL);
        if (!*ppsa) RpcRaiseException(E_OUTOFMEMORY);
    }
    else
    {
        SafeArrayDestroy(*ppsa);
        if (FAILED(SafeArrayAllocDescriptor(wiresa->cDims, ppsa)))
            RpcRaiseException(E_OUTOFMEMORY);
        memcpy((*ppsa)->rgsabound, wiresab, sizeof(SAFEARRAYBOUND) * wiresa->cDims);
    }

    /* be careful about which flags we set since they could be a security
     * risk */
    (*ppsa)->fFeatures &= FADF_AUTOSETFLAGS;
    (*ppsa)->fFeatures |= (wiresa->fFeatures & ~(FADF_AUTOSETFLAGS));
    /* FIXME: there should be a limit on how large wiresa->cbElements can be */
    (*ppsa)->cbElements = elem_mem_size(wiresa, sftype);

    /* SafeArrayCreateEx allocates the data for us, but
     * SafeArrayAllocDescriptor doesn't */
    if(!(*ppsa)->pvData)
    {
        hr = SafeArrayAllocData(*ppsa);
        if (FAILED(hr))
            RpcRaiseException(hr);
    }

    if ((*(ULONG *)Buffer != cell_count) || (SAFEARRAY_GetCellCount(*ppsa) != cell_count))
        RpcRaiseException(RPC_S_INVALID_BOUND);
    Buffer += sizeof(ULONG);

    if (ptr)
    {
        switch (sftype)
        {
            case SF_BSTR:
            {
                BSTR* lpBstr;

                for (lpBstr = (*ppsa)->pvData; cell_count; cell_count--, lpBstr++)
                    Buffer = BSTR_UserUnmarshal(pFlags, Buffer, lpBstr);

                break;
            }
            case SF_DISPATCH:
            case SF_UNKNOWN:
            case SF_HAVEIID:
                FIXME("marshal interfaces\n");
                break;
            case SF_VARIANT:
            {
                VARIANT* lpVariant;

                for (lpVariant = (*ppsa)->pvData; cell_count; cell_count--, lpVariant++)
                    Buffer = VARIANT_UserUnmarshal(pFlags, Buffer, lpVariant);

                break;
            }
            case SF_RECORD:
            {
                FIXME("set record info\n");

                break;
            }

            case SF_I8:
                ALIGN_POINTER(Buffer, 7);
                /* fallthrough */
            case SF_I1:
            case SF_I2:
            case SF_I4:
                /* Just copy the data over */
                memcpy((*ppsa)->pvData, Buffer, cell_count * (*ppsa)->cbElements);
                Buffer += cell_count * (*ppsa)->cbElements;
                break;
            default:
                break;
        }
    }

    TRACE("safe array unmarshaled: %p\n", *ppsa);

    return Buffer;
}

void WINAPI LPSAFEARRAY_UserFree(ULONG *pFlags, LPSAFEARRAY *ppsa)
{
    TRACE("("); dump_user_flags(pFlags); TRACE(", &%p\n", *ppsa);

    SafeArrayDestroy(*ppsa);
    *ppsa = NULL;
}


ULONG WINAPI HFONT_UserSize(ULONG *pFlags, ULONG Start, HFONT *phfont)
{
    FIXME(":stub\n");
    return 0;
}

unsigned char * WINAPI HFONT_UserMarshal(ULONG *pFlags, unsigned char *Buffer, HFONT *phfont)
{
    FIXME(":stub\n");
    return NULL;
}

unsigned char * WINAPI HFONT_UserUnmarshal(ULONG *pFlags, unsigned char *Buffer, HFONT *phfont)
{
    FIXME(":stub\n");
    return NULL;
}

void WINAPI HFONT_UserFree(ULONG *pFlags, HFONT *phfont)
{
    FIXME(":stub\n");
    return;
}


/* IDispatch */
/* exactly how Invoke is marshalled is not very clear to me yet,
 * but the way I've done it seems to work for me */

HRESULT CALLBACK IDispatch_Invoke_Proxy(
    IDispatch* This,
    DISPID dispIdMember,
    REFIID riid,
    LCID lcid,
    WORD wFlags,
    DISPPARAMS* pDispParams,
    VARIANT* pVarResult,
    EXCEPINFO* pExcepInfo,
    UINT* puArgErr)
{
  HRESULT hr;
  VARIANT VarResult;
  UINT* rgVarRefIdx = NULL;
  VARIANTARG* rgVarRef = NULL;
  UINT u, cVarRef;
  UINT uArgErr;
  EXCEPINFO ExcepInfo;

  TRACE("(%p)->(%d,%s,%x,%x,%p,%p,%p,%p)\n", This,
        dispIdMember, debugstr_guid(riid),
        lcid, wFlags, pDispParams, pVarResult,
        pExcepInfo, puArgErr);

  /* [out] args can't be null, use dummy vars if needed */
  if (!pVarResult) pVarResult = &VarResult;
  if (!puArgErr) puArgErr = &uArgErr;
  if (!pExcepInfo) pExcepInfo = &ExcepInfo;

  /* count by-ref args */
  for (cVarRef=0,u=0; u<pDispParams->cArgs; u++) {
    VARIANTARG* arg = &pDispParams->rgvarg[u];
    if (V_ISBYREF(arg)) {
      cVarRef++;
    }
  }
  if (cVarRef) {
    rgVarRefIdx = CoTaskMemAlloc(sizeof(UINT)*cVarRef);
    rgVarRef = CoTaskMemAlloc(sizeof(VARIANTARG)*cVarRef);
    /* make list of by-ref args */
    for (cVarRef=0,u=0; u<pDispParams->cArgs; u++) {
      VARIANTARG* arg = &pDispParams->rgvarg[u];
      if (V_ISBYREF(arg)) {
	rgVarRefIdx[cVarRef] = u;
	VariantInit(&rgVarRef[cVarRef]);
	VariantCopy(&rgVarRef[cVarRef], arg);
	VariantClear(arg);
	cVarRef++;
      }
    }
  } else {
    /* [out] args still can't be null,
     * but we can point these anywhere in this case,
     * since they won't be written to when cVarRef is 0 */
    rgVarRefIdx = puArgErr;
    rgVarRef = pVarResult;
  }
  TRACE("passed by ref: %d args\n", cVarRef);
  hr = IDispatch_RemoteInvoke_Proxy(This,
				    dispIdMember,
				    riid,
				    lcid,
				    wFlags,
				    pDispParams,
				    pVarResult,
				    pExcepInfo,
				    puArgErr,
				    cVarRef,
				    rgVarRefIdx,
				    rgVarRef);
  if (cVarRef) {
    for (u=0; u<cVarRef; u++) {
      unsigned i = rgVarRefIdx[u];
      VariantCopy(&pDispParams->rgvarg[i],
		  &rgVarRef[u]);
      VariantClear(&rgVarRef[u]);
    }
    CoTaskMemFree(rgVarRef);
    CoTaskMemFree(rgVarRefIdx);
  }

  if(pExcepInfo == &ExcepInfo)
  {
    SysFreeString(pExcepInfo->bstrSource);
    SysFreeString(pExcepInfo->bstrDescription);
    SysFreeString(pExcepInfo->bstrHelpFile);
  }
  return hr;
}

HRESULT __RPC_STUB IDispatch_Invoke_Stub(
    IDispatch* This,
    DISPID dispIdMember,
    REFIID riid,
    LCID lcid,
    DWORD dwFlags,
    DISPPARAMS* pDispParams,
    VARIANT* pVarResult,
    EXCEPINFO* pExcepInfo,
    UINT* pArgErr,
    UINT cVarRef,
    UINT* rgVarRefIdx,
    VARIANTARG* rgVarRef)
{
  HRESULT hr = S_OK;
  VARIANTARG *rgvarg, *arg;
  UINT u;

  /* initialize out parameters, so that they can be marshalled
   * in case the real Invoke doesn't initialize them */
  VariantInit(pVarResult);
  memset(pExcepInfo, 0, sizeof(*pExcepInfo));
  *pArgErr = 0;

  /* let the real Invoke operate on a copy of the in parameters,
   * so we don't risk losing pointers to allocated memory */
  rgvarg = pDispParams->rgvarg;
  arg = CoTaskMemAlloc(sizeof(VARIANTARG)*pDispParams->cArgs);
  if (!arg) return E_OUTOFMEMORY;

  /* init all args so we can call VariantClear on all the args if the copy
   * below fails */
  for (u = 0; u < pDispParams->cArgs; u++)
    VariantInit(&arg[u]);

  for (u = 0; u < pDispParams->cArgs; u++) {
    hr = VariantCopy(&arg[u], &rgvarg[u]);
    if (FAILED(hr))
        break;
  }

  if (SUCCEEDED(hr)) {
    /* copy ref args to arg array */
    for (u=0; u<cVarRef; u++) {
      unsigned i = rgVarRefIdx[u];
      VariantCopy(&arg[i], &rgVarRef[u]);
    }

    pDispParams->rgvarg = arg;

    hr = IDispatch_Invoke(This,
			  dispIdMember,
			  riid,
			  lcid,
			  dwFlags,
			  pDispParams,
			  pVarResult,
			  pExcepInfo,
			  pArgErr);

    /* copy ref args from arg array */
    for (u=0; u<cVarRef; u++) {
      unsigned i = rgVarRefIdx[u];
      VariantCopy(&rgVarRef[u], &arg[i]);
    }
  }

  /* clear the duplicate argument list */
  for (u=0; u<pDispParams->cArgs; u++)
    VariantClear(&arg[u]);

  pDispParams->rgvarg = rgvarg;
  CoTaskMemFree(arg);

  return hr;
}

/* IEnumVARIANT */

HRESULT CALLBACK IEnumVARIANT_Next_Proxy(
    IEnumVARIANT* This,
    ULONG celt,
    VARIANT* rgVar,
    ULONG* pCeltFetched)
{
  ULONG fetched;
  if (!pCeltFetched)
    pCeltFetched = &fetched;
  return IEnumVARIANT_RemoteNext_Proxy(This,
				       celt,
				       rgVar,
				       pCeltFetched);
}

HRESULT __RPC_STUB IEnumVARIANT_Next_Stub(
    IEnumVARIANT* This,
    ULONG celt,
    VARIANT* rgVar,
    ULONG* pCeltFetched)
{
  HRESULT hr;
  *pCeltFetched = 0;
  hr = IEnumVARIANT_Next(This,
			 celt,
			 rgVar,
			 pCeltFetched);
  if (hr == S_OK) *pCeltFetched = celt;
  return hr;
}

/* TypeInfo related freers */

static void free_embedded_typedesc(TYPEDESC *tdesc);
static void free_embedded_arraydesc(ARRAYDESC *adesc)
{
    switch(adesc->tdescElem.vt)
    {
    case VT_PTR:
    case VT_SAFEARRAY:
        free_embedded_typedesc(adesc->tdescElem.u.lptdesc);
        CoTaskMemFree(adesc->tdescElem.u.lptdesc);
        break;
    case VT_CARRAY:
        free_embedded_arraydesc(adesc->tdescElem.u.lpadesc);
        CoTaskMemFree(adesc->tdescElem.u.lpadesc);
        break;
    }
}

static void free_embedded_typedesc(TYPEDESC *tdesc)
{
    switch(tdesc->vt)
    {
    case VT_PTR:
    case VT_SAFEARRAY:
        free_embedded_typedesc(tdesc->u.lptdesc);
        CoTaskMemFree(tdesc->u.lptdesc);
        break;
    case VT_CARRAY:
        free_embedded_arraydesc(tdesc->u.lpadesc);
        CoTaskMemFree(tdesc->u.lpadesc);
        break;
    }
}

static void free_embedded_elemdesc(ELEMDESC *edesc)
{
    free_embedded_typedesc(&edesc->tdesc);
    if(edesc->u.paramdesc.wParamFlags & PARAMFLAG_FHASDEFAULT)
        CoTaskMemFree(edesc->u.paramdesc.pparamdescex);
}

/* ITypeComp */

HRESULT CALLBACK ITypeComp_Bind_Proxy(
    ITypeComp* This,
    LPOLESTR name,
    ULONG lHashVal,
    WORD flags,
    ITypeInfo **ti,
    DESCKIND *desckind,
    BINDPTR *bindptr)
{
    CLEANLOCALSTORAGE stg = { 0 };
    ITypeComp *typecomp;
    FUNCDESC *funcdesc;
    VARDESC *vardesc;
    HRESULT hr;

    TRACE("(%p, %s, %#x, %#x, %p, %p, %p)\n", This, debugstr_w(name), lHashVal, flags, ti,
        desckind, bindptr);

    *desckind = DESCKIND_NONE;
    memset(bindptr, 0, sizeof(*bindptr));

    hr = ITypeComp_RemoteBind_Proxy(This, name, lHashVal, flags, ti, desckind,
        &funcdesc, &vardesc, &typecomp, &stg);

    if (hr == S_OK)
    {
        switch (*desckind)
        {
        case DESCKIND_FUNCDESC:
            bindptr->lpfuncdesc = funcdesc;
            break;
        case DESCKIND_VARDESC:
        case DESCKIND_IMPLICITAPPOBJ:
            bindptr->lpvardesc = vardesc;
            break;
        case DESCKIND_TYPECOMP:
            bindptr->lptcomp = typecomp;
            break;
        default:
            ;
        }
    }

    return hr;
}

HRESULT __RPC_STUB ITypeComp_Bind_Stub(
    ITypeComp* This,
    LPOLESTR name,
    ULONG lHashVal,
    WORD flags,
    ITypeInfo **ti,
    DESCKIND *desckind,
    FUNCDESC **funcdesc,
    VARDESC **vardesc,
    ITypeComp **typecomp,
    CLEANLOCALSTORAGE *stg)
{
    BINDPTR bindptr;
    HRESULT hr;

    TRACE("(%p, %s, %#x, %#x, %p, %p, %p, %p, %p, %p)\n", This, debugstr_w(name),
        lHashVal, flags, ti, desckind, funcdesc, vardesc, typecomp, stg);

    memset(stg, 0, sizeof(*stg));
    memset(&bindptr, 0, sizeof(bindptr));

    *funcdesc = NULL;
    *vardesc = NULL;
    *typecomp = NULL;
    *ti = NULL;

    hr = ITypeComp_Bind(This, name, lHashVal, flags, ti, desckind, &bindptr);
    if(hr != S_OK)
        return hr;

    switch (*desckind)
    {
    case DESCKIND_FUNCDESC:
        *funcdesc = bindptr.lpfuncdesc;
        stg->pInterface = (IUnknown*)*ti;
        stg->pStorage = funcdesc;
        stg->flags = CLS_FUNCDESC;
        break;
    case DESCKIND_VARDESC:
    case DESCKIND_IMPLICITAPPOBJ:
        *vardesc = bindptr.lpvardesc;
        stg->pInterface = (IUnknown*)*ti;
        stg->pStorage = vardesc;
        stg->flags = CLS_VARDESC;
        break;
    case DESCKIND_TYPECOMP:
        *typecomp = bindptr.lptcomp;
        break;
    default:
        ;
    }

    if (stg->pInterface)
        IUnknown_AddRef(stg->pInterface);

    return hr;
}

HRESULT CALLBACK ITypeComp_BindType_Proxy(
    ITypeComp* This,
    LPOLESTR name,
    ULONG lHashVal,
    ITypeInfo **ti,
    ITypeComp **typecomp)
{
    HRESULT hr;

    TRACE("(%p, %s, %#x, %p, %p)\n", This, debugstr_w(name), lHashVal, ti, typecomp);

    hr = ITypeComp_RemoteBindType_Proxy(This, name, lHashVal, ti);
    if (hr == S_OK)
        ITypeInfo_GetTypeComp(*ti, typecomp);
    else if (typecomp)
        *typecomp = NULL;

    return hr;
}

HRESULT __RPC_STUB ITypeComp_BindType_Stub(
    ITypeComp* This,
    LPOLESTR name,
    ULONG lHashVal,
    ITypeInfo **ti)
{
    ITypeComp *typecomp = NULL;
    HRESULT hr;

    TRACE("(%p, %s, %#x, %p)\n", This, debugstr_w(name), lHashVal, ti);

    hr = ITypeComp_BindType(This, name, lHashVal, ti, &typecomp);

    if (typecomp)
        ITypeComp_Release(typecomp);

    return hr;
}

/* ITypeInfo */

HRESULT CALLBACK ITypeInfo_GetTypeAttr_Proxy(
    ITypeInfo* This,
    TYPEATTR** ppTypeAttr)

{
    CLEANLOCALSTORAGE stg;
    TRACE("(%p, %p)\n", This, ppTypeAttr);

    stg.flags = 0;
    stg.pStorage = NULL;
    stg.pInterface = NULL;

    return ITypeInfo_RemoteGetTypeAttr_Proxy(This, ppTypeAttr, &stg);
}

HRESULT __RPC_STUB ITypeInfo_GetTypeAttr_Stub(
    ITypeInfo* This,
    LPTYPEATTR* ppTypeAttr,
    CLEANLOCALSTORAGE* pDummy)
{
    HRESULT hr;
    TRACE("(%p, %p)\n", This, ppTypeAttr);

    hr = ITypeInfo_GetTypeAttr(This, ppTypeAttr);
    if(hr != S_OK)
        return hr;

    pDummy->flags = CLS_TYPEATTR;
    ITypeInfo_AddRef(This);
    pDummy->pInterface = (IUnknown*)This;
    pDummy->pStorage = ppTypeAttr;
    return hr;
}

HRESULT CALLBACK ITypeInfo_GetFuncDesc_Proxy(
    ITypeInfo* This,
    UINT index,
    FUNCDESC** ppFuncDesc)
{
    CLEANLOCALSTORAGE stg;
    TRACE("(%p, %d, %p)\n", This, index, ppFuncDesc);

    stg.flags = 0;
    stg.pStorage = NULL;
    stg.pInterface = NULL;

    return ITypeInfo_RemoteGetFuncDesc_Proxy(This, index, ppFuncDesc, &stg);
}

HRESULT __RPC_STUB ITypeInfo_GetFuncDesc_Stub(
    ITypeInfo* This,
    UINT index,
    LPFUNCDESC* ppFuncDesc,
    CLEANLOCALSTORAGE* pDummy)
{
    HRESULT hr;
    TRACE("(%p, %d, %p)\n", This, index, ppFuncDesc);

    hr = ITypeInfo_GetFuncDesc(This, index, ppFuncDesc);
    if(hr != S_OK)
        return hr;

    pDummy->flags = CLS_FUNCDESC;
    ITypeInfo_AddRef(This);
    pDummy->pInterface = (IUnknown*)This;
    pDummy->pStorage = ppFuncDesc;
    return hr;
}

HRESULT CALLBACK ITypeInfo_GetVarDesc_Proxy(
    ITypeInfo* This,
    UINT index,
    VARDESC** ppVarDesc)
{
    CLEANLOCALSTORAGE stg;
    TRACE("(%p, %d, %p)\n", This, index, ppVarDesc);

    stg.flags = 0;
    stg.pStorage = NULL;
    stg.pInterface = NULL;

    return ITypeInfo_RemoteGetVarDesc_Proxy(This, index, ppVarDesc, &stg);
}

HRESULT __RPC_STUB ITypeInfo_GetVarDesc_Stub(
    ITypeInfo* This,
    UINT index,
    LPVARDESC* ppVarDesc,
    CLEANLOCALSTORAGE* pDummy)
{
    HRESULT hr;
    TRACE("(%p, %d, %p)\n", This, index, ppVarDesc);

    hr = ITypeInfo_GetVarDesc(This, index, ppVarDesc);
    if(hr != S_OK)
        return hr;

    pDummy->flags = CLS_VARDESC;
    ITypeInfo_AddRef(This);
    pDummy->pInterface = (IUnknown*)This;
    pDummy->pStorage = ppVarDesc;
    return hr;
}

HRESULT CALLBACK ITypeInfo_GetNames_Proxy(
    ITypeInfo* This,
    MEMBERID memid,
    BSTR* rgBstrNames,
    UINT cMaxNames,
    UINT* pcNames)
{
    TRACE("(%p, %08x, %p, %d, %p)\n", This, memid, rgBstrNames, cMaxNames, pcNames);

    return ITypeInfo_RemoteGetNames_Proxy(This, memid, rgBstrNames, cMaxNames, pcNames);
}

HRESULT __RPC_STUB ITypeInfo_GetNames_Stub(
    ITypeInfo* This,
    MEMBERID memid,
    BSTR* rgBstrNames,
    UINT cMaxNames,
    UINT* pcNames)
{
    TRACE("(%p, %08x, %p, %d, %p)\n", This, memid, rgBstrNames, cMaxNames, pcNames);

    return ITypeInfo_GetNames(This, memid, rgBstrNames, cMaxNames, pcNames);
}

HRESULT CALLBACK ITypeInfo_GetIDsOfNames_Proxy(
    ITypeInfo* This,
    LPOLESTR* rgszNames,
    UINT cNames,
    MEMBERID* pMemId)
{
  FIXME("not implemented\n");
  return E_FAIL;
}

HRESULT __RPC_STUB ITypeInfo_GetIDsOfNames_Stub(
    ITypeInfo* This)
{
  FIXME("not implemented\n");
  return E_FAIL;
}

HRESULT CALLBACK ITypeInfo_Invoke_Proxy(
    ITypeInfo* This,
    PVOID pvInstance,
    MEMBERID memid,
    WORD wFlags,
    DISPPARAMS* pDispParams,
    VARIANT* pVarResult,
    EXCEPINFO* pExcepInfo,
    UINT* puArgErr)
{
  FIXME("not implemented\n");
  return E_FAIL;
}

HRESULT __RPC_STUB ITypeInfo_Invoke_Stub(
    ITypeInfo* This)
{
  FIXME("not implemented\n");
  return E_FAIL;
}

HRESULT CALLBACK ITypeInfo_GetDocumentation_Proxy(ITypeInfo *This, MEMBERID memid,
                                                  BSTR *name, BSTR *doc_string,
                                                  DWORD *help_context, BSTR *help_file)
{
    DWORD dummy_help_context, flags = 0;
    BSTR dummy_name, dummy_doc_string, dummy_help_file;
    HRESULT hr;
    TRACE("(%p, %08x, %p, %p, %p, %p)\n", This, memid, name, doc_string, help_context, help_file);

    if(!name) name = &dummy_name;
    else flags = 1;

    if(!doc_string) doc_string = &dummy_doc_string;
    else flags |= 2;

    if(!help_context) help_context = &dummy_help_context;
    else flags |= 4;

    if(!help_file) help_file = &dummy_help_file;
    else flags |= 8;

    hr = ITypeInfo_RemoteGetDocumentation_Proxy(This, memid, flags, name, doc_string, help_context, help_file);

    /* We don't need to free the dummy BSTRs since the stub ensures that these will be NULLs. */

    return hr;
}

HRESULT __RPC_STUB ITypeInfo_GetDocumentation_Stub(ITypeInfo *This, MEMBERID memid,
                                                   DWORD flags, BSTR *name, BSTR *doc_string,
                                                   DWORD *help_context, BSTR *help_file)
{
    TRACE("(%p, %08x, %08x, %p, %p, %p, %p)\n", This, memid, flags, name, doc_string, help_context, help_file);

    *name = *doc_string = *help_file = NULL;
    *help_context = 0;

    if(!(flags & 1)) name = NULL;
    if(!(flags & 2)) doc_string = NULL;
    if(!(flags & 4)) help_context = NULL;
    if(!(flags & 8)) help_file = NULL;

    return ITypeInfo_GetDocumentation(This, memid, name, doc_string, help_context, help_file);
}

HRESULT CALLBACK ITypeInfo_GetDllEntry_Proxy(ITypeInfo *This, MEMBERID memid,
                                             INVOKEKIND invkind, BSTR *dll_name,
                                             BSTR* name, WORD *ordinal)
{
    DWORD flags = 0;
    BSTR dummy_dll_name, dummy_name;
    WORD dummy_ordinal;
    HRESULT hr;
    TRACE("(%p, %08x, %x, %p, %p, %p)\n", This, memid, invkind, dll_name, name, ordinal);

    if(!dll_name) dll_name = &dummy_dll_name;
    else flags = 1;

    if(!name) name = &dummy_name;
    else flags |= 2;

    if(!ordinal) ordinal = &dummy_ordinal;
    else flags |= 4;

    hr = ITypeInfo_RemoteGetDllEntry_Proxy(This, memid, invkind, flags, dll_name, name, ordinal);

    /* We don't need to free the dummy BSTRs since the stub ensures that these will be NULLs. */

    return hr;
}

HRESULT __RPC_STUB ITypeInfo_GetDllEntry_Stub(ITypeInfo *This, MEMBERID memid,
                                              INVOKEKIND invkind, DWORD flags,
                                              BSTR *dll_name, BSTR *name,
                                              WORD *ordinal)
{
    TRACE("(%p, %08x, %x, %p, %p, %p)\n", This, memid, invkind, dll_name, name, ordinal);

    *dll_name = *name = NULL;
    *ordinal = 0;

    if(!(flags & 1)) dll_name = NULL;
    if(!(flags & 2)) name = NULL;
    if(!(flags & 4)) ordinal = NULL;

    return ITypeInfo_GetDllEntry(This, memid, invkind, dll_name, name, ordinal);
}

HRESULT CALLBACK ITypeInfo_AddressOfMember_Proxy(
    ITypeInfo* This,
    MEMBERID memid,
    INVOKEKIND invKind,
    PVOID* ppv)
{
  FIXME("not implemented\n");
  return E_FAIL;
}

HRESULT __RPC_STUB ITypeInfo_AddressOfMember_Stub(
    ITypeInfo* This)
{
  FIXME("not implemented\n");
  return E_FAIL;
}

HRESULT CALLBACK ITypeInfo_CreateInstance_Proxy(
    ITypeInfo* This,
    IUnknown* pUnkOuter,
    REFIID riid,
    PVOID* ppvObj)
{
  FIXME("not implemented\n");
  return E_FAIL;
}

HRESULT __RPC_STUB ITypeInfo_CreateInstance_Stub(
    ITypeInfo* This,
    REFIID riid,
    IUnknown** ppvObj)
{
  FIXME("not implemented\n");
  return E_FAIL;
}

HRESULT CALLBACK ITypeInfo_GetContainingTypeLib_Proxy(
    ITypeInfo* This,
    ITypeLib** ppTLib,
    UINT* pIndex)
{
    ITypeLib *pTL;
    UINT index;
    HRESULT hr;

    TRACE("(%p, %p, %p)\n", This, ppTLib, pIndex );
    
    hr = ITypeInfo_RemoteGetContainingTypeLib_Proxy(This, &pTL, &index);
    if(SUCCEEDED(hr))
    {
        if(pIndex)
            *pIndex = index;

        if(ppTLib)
            *ppTLib = pTL;
        else
            ITypeLib_Release(pTL);
    }
    return hr;
}

HRESULT __RPC_STUB ITypeInfo_GetContainingTypeLib_Stub(
    ITypeInfo* This,
    ITypeLib** ppTLib,
    UINT* pIndex)
{
    TRACE("(%p, %p, %p)\n", This, ppTLib, pIndex );
    return ITypeInfo_GetContainingTypeLib(This, ppTLib, pIndex);
}

void CALLBACK ITypeInfo_ReleaseTypeAttr_Proxy(
    ITypeInfo* This,
    TYPEATTR* pTypeAttr)
{
    TRACE("(%p, %p)\n", This, pTypeAttr);
    free_embedded_typedesc(&pTypeAttr->tdescAlias);
    CoTaskMemFree(pTypeAttr);
}

HRESULT __RPC_STUB ITypeInfo_ReleaseTypeAttr_Stub(
    ITypeInfo* This)
{
    TRACE("nothing to do\n");
    return S_OK;
}

void CALLBACK ITypeInfo_ReleaseFuncDesc_Proxy(
    ITypeInfo* This,
    FUNCDESC* pFuncDesc)
{
    SHORT param;
    TRACE("(%p, %p)\n", This, pFuncDesc);

    for(param = 0; param < pFuncDesc->cParams; param++)
        free_embedded_elemdesc(pFuncDesc->lprgelemdescParam + param);
    if(param)
        CoTaskMemFree(pFuncDesc->lprgelemdescParam);

    free_embedded_elemdesc(&pFuncDesc->elemdescFunc);

    if(pFuncDesc->cScodes != 0 && pFuncDesc->cScodes != -1)
        CoTaskMemFree(pFuncDesc->lprgscode);

    CoTaskMemFree(pFuncDesc);
}

HRESULT __RPC_STUB ITypeInfo_ReleaseFuncDesc_Stub(
    ITypeInfo* This)
{
    TRACE("nothing to do\n");
    return S_OK;
}

void CALLBACK ITypeInfo_ReleaseVarDesc_Proxy(
    ITypeInfo* This,
    VARDESC* pVarDesc)
{
    TRACE("(%p, %p)\n", This, pVarDesc);

    CoTaskMemFree(pVarDesc->lpstrSchema);

    if(pVarDesc->varkind == VAR_CONST)
        CoTaskMemFree(pVarDesc->u.lpvarValue);

    free_embedded_elemdesc(&pVarDesc->elemdescVar);
    CoTaskMemFree(pVarDesc);
}

HRESULT __RPC_STUB ITypeInfo_ReleaseVarDesc_Stub(
    ITypeInfo* This)
{
    TRACE("nothing to do\n");
    return S_OK;
}


/* ITypeInfo2 */

HRESULT CALLBACK ITypeInfo2_GetDocumentation2_Proxy(ITypeInfo2 *This, MEMBERID memid,
                                                    LCID lcid, BSTR *help_string,
                                                    DWORD *help_context, BSTR *help_dll)
{
    DWORD dummy_help_context, flags = 0;
    BSTR dummy_help_string, dummy_help_dll;
    HRESULT hr;
    TRACE("(%p, %08x, %08x, %p, %p, %p)\n", This, memid, lcid, help_string, help_context, help_dll);

    if(!help_string) help_string = &dummy_help_string;
    else flags = 1;

    if(!help_context) help_context = &dummy_help_context;
    else flags |= 2;

    if(!help_dll) help_dll = &dummy_help_dll;
    else flags |= 4;

    hr = ITypeInfo2_RemoteGetDocumentation2_Proxy(This, memid, lcid, flags, help_string, help_context, help_dll);

    /* We don't need to free the dummy BSTRs since the stub ensures that these will be NULLs. */

    return hr;
}

HRESULT __RPC_STUB ITypeInfo2_GetDocumentation2_Stub(ITypeInfo2 *This, MEMBERID memid,
                                                     LCID lcid, DWORD flags,
                                                     BSTR *help_string, DWORD *help_context,
                                                     BSTR *help_dll)
{
    TRACE("(%p, %08x, %08x, %08x, %p, %p, %p)\n", This, memid, lcid, flags, help_string, help_context, help_dll);

    *help_string = *help_dll = NULL;
    *help_context = 0;

    if(!(flags & 1)) help_string = NULL;
    if(!(flags & 2)) help_context = NULL;
    if(!(flags & 4)) help_dll = NULL;

    return ITypeInfo2_GetDocumentation2(This, memid, lcid, help_string, help_context, help_dll);
}

/* ITypeLib */

UINT CALLBACK ITypeLib_GetTypeInfoCount_Proxy(
    ITypeLib* This)
{
    UINT count = 0;
    TRACE("(%p)\n", This);

    ITypeLib_RemoteGetTypeInfoCount_Proxy(This, &count);
    
    return count;
}

HRESULT __RPC_STUB ITypeLib_GetTypeInfoCount_Stub(
    ITypeLib* This,
    UINT* pcTInfo)
{
    TRACE("(%p, %p)\n", This, pcTInfo);
    *pcTInfo = ITypeLib_GetTypeInfoCount(This);
    return S_OK;
}

HRESULT CALLBACK ITypeLib_GetLibAttr_Proxy(
    ITypeLib* This,
    TLIBATTR** ppTLibAttr)
{
    CLEANLOCALSTORAGE stg;
    TRACE("(%p, %p)\n", This, ppTLibAttr);

    stg.flags = 0;
    stg.pStorage = NULL;
    stg.pInterface = NULL;

    return ITypeLib_RemoteGetLibAttr_Proxy(This, ppTLibAttr, &stg);    
}

HRESULT __RPC_STUB ITypeLib_GetLibAttr_Stub(
    ITypeLib* This,
    LPTLIBATTR* ppTLibAttr,
    CLEANLOCALSTORAGE* pDummy)
{
    HRESULT hr;
    TRACE("(%p, %p)\n", This, ppTLibAttr);
    
    hr = ITypeLib_GetLibAttr(This, ppTLibAttr);
    if(hr != S_OK)
        return hr;

    pDummy->flags = CLS_LIBATTR;
    ITypeLib_AddRef(This);
    pDummy->pInterface = (IUnknown*)This;
    pDummy->pStorage = ppTLibAttr;
    return hr;
}

HRESULT CALLBACK ITypeLib_GetDocumentation_Proxy(ITypeLib *This, INT index, BSTR *name,
                                                 BSTR *doc_string, DWORD *help_context,
                                                 BSTR *help_file)
{
    DWORD dummy_help_context, flags = 0;
    BSTR dummy_name, dummy_doc_string, dummy_help_file;
    HRESULT hr;
    TRACE("(%p, %d, %p, %p, %p, %p)\n", This, index, name, doc_string, help_context, help_file);

    if(!name) name = &dummy_name;
    else flags = 1;

    if(!doc_string) doc_string = &dummy_doc_string;
    else flags |= 2;

    if(!help_context) help_context = &dummy_help_context;
    else flags |= 4;

    if(!help_file) help_file = &dummy_help_file;
    else flags |= 8;

    hr = ITypeLib_RemoteGetDocumentation_Proxy(This, index, flags, name, doc_string, help_context, help_file);

    /* We don't need to free the dummy BSTRs since the stub ensures that these will be NULLs. */

    return hr;
}

HRESULT __RPC_STUB ITypeLib_GetDocumentation_Stub(ITypeLib *This, INT index, DWORD flags,
                                                  BSTR *name, BSTR *doc_string,
                                                  DWORD *help_context, BSTR *help_file)
{
    TRACE("(%p, %d, %08x, %p, %p, %p, %p)\n", This, index, flags, name, doc_string, help_context, help_file);

    *name = *doc_string = *help_file = NULL;
    *help_context = 0;

    if(!(flags & 1)) name = NULL;
    if(!(flags & 2)) doc_string = NULL;
    if(!(flags & 4)) help_context = NULL;
    if(!(flags & 8)) help_file = NULL;

    return ITypeLib_GetDocumentation(This, index, name, doc_string, help_context, help_file);
}

HRESULT CALLBACK ITypeLib_IsName_Proxy(
    ITypeLib* This,
    LPOLESTR szNameBuf,
    ULONG lHashVal,
    BOOL* pfName)
{
  FIXME("not implemented\n");
  return E_FAIL;
}

HRESULT __RPC_STUB ITypeLib_IsName_Stub(
    ITypeLib* This,
    LPOLESTR szNameBuf,
    ULONG lHashVal,
    BOOL* pfName,
    BSTR* pBstrLibName)
{
  FIXME("not implemented\n");
  return E_FAIL;
}

HRESULT CALLBACK ITypeLib_FindName_Proxy(
    ITypeLib* This,
    LPOLESTR szNameBuf,
    ULONG lHashVal,
    ITypeInfo** ppTInfo,
    MEMBERID* rgMemId,
    USHORT* pcFound)
{
  FIXME("not implemented\n");
  return E_FAIL;
}

HRESULT __RPC_STUB ITypeLib_FindName_Stub(
    ITypeLib* This,
    LPOLESTR szNameBuf,
    ULONG lHashVal,
    ITypeInfo** ppTInfo,
    MEMBERID* rgMemId,
    USHORT* pcFound,
    BSTR* pBstrLibName)
{
  FIXME("not implemented\n");
  return E_FAIL;
}

void CALLBACK ITypeLib_ReleaseTLibAttr_Proxy(
    ITypeLib* This,
    TLIBATTR* pTLibAttr)
{
    TRACE("(%p, %p)\n", This, pTLibAttr);
    CoTaskMemFree(pTLibAttr);
}

HRESULT __RPC_STUB ITypeLib_ReleaseTLibAttr_Stub(
    ITypeLib* This)
{
    TRACE("nothing to do\n");
    return S_OK;
}


/* ITypeLib2 */

HRESULT CALLBACK ITypeLib2_GetLibStatistics_Proxy(
    ITypeLib2* This,
    ULONG* pcUniqueNames,
    ULONG* pcchUniqueNames)
{
  FIXME("not implemented\n");
  return E_FAIL;
}

HRESULT __RPC_STUB ITypeLib2_GetLibStatistics_Stub(
    ITypeLib2* This,
    ULONG* pcUniqueNames,
    ULONG* pcchUniqueNames)
{
  FIXME("not implemented\n");
  return E_FAIL;
}

HRESULT CALLBACK ITypeLib2_GetDocumentation2_Proxy(ITypeLib2 *This, INT index,
                                                   LCID lcid, BSTR *help_string,
                                                   DWORD *help_context, BSTR *help_dll)
{
    DWORD dummy_help_context, flags = 0;
    BSTR dummy_help_string, dummy_help_dll;
    HRESULT hr;
    TRACE("(%p, %d, %08x, %p, %p, %p)\n", This, index, lcid, help_string, help_context, help_dll);

    if(!help_string) help_string = &dummy_help_string;
    else flags = 1;

    if(!help_context) help_context = &dummy_help_context;
    else flags |= 2;

    if(!help_dll) help_dll = &dummy_help_dll;
    else flags |= 4;

    hr = ITypeLib2_RemoteGetDocumentation2_Proxy(This, index, lcid, flags, help_string, help_context, help_dll);

    /* We don't need to free the dummy BSTRs since the stub ensures that these will be NULLs. */

    return hr;
}

HRESULT __RPC_STUB ITypeLib2_GetDocumentation2_Stub(ITypeLib2 *This, INT index, LCID lcid,
                                                    DWORD flags, BSTR *help_string,
                                                    DWORD *help_context, BSTR *help_dll)
{
    TRACE("(%p, %d, %08x, %08x, %p, %p, %p)\n", This, index, lcid, flags, help_string, help_context, help_dll);

    *help_string = *help_dll = NULL;
    *help_context = 0;

    if(!(flags & 1)) help_string = NULL;
    if(!(flags & 2)) help_context = NULL;
    if(!(flags & 4)) help_dll = NULL;

    return ITypeLib2_GetDocumentation2(This, index, lcid, help_string, help_context, help_dll);
}

HRESULT CALLBACK IPropertyBag_Read_Proxy(
    IPropertyBag* This,
    LPCOLESTR pszPropName,
    VARIANT *pVar,
    IErrorLog *pErrorLog)
{
  IUnknown *pUnk = NULL;
  TRACE("(%p, %s, %p, %p)\n", This, debugstr_w(pszPropName), pVar, pErrorLog);

  if(!pVar)
    return E_POINTER;

  if(V_VT(pVar) & (VT_BYREF | VT_ARRAY | VT_VECTOR))
  {
    FIXME("Variant type %x is byref, array or vector. Not implemented.\n", V_VT(pVar));
    return E_NOTIMPL;
  }

  switch(V_VT(pVar))
  {
    case VT_DISPATCH:
      pUnk = (IUnknown*)V_DISPATCH(pVar);
      break;
    case VT_UNKNOWN:
      pUnk = V_UNKNOWN(pVar);
      break;
    case VT_SAFEARRAY:
      FIXME("Safearray support not yet implemented.\n");
      return E_NOTIMPL;
    default:
      FIXME("Unknown V_VT %d - support not yet implemented.\n", V_VT(pVar));
      return E_NOTIMPL;
  }

  return IPropertyBag_RemoteRead_Proxy(This, pszPropName, pVar, pErrorLog,
                                       V_VT(pVar), pUnk);
}

HRESULT __RPC_STUB IPropertyBag_Read_Stub(
    IPropertyBag* This,
    LPCOLESTR pszPropName,
    VARIANT *pVar,
    IErrorLog *pErrorLog,
    DWORD varType,
    IUnknown *pUnkObj)
{
  IDispatch *disp;
  HRESULT hr;
  TRACE("(%p, %s, %p, %p, %x, %p)\n", This, debugstr_w(pszPropName), pVar,
                                     pErrorLog, varType, pUnkObj);

  if(varType & (VT_BYREF | VT_ARRAY | VT_VECTOR))
  {
    FIXME("Variant type %x is byref, array or vector. Not implemented.\n", V_VT(pVar));
    return E_NOTIMPL;
  }

  V_VT(pVar) = varType;
  switch(varType)
  {
    case VT_DISPATCH:
      hr = IUnknown_QueryInterface(pUnkObj, &IID_IDispatch, (LPVOID*)&disp);
      if(FAILED(hr))
        return hr;
      IUnknown_Release(pUnkObj);
      V_DISPATCH(pVar) = disp;
      break;
    case VT_UNKNOWN:
      V_UNKNOWN(pVar) = pUnkObj;
      break;
    case VT_BSTR:
      V_BSTR(pVar) = SysAllocString(L"");
      break;
    case VT_SAFEARRAY:
      FIXME("Safearray support not yet implemented.\n");
      return E_NOTIMPL;
    default:
      break;
  }
  hr = IPropertyBag_Read(This, pszPropName, pVar, pErrorLog);
  if(FAILED(hr))
    VariantClear(pVar);

  return hr;
}

/* call_as/local stubs for ocidl.idl */

HRESULT CALLBACK IClassFactory2_CreateInstanceLic_Proxy(
    IClassFactory2* This,
    IUnknown *pUnkOuter,
    IUnknown *pUnkReserved,
    REFIID riid,
    BSTR bstrKey,
    PVOID *ppvObj)
{
    TRACE("(%p, %s, %p)\n", pUnkOuter, debugstr_guid(riid), ppvObj);

    *ppvObj = NULL;

    if (pUnkOuter)
    {
        ERR("aggregation is not allowed on remote objects\n");
        return CLASS_E_NOAGGREGATION;
    }

    return IClassFactory2_RemoteCreateInstanceLic_Proxy(This, riid, bstrKey, (IUnknown**)ppvObj);
}

HRESULT __RPC_STUB IClassFactory2_CreateInstanceLic_Stub(
    IClassFactory2* This,
    REFIID riid,
    BSTR bstrKey,
    IUnknown **ppvObj)
{
    TRACE("(%s, %p)\n", debugstr_guid(riid), ppvObj);
    return IClassFactory2_CreateInstanceLic(This, NULL, NULL, riid, bstrKey, (void**)ppvObj);
}

HRESULT CALLBACK IEnumConnections_Next_Proxy(
    IEnumConnections* This,
    ULONG cConnections,
    LPCONNECTDATA rgcd,
    ULONG *pcFetched)
{
    ULONG fetched;

    TRACE("(%u, %p %p)\n", cConnections, rgcd, pcFetched);

    if (!pcFetched)
        pcFetched = &fetched;

    return IEnumConnections_RemoteNext_Proxy(This, cConnections, rgcd, pcFetched);
}

HRESULT __RPC_STUB IEnumConnections_Next_Stub(
    IEnumConnections* This,
    ULONG cConnections,
    LPCONNECTDATA rgcd,
    ULONG *pcFetched)
{
    HRESULT hr;

    TRACE("(%u, %p, %p)\n", cConnections, rgcd, pcFetched);

    *pcFetched = 0;
    hr = IEnumConnections_Next(This, cConnections, rgcd, pcFetched);
    if (hr == S_OK)
        *pcFetched = cConnections;

    return hr;
}

HRESULT CALLBACK IEnumConnectionPoints_Next_Proxy(
    IEnumConnectionPoints* This,
    ULONG cConnections,
    IConnectionPoint **ppCP,
    ULONG *pcFetched)
{
    ULONG fetched;

    TRACE("(%u, %p %p)\n", cConnections, ppCP, pcFetched);

    if (!pcFetched)
        pcFetched = &fetched;

    return IEnumConnectionPoints_RemoteNext_Proxy(This, cConnections, ppCP, pcFetched);
}

HRESULT __RPC_STUB IEnumConnectionPoints_Next_Stub(
    IEnumConnectionPoints* This,
    ULONG cConnections,
    IConnectionPoint **ppCP,
    ULONG *pcFetched)
{
    HRESULT hr;

    TRACE("(%u, %p, %p)\n", cConnections, ppCP, pcFetched);

    *pcFetched = 0;
    hr = IEnumConnectionPoints_Next(This, cConnections, ppCP, pcFetched);
    if (hr == S_OK)
        *pcFetched = cConnections;

    return hr;
}

HRESULT CALLBACK IPersistMemory_Load_Proxy(
    IPersistMemory* This,
    LPVOID pMem,
    ULONG cbSize)
{
    TRACE("(%p, %u)\n", pMem, cbSize);

    if (!pMem)
        return E_POINTER;

    return IPersistMemory_RemoteLoad_Proxy(This, pMem, cbSize);
}

HRESULT __RPC_STUB IPersistMemory_Load_Stub(
    IPersistMemory* This,
    BYTE *pMem,
    ULONG cbSize)
{
    TRACE("(%p, %u)\n", pMem, cbSize);
    return IPersistMemory_Load(This, pMem, cbSize);
}

HRESULT CALLBACK IPersistMemory_Save_Proxy(
    IPersistMemory* This,
    LPVOID pMem,
    BOOL fClearDirty,
    ULONG cbSize)
{
    TRACE("(%p, %d, %u)\n", pMem, fClearDirty, cbSize);

    if (!pMem)
        return E_POINTER;

    return IPersistMemory_RemoteSave_Proxy(This, pMem, fClearDirty, cbSize);
}

HRESULT __RPC_STUB IPersistMemory_Save_Stub(
    IPersistMemory* This,
    BYTE *pMem,
    BOOL fClearDirty,
    ULONG cbSize)
{
    TRACE("(%p, %d, %u)\n", pMem, fClearDirty, cbSize);
    return IPersistMemory_Save(This, pMem, fClearDirty, cbSize);
}

void CALLBACK IAdviseSinkEx_OnViewStatusChange_Proxy(
    IAdviseSinkEx* This,
    DWORD dwViewStatus)
{
    TRACE("(%p, 0x%08x)\n", This, dwViewStatus);
    IAdviseSinkEx_RemoteOnViewStatusChange_Proxy(This, dwViewStatus);
}

HRESULT __RPC_STUB IAdviseSinkEx_OnViewStatusChange_Stub(
    IAdviseSinkEx* This,
    DWORD dwViewStatus)
{
    TRACE("(%p, 0x%08x)\n", This, dwViewStatus);
    IAdviseSinkEx_OnViewStatusChange(This, dwViewStatus);
    return S_OK;
}

HRESULT CALLBACK IEnumOleUndoUnits_Next_Proxy(
    IEnumOleUndoUnits* This,
    ULONG cElt,
    IOleUndoUnit **rgElt,
    ULONG *pcEltFetched)
{
    ULONG fetched;

    TRACE("(%u, %p %p)\n", cElt, rgElt, pcEltFetched);

    if (!pcEltFetched)
        pcEltFetched = &fetched;

    return IEnumOleUndoUnits_RemoteNext_Proxy(This, cElt, rgElt, pcEltFetched);
}

HRESULT __RPC_STUB IEnumOleUndoUnits_Next_Stub(
    IEnumOleUndoUnits* This,
    ULONG cElt,
    IOleUndoUnit **rgElt,
    ULONG *pcEltFetched)
{
    HRESULT hr;

    TRACE("(%u, %p, %p)\n", cElt, rgElt, pcEltFetched);

    *pcEltFetched = 0;
    hr = IEnumOleUndoUnits_Next(This, cElt, rgElt, pcEltFetched);
    if (hr == S_OK)
        *pcEltFetched = cElt;

    return hr;
}

HRESULT CALLBACK IQuickActivate_QuickActivate_Proxy(
    IQuickActivate* This,
    QACONTAINER *pQaContainer,
    QACONTROL *pQaControl)
{
    FIXME("not implemented\n");
    return E_NOTIMPL;
}

HRESULT __RPC_STUB IQuickActivate_QuickActivate_Stub(
    IQuickActivate* This,
    QACONTAINER *pQaContainer,
    QACONTROL *pQaControl)
{
    FIXME("not implemented\n");
    return E_NOTIMPL;
}