RPC marshalling patch

Ove Kaaven ovehk at ping.uio.no
Thu Jan 30 10:44:35 CST 2003


This one is sure to give Greg something to work with... all of this was
implemented in a bit of a hurry, but since it's based on my research, it
should be a good starting point in understanding how Microsoft's NDR
engine works. It doesn't properly implement marshalling alignment or
memory sizing, may not handle a number of fringe cases, does not conform
to the DCE RPC wire protocol (mostly because I don't have a description of
it... where did you find it, Greg?), and probably needs some cleanup, but
it seems to do the job for me.

Log:
Ove Kaaven <ovek at transgaming.com>
Implemented marshalling of pointers, simple and complex structures,
conformant and complex arrays, and user-marshalled types.
Improved marshalling of conformant strings and interface pointers a bit.

Index: dlls/rpcrt4/ndr_marshall.c
===================================================================
RCS file: /home/wine/wine/dlls/rpcrt4/ndr_marshall.c,v
retrieving revision 1.15
diff -u -r1.15 ndr_marshall.c
--- dlls/rpcrt4/ndr_marshall.c	5 Dec 2002 20:33:08 -0000	1.15
+++ dlls/rpcrt4/ndr_marshall.c	30 Jan 2003 16:32:00 -0000
@@ -34,8 +34,8 @@
 #include "ndr_misc.h"
 #include "rpcndr.h"
 
+#include "wine/unicode.h"
 #include "wine/rpcfc.h"
-#include "objbase.h"
 
 #include "wine/debug.h"
 
@@ -88,6 +88,266 @@
     LITTLE_ENDIAN_UINT32_READ(pchar)
 #endif
 
+/* _Align must be the desired alignment minus 1,
+ * e.g. ALIGN_LENGTH(len, 3) to align on a dword boundary. */
+#define ALIGNED_LENGTH(_Len, _Align) (((_Len)+(_Align))&~(_Align))
+#define ALIGNED_POINTER(_Ptr, _Align) ((LPVOID)ALIGN_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)
+
+#define NDR_TABLE_SIZE 128
+#define NDR_TABLE_MASK 127
+
+NDR_MARSHALL NdrMarshaller[NDR_TABLE_SIZE] = {
+  0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0,
+  /* 0x10 */
+  0,
+  /* 0x11 */
+  NdrPointerMarshall, NdrPointerMarshall,
+  NdrPointerMarshall, NdrPointerMarshall,
+  /* 0x15 */
+  NdrSimpleStructMarshall, NdrSimpleStructMarshall,
+  0, 0, 0,
+  NdrComplexStructMarshall,
+  /* 0x1b */
+  NdrConformantArrayMarshall, 0, 0, 0, 0, 0,
+  NdrComplexArrayMarshall,
+  /* 0x22 */
+  NdrConformantStringMarshall, 0, 0,
+  NdrConformantStringMarshall, 0, 0, 0, 0,
+  /* 0x2a */
+  0, 0, 0, 0, 0,
+  /* 0x2f */
+  NdrInterfacePointerMarshall,
+  /* 0xb0 */
+  0, 0, 0, 0,
+  NdrUserMarshalMarshall
+};
+NDR_UNMARSHALL NdrUnmarshaller[NDR_TABLE_SIZE] = {
+  0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0,
+  /* 0x10 */
+  0,
+  /* 0x11 */
+  NdrPointerUnmarshall, NdrPointerUnmarshall,
+  NdrPointerUnmarshall, NdrPointerUnmarshall,
+  /* 0x15 */
+  NdrSimpleStructUnmarshall, NdrSimpleStructUnmarshall,
+  0, 0, 0,
+  NdrComplexStructUnmarshall,
+  /* 0x1b */
+  NdrConformantArrayUnmarshall, 0, 0, 0, 0, 0,
+  NdrComplexArrayUnmarshall,
+  /* 0x22 */
+  NdrConformantStringUnmarshall, 0, 0,
+  NdrConformantStringUnmarshall, 0, 0, 0, 0,
+  /* 0x2a */
+  0, 0, 0, 0, 0,
+  /* 0x2f */
+  NdrInterfacePointerUnmarshall,
+  /* 0xb0 */
+  0, 0, 0, 0,
+  NdrUserMarshalUnmarshall
+};
+NDR_BUFFERSIZE NdrBufferSizer[NDR_TABLE_SIZE] = {
+  0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0,
+  /* 0x10 */
+  0,
+  /* 0x11 */
+  NdrPointerBufferSize, NdrPointerBufferSize,
+  NdrPointerBufferSize, NdrPointerBufferSize,
+  /* 0x15 */
+  NdrSimpleStructBufferSize, NdrSimpleStructBufferSize,
+  0, 0, 0,
+  NdrComplexStructBufferSize,
+  /* 0x1b */
+  NdrConformantArrayBufferSize, 0, 0, 0, 0, 0,
+  NdrComplexArrayBufferSize,
+  /* 0x22 */
+  NdrConformantStringBufferSize, 0, 0,
+  NdrConformantStringBufferSize, 0, 0, 0, 0,
+  /* 0x2a */
+  0, 0, 0, 0, 0,
+  /* 0x2f */
+  NdrInterfacePointerBufferSize,
+  /* 0xb0 */
+  0, 0, 0, 0,
+  NdrUserMarshalBufferSize
+};
+NDR_MEMORYSIZE NdrMemorySizer[NDR_TABLE_SIZE] = {
+  0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0,
+  /* 0x10 */
+  0,
+  /* 0x11 */
+  NdrPointerMemorySize, NdrPointerMemorySize,
+  NdrPointerMemorySize, NdrPointerMemorySize,
+  /* 0x15 */
+  NdrSimpleStructMemorySize, NdrSimpleStructMemorySize,
+  0, 0, 0,
+  NdrComplexStructMemorySize,
+  /* 0x1b */
+  NdrConformantArrayMemorySize, 0, 0, 0, 0, 0,
+  NdrComplexArrayMemorySize,
+  /* 0x22 */
+  NdrConformantStringMemorySize, 0, 0,
+  NdrConformantStringMemorySize, 0, 0, 0, 0,
+  /* 0x2a */
+  0, 0, 0, 0, 0,
+  /* 0x2f */
+  NdrInterfacePointerMemorySize,
+  /* 0xb0 */
+  0, 0, 0, 0,
+  NdrUserMarshalMemorySize
+};
+NDR_FREE NdrFreer[NDR_TABLE_SIZE] = {
+  0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0,
+  /* 0x10 */
+  0,
+  /* 0x11 */
+  NdrPointerFree, NdrPointerFree,
+  NdrPointerFree, NdrPointerFree,
+  /* 0x15 */
+  NdrSimpleStructFree, NdrSimpleStructFree,
+  0, 0, 0,
+  NdrComplexStructFree,
+  /* 0x1b */
+  NdrConformantArrayFree, 0, 0, 0, 0, 0,
+  NdrComplexArrayFree,
+  /* 0x22 */
+  0, 0, 0, 0, 0, 0, 0, 0,
+  /* 0x2a */
+  0, 0, 0, 0, 0,
+  /* 0x2f */
+  NdrInterfacePointerFree,
+  /* 0xb0 */
+  0, 0, 0, 0,
+  NdrUserMarshalFree
+};
+
+void * WINAPI NdrAllocate(MIDL_STUB_MESSAGE *pStubMsg, size_t len)
+{
+  /* hmm, this is probably supposed to do more? */
+  return pStubMsg->pfnAllocate(len);
+}
+
+PFORMAT_STRING ReadConformance(MIDL_STUB_MESSAGE *pStubMsg, PFORMAT_STRING pFormat)
+{
+  pStubMsg->MaxCount = NDR_LOCAL_UINT32_READ(pStubMsg->Buffer);
+  pStubMsg->Buffer += 4;
+  TRACE("unmarshalled conformance is %d\n", pStubMsg->MaxCount);
+  return pFormat+4;
+}
+
+PFORMAT_STRING ComputeConformance(MIDL_STUB_MESSAGE *pStubMsg, unsigned char *pMemory,
+                                  PFORMAT_STRING pFormat, ULONG_PTR def)
+{
+  BYTE dtype = pFormat[0] & 0xf;
+  DWORD ofs = (DWORD)pFormat[2] | ((DWORD)pFormat[3] << 8);
+  LPVOID ptr = NULL;
+  DWORD data = 0;
+
+  if (pFormat[0] == 0xff) {
+    /* null descriptor */
+    pStubMsg->MaxCount = def;
+    goto finish_conf;
+  }
+
+  switch (pFormat[0] & 0xf0) {
+  case RPC_FC_NORMAL_CONFORMANCE:
+    TRACE("normal conformance, ofs=%ld\n", ofs);
+    ptr = pMemory + ofs;
+    break;
+  case RPC_FC_POINTER_CONFORMANCE:
+    TRACE("pointer conformance, ofs=%ld\n", ofs);
+    ptr = pStubMsg->Memory + ofs;
+    break;
+  case RPC_FC_TOP_LEVEL_CONFORMANCE:
+    TRACE("toplevel conformance, ofs=%ld\n", ofs);
+    if (pStubMsg->StackTop) {
+      ptr = pStubMsg->StackTop + ofs;
+    }
+    else {
+      /* -Os mode, MaxCount is already set */
+      goto finish_conf;
+    }
+    break;
+  case RPC_FC_CONSTANT_CONFORMANCE:
+    data = ofs | ((DWORD)pFormat[1] << 16);
+    TRACE("constant conformance, val=%ld\n", data);
+    pStubMsg->MaxCount = data;
+    goto finish_conf;
+  case RPC_FC_TOP_LEVEL_MULTID_CONFORMANCE:
+    FIXME("toplevel multidimensional conformance, ofs=%ld\n", ofs);
+    if (pStubMsg->StackTop) {
+      ptr = pStubMsg->StackTop + ofs;
+    }
+    else {
+      /* ? */
+      goto done_conf_grab;
+    }
+    break;
+  default:
+    FIXME("unknown conformance type %x\n", pFormat[0] & 0xf0);
+  }
+
+  switch (pFormat[1]) {
+  case RPC_FC_DEREFERENCE:
+    ptr = *(LPVOID*)ptr;
+    break;
+  case RPC_FC_CALLBACK:
+    /* ofs is index into StubDesc->apfnExprEval */
+    FIXME("handle callback\n");
+    goto finish_conf;
+  default:
+    break;
+  }
+
+  switch (dtype) {
+  case RPC_FC_LONG:
+  case RPC_FC_ULONG:
+    data = *(DWORD*)ptr;
+    break;
+  case RPC_FC_SHORT:
+    data = *(SHORT*)ptr;
+    break;
+  case RPC_FC_USHORT:
+    data = *(USHORT*)ptr;
+    break;
+  case RPC_FC_SMALL:
+    data = *(CHAR*)ptr;
+    break;
+  case RPC_FC_USMALL:
+    data = *(UCHAR*)ptr;
+    break;
+  default:
+    FIXME("unknown conformance data type %x\n", dtype);
+    goto done_conf_grab;
+  }
+  TRACE("dereferenced data type %x at %p, got %ld\n", dtype, ptr, data);
+
+done_conf_grab:
+  switch (pFormat[1]) {
+  case 0: /* no op */
+    pStubMsg->MaxCount = data;
+    break;
+  case RPC_FC_DEREFERENCE:
+    /* already handled */
+    break;
+  default:
+    FIXME("unknown conformance op %d\n", pFormat[1]);
+    goto finish_conf;
+  }
+
+finish_conf:
+  TRACE("resulting conformance is %d\n", pStubMsg->MaxCount);
+  return pFormat+4;
+}
+
+
 /*
  * NdrConformantString:
  * 
@@ -111,31 +371,45 @@
 unsigned char *WINAPI NdrConformantStringMarshall(MIDL_STUB_MESSAGE *pStubMsg,
   unsigned char *pszMessage, PFORMAT_STRING pFormat)
 { 
-  UINT32 len, i;
+  unsigned long len, esize;
   unsigned char *c;
 
   TRACE("(pStubMsg == ^%p, pszMessage == ^%p, pFormat == ^%p)\n", pStubMsg, pszMessage, pFormat);
   
   assert(pFormat);
   if (*pFormat == RPC_FC_C_CSTRING) {
-    len = strlen(pszMessage);
-    assert( (pStubMsg->BufferLength > (len + 13)) && (pStubMsg->Buffer != NULL) );
-    c = pStubMsg->Buffer;
-    memset(c, 0, 12);
-    NDR_LOCAL_UINT32_WRITE(c, len + 1); /* max length: strlen + 1 (for '\0') */
-    c += 8;                             /* offset: 0 */
-    NDR_LOCAL_UINT32_WRITE(c, len + 1); /* actual length: (same) */
-    c += 4;
-    for (i = 0; i <= len; i++)
-      *(c++) = *(pszMessage++);         /* the string itself */
-  } else {
+    TRACE("string=%s\n", debugstr_a(pszMessage));
+    len = strlen(pszMessage)+1;
+    esize = 1;
+  }
+  else if (*pFormat == RPC_FC_C_WSTRING) {
+    TRACE("string=%s\n", debugstr_w((LPWSTR)pszMessage));
+    len = strlenW((LPWSTR)pszMessage)+1;
+    esize = 2;
+  }
+  else {
     ERR("Unhandled string type: %#x\n", *pFormat); 
     /* FIXME: raise an exception. */
     return NULL;
   }
+
+  if (pFormat[1] != RPC_FC_PAD) {
+    FIXME("sized string format=%d\n", pFormat[1]);
+  }
+
+  assert( (pStubMsg->BufferLength >= (len*esize + 13)) && (pStubMsg->Buffer != NULL) );
+
+  c = pStubMsg->Buffer;
+  memset(c, 0, 12);
+  NDR_LOCAL_UINT32_WRITE(c, len); /* max length: strlen + 1 (for '\0') */
+  c += 8;                         /* offset: 0 */
+  NDR_LOCAL_UINT32_WRITE(c, len); /* actual length: (same) */
+  c += 4;
+  memcpy(c, pszMessage, len*esize); /* the string itself */
+  c += len*esize;
+  pStubMsg->Buffer = c;
   
   /* success */
-  pStubMsg->fBufferValid = 1;
   return NULL; /* is this always right? */
 }
 
@@ -150,11 +424,22 @@
   assert(pFormat);
   if (*pFormat == RPC_FC_C_CSTRING) {
     /* we need 12 octets for the [maxlen, offset, len] DWORDS, + 1 octet for '\0' */
-    pStubMsg->BufferLength = strlen(pMemory) + 13 + BUFFER_PARANOIA;
-  } else {
+    TRACE("string=%s\n", debugstr_a(pMemory));
+    pStubMsg->BufferLength += strlen(pMemory) + 13 + BUFFER_PARANOIA;
+  }
+  else if (*pFormat == RPC_FC_C_WSTRING) {
+    /* we need 12 octets for the [maxlen, offset, len] DWORDS, + 2 octets for L'\0' */
+    TRACE("string=%s\n", debugstr_w((LPWSTR)pMemory));
+    pStubMsg->BufferLength += strlenW((LPWSTR)pMemory)*2 + 14 + BUFFER_PARANOIA;
+  }
+  else {
     ERR("Unhandled string type: %#x\n", *pFormat); 
     /* FIXME: raise an exception */
   }
+
+  if (pFormat[1] != RPC_FC_PAD) {
+    FIXME("sized string format=%d\n", pFormat[1]);
+  }
 }
 
 /************************************************************************
@@ -171,11 +456,19 @@
 
   if (*pFormat == RPC_FC_C_CSTRING) {
     rslt = NDR_LOCAL_UINT32_READ(pStubMsg->Buffer); /* maxlen */
-  } else {
+  }
+  else if (*pFormat == RPC_FC_C_WSTRING) {
+    rslt = NDR_LOCAL_UINT32_READ(pStubMsg->Buffer)*2; /* maxlen */
+  }
+  else {
     ERR("Unhandled string type: %#x\n", *pFormat);
     /* FIXME: raise an exception */
   }
 
+  if (pFormat[1] != RPC_FC_PAD) {
+    FIXME("sized string format=%d\n", pFormat[1]);
+  }
+
   TRACE("  --> %lu\n", rslt);
   return rslt;
 }
@@ -186,58 +479,1470 @@
 unsigned char *WINAPI NdrConformantStringUnmarshall( PMIDL_STUB_MESSAGE pStubMsg,
   unsigned char** ppMemory, PFORMAT_STRING pFormat, unsigned char fMustAlloc )
 {
-  unsigned long len, ofs;
+  unsigned long len, esize, ofs;
+  unsigned char *pMem;
 
   TRACE("(pStubMsg == ^%p, *pMemory == ^%p, pFormat == ^%p, fMustAlloc == %u)\n",
     pStubMsg, *ppMemory, pFormat, fMustAlloc);
 
   assert(pFormat && ppMemory && pStubMsg);
 
-  len = NdrConformantStringMemorySize(pStubMsg, pFormat);
-
-  /* now the actual length (in bytes) that we need to store
-     the unmarshalled string is in len, including terminating '\0' */
+  pStubMsg->Buffer += 4;
+  ofs = NDR_LOCAL_UINT32_READ(pStubMsg->Buffer);
+  pStubMsg->Buffer += 4;
+  len = NDR_LOCAL_UINT32_READ(pStubMsg->Buffer);
+  pStubMsg->Buffer += 4;
+
+  if (*pFormat == RPC_FC_C_CSTRING) esize = 1;
+  else if (*pFormat == RPC_FC_C_WSTRING) esize = 2;
+  else {
+    ERR("Unhandled string type: %#x\n", *pFormat);
+    /* FIXME: raise an exception */
+    esize = 0;
+  }
 
-  if ( fMustAlloc || (!(*ppMemory)) || (pStubMsg->Memory != *ppMemory) || 
-       (pStubMsg->MemorySize < (len+BUFFER_PARANOIA)) ) {
-    /* crap, looks like we need to do something about the Memory.  I don't
-       understand, it doesn't look like Microsoft is doing this the same
-       way... but then how do they do it?  AFAICS the Memory is never deallocated by
-       the stub code so where does it go?... anyhow, I guess we'll just do it
-       our own way for now... */
-    pStubMsg->MemorySize = len + BUFFER_PARANOIA;
-    pStubMsg->Memory = *ppMemory;
-    /* FIXME: pfnAllocate? or does that not apply to these "Memory" parts? */
-    *ppMemory = pStubMsg->Memory =
-      HeapReAlloc(GetProcessHeap(), 0, pStubMsg->Memory, pStubMsg->MemorySize);   
-  } 
+  if (pFormat[1] != RPC_FC_PAD) {
+    FIXME("sized string format=%d\n", pFormat[1]);
+  }
 
-  if (!(pStubMsg->Memory)) {
-    ERR("Memory Allocation Failure\n");
-    /* FIXME: raise an exception */
-    return NULL;
+  if (fMustAlloc) {
+    *ppMemory = NdrAllocate(pStubMsg, len*esize + BUFFER_PARANOIA);
+  } else {
+    if (pStubMsg->ReuseBuffer && !*ppMemory)
+      /* for servers, we may just point straight into the RPC buffer, I think
+       * (I guess that's what MS does since MIDL code doesn't try to free) */
+      *ppMemory = pStubMsg->Buffer - ofs*esize;
+    /* for clients, memory should be provided by caller */
   }
 
-  /* OK, we've got our ram.  now do the real unmarshalling */
+  pMem = *ppMemory + ofs*esize;
+
+  if (pMem != pStubMsg->Buffer)
+    memcpy(pMem, pStubMsg->Buffer, len*esize);
+
+  pStubMsg->Buffer += len*esize;
+
   if (*pFormat == RPC_FC_C_CSTRING) {
-    char *c = *ppMemory;
+    TRACE("string=%s\n", debugstr_a(pMem));
+  }
+  else if (*pFormat == RPC_FC_C_WSTRING) {
+    TRACE("string=%s\n", debugstr_w((LPWSTR)pMem));
+  }
 
-    pStubMsg->Buffer += 4;
-    ofs = NDR_LOCAL_UINT32_READ(pStubMsg->Buffer);
-    pStubMsg->Buffer += 4;
-    len = NDR_LOCAL_UINT32_READ(pStubMsg->Buffer);
-    pStubMsg->Buffer += 4;
+  return NULL; /* FIXME: is this always right? */
+}
 
-    c += ofs; /* presumably this will always be zero, otherwise the string is no good */
+/***********************************************************************
+ *           PointerMarshall
+ */
+void WINAPI PointerMarshall(PMIDL_STUB_MESSAGE pStubMsg,
+                            unsigned char *Buffer,
+                            unsigned char *Pointer,
+                            PFORMAT_STRING pFormat)
+{
+  unsigned type = pFormat[0], attr = pFormat[1];
+  PFORMAT_STRING desc;
+  NDR_MARSHALL m;
+
+  TRACE("(%p,%p,%p,%p)\n", pStubMsg, Buffer, Pointer, pFormat);
+  TRACE("type=%d, attr=%d\n", type, attr);
+  pFormat += 2;
+  if (attr & RPC_FC_P_SIMPLEPOINTER) desc = pFormat;
+  else desc = pFormat + *(SHORT*)pFormat;
+  if (attr & RPC_FC_P_DEREF) FIXME("deref?\n");
+
+  *(LPVOID*)Buffer = 0;
+
+  switch (type) {
+  case RPC_FC_RP: /* ref pointer (always non-null) */
+    break;
+  default:
+    FIXME("unhandled ptr type=%02x\n", type);
+  }
 
-    while ((*c++ = *(pStubMsg->Buffer++)) != '\0') 
-      ;
+  m = NdrMarshaller[*desc & NDR_TABLE_MASK];
+  if (m) m(pStubMsg, Pointer, desc);
+  else FIXME("no marshaller for data type=%02x\n", *desc);
+}
+
+/***********************************************************************
+ *           PointerUnmarshall
+ */
+void WINAPI PointerUnmarshall(PMIDL_STUB_MESSAGE pStubMsg,
+                              unsigned char *Buffer,
+                              unsigned char **pPointer,
+                              PFORMAT_STRING pFormat,
+                              unsigned char fMustAlloc)
+{
+  unsigned type = pFormat[0], attr = pFormat[1];
+  PFORMAT_STRING desc;
+  NDR_UNMARSHALL m;
+
+  TRACE("(%p,%p,%p,%p,%d)\n", pStubMsg, Buffer, pPointer, pFormat, fMustAlloc);
+  TRACE("type=%d, attr=%d\n", type, attr);
+  pFormat += 2;
+  if (attr & RPC_FC_P_SIMPLEPOINTER) desc = pFormat;
+  else desc = pFormat + *(SHORT*)pFormat;
+  if (attr & RPC_FC_P_DEREF) FIXME("deref?\n");
+
+  switch (type) {
+  case RPC_FC_RP: /* ref pointer (always non-null) */
+    break;
+  default:
+    FIXME("unhandled ptr type=%02x\n", type);
+  }
+
+  *pPointer = NULL;
+
+  m = NdrUnmarshaller[*desc & NDR_TABLE_MASK];
+  if (m) m(pStubMsg, pPointer, desc, fMustAlloc);
+  else FIXME("no unmarshaller for data type=%02x\n", *desc);
+  TRACE("pointer=%p\n", *pPointer);
+}
+
+/***********************************************************************
+ *           PointerBufferSize
+ */
+void WINAPI PointerBufferSize(PMIDL_STUB_MESSAGE pStubMsg,
+                              unsigned char *Pointer,
+                              PFORMAT_STRING pFormat)
+{
+  unsigned type = pFormat[0], attr = pFormat[1];
+  PFORMAT_STRING desc;
+  NDR_BUFFERSIZE m;
+
+  TRACE("(%p,%p,%p)\n", pStubMsg, Pointer, pFormat);
+  TRACE("type=%d, attr=%d\n", type, attr);
+  pFormat += 2;
+  if (attr & RPC_FC_P_SIMPLEPOINTER) desc = pFormat;
+  else desc = pFormat + *(SHORT*)pFormat;
+  if (attr & RPC_FC_P_DEREF) FIXME("deref?\n");
+
+  switch (type) {
+  case RPC_FC_RP: /* ref pointer (always non-null) */
+    break;
+  default:
+    FIXME("unhandled ptr type=%02x\n", type);
+  }
+
+  m = NdrBufferSizer[*desc & NDR_TABLE_MASK];
+  if (m) m(pStubMsg, Pointer, desc);
+  else FIXME("no buffersizer for data type=%02x\n", *desc);
+}
+
+/***********************************************************************
+ *           PointerMemorySize [RPCRT4.@]
+ */
+unsigned long WINAPI PointerMemorySize(PMIDL_STUB_MESSAGE pStubMsg,
+                                       unsigned char *Buffer,
+                                       PFORMAT_STRING pFormat)
+{
+  unsigned type = pFormat[0], attr = pFormat[1];
+  PFORMAT_STRING desc;
+  NDR_MEMORYSIZE m;
+
+  FIXME("(%p,%p,%p): stub\n", pStubMsg, Buffer, pFormat);
+  TRACE("type=%d, attr=%d\n", type, attr);
+  pFormat += 2;
+  if (attr & RPC_FC_P_SIMPLEPOINTER) desc = pFormat;
+  else desc = pFormat + *(SHORT*)pFormat;
+  if (attr & RPC_FC_P_DEREF) FIXME("deref?\n");
+
+  switch (type) {
+  case RPC_FC_RP: /* ref pointer (always non-null) */
+    break;
+  default:
+    FIXME("unhandled ptr type=%02x\n", type);
+  }
+
+  m = NdrMemorySizer[*desc & NDR_TABLE_MASK];
+  if (m) m(pStubMsg, desc);
+  else FIXME("no memorysizer for data type=%02x\n", *desc);
+
+  return 0;
+}
+
+/***********************************************************************
+ *           PointerFree [RPCRT4.@]
+ */
+void WINAPI PointerFree(PMIDL_STUB_MESSAGE pStubMsg,
+                        unsigned char *Pointer,
+                        PFORMAT_STRING pFormat)
+{
+  unsigned type = pFormat[0], attr = pFormat[1];
+  PFORMAT_STRING desc;
+  NDR_FREE m;
+
+  TRACE("(%p,%p,%p)\n", pStubMsg, Pointer, pFormat);
+  TRACE("type=%d, attr=%d\n", type, attr);
+  if (attr & RPC_FC_P_DONTFREE) return;
+  pFormat += 2;
+  if (attr & RPC_FC_P_SIMPLEPOINTER) desc = pFormat;
+  else desc = pFormat + *(SHORT*)pFormat;
+  if (attr & RPC_FC_P_DEREF) FIXME("deref?\n");
+
+  if (!Pointer) return;
+
+  m = NdrFreer[*desc & NDR_TABLE_MASK];
+  if (m) m(pStubMsg, Pointer, desc);
+
+  /* hmm... is this sensible?
+   * perhaps we should check if the memory comes from NdrAllocate,
+   * and deallocate only if so - checking if the pointer is between
+   * BufferStart and BufferEnd is probably no good since the buffer
+   * may be reallocated when the server wants to marshal the reply */
+  switch (*desc) {
+  case RPC_FC_BOGUS_STRUCT:
+  case RPC_FC_BOGUS_ARRAY:
+    break;
+  default:
+    FIXME("unhandled data type=%02x\n", *desc);
+  case RPC_FC_CARRAY:
+  case RPC_FC_C_CSTRING:
+  case RPC_FC_C_WSTRING:
+    if (pStubMsg->ReuseBuffer) goto notfree;
+    break;
+  }
+
+  if (attr & RPC_FC_P_ONSTACK) {
+    TRACE("not freeing stack ptr %p\n", Pointer);
+    return;
+  }
+  TRACE("freeing %p\n", Pointer);
+  pStubMsg->pfnFree(Pointer);
+  return;
+notfree:
+  TRACE("not freeing %p\n", Pointer);
+}
+
+/***********************************************************************
+ *           EmbeddedPointerMarshall
+ */
+unsigned char * WINAPI EmbeddedPointerMarshall(PMIDL_STUB_MESSAGE pStubMsg,
+                                               unsigned char *pMemory,
+                                               PFORMAT_STRING pFormat)
+{
+  unsigned char *Mark = pStubMsg->BufferMark;
+  unsigned long Offset = pStubMsg->Offset;
+  unsigned ofs, rep, count, stride, xofs;
+
+  TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);
+
+  if (*pFormat != RPC_FC_PP) return NULL;
+  pFormat += 2;
+
+  while (pFormat[0] != RPC_FC_END) {
+    switch (pFormat[0]) {
+    default:
+      FIXME("unknown repeat type %d\n", pFormat[0]);
+    case RPC_FC_NO_REPEAT:
+      rep = 1;
+      stride = 0;
+      ofs = 0;
+      count = 1;
+      xofs = 0;
+      pFormat += 2;
+      break;
+    case RPC_FC_FIXED_REPEAT:
+      rep = *(WORD*)&pFormat[2];
+      stride = *(WORD*)&pFormat[4];
+      ofs = *(WORD*)&pFormat[6];
+      count = *(WORD*)&pFormat[8];
+      xofs = 0;
+      pFormat += 10;
+      break;
+    case RPC_FC_VARIABLE_REPEAT:
+      rep = pStubMsg->MaxCount;
+      stride = *(WORD*)&pFormat[2];
+      ofs = *(WORD*)&pFormat[4];
+      count = *(WORD*)&pFormat[6];
+      xofs = (pFormat[1] == RPC_FC_VARIABLE_OFFSET) ? Offset * stride : 0;
+      pFormat += 8;
+      break;
+    }
+    /* ofs doesn't seem to matter in this context */
+    while (rep) {
+      PFORMAT_STRING info = pFormat;
+      unsigned char *membase = pMemory + xofs;
+      unsigned u;
+      for (u=0; u<count; u++,info+=8) {
+        unsigned char *memptr = membase + *(SHORT*)&info[0];
+        unsigned char *bufptr = Mark + *(SHORT*)&info[2];
+        PointerMarshall(pStubMsg, bufptr, *(unsigned char**)memptr, info+4);
+      }
+      rep--;
+    }
+    pFormat += 8 * count;
+  }
+
+  return NULL;
+}
+
+/***********************************************************************
+ *           EmbeddedPointerUnmarshall
+ */
+unsigned char * WINAPI EmbeddedPointerUnmarshall(PMIDL_STUB_MESSAGE pStubMsg,
+                                                 unsigned char **ppMemory,
+                                                 PFORMAT_STRING pFormat,
+                                                 unsigned char fMustAlloc)
+{
+  unsigned char *Mark = pStubMsg->BufferMark;
+  unsigned long Offset = pStubMsg->Offset;
+  unsigned ofs, rep, count, stride, xofs;
+
+  TRACE("(%p,%p,%p,%d)\n", pStubMsg, ppMemory, pFormat, fMustAlloc);
+
+  if (*pFormat != RPC_FC_PP) return NULL;
+  pFormat += 2;
+
+  while (pFormat[0] != RPC_FC_END) {
+    switch (pFormat[0]) {
+    default:
+      FIXME("unknown repeat type %d\n", pFormat[0]);
+    case RPC_FC_NO_REPEAT:
+      rep = 1;
+      stride = 0;
+      ofs = 0;
+      count = 1;
+      xofs = 0;
+      pFormat += 2;
+      break;
+    case RPC_FC_FIXED_REPEAT:
+      rep = *(WORD*)&pFormat[2];
+      stride = *(WORD*)&pFormat[4];
+      ofs = *(WORD*)&pFormat[6];
+      count = *(WORD*)&pFormat[8];
+      xofs = 0;
+      pFormat += 10;
+      break;
+    case RPC_FC_VARIABLE_REPEAT:
+      rep = pStubMsg->MaxCount;
+      stride = *(WORD*)&pFormat[2];
+      ofs = *(WORD*)&pFormat[4];
+      count = *(WORD*)&pFormat[6];
+      xofs = (pFormat[1] == RPC_FC_VARIABLE_OFFSET) ? Offset * stride : 0;
+      pFormat += 8;
+      break;
+    }
+    /* ofs doesn't seem to matter in this context */
+    while (rep) {
+      PFORMAT_STRING info = pFormat;
+      unsigned char *membase = *ppMemory + xofs;
+      unsigned u;
+      for (u=0; u<count; u++,info+=8) {
+        unsigned char *memptr = membase + *(SHORT*)&info[0];
+        unsigned char *bufptr = Mark + *(SHORT*)&info[2];
+        PointerUnmarshall(pStubMsg, bufptr, (unsigned char**)memptr, info+4, fMustAlloc);
+      }
+      rep--;
+    }
+    pFormat += 8 * count;
+  }
+
+  return NULL;
+}
+
+/***********************************************************************
+ *           EmbeddedPointerBufferSize
+ */
+void WINAPI EmbeddedPointerBufferSize(PMIDL_STUB_MESSAGE pStubMsg,
+                                      unsigned char *pMemory,
+                                      PFORMAT_STRING pFormat)
+{
+  unsigned long Offset = pStubMsg->Offset;
+  unsigned ofs, rep, count, stride, xofs;
+
+  TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);
+  if (*pFormat != RPC_FC_PP) return;
+  pFormat += 2;
+
+  while (pFormat[0] != RPC_FC_END) {
+    switch (pFormat[0]) {
+    default:
+      FIXME("unknown repeat type %d\n", pFormat[0]);
+    case RPC_FC_NO_REPEAT:
+      rep = 1;
+      stride = 0;
+      ofs = 0;
+      count = 1;
+      xofs = 0;
+      pFormat += 2;
+      break;
+    case RPC_FC_FIXED_REPEAT:
+      rep = *(WORD*)&pFormat[2];
+      stride = *(WORD*)&pFormat[4];
+      ofs = *(WORD*)&pFormat[6];
+      count = *(WORD*)&pFormat[8];
+      xofs = 0;
+      pFormat += 10;
+      break;
+    case RPC_FC_VARIABLE_REPEAT:
+      rep = pStubMsg->MaxCount;
+      stride = *(WORD*)&pFormat[2];
+      ofs = *(WORD*)&pFormat[4];
+      count = *(WORD*)&pFormat[6];
+      xofs = (pFormat[1] == RPC_FC_VARIABLE_OFFSET) ? Offset * stride : 0;
+      pFormat += 8;
+      break;
+    }
+    /* ofs doesn't seem to matter in this context */
+    while (rep) {
+      PFORMAT_STRING info = pFormat;
+      unsigned char *membase = pMemory + xofs;
+      unsigned u;
+      for (u=0; u<count; u++,info+=8) {
+        unsigned char *memptr = membase + *(SHORT*)&info[0];
+        PointerBufferSize(pStubMsg, *(unsigned char**)memptr, info+4);
+      }
+      rep--;
+    }
+    pFormat += 8 * count;
+  }
+}
+
+/***********************************************************************
+ *           EmbeddedPointerMemorySize
+ */
+unsigned long WINAPI EmbeddedPointerMemorySize(PMIDL_STUB_MESSAGE pStubMsg,
+                                               PFORMAT_STRING pFormat)
+{
+  unsigned long Offset = pStubMsg->Offset;
+  unsigned char *Mark = pStubMsg->BufferMark;
+  unsigned ofs, rep, count, stride, xofs;
+
+  FIXME("(%p,%p): stub\n", pStubMsg, pFormat);
+  if (*pFormat != RPC_FC_PP) return 0;
+  pFormat += 2;
+
+  while (pFormat[0] != RPC_FC_END) {
+    switch (pFormat[0]) {
+    default:
+      FIXME("unknown repeat type %d\n", pFormat[0]);
+    case RPC_FC_NO_REPEAT:
+      rep = 1;
+      stride = 0;
+      ofs = 0;
+      count = 1;
+      xofs = 0;
+      pFormat += 2;
+      break;
+    case RPC_FC_FIXED_REPEAT:
+      rep = *(WORD*)&pFormat[2];
+      stride = *(WORD*)&pFormat[4];
+      ofs = *(WORD*)&pFormat[6];
+      count = *(WORD*)&pFormat[8];
+      xofs = 0;
+      pFormat += 10;
+      break;
+    case RPC_FC_VARIABLE_REPEAT:
+      rep = pStubMsg->MaxCount;
+      stride = *(WORD*)&pFormat[2];
+      ofs = *(WORD*)&pFormat[4];
+      count = *(WORD*)&pFormat[6];
+      xofs = (pFormat[1] == RPC_FC_VARIABLE_OFFSET) ? Offset * stride : 0;
+      pFormat += 8;
+      break;
+    }
+    /* ofs doesn't seem to matter in this context */
+    while (rep) {
+      PFORMAT_STRING info = pFormat;
+      unsigned u;
+      for (u=0; u<count; u++,info+=8) {
+        unsigned char *bufptr = Mark + *(SHORT*)&info[2];
+        PointerMemorySize(pStubMsg, bufptr, info+4);
+      }
+      rep--;
+    }
+    pFormat += 8 * count;
+  }
+
+  return 0;
+}
+
+/***********************************************************************
+ *           EmbeddedPointerFree
+ */
+void WINAPI EmbeddedPointerFree(PMIDL_STUB_MESSAGE pStubMsg,
+                                unsigned char *pMemory,
+                                PFORMAT_STRING pFormat)
+{
+  unsigned long Offset = pStubMsg->Offset;
+  unsigned ofs, rep, count, stride, xofs;
+
+  TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);
+  if (*pFormat != RPC_FC_PP) return;
+  pFormat += 2;
+
+  while (pFormat[0] != RPC_FC_END) {
+    switch (pFormat[0]) {
+    default:
+      FIXME("unknown repeat type %d\n", pFormat[0]);
+    case RPC_FC_NO_REPEAT:
+      rep = 1;
+      stride = 0;
+      ofs = 0;
+      count = 1;
+      xofs = 0;
+      pFormat += 2;
+      break;
+    case RPC_FC_FIXED_REPEAT:
+      rep = *(WORD*)&pFormat[2];
+      stride = *(WORD*)&pFormat[4];
+      ofs = *(WORD*)&pFormat[6];
+      count = *(WORD*)&pFormat[8];
+      xofs = 0;
+      pFormat += 10;
+      break;
+    case RPC_FC_VARIABLE_REPEAT:
+      rep = pStubMsg->MaxCount;
+      stride = *(WORD*)&pFormat[2];
+      ofs = *(WORD*)&pFormat[4];
+      count = *(WORD*)&pFormat[6];
+      xofs = (pFormat[1] == RPC_FC_VARIABLE_OFFSET) ? Offset * stride : 0;
+      pFormat += 8;
+      break;
+    }
+    /* ofs doesn't seem to matter in this context */
+    while (rep) {
+      PFORMAT_STRING info = pFormat;
+      unsigned char *membase = pMemory + xofs;
+      unsigned u;
+      for (u=0; u<count; u++,info+=8) {
+        unsigned char *memptr = membase + *(SHORT*)&info[0];
+        PointerFree(pStubMsg, *(unsigned char**)memptr, info+4);
+      }
+      rep--;
+    }
+    pFormat += 8 * count;
+  }
+}
+
+/***********************************************************************
+ *           NdrPointerMarshall [RPCRT4.@]
+ */
+unsigned char * WINAPI NdrPointerMarshall(PMIDL_STUB_MESSAGE pStubMsg,
+                                          unsigned char *pMemory,
+                                          PFORMAT_STRING pFormat)
+{
+  TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);
+
+  pStubMsg->BufferMark = pStubMsg->Buffer;
+  PointerMarshall(pStubMsg, pStubMsg->Buffer, *(unsigned char**)pMemory, pFormat);
+  pStubMsg->Buffer += 4;
+  return NULL;
+}
+
+/***********************************************************************
+ *           NdrPointerUnmarshall [RPCRT4.@]
+ */
+unsigned char * WINAPI NdrPointerUnmarshall(PMIDL_STUB_MESSAGE pStubMsg,
+                                            unsigned char **ppMemory,
+                                            PFORMAT_STRING pFormat,
+                                            unsigned char fMustAlloc)
+{
+  TRACE("(%p,%p,%p,%d)\n", pStubMsg, ppMemory, pFormat, fMustAlloc);
+
+  if (fMustAlloc) {
+    *ppMemory = NdrAllocate(pStubMsg, 4);
   } else {
-    ERR("Unhandled string type: %#x\n", *pFormat);
-    /* FIXME: raise an exception */
+    if (pStubMsg->ReuseBuffer && !*ppMemory)
+      /* for servers, we may just point straight into the RPC buffer, I think */
+      *ppMemory = pStubMsg->Buffer;
+    /* else: for clients, memory should be provided by caller */
   }
 
-  return NULL; /* FIXME: is this always right? */
+  pStubMsg->BufferMark = pStubMsg->Buffer;
+  PointerUnmarshall(pStubMsg, pStubMsg->Buffer, *(unsigned char***)ppMemory, pFormat, fMustAlloc);
+  pStubMsg->Buffer += 4;
+
+  return NULL;
+}
+
+/***********************************************************************
+ *           NdrPointerBufferSize [RPCRT4.@]
+ */
+void WINAPI NdrPointerBufferSize(PMIDL_STUB_MESSAGE pStubMsg,
+                                      unsigned char *pMemory,
+                                      PFORMAT_STRING pFormat)
+{
+  TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);
+  pStubMsg->BufferLength += 4;
+  PointerBufferSize(pStubMsg, *(unsigned char**)pMemory, pFormat);
+}
+
+/***********************************************************************
+ *           NdrPointerMemorySize [RPCRT4.@]
+ */
+unsigned long WINAPI NdrPointerMemorySize(PMIDL_STUB_MESSAGE pStubMsg,
+                                          PFORMAT_STRING pFormat)
+{
+  unsigned size = *(LPWORD)(pFormat+2);
+  FIXME("(%p,%p): stub\n", pStubMsg, pFormat);
+  PointerMemorySize(pStubMsg, pStubMsg->Buffer, pFormat);
+  return 0;
+}
+
+/***********************************************************************
+ *           NdrPointerFree [RPCRT4.@]
+ */
+void WINAPI NdrPointerFree(PMIDL_STUB_MESSAGE pStubMsg,
+                           unsigned char *pMemory,
+                           PFORMAT_STRING pFormat)
+{
+  TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);
+  PointerFree(pStubMsg, *(unsigned char**)pMemory, pFormat);
+  *(unsigned char**)pMemory = NULL; /* just in case */
+}
+
+/***********************************************************************
+ *           NdrSimpleStructMarshall [RPCRT4.@]
+ */
+unsigned char * WINAPI NdrSimpleStructMarshall(PMIDL_STUB_MESSAGE pStubMsg,
+                                               unsigned char *pMemory,
+                                               PFORMAT_STRING pFormat)
+{
+  unsigned size = *(LPWORD)(pFormat+2);
+  TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);
+
+  memcpy(pStubMsg->Buffer, pMemory, size);
+  pStubMsg->BufferMark = pStubMsg->Buffer;
+  pStubMsg->Buffer += size;
+
+  if (pFormat[0] != RPC_FC_STRUCT)
+    EmbeddedPointerMarshall(pStubMsg, pMemory, pFormat+4);
+
+  return NULL;
+}
+
+/***********************************************************************
+ *           NdrSimpleStructUnmarshall [RPCRT4.@]
+ */
+unsigned char * WINAPI NdrSimpleStructUnmarshall(PMIDL_STUB_MESSAGE pStubMsg,
+                                                 unsigned char **ppMemory,
+                                                 PFORMAT_STRING pFormat,
+                                                 unsigned char fMustAlloc)
+{
+  unsigned size = *(LPWORD)(pFormat+2);
+  TRACE("(%p,%p,%p,%d)\n", pStubMsg, ppMemory, pFormat, fMustAlloc);
+
+  if (fMustAlloc) {
+    *ppMemory = NdrAllocate(pStubMsg, size);
+    memcpy(*ppMemory, pStubMsg->Buffer, size);
+  } else {
+    if (pStubMsg->ReuseBuffer && !*ppMemory)
+      /* for servers, we may just point straight into the RPC buffer, I think
+       * (I guess that's what MS does since MIDL code doesn't try to free) */
+      *ppMemory = pStubMsg->Buffer;
+    else
+      /* for clients, memory should be provided by caller */
+      memcpy(*ppMemory, pStubMsg->Buffer, size);
+  }
+
+  pStubMsg->BufferMark = pStubMsg->Buffer;
+  pStubMsg->Buffer += size;
+
+  if (pFormat[0] != RPC_FC_STRUCT)
+    EmbeddedPointerUnmarshall(pStubMsg, ppMemory, pFormat+4, fMustAlloc);
+
+  return NULL;
+}
+
+/***********************************************************************
+ *           NdrSimpleStructBufferSize [RPCRT4.@]
+ */
+void WINAPI NdrSimpleStructBufferSize(PMIDL_STUB_MESSAGE pStubMsg,
+                                      unsigned char *pMemory,
+                                      PFORMAT_STRING pFormat)
+{
+  unsigned size = *(LPWORD)(pFormat+2);
+  TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);
+  pStubMsg->BufferLength += size;
+  if (pFormat[0] != RPC_FC_STRUCT)
+    EmbeddedPointerBufferSize(pStubMsg, pMemory, pFormat+4);
+}
+
+/***********************************************************************
+ *           NdrSimpleStructMemorySize [RPCRT4.@]
+ */
+unsigned long WINAPI NdrSimpleStructMemorySize(PMIDL_STUB_MESSAGE pStubMsg,
+                                               PFORMAT_STRING pFormat)
+{
+  unsigned size = *(LPWORD)(pFormat+2);
+  FIXME("(%p,%p): stub\n", pStubMsg, pFormat);
+  if (pFormat[0] != RPC_FC_STRUCT)
+    EmbeddedPointerMemorySize(pStubMsg, pFormat+4);
+  return 0;
+}
+
+/***********************************************************************
+ *           NdrSimpleStructFree [RPCRT4.@]
+ */
+void WINAPI NdrSimpleStructFree(PMIDL_STUB_MESSAGE pStubMsg,
+                                unsigned char *pMemory,
+                                PFORMAT_STRING pFormat)
+{
+  TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);
+  if (pFormat[0] != RPC_FC_STRUCT)
+    EmbeddedPointerFree(pStubMsg, pMemory, pFormat+4);
+}
+
+
+unsigned char * WINAPI ComplexMarshall(PMIDL_STUB_MESSAGE pStubMsg,
+                                       unsigned char *pMemory,
+                                       PFORMAT_STRING pFormat,
+                                       PFORMAT_STRING pPointer)
+{
+  PFORMAT_STRING desc;
+  NDR_MARSHALL m;
+
+  while (*pFormat != RPC_FC_END) {
+    switch (*pFormat) {
+    case RPC_FC_SHORT:
+    case RPC_FC_USHORT:
+      TRACE("short=%d\n", *(WORD*)pMemory);
+      memcpy(pStubMsg->Buffer, pMemory, 2);
+      pStubMsg->Buffer += 2;
+      pMemory += 2;
+      break;
+    case RPC_FC_LONG:
+    case RPC_FC_ULONG:
+      TRACE("long=%ld\n", *(DWORD*)pMemory);
+      memcpy(pStubMsg->Buffer, pMemory, 4);
+      pStubMsg->Buffer += 4;
+      pMemory += 4;
+      break;
+    case RPC_FC_POINTER:
+      NdrPointerMarshall(pStubMsg, pMemory, pPointer);
+      pPointer += 4;
+      pMemory += 4;
+      break;
+    case RPC_FC_ALIGNM4:
+      ALIGN_POINTER(pMemory, 3);
+      break;
+    case RPC_FC_ALIGNM8:
+      ALIGN_POINTER(pMemory, 7);
+      break;
+    case RPC_FC_EMBEDDED_COMPLEX:
+      pMemory += pFormat[1];
+      pFormat += 2;
+      desc = pFormat + *(SHORT*)pFormat;
+      m = NdrMarshaller[*desc & NDR_TABLE_MASK];
+      if (m) m(pStubMsg, pMemory, desc);
+      else FIXME("no marshaller for embedded type %02x\n", *desc);
+      pFormat += 2;
+      continue;
+    case RPC_FC_PAD:
+      break;
+    default:
+      FIXME("unhandled format %02x\n", *pFormat);
+    }
+    pFormat++;
+  }
+
+  return pMemory;
+}
+
+unsigned char * WINAPI ComplexUnmarshall(PMIDL_STUB_MESSAGE pStubMsg,
+                                         unsigned char *pMemory,
+                                         PFORMAT_STRING pFormat,
+                                         PFORMAT_STRING pPointer,
+                                         unsigned char fMustAlloc)
+{
+  PFORMAT_STRING desc;
+  NDR_UNMARSHALL m;
+
+  while (*pFormat != RPC_FC_END) {
+    switch (*pFormat) {
+    case RPC_FC_SHORT:
+    case RPC_FC_USHORT:
+      memcpy(pMemory, pStubMsg->Buffer, 2);
+      TRACE("short=%d\n", *(WORD*)pMemory);
+      pStubMsg->Buffer += 2;
+      pMemory += 2;
+      break;
+    case RPC_FC_LONG:
+    case RPC_FC_ULONG:
+      memcpy(pMemory, pStubMsg->Buffer, 4);
+      TRACE("long=%ld\n", *(DWORD*)pMemory);
+      pStubMsg->Buffer += 4;
+      pMemory += 4;
+      break;
+    case RPC_FC_POINTER:
+      NdrPointerUnmarshall(pStubMsg, &pMemory, pPointer, fMustAlloc);
+      pPointer += 4;
+      pMemory += 4;
+      break;
+    case RPC_FC_ALIGNM4:
+      ALIGN_POINTER(pMemory, 3);
+      break;
+    case RPC_FC_ALIGNM8:
+      ALIGN_POINTER(pMemory, 7);
+      break;
+    case RPC_FC_EMBEDDED_COMPLEX:
+      pMemory += pFormat[1];
+      pFormat += 2;
+      desc = pFormat + *(SHORT*)pFormat;
+      m = NdrUnmarshaller[*desc & NDR_TABLE_MASK];
+      if (m) m(pStubMsg, &pMemory, desc, fMustAlloc);
+      else FIXME("no unmarshaller for embedded type %02x\n", *desc);
+      pFormat += 2;
+      continue;
+    case RPC_FC_PAD:
+      break;
+    default:
+      FIXME("unhandled format %d\n", *pFormat);
+    }
+    pFormat++;
+  }
+
+  return pMemory;
+}
+
+unsigned char * WINAPI ComplexBufferSize(PMIDL_STUB_MESSAGE pStubMsg,
+                                         unsigned char *pMemory,
+                                         PFORMAT_STRING pFormat,
+                                         PFORMAT_STRING pPointer)
+{
+  PFORMAT_STRING desc;
+  NDR_BUFFERSIZE m;
+
+  while (*pFormat != RPC_FC_END) {
+    switch (*pFormat) {
+    case RPC_FC_SHORT:
+    case RPC_FC_USHORT:
+      pStubMsg->BufferLength += 2;
+      pMemory += 2;
+      break;
+    case RPC_FC_LONG:
+    case RPC_FC_ULONG:
+      pStubMsg->BufferLength += 4;
+      pMemory += 4;
+      break;
+    case RPC_FC_POINTER:
+      NdrPointerBufferSize(pStubMsg, pMemory, pPointer);
+      pPointer += 4;
+      pMemory += 4;
+      break;
+    case RPC_FC_ALIGNM4:
+      ALIGN_POINTER(pMemory, 3);
+      break;
+    case RPC_FC_ALIGNM8:
+      ALIGN_POINTER(pMemory, 7);
+      break;
+    case RPC_FC_EMBEDDED_COMPLEX:
+      pMemory += pFormat[1];
+      pFormat += 2;
+      desc = pFormat + *(SHORT*)pFormat;
+      m = NdrBufferSizer[*desc & NDR_TABLE_MASK];
+      if (m) m(pStubMsg, pMemory, desc);
+      else FIXME("no buffersizer for embedded type %02x\n", *desc);
+      pFormat += 2;
+      continue;
+    case RPC_FC_PAD:
+      break;
+    default:
+      FIXME("unhandled format %d\n", *pFormat);
+    }
+    pFormat++;
+  }
+
+  return pMemory;
+}
+
+unsigned char * WINAPI ComplexFree(PMIDL_STUB_MESSAGE pStubMsg,
+                                   unsigned char *pMemory,
+                                   PFORMAT_STRING pFormat,
+                                   PFORMAT_STRING pPointer)
+{
+  PFORMAT_STRING desc;
+  NDR_FREE m;
+
+  while (*pFormat != RPC_FC_END) {
+    switch (*pFormat) {
+    case RPC_FC_SHORT:
+    case RPC_FC_USHORT:
+      pMemory += 2;
+      break;
+    case RPC_FC_LONG:
+    case RPC_FC_ULONG:
+      pMemory += 4;
+      break;
+    case RPC_FC_POINTER:
+      NdrPointerFree(pStubMsg, pMemory, pPointer);
+      pPointer += 4;
+      pMemory += 4;
+      break;
+    case RPC_FC_ALIGNM4:
+      ALIGN_POINTER(pMemory, 3);
+      break;
+    case RPC_FC_ALIGNM8:
+      ALIGN_POINTER(pMemory, 7);
+      break;
+    case RPC_FC_EMBEDDED_COMPLEX:
+      pMemory += pFormat[1];
+      pFormat += 2;
+      desc = pFormat + *(SHORT*)pFormat;
+      m = NdrFreer[*desc & NDR_TABLE_MASK];
+      if (m) m(pStubMsg, pMemory, desc);
+      else FIXME("no freer for embedded type %02x\n", *desc);
+      pFormat += 2;
+      continue;
+    case RPC_FC_PAD:
+      break;
+    default:
+      FIXME("unhandled format %d\n", *pFormat);
+    }
+    pFormat++;
+  }
+
+  return pMemory;
+}
+
+unsigned long WINAPI ComplexStructSize(PMIDL_STUB_MESSAGE pStubMsg,
+                                       PFORMAT_STRING pFormat)
+{
+  PFORMAT_STRING desc;
+  unsigned long size = 0;
+
+  while (*pFormat != RPC_FC_END) {
+    switch (*pFormat) {
+    case RPC_FC_SHORT:
+    case RPC_FC_USHORT:
+      size += 2;
+      break;
+    case RPC_FC_LONG:
+    case RPC_FC_ULONG:
+      size += 4;
+      break;
+    case RPC_FC_POINTER:
+      size += 4;
+      break;
+    case RPC_FC_ALIGNM4:
+      ALIGN_LENGTH(size, 3);
+      break;
+    case RPC_FC_ALIGNM8:
+      ALIGN_LENGTH(size, 7);
+      break;
+    case RPC_FC_EMBEDDED_COMPLEX:
+      size += pFormat[1];
+      pFormat += 2;
+      desc = pFormat + *(SHORT*)pFormat;
+      switch (*desc) {
+      case RPC_FC_STRUCT:
+      case RPC_FC_PSTRUCT:
+      case RPC_FC_CSTRUCT:
+      case RPC_FC_BOGUS_STRUCT:
+        size += *(WORD*)&pFormat[2];
+        break;
+      case RPC_FC_USER_MARSHAL:
+        size += *(WORD*)&pFormat[4];
+        break;
+      default:
+        FIXME("unhandled embedded type %02x\n", *desc);
+      }
+      pFormat += 2;
+      continue;
+    case RPC_FC_PAD:
+      break;
+    default:
+      FIXME("unhandled format %d\n", *pFormat);
+    }
+    pFormat++;
+  }
+
+  return size;
+}
+
+/***********************************************************************
+ *           NdrComplexStructMarshall [RPCRT4.@]
+ */
+unsigned char * WINAPI NdrComplexStructMarshall(PMIDL_STUB_MESSAGE pStubMsg,
+                                                unsigned char *pMemory,
+                                                PFORMAT_STRING pFormat)
+{
+  PFORMAT_STRING conf_array = NULL;
+  PFORMAT_STRING pointer_desc = NULL;
+  unsigned char *OldMemory = pStubMsg->Memory;
+
+  TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);
+
+  pFormat += 4;
+  if (*(WORD*)pFormat) conf_array = pFormat + *(WORD*)pFormat;
+  pFormat += 2;
+  if (*(WORD*)pFormat) pointer_desc = pFormat + *(WORD*)pFormat;
+  pFormat += 2;
+
+  pStubMsg->Memory = pMemory;
+
+  ComplexMarshall(pStubMsg, pMemory, pFormat, pointer_desc);
+
+  if (conf_array)
+    NdrConformantArrayMarshall(pStubMsg, pMemory, conf_array);
+
+  pStubMsg->Memory = OldMemory;
+
+  return NULL;
+}
+
+/***********************************************************************
+ *           NdrComplexStructUnmarshall [RPCRT4.@]
+ */
+unsigned char * WINAPI NdrComplexStructUnmarshall(PMIDL_STUB_MESSAGE pStubMsg,
+                                                  unsigned char **ppMemory,
+                                                  PFORMAT_STRING pFormat,
+                                                  unsigned char fMustAlloc)
+{
+  unsigned size = *(LPWORD)(pFormat+2);
+  PFORMAT_STRING conf_array = NULL;
+  PFORMAT_STRING pointer_desc = NULL;
+  unsigned char *pMemory;
+
+  TRACE("(%p,%p,%p,%d)\n", pStubMsg, ppMemory, pFormat, fMustAlloc);
+
+  if (fMustAlloc || !*ppMemory)
+    *ppMemory = NdrAllocate(pStubMsg, size);
+
+  pFormat += 4;
+  if (*(WORD*)pFormat) conf_array = pFormat + *(WORD*)pFormat;
+  pFormat += 2;
+  if (*(WORD*)pFormat) pointer_desc = pFormat + *(WORD*)pFormat;
+  pFormat += 2;
+
+  pMemory = ComplexUnmarshall(pStubMsg, *ppMemory, pFormat, pointer_desc, fMustAlloc);
+
+  if (conf_array)
+    NdrConformantArrayUnmarshall(pStubMsg, &pMemory, conf_array, fMustAlloc);
+
+  return NULL;
+}
+
+/***********************************************************************
+ *           NdrComplexStructBufferSize [RPCRT4.@]
+ */
+void WINAPI NdrComplexStructBufferSize(PMIDL_STUB_MESSAGE pStubMsg,
+                                       unsigned char *pMemory,
+                                       PFORMAT_STRING pFormat)
+{
+  PFORMAT_STRING conf_array = NULL;
+  PFORMAT_STRING pointer_desc = NULL;
+  unsigned char *OldMemory = pStubMsg->Memory;
+
+  TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);
+
+  pFormat += 4;
+  if (*(WORD*)pFormat) conf_array = pFormat + *(WORD*)pFormat;
+  pFormat += 2;
+  if (*(WORD*)pFormat) pointer_desc = pFormat + *(WORD*)pFormat;
+  pFormat += 2;
+
+  pStubMsg->Memory = pMemory;
+
+  pMemory = ComplexBufferSize(pStubMsg, pMemory, pFormat, pointer_desc);
+
+  if (conf_array)
+    NdrConformantArrayBufferSize(pStubMsg, pMemory, conf_array);
+
+  pStubMsg->Memory = OldMemory;
+}
+
+/***********************************************************************
+ *           NdrComplexStructMemorySize [RPCRT4.@]
+ */
+unsigned long WINAPI NdrComplexStructMemorySize(PMIDL_STUB_MESSAGE pStubMsg,
+                                                PFORMAT_STRING pFormat)
+{
+  unsigned size = *(LPWORD)(pFormat+2);
+  PFORMAT_STRING conf_array = NULL;
+  PFORMAT_STRING pointer_desc = NULL;
+
+  FIXME("(%p,%p): stub\n", pStubMsg, pFormat);
+
+  pFormat += 4;
+  if (*(WORD*)pFormat) conf_array = pFormat + *(WORD*)pFormat;
+  pFormat += 2;
+  if (*(WORD*)pFormat) pointer_desc = pFormat + *(WORD*)pFormat;
+  pFormat += 2;
+
+  return 0;
+}
+
+/***********************************************************************
+ *           NdrComplexStructFree [RPCRT4.@]
+ */
+void WINAPI NdrComplexStructFree(PMIDL_STUB_MESSAGE pStubMsg,
+                                 unsigned char *pMemory,
+                                 PFORMAT_STRING pFormat)
+{
+  PFORMAT_STRING conf_array = NULL;
+  PFORMAT_STRING pointer_desc = NULL;
+  unsigned char *OldMemory = pStubMsg->Memory;
+
+  TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);
+
+  pFormat += 4;
+  if (*(WORD*)pFormat) conf_array = pFormat + *(WORD*)pFormat;
+  pFormat += 2;
+  if (*(WORD*)pFormat) pointer_desc = pFormat + *(WORD*)pFormat;
+  pFormat += 2;
+
+  pStubMsg->Memory = pMemory;
+
+  pMemory = ComplexFree(pStubMsg, pMemory, pFormat, pointer_desc);
+
+  if (conf_array)
+    NdrConformantArrayFree(pStubMsg, pMemory, conf_array);
+
+  pStubMsg->Memory = OldMemory;
+}
+
+/***********************************************************************
+ *           NdrConformantArrayMarshall [RPCRT4.@]
+ */
+unsigned char * WINAPI NdrConformantArrayMarshall(PMIDL_STUB_MESSAGE pStubMsg,
+                                                  unsigned char *pMemory,
+                                                  PFORMAT_STRING pFormat)
+{
+  DWORD size = 0, esize = *(LPWORD)(pFormat+2);
+  TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);
+  if (pFormat[0] != RPC_FC_CARRAY) FIXME("format=%d\n", pFormat[0]);
+
+  pFormat = ComputeConformance(pStubMsg, pMemory, pFormat+4, 0);
+  size = pStubMsg->MaxCount;
+
+  NDR_LOCAL_UINT32_WRITE(pStubMsg->Buffer, size);
+  pStubMsg->Buffer += 4;
+
+  memcpy(pStubMsg->Buffer, pMemory, size*esize);
+  pStubMsg->BufferMark = pStubMsg->Buffer;
+  pStubMsg->Buffer += size*esize;
+
+  EmbeddedPointerMarshall(pStubMsg, pMemory, pFormat);
+
+  return NULL;
+}
+
+/***********************************************************************
+ *           NdrConformantArrayUnmarshall [RPCRT4.@]
+ */
+unsigned char * WINAPI NdrConformantArrayUnmarshall(PMIDL_STUB_MESSAGE pStubMsg,
+                                                    unsigned char **ppMemory,
+                                                    PFORMAT_STRING pFormat,
+                                                    unsigned char fMustAlloc)
+{
+  DWORD size = 0, esize = *(LPWORD)(pFormat+2);
+  TRACE("(%p,%p,%p,%d)\n", pStubMsg, ppMemory, pFormat, fMustAlloc);
+  if (pFormat[0] != RPC_FC_CARRAY) FIXME("format=%d\n", pFormat[0]);
+
+  pFormat = ReadConformance(pStubMsg, pFormat+4);
+  size = pStubMsg->MaxCount;
+
+  if (fMustAlloc) {
+    *ppMemory = NdrAllocate(pStubMsg, size*esize);
+    memcpy(*ppMemory, pStubMsg->Buffer, size*esize);
+  } else {
+    if (pStubMsg->ReuseBuffer && !*ppMemory)
+      /* for servers, we may just point straight into the RPC buffer, I think
+       * (I guess that's what MS does since MIDL code doesn't try to free) */
+      *ppMemory = pStubMsg->Buffer;
+    else
+      /* for clients, memory should be provided by caller */
+      memcpy(*ppMemory, pStubMsg->Buffer, size*esize);
+  }
+
+  pStubMsg->BufferMark = pStubMsg->Buffer;
+  pStubMsg->Buffer += size*esize;
+
+  EmbeddedPointerUnmarshall(pStubMsg, ppMemory, pFormat, fMustAlloc);
+
+  return NULL;
+}
+
+/***********************************************************************
+ *           NdrConformantArrayBufferSize [RPCRT4.@]
+ */
+void WINAPI NdrConformantArrayBufferSize(PMIDL_STUB_MESSAGE pStubMsg,
+                                         unsigned char *pMemory,
+                                         PFORMAT_STRING pFormat)
+{
+  DWORD size = 0, esize = *(LPWORD)(pFormat+2);
+  TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);
+  if (pFormat[0] != RPC_FC_CARRAY) FIXME("format=%d\n", pFormat[0]);
+
+  pFormat = ComputeConformance(pStubMsg, pMemory, pFormat+4, 0);
+  size = pStubMsg->MaxCount;
+
+  pStubMsg->BufferLength += size*esize;
+
+  EmbeddedPointerBufferSize(pStubMsg, pMemory, pFormat);
+}
+
+/***********************************************************************
+ *           NdrConformantArrayMemorySize [RPCRT4.@]
+ */
+unsigned long WINAPI NdrConformantArrayMemorySize(PMIDL_STUB_MESSAGE pStubMsg,
+                                                  PFORMAT_STRING pFormat)
+{
+  DWORD size = 0, esize = *(LPWORD)(pFormat+2);
+  FIXME("(%p,%p): stub\n", pStubMsg, pFormat);
+  if (pFormat[0] != RPC_FC_CARRAY) FIXME("format=%d\n", pFormat[0]);
+
+  pFormat = ReadConformance(pStubMsg, pFormat+4);
+  size = pStubMsg->MaxCount;
+
+  EmbeddedPointerMemorySize(pStubMsg, pFormat);
+
+  return 0;
+}
+
+/***********************************************************************
+ *           NdrConformantArrayFree [RPCRT4.@]
+ */
+void WINAPI NdrConformantArrayFree(PMIDL_STUB_MESSAGE pStubMsg,
+                                   unsigned char *pMemory,
+                                   PFORMAT_STRING pFormat)
+{
+  TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);
+  if (pFormat[0] != RPC_FC_CARRAY) FIXME("format=%d\n", pFormat[0]);
+
+  EmbeddedPointerFree(pStubMsg, pMemory, pFormat);
+}
+
+/***********************************************************************
+ *           NdrComplexArrayMarshall [RPCRT4.@]
+ */
+unsigned char * WINAPI NdrComplexArrayMarshall(PMIDL_STUB_MESSAGE pStubMsg,
+                                               unsigned char *pMemory,
+                                               PFORMAT_STRING pFormat)
+{
+  DWORD size = 0, count, def;
+  TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);
+
+  def = *(WORD*)&pFormat[2];
+  pFormat += 4;
+
+  pFormat = ComputeConformance(pStubMsg, pMemory, pFormat, def);
+  size = pStubMsg->MaxCount;
+  TRACE("conformance=%ld\n", size);
+
+  if (*(DWORD*)pFormat != 0xffffffff)
+    FIXME("compute variance\n");
+  pFormat += 4;
+
+  NDR_LOCAL_UINT32_WRITE(pStubMsg->Buffer, size);
+  pStubMsg->Buffer += 4;
+
+  for (count=0; count<size; count++)
+    pMemory = ComplexMarshall(pStubMsg, pMemory, pFormat, NULL);
+
+  return NULL;
+}
+
+/***********************************************************************
+ *           NdrComplexArrayUnmarshall [RPCRT4.@]
+ */
+unsigned char * WINAPI NdrComplexArrayUnmarshall(PMIDL_STUB_MESSAGE pStubMsg,
+                                                 unsigned char **ppMemory,
+                                                 PFORMAT_STRING pFormat,
+                                                 unsigned char fMustAlloc)
+{
+  DWORD size = 0, count, esize;
+  unsigned char *pMemory;
+  TRACE("(%p,%p,%p,%d)\n", pStubMsg, ppMemory, pFormat, fMustAlloc);
+
+  pFormat += 4;
+
+  pFormat = ReadConformance(pStubMsg, pFormat);
+  size = pStubMsg->MaxCount;
+  TRACE("conformance=%ld\n", size);
+
+  pFormat += 4;
+
+  esize = ComplexStructSize(pStubMsg, pFormat);
+
+  if (fMustAlloc || !*ppMemory)
+    *ppMemory = NdrAllocate(pStubMsg, size*esize);
+
+  pMemory = *ppMemory;
+  for (count=0; count<size; count++)
+    pMemory = ComplexUnmarshall(pStubMsg, pMemory, pFormat, NULL, fMustAlloc);
+
+  return NULL;
+}
+
+/***********************************************************************
+ *           NdrComplexArrayBufferSize [RPCRT4.@]
+ */
+void WINAPI NdrComplexArrayBufferSize(PMIDL_STUB_MESSAGE pStubMsg,
+                                      unsigned char *pMemory,
+                                      PFORMAT_STRING pFormat)
+{
+  DWORD size = 0, count, def;
+  TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);
+
+  def = *(WORD*)&pFormat[2];
+  pFormat += 4;
+
+  pFormat = ComputeConformance(pStubMsg, pMemory, pFormat, def);
+  size = pStubMsg->MaxCount;
+  TRACE("conformance=%ld\n", size);
+
+  if (*(DWORD*)pFormat != 0xffffffff)
+    FIXME("compute variance\n");
+  pFormat += 4;
+
+  for (count=0; count<size; count++)
+    pMemory = ComplexBufferSize(pStubMsg, pMemory, pFormat, NULL);
+}
+
+/***********************************************************************
+ *           NdrComplexArrayMemorySize [RPCRT4.@]
+ */
+unsigned long WINAPI NdrComplexArrayMemorySize(PMIDL_STUB_MESSAGE pStubMsg,
+                                               PFORMAT_STRING pFormat)
+{
+  DWORD size = 0, count;
+  FIXME("(%p,%p): stub\n", pStubMsg, pFormat);
+
+  pFormat += 4;
+
+  pFormat = ReadConformance(pStubMsg, pFormat);
+  size = pStubMsg->MaxCount;
+  TRACE("conformance=%ld\n", size);
+
+  pFormat += 4;
+
+  return 0;
+}
+
+/***********************************************************************
+ *           NdrComplexArrayFree [RPCRT4.@]
+ */
+void WINAPI NdrComplexArrayFree(PMIDL_STUB_MESSAGE pStubMsg,
+                                unsigned char *pMemory,
+                                PFORMAT_STRING pFormat)
+{
+  DWORD size = 0, count, def;
+  TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);
+
+  def = *(WORD*)&pFormat[2];
+  pFormat += 4;
+
+  pFormat = ComputeConformance(pStubMsg, pMemory, pFormat, def);
+  size = pStubMsg->MaxCount;
+  TRACE("conformance=%ld\n", size);
+
+  if (*(DWORD*)pFormat != 0xffffffff)
+    FIXME("compute variance\n");
+  pFormat += 4;
+
+  for (count=0; count<size; count++)
+    pMemory = ComplexFree(pStubMsg, pMemory, pFormat, NULL);
+}
+
+unsigned long UserMarshalFlags(PMIDL_STUB_MESSAGE pStubMsg)
+{
+  return MAKELONG(pStubMsg->dwDestContext,
+                  pStubMsg->RpcMsg->DataRepresentation);
+}
+
+/***********************************************************************
+ *           NdrUserMarshalMarshall [RPCRT4.@]
+ */
+unsigned char * WINAPI NdrUserMarshalMarshall(PMIDL_STUB_MESSAGE pStubMsg,
+                                              unsigned char *pMemory,
+                                              PFORMAT_STRING pFormat)
+{
+  unsigned flags = pFormat[1];
+  unsigned index = *(WORD*)&pFormat[2];
+  unsigned long uflag = UserMarshalFlags(pStubMsg);
+  TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);
+  TRACE("index=%d\n", index);
+
+  pStubMsg->Buffer =
+    pStubMsg->StubDesc->aUserMarshalQuadruple[index].pfnMarshall(
+      &uflag, pStubMsg->Buffer, pMemory);
+
+  return NULL;
+}
+
+/***********************************************************************
+ *           NdrUserMarshalUnmarshall [RPCRT4.@]
+ */
+unsigned char * WINAPI NdrUserMarshalUnmarshall(PMIDL_STUB_MESSAGE pStubMsg,
+                                                 unsigned char **ppMemory,
+                                                 PFORMAT_STRING pFormat,
+                                                 unsigned char fMustAlloc)
+{
+  unsigned flags = pFormat[1];
+  unsigned index = *(WORD*)&pFormat[2];
+  DWORD memsize = *(WORD*)&pFormat[4];
+  unsigned long uflag = UserMarshalFlags(pStubMsg);
+  TRACE("(%p,%p,%p,%d)\n", pStubMsg, ppMemory, pFormat, fMustAlloc);
+  TRACE("index=%d\n", index);
+
+  if (fMustAlloc || !*ppMemory)
+    *ppMemory = NdrAllocate(pStubMsg, memsize);
+
+  pStubMsg->Buffer =
+    pStubMsg->StubDesc->aUserMarshalQuadruple[index].pfnUnmarshall(
+      &uflag, pStubMsg->Buffer, *ppMemory);
+
+  return NULL;
+}
+
+/***********************************************************************
+ *           NdrUserMarshalBufferSize [RPCRT4.@]
+ */
+void WINAPI NdrUserMarshalBufferSize(PMIDL_STUB_MESSAGE pStubMsg,
+                                      unsigned char *pMemory,
+                                      PFORMAT_STRING pFormat)
+{
+  unsigned flags = pFormat[1];
+  unsigned index = *(WORD*)&pFormat[2];
+  DWORD bufsize = *(WORD*)&pFormat[6];
+  unsigned long uflag = UserMarshalFlags(pStubMsg);
+  TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);
+  TRACE("index=%d\n", index);
+
+  if (bufsize) {
+    TRACE("size=%ld\n", bufsize);
+    pStubMsg->BufferLength += bufsize;
+    return;
+  }
+
+  pStubMsg->BufferLength =
+    pStubMsg->StubDesc->aUserMarshalQuadruple[index].pfnBufferSize(
+      &uflag, pStubMsg->BufferLength, pMemory);
+}
+
+/***********************************************************************
+ *           NdrUserMarshalMemorySize [RPCRT4.@]
+ */
+unsigned long WINAPI NdrUserMarshalMemorySize(PMIDL_STUB_MESSAGE pStubMsg,
+                                               PFORMAT_STRING pFormat)
+{
+  DWORD memsize = *(WORD*)&pFormat[4];
+  FIXME("(%p,%p): stub\n", pStubMsg, pFormat);
+  TRACE("index=%d\n", index);
+
+  return 0;
+}
+
+/***********************************************************************
+ *           NdrUserMarshalFree [RPCRT4.@]
+ */
+void WINAPI NdrUserMarshalFree(PMIDL_STUB_MESSAGE pStubMsg,
+                                unsigned char *pMemory,
+                                PFORMAT_STRING pFormat)
+{
+  unsigned flags = pFormat[1];
+  unsigned index = *(WORD*)&pFormat[2];
+  unsigned long uflag = UserMarshalFlags(pStubMsg);
+  TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);
+  TRACE("index=%d\n", index);
+
+  pStubMsg->StubDesc->aUserMarshalQuadruple[index].pfnFree(
+    &uflag, pMemory);
 }
 
 /***********************************************************************
Index: dlls/rpcrt4/ndr_misc.h
===================================================================
RCS file: /home/wine/wine/dlls/rpcrt4/ndr_misc.h,v
retrieving revision 1.3
diff -u -r1.3 ndr_misc.h
--- dlls/rpcrt4/ndr_misc.h	25 Oct 2002 19:03:43 -0000	1.3
+++ dlls/rpcrt4/ndr_misc.h	30 Jan 2003 16:32:00 -0000
@@ -32,4 +32,19 @@
 
 HRESULT RPCRT4_GetPSFactory(REFIID riid, struct IPSFactoryBuffer **ppPS);
 
+PFORMAT_STRING ComputeConformance(MIDL_STUB_MESSAGE *pStubMsg, unsigned char *pMemory,
+                                  PFORMAT_STRING pFormat, ULONG_PTR def);
+
+typedef unsigned char* (WINAPI *NDR_MARSHALL)  (PMIDL_STUB_MESSAGE, unsigned char*, PFORMAT_STRING);
+typedef unsigned char* (WINAPI *NDR_UNMARSHALL)(PMIDL_STUB_MESSAGE, unsigned char**,PFORMAT_STRING, unsigned char);
+typedef void           (WINAPI *NDR_BUFFERSIZE)(PMIDL_STUB_MESSAGE, unsigned char*, PFORMAT_STRING);
+typedef unsigned long  (WINAPI *NDR_MEMORYSIZE)(PMIDL_STUB_MESSAGE,                 PFORMAT_STRING);
+typedef void           (WINAPI *NDR_FREE)      (PMIDL_STUB_MESSAGE, unsigned char*, PFORMAT_STRING);
+
+extern NDR_MARSHALL   NdrMarshaller[];
+extern NDR_UNMARSHALL NdrUnmarshaller[];
+extern NDR_BUFFERSIZE NdrBufferSizer[];
+extern NDR_MEMORYSIZE NdrMemorySizer[];
+extern NDR_FREE       NdrFreer[];
+
 #endif  /* __WINE_NDR_MISC_H */
Index: dlls/rpcrt4/ndr_ole.c
===================================================================
RCS file: /home/wine/wine/dlls/rpcrt4/ndr_ole.c,v
retrieving revision 1.3
diff -u -r1.3 ndr_ole.c
--- dlls/rpcrt4/ndr_ole.c	7 Jan 2003 20:36:24 -0000	1.3
+++ dlls/rpcrt4/ndr_ole.c	30 Jan 2003 16:32:00 -0000
@@ -36,7 +36,9 @@
 
 #include "objbase.h"
 
+#include "ndr_misc.h"
 #include "rpcndr.h"
+#include "wine/rpcfc.h"
 
 #include "wine/debug.h"
 
@@ -212,6 +214,21 @@
   return (LPSTREAM)This;
 }
 
+const IID* get_ip_iid(PMIDL_STUB_MESSAGE pStubMsg, unsigned char *pMemory, PFORMAT_STRING pFormat)
+{
+  const IID *riid;
+  if (!pFormat) return &IID_IUnknown;
+  TRACE("format=%02x %02x\n", pFormat[0], pFormat[1]);
+  if (pFormat[0] != RPC_FC_IP) FIXME("format=%d\n", pFormat[0]);
+  if (pFormat[1] == RPC_FC_CONSTANT_IID)
+    return (const IID*)&pFormat[2];
+
+  ComputeConformance(pStubMsg, pMemory, pFormat+2, 0);
+  riid = (const IID *)pStubMsg->MaxCount;
+  if (!riid) riid = &IID_IUnknown;
+  return riid;
+}
+
 /***********************************************************************
  *           NdrInterfacePointerMarshall [RPCRT4.@]
  */
@@ -219,12 +236,11 @@
                                                   unsigned char *pMemory,
                                                   PFORMAT_STRING pFormat)
 {
-  const IID *riid = (const IID *)pStubMsg->MaxCount;
+  const IID *riid = get_ip_iid(pStubMsg, pMemory, pFormat);
   LPSTREAM stream;
   HRESULT hr;
 
   TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);
-  if (!riid) riid = &IID_IUnknown;
   pStubMsg->MaxCount = 0;
   if (!LoadCOM()) return NULL;
   stream = RpcStream_Create(pStubMsg, TRUE);
@@ -262,12 +278,11 @@
                                          unsigned char *pMemory,
                                          PFORMAT_STRING pFormat)
 {
-  const IID *riid = (const IID *)pStubMsg->MaxCount;
+  const IID *riid = get_ip_iid(pStubMsg, pMemory, pFormat);
   ULONG size = 0;
   HRESULT hr;
 
   TRACE("(%p,%p,%p)\n", pStubMsg, pMemory, pFormat);
-  if (!riid) riid = &IID_IUnknown;
   if (!LoadCOM()) return;
   hr = COM_GetMarshalSizeMax(&size, riid, (LPUNKNOWN)pMemory,
                             pStubMsg->dwDestContext, pStubMsg->pvDestContext,
Index: dlls/rpcrt4/rpcrt4.spec
===================================================================
RCS file: /home/wine/wine/dlls/rpcrt4/rpcrt4.spec,v
retrieving revision 1.40
diff -u -r1.40 rpcrt4.spec
--- dlls/rpcrt4/rpcrt4.spec	2 Dec 2002 21:17:05 -0000	1.40
+++ dlls/rpcrt4/rpcrt4.spec	30 Jan 2003 16:32:01 -0000
@@ -222,7 +222,7 @@
 @ stdcall NdrDllRegisterProxy(long ptr ptr) NdrDllRegisterProxy
 @ stdcall NdrDllUnregisterProxy(long ptr ptr) NdrDllUnregisterProxy
 
-@ stub NdrAllocate
+@ stdcall NdrAllocate(ptr long) NdrAllocate
 @ stub NdrAsyncClientCall
 @ stub NdrAsyncServerCall
 @ stub NdrClearOutParameters
@@ -330,21 +330,21 @@
 @ stub NdrByteCountPointerUnmarshall
 @ stub NdrClientContextMarshall
 @ stub NdrClientContextUnmarshall
-@ stub NdrComplexArrayBufferSize
-@ stub NdrComplexArrayFree
-@ stub NdrComplexArrayMarshall
-@ stub NdrComplexArrayMemorySize
-@ stub NdrComplexArrayUnmarshall
-@ stub NdrComplexStructBufferSize
-@ stub NdrComplexStructFree
-@ stub NdrComplexStructMarshall
-@ stub NdrComplexStructMemorySize
-@ stub NdrComplexStructUnmarshall
-@ stub NdrConformantArrayBufferSize
-@ stub NdrConformantArrayFree
-@ stub NdrConformantArrayMarshall
-@ stub NdrConformantArrayMemorySize
-@ stub NdrConformantArrayUnmarshall
+@ stdcall NdrComplexArrayBufferSize(ptr ptr ptr) NdrComplexArrayBufferSize
+@ stdcall NdrComplexArrayFree(ptr ptr ptr) NdrComplexArrayFree
+@ stdcall NdrComplexArrayMarshall(ptr ptr ptr) NdrComplexArrayMarshall
+@ stdcall NdrComplexArrayMemorySize(ptr ptr) NdrComplexArrayMemorySize
+@ stdcall NdrComplexArrayUnmarshall(ptr ptr ptr long) NdrComplexArrayUnmarshall
+@ stdcall NdrComplexStructBufferSize(ptr ptr ptr) NdrComplexStructBufferSize
+@ stdcall NdrComplexStructFree(ptr ptr ptr) NdrComplexStructFree
+@ stdcall NdrComplexStructMarshall(ptr ptr ptr) NdrComplexStructMarshall
+@ stdcall NdrComplexStructMemorySize(ptr ptr) NdrComplexStructMemorySize
+@ stdcall NdrComplexStructUnmarshall(ptr ptr ptr long) NdrComplexStructUnmarshall
+@ stdcall NdrConformantArrayBufferSize(ptr ptr ptr) NdrConformantArrayBufferSize
+@ stdcall NdrConformantArrayFree(ptr ptr ptr) NdrConformantArrayFree
+@ stdcall NdrConformantArrayMarshall(ptr ptr ptr) NdrConformantArrayMarshall
+@ stdcall NdrConformantArrayMemorySize(ptr ptr) NdrConformantArrayMemorySize
+@ stdcall NdrConformantArrayUnmarshall(ptr ptr ptr long) NdrConformantArrayUnmarshall
 @ stdcall NdrConformantStringBufferSize(ptr ptr ptr) NdrConformantStringBufferSize
 @ stdcall NdrConformantStringMarshall(ptr ptr ptr) NdrConformantStringMarshall
 @ stdcall NdrConformantStringMemorySize(ptr ptr) NdrConformantStringMemorySize
@@ -388,11 +388,11 @@
 @ stub NdrNonEncapsulatedUnionMarshall
 @ stub NdrNonEncapsulatedUnionMemorySize
 @ stub NdrNonEncapsulatedUnionUnmarshall
-@ stub NdrPointerBufferSize
-@ stub NdrPointerFree
-@ stub NdrPointerMarshall
-@ stub NdrPointerMemorySize
-@ stub NdrPointerUnmarshall
+@ stdcall NdrPointerBufferSize(ptr ptr ptr) NdrPointerBufferSize
+@ stdcall NdrPointerFree(ptr ptr ptr) NdrPointerFree
+@ stdcall NdrPointerMarshall(ptr ptr ptr) NdrPointerMarshall
+@ stdcall NdrPointerMemorySize(ptr ptr) NdrPointerMemorySize
+@ stdcall NdrPointerUnmarshall(ptr ptr ptr long) NdrPointerUnmarshall
 @ stub NdrServerContextMarshall
 @ stub NdrServerContextUnmarshall
 @ stub NdrServerContextNewMarshall # wxp
@@ -404,19 +404,19 @@
 @ stub NdrServerInitializeUnmarshall
 @ stub NdrServerMarshall
 @ stub NdrServerUnmarshall
-@ stub NdrSimpleStructBufferSize
-@ stub NdrSimpleStructFree
-@ stub NdrSimpleStructMarshall
-@ stub NdrSimpleStructMemorySize
-@ stub NdrSimpleStructUnmarshall
+@ stdcall NdrSimpleStructBufferSize(ptr ptr ptr) NdrSimpleStructBufferSize
+@ stdcall NdrSimpleStructFree(ptr ptr ptr) NdrSimpleStructFree
+@ stdcall NdrSimpleStructMarshall(ptr ptr ptr) NdrSimpleStructMarshall
+@ stdcall NdrSimpleStructMemorySize(ptr ptr) NdrSimpleStructMemorySize
+@ stdcall NdrSimpleStructUnmarshall(ptr ptr ptr long) NdrSimpleStructUnmarshall
 @ stub NdrSimpleTypeMarshall
 @ stub NdrSimpleTypeUnmarshall
-@ stub NdrUserMarshalBufferSize
-@ stub NdrUserMarshalFree
-@ stub NdrUserMarshalMarshall
-@ stub NdrUserMarshalMemorySize
+@ stdcall NdrUserMarshalBufferSize(ptr ptr ptr) NdrUserMarshalBufferSize
+@ stdcall NdrUserMarshalFree(ptr ptr ptr) NdrUserMarshalFree
+@ stdcall NdrUserMarshalMarshall(ptr ptr ptr) NdrUserMarshalMarshall
+@ stdcall NdrUserMarshalMemorySize(ptr ptr) NdrUserMarshalMemorySize
 @ stub NdrUserMarshalSimpleTypeConvert
-@ stub NdrUserMarshalUnmarshall
+@ stdcall NdrUserMarshalUnmarshall(ptr ptr ptr long) NdrUserMarshalUnmarshall
 @ stub NdrVaryingArrayBufferSize
 @ stub NdrVaryingArrayFree
 @ stub NdrVaryingArrayMarshall




More information about the wine-patches mailing list