https://git.reactos.org/?p=reactos.git;a=commitdiff;h=f2f9161a02d974b75c36dc494bd8c0bb3e218fbd

commit f2f9161a02d974b75c36dc494bd8c0bb3e218fbd
Author:     Amine Khaldi <[email protected]>
AuthorDate: Sat Feb 2 14:11:36 2019 +0100
Commit:     Amine Khaldi <[email protected]>
CommitDate: Sat Feb 2 14:11:36 2019 +0100

    [RPCRT4_WINETEST] Sync with Wine Staging 4.0. CORE-15682
---
 modules/rostests/winetests/rpcrt4/CMakeLists.txt |   6 +-
 modules/rostests/winetests/rpcrt4/cstub.c        | 248 ++++++++-
 modules/rostests/winetests/rpcrt4/cstub.idl      |  37 ++
 modules/rostests/winetests/rpcrt4/ndr_marshall.c | 664 ++++++++++++++++++++---
 modules/rostests/winetests/rpcrt4/rpc.c          |  29 +-
 modules/rostests/winetests/rpcrt4/rpc_async.c    |  23 +-
 modules/rostests/winetests/rpcrt4/server.c       | 179 +++++-
 modules/rostests/winetests/rpcrt4/server.idl     |   3 +
 8 files changed, 1059 insertions(+), 130 deletions(-)

diff --git a/modules/rostests/winetests/rpcrt4/CMakeLists.txt 
b/modules/rostests/winetests/rpcrt4/CMakeLists.txt
index ea8c329a3d..dbb7cf7fc1 100644
--- a/modules/rostests/winetests/rpcrt4/CMakeLists.txt
+++ b/modules/rostests/winetests/rpcrt4/CMakeLists.txt
@@ -3,13 +3,15 @@ remove_definitions(-DWINVER=0x502 -D_WIN32_IE=0x600 
-D_WIN32_WINNT=0x502)
 
 add_definitions(
     -DUSE_WINE_TODOS
-    -DWINETEST_USE_DBGSTR_LONGLONG)
+    -DWINETEST_USE_DBGSTR_LONGLONG
+    -DPROXY_DELEGATION)
 
 include_directories(${CMAKE_CURRENT_BINARY_DIR})
 set(IDL_FLAGS ${IDL_FLAGS} --prefix-server=s_)
 add_rpc_files(client server.idl)
 add_rpc_files(server server.idl)
 unset(IDL_FLAGS)
+add_rpcproxy_files(cstub.idl)
 
 list(APPEND SOURCE
     cstub.c
@@ -19,6 +21,8 @@ list(APPEND SOURCE
     rpc_async.c
     server.c
     testlist.c
+    ${CMAKE_CURRENT_BINARY_DIR}/cstub_p.c
+    ${CMAKE_CURRENT_BINARY_DIR}/proxy.dlldata.c
     ${CMAKE_CURRENT_BINARY_DIR}/server_c.c
     ${CMAKE_CURRENT_BINARY_DIR}/server_s.c)
 
diff --git a/modules/rostests/winetests/rpcrt4/cstub.c 
b/modules/rostests/winetests/rpcrt4/cstub.c
index 606107fcfd..a182b3ba27 100644
--- a/modules/rostests/winetests/rpcrt4/cstub.c
+++ b/modules/rostests/winetests/rpcrt4/cstub.c
@@ -19,11 +19,13 @@
  */
 
 #include <stdarg.h>
+#include <stdio.h>
 
-#define PROXY_DELEGATION
 #define COBJMACROS
+#ifdef __REACTOS__
+#define CONST_VTABLE
+#endif
 
-#include "wine/test.h"
 #include <windef.h>
 #include <winbase.h>
 #include <winnt.h>
@@ -35,10 +37,22 @@
 #include "rpcdce.h"
 #include "rpcproxy.h"
 
+#include "wine/heap.h"
+#include "wine/test.h"
+
+#include "cstub_p.h"
+
 static CStdPSFactoryBuffer PSFactoryBuffer;
 
-CSTDSTUBBUFFERRELEASE(&PSFactoryBuffer)
-CSTDSTUBBUFFER2RELEASE(&PSFactoryBuffer)
+static ULONG WINAPI test_CStdStubBuffer_Release(IRpcStubBuffer *This)
+{
+    return NdrCStdStubBuffer_Release(This, (IPSFactoryBuffer 
*)&PSFactoryBuffer);
+}
+
+static ULONG WINAPI test_CStdStubBuffer2_Release(IRpcStubBuffer *This)
+{
+    return NdrCStdStubBuffer2_Release(This, (IPSFactoryBuffer 
*)&PSFactoryBuffer);
+}
 
 static GUID IID_if1 = {0x12345678, 1234, 5678, 
{12,34,56,78,90,0xab,0xcd,0xef}};
 static GUID IID_if2 = {0x12345679, 1234, 5678, 
{12,34,56,78,90,0xab,0xcd,0xef}};
@@ -186,7 +200,18 @@ static CInterfaceStubVtbl if1_stub_vtbl =
         5,
         &if1_table[-3]
     },
-    { CStdStubBuffer_METHODS }
+    {
+        CStdStubBuffer_QueryInterface,
+        CStdStubBuffer_AddRef,
+        test_CStdStubBuffer_Release,
+        CStdStubBuffer_Connect,
+        CStdStubBuffer_Disconnect,
+        CStdStubBuffer_Invoke,
+        CStdStubBuffer_IsIIDSupported,
+        CStdStubBuffer_CountRefs,
+        CStdStubBuffer_DebugServerQueryInterface,
+        CStdStubBuffer_DebugServerRelease
+    }
 };
 
 static CINTERFACE_PROXY_VTABLE(13) if2_proxy_vtbl =
@@ -257,7 +282,7 @@ static CInterfaceStubVtbl if2_stub_vtbl =
         13,
         &if2_table[-3]
     },
-    { CStdStubBuffer_DELEGATING_METHODS }
+    { 0, 0, test_CStdStubBuffer2_Release, 0, 0, 0, 0, 0, 0, 0 }
 };
 
 static CINTERFACE_PROXY_VTABLE(5) if3_proxy_vtbl =
@@ -596,7 +621,7 @@ static IPSFactoryBuffer *test_NdrDllGetClassObject(void)
 #undef VTBL_PROXY_TEST
 #undef VTBL_PROXY_TEST_NOT_ZERO
 
-    for (i = 0; i < sizeof(interfaces)/sizeof(interfaces[0]); i++)
+    for (i = 0; i < ARRAY_SIZE(interfaces); i++)
         ok( proxy_vtbl[i]->header.piid == interfaces[i],
             "wrong proxy %u iid %p/%p\n", i, proxy_vtbl[i]->header.piid, 
interfaces[i] );
 
@@ -1194,9 +1219,217 @@ static void test_NdrDllRegisterProxy( void )
     }
 }
 
+static HANDLE create_process(const char *arg)
+{
+    PROCESS_INFORMATION pi;
+    STARTUPINFOA si = {0};
+    char cmdline[200];
+    char **argv;
+    BOOL ret;
+
+    si.cb = sizeof(si);
+    winetest_get_mainargs(&argv);
+    sprintf(cmdline, "\"%s\" %s %s", argv[0], argv[1], arg);
+    ret = CreateProcessA(argv[0], cmdline, NULL, NULL, FALSE, 0, NULL, NULL, 
&si, &pi);
+    ok(ret, "CreateProcess failed: %u\n", GetLastError());
+    CloseHandle(pi.hThread);
+    return pi.hProcess;
+}
+
+DEFINE_GUID(CLSID_test1,0xdeadf00d,0x0001,0x44c7,0x85,0x0f,0x2a,0x0f,0x46,0x5c,0x0c,0x6c);
+
+static HRESULT WINAPI test1_QueryInterface(ITest1 *iface, REFIID iid, void 
**out)
+{
+    if (winetest_debug > 1) trace("%s\n", wine_dbgstr_guid(iid));
+    if (IsEqualGUID(iid, &IID_IUnknown) || IsEqualGUID(iid, &IID_ITest1))
+    {
+        *out = iface;
+        return S_OK;
+    }
+    *out = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI test1_AddRef(ITest1 *iface)
+{
+    return 2;
+}
+
+static ULONG WINAPI test1_Release(ITest1 *iface)
+{
+    return 1;
+}
+
+static HRESULT WINAPI test1_GetClassID(ITest1 *iface, CLSID *clsid)
+{
+    *clsid = CLSID_test1;
+    return S_OK;
+}
+
+static int WINAPI test1_square(ITest1 *iface, int x)
+{
+    return x * x;
+}
+
+static const ITest1Vtbl test1_vtbl =
+{
+    test1_QueryInterface,
+    test1_AddRef,
+    test1_Release,
+    test1_GetClassID,
+    test1_square,
+};
+
+static HRESULT WINAPI test_cf_QueryInterface(IClassFactory *iface, REFIID iid, 
void **out)
+{
+    if (IsEqualGUID(iid, &IID_IUnknown) || IsEqualGUID(iid, 
&IID_IClassFactory))
+    {
+        *out = iface;
+        return S_OK;
+    }
+    *out = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI test_cf_AddRef(IClassFactory *iface)
+{
+    return 2;
+}
+
+static ULONG WINAPI test_cf_Release(IClassFactory *iface)
+{
+    return 1;
+}
+
+static HRESULT WINAPI test_cf_CreateInstance(IClassFactory *iface, IUnknown 
*outer, REFIID iid, void **out)
+{
+    ITest1 *obj = heap_alloc(sizeof(*obj));
+
+    obj->lpVtbl = &test1_vtbl;
+
+    return ITest1_QueryInterface(obj, iid, out);
+}
+
+static HRESULT WINAPI test_cf_LockServer(IClassFactory *iface, BOOL lock)
+{
+    return S_OK;
+}
+
+static const IClassFactoryVtbl test_cf_vtbl =
+{
+    test_cf_QueryInterface,
+    test_cf_AddRef,
+    test_cf_Release,
+    test_cf_CreateInstance,
+    test_cf_LockServer,
+};
+
+static IClassFactory test_cf = { &test_cf_vtbl };
+
+extern CStdPSFactoryBuffer gPFactory;
+extern const ProxyFileInfo * aProxyFileList;
+
+static void local_server_proc(void)
+{
+    DWORD obj_cookie, ps_cookie, index;
+    HANDLE stop_event, ready_event;
+    IPSFactoryBuffer *ps;
+    HRESULT hr;
+
+    stop_event = OpenEventA(EVENT_ALL_ACCESS, FALSE, 
"wine_cstub_test_server_stop");
+    ready_event = OpenEventA(EVENT_ALL_ACCESS, FALSE, 
"wine_cstub_test_server_ready");
+
+    CoInitialize(NULL);
+
+    hr = CoRegisterClassObject(&CLSID_test1, (IUnknown *)&test_cf,
+        CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE, &obj_cookie);
+    ok(hr == S_OK, "got %#x\n", hr);
+
+    hr = NdrDllGetClassObject(&CLSID_test_ps, &IID_IPSFactoryBuffer, (void 
**)&ps,
+        &aProxyFileList, &CLSID_test_ps, &gPFactory);
+    ok(hr == S_OK, "got %#x\n", hr);
+
+    hr = CoRegisterClassObject(&CLSID_test_ps, (IUnknown *)ps,
+        CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &ps_cookie);
+    ok(hr == S_OK, "got %#x\n", hr);
+
+    hr = CoRegisterPSClsid(&IID_ITest1, &CLSID_test_ps);
+    ok(hr == S_OK, "got %#x\n", hr);
+
+    SetEvent(ready_event);
+
+    hr = CoWaitForMultipleHandles(0, 1000, 1, &stop_event, &index);
+    ok(hr == S_OK, "got %#x\n", hr);
+    ok(!index, "got %u\n", index);
+
+    hr = CoRevokeClassObject(ps_cookie);
+    ok(hr == S_OK, "got %#x\n", hr);
+
+    hr = CoRevokeClassObject(obj_cookie);
+    ok(hr == S_OK, "got %#x\n", hr);
+
+    CoUninitialize();
+    ExitProcess(0);
+}
+
+static void test_delegated_methods(void)
+{
+    HANDLE process, stop_event, ready_event;
+    IPSFactoryBuffer *ps;
+    ITest1 *test_obj;
+    DWORD ps_cookie;
+    CLSID clsid;
+    HRESULT hr;
+    int ret;
+
+    stop_event = CreateEventA(NULL, TRUE, FALSE, 
"wine_cstub_test_server_stop");
+    ready_event = CreateEventA(NULL, TRUE, FALSE, 
"wine_cstub_test_server_ready");
+
+    process = create_process("server");
+    ok(!WaitForSingleObject(ready_event, 1000), "wait failed\n");
+
+    hr = NdrDllGetClassObject(&CLSID_test_ps, &IID_IPSFactoryBuffer, (void 
**)&ps,
+        &aProxyFileList, &CLSID_test_ps, &gPFactory);
+    ok(hr == S_OK, "got %#x\n", hr);
+
+    hr = CoRegisterClassObject(&CLSID_test_ps, (IUnknown *)ps,
+        CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &ps_cookie);
+    ok(hr == S_OK, "got %#x\n", hr);
+
+    hr = CoRegisterPSClsid(&IID_ITest1, &CLSID_test_ps);
+    ok(hr == S_OK, "got %#x\n", hr);
+
+    hr = CoCreateInstance(&CLSID_test1, NULL, CLSCTX_LOCAL_SERVER, 
&IID_ITest1, (void **)&test_obj);
+    ok(hr == S_OK, "got %#x\n", hr);
+
+    ret = ITest1_square(test_obj, 3);
+    ok(ret == 9, "got %d\n", ret);
+
+    hr = ITest1_GetClassID(test_obj, &clsid);
+    ok(hr == S_OK, "got %#x\n", hr);
+    ok(IsEqualGUID(&clsid, &CLSID_test1), "got %s\n", 
wine_dbgstr_guid(&clsid));
+
+    ITest1_Release(test_obj);
+
+    SetEvent(stop_event);
+    ok(!WaitForSingleObject(process, 1000), "wait failed\n");
+
+    hr = CoRevokeClassObject(ps_cookie);
+    ok(hr == S_OK, "got %#x\n", hr);
+}
+
 START_TEST( cstub )
 {
     IPSFactoryBuffer *ppsf;
+    int argc;
+    char **argv;
+
+    argc = winetest_get_mainargs( &argv );
+    if (argc > 2 && !strcmp(argv[2], "server"))
+    {
+        local_server_proc();
+        return;
+    }
 
     OleInitialize(NULL);
 
@@ -1209,6 +1442,7 @@ START_TEST( cstub )
     test_Release(ppsf);
     test_delegating_Invoke(ppsf);
     test_NdrDllRegisterProxy();
+    test_delegated_methods();
 
     OleUninitialize();
 }
diff --git a/modules/rostests/winetests/rpcrt4/cstub.idl 
b/modules/rostests/winetests/rpcrt4/cstub.idl
new file mode 100644
index 0000000000..461bd87b30
--- /dev/null
+++ b/modules/rostests/winetests/rpcrt4/cstub.idl
@@ -0,0 +1,37 @@
+/*
+ * IDL to test COM object proxy/stub functions
+ *
+ * Copyright 2018 Zebediah Figura
+ *
+ * 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
+ */
+
+#pragma makedep proxy
+
+import "oleidl.idl";
+
+[
+    uuid(deadbeef-0001-44c7-850f-2a0f465c0c6c),
+    object
+]
+interface ITest1 : IPersist
+{
+    int square(int x);
+}
+
+[
+    uuid(deadbeef-ffff-44c7-850f-2a0f465c0c6c)
+]
+coclass test_ps { interface IPSFactoryBuffer; }
diff --git a/modules/rostests/winetests/rpcrt4/ndr_marshall.c 
b/modules/rostests/winetests/rpcrt4/ndr_marshall.c
index 21baba74ac..e4a4c6a727 100644
--- a/modules/rostests/winetests/rpcrt4/ndr_marshall.c
+++ b/modules/rostests/winetests/rpcrt4/ndr_marshall.c
@@ -21,10 +21,10 @@
 #define _WIN32_WINNT  0x0500
 #define NTDDI_WIN2K   0x05000000
 #define NTDDI_VERSION NTDDI_WIN2K /* for some MIDL_STUB_MESSAGE fields */
+#define COBJMACROS
 
 #include <stdarg.h>
 
-#include "wine/test.h"
 #include <windef.h>
 #include <winbase.h>
 #include <winnt.h>
@@ -35,6 +35,10 @@
 #include "rpcdce.h"
 #include "rpcproxy.h"
 #include "midles.h"
+#include "ndrtypes.h"
+
+#include "wine/heap.h"
+#include "wine/test.h"
 
 static int my_alloc_called;
 static int my_free_called;
@@ -154,17 +158,17 @@ static void test_ndr_simple_type(void)
     StubMsg.BufferLength = 16;
     StubMsg.RpcMsg->Buffer = StubMsg.BufferStart = StubMsg.Buffer = 
HeapAlloc(GetProcessHeap(), 0, StubMsg.BufferLength);
     l = 0xcafebabe;
-    NdrSimpleTypeMarshall(&StubMsg, (unsigned char*)&l, 8 /* FC_LONG */);
+    NdrSimpleTypeMarshall(&StubMsg, (unsigned char*)&l, FC_LONG);
     ok(StubMsg.Buffer == StubMsg.BufferStart + 4, "%p %p\n", StubMsg.Buffer, 
StubMsg.BufferStart);
     ok(*(LONG*)StubMsg.BufferStart == l, "%d\n", *(LONG*)StubMsg.BufferStart);
 
     StubMsg.Buffer = StubMsg.BufferStart + 1;
-    NdrSimpleTypeMarshall(&StubMsg, (unsigned char*)&l, 8 /* FC_LONG */);
+    NdrSimpleTypeMarshall(&StubMsg, (unsigned char*)&l, FC_LONG);
     ok(StubMsg.Buffer == StubMsg.BufferStart + 8, "%p %p\n", StubMsg.Buffer, 
StubMsg.BufferStart);
     ok(*(LONG*)(StubMsg.BufferStart + 4) == l, "%d\n", 
*(LONG*)StubMsg.BufferStart);
 
     StubMsg.Buffer = StubMsg.BufferStart + 1;
-    NdrSimpleTypeUnmarshall(&StubMsg, (unsigned char*)&l2, 8 /* FC_LONG */);
+    NdrSimpleTypeUnmarshall(&StubMsg, (unsigned char*)&l2, FC_LONG);
     ok(StubMsg.Buffer == StubMsg.BufferStart + 8, "%p %p\n", StubMsg.Buffer, 
StubMsg.BufferStart);
     ok(l2 == l, "%d\n", l2);
 
@@ -186,7 +190,6 @@ static void test_pointer_marshal(const unsigned char 
*formattypes,
     void *ptr;
     unsigned char *mem, *mem_orig;
 
-    my_alloc_called = my_free_called = 0;
     if(!cmp)
         cmp = memcmp;
 
@@ -230,7 +233,7 @@ static void test_pointer_marshal(const unsigned char 
*formattypes,
     size = NdrPointerMemorySize( &StubMsg, formattypes );
     ok(size == StubMsg.MemorySize, "%s: mem size %u size %u\n", msgpfx, 
StubMsg.MemorySize, size);
     ok(StubMsg.Buffer - StubMsg.BufferStart == wiredatalen, "%s: Buffer %p 
Start %p len %d\n", msgpfx, StubMsg.Buffer, StubMsg.BufferStart, wiredatalen);
-    if(formattypes[1] & 0x10 /* FC_POINTER_DEREF */)
+    if (formattypes[1] & FC_POINTER_DEREF)
         ok(size == srcsize + sizeof(void *), "%s: mem size %u\n", msgpfx, 
size);
     else
         ok(size == srcsize, "%s: mem size %u\n", msgpfx, size);
@@ -240,7 +243,7 @@ static void test_pointer_marshal(const unsigned char 
*formattypes,
     size = NdrPointerMemorySize( &StubMsg, formattypes );
     ok(size == StubMsg.MemorySize, "%s: mem size %u size %u\n", msgpfx, 
StubMsg.MemorySize, size);
     ok(StubMsg.Buffer - StubMsg.BufferStart == wiredatalen, "%s: Buffer %p 
Start %p len %d\n", msgpfx, StubMsg.Buffer, StubMsg.BufferStart, wiredatalen);
-    if(formattypes[1] & 0x10 /* FC_POINTER_DEREF */)
+    if (formattypes[1] & FC_POINTER_DEREF)
         ok(size == srcsize + sizeof(void *) + 16, "%s: mem size %u\n", msgpfx, 
size);
     else
         ok(size == srcsize + 16, "%s: mem size %u\n", msgpfx, size);
@@ -250,19 +253,21 @@ static void test_pointer_marshal(const unsigned char 
*formattypes,
     size = NdrPointerMemorySize( &StubMsg, formattypes );
     ok(size == StubMsg.MemorySize, "%s: mem size %u size %u\n", msgpfx, 
StubMsg.MemorySize, size);
     ok(StubMsg.Buffer - StubMsg.BufferStart == wiredatalen, "%s: Buffer %p 
Start %p len %d\n", msgpfx, StubMsg.Buffer, StubMsg.BufferStart, wiredatalen);
-    if(formattypes[1] & 0x10 /* FC_POINTER_DEREF */)
+    if (formattypes[1] & FC_POINTER_DEREF)
         ok(size == srcsize + sizeof(void *) + (srcsize == 8 ? 8 : sizeof(void 
*)), "%s: mem size %u\n", msgpfx, size);
     else
         ok(size == srcsize + (srcsize == 8 ? 8 : sizeof(void *)), "%s: mem 
size %u\n", msgpfx, size);
 
     size = srcsize;
-    if(formattypes[1] & 0x10) size += 4;
+    if (formattypes[1] & FC_POINTER_DEREF) size += 4;
 
     StubMsg.Buffer = StubMsg.BufferStart;
     StubMsg.MemorySize = 0;
-    mem_orig = mem = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
-
-    if(formattypes[1] & 0x10 /* FC_POINTER_DEREF */)
+    /* Using my_alloc() here is necessary to prevent a crash in Windows 7+. */
+    mem_orig = mem = my_alloc(size);
+    memset(mem, 0, size);
+    my_alloc_called = my_free_called = 0;
+    if (formattypes[1] & FC_POINTER_DEREF)
         *(void**)mem = NULL;
     ptr = NdrPointerUnmarshall( &StubMsg, &mem, formattypes, 0 );
     ok(ptr == NULL, "%s: ret %p\n", msgpfx, ptr);
@@ -270,31 +275,51 @@ static void test_pointer_marshal(const unsigned char 
*formattypes,
     ok(!cmp(mem, memsrc, srcsize), "%s: incorrectly unmarshaled\n", msgpfx);
     ok(StubMsg.Buffer - StubMsg.BufferStart == wiredatalen, "%s: Buffer %p 
Start %p len %d\n", msgpfx, StubMsg.Buffer, StubMsg.BufferStart, wiredatalen);
     ok(StubMsg.MemorySize == 0, "%s: memorysize %d\n", msgpfx, 
StubMsg.MemorySize);
-    ok(my_alloc_called == num_additional_allocs, "%s: my_alloc got called %d 
times\n", msgpfx, my_alloc_called); 
-    my_alloc_called = 0;
+    ok(my_alloc_called == num_additional_allocs, "%s: my_alloc got called %d 
times\n", msgpfx, my_alloc_called);
+    /* On Windows 7+ unmarshalling may involve calls to NdrFree, for unclear 
reasons. */
+    my_free_called = 0;
+
+    NdrPointerFree(&StubMsg, mem, formattypes);
+    if ((formattypes[1] & FC_ALLOCED_ON_STACK) && (formattypes[1] & 
FC_POINTER_DEREF))
+    {
+        /* In this case the top-level pointer is not freed. */
+        ok(my_free_called == num_additional_allocs, "%s: my_free got called %d 
times\n", msgpfx, my_free_called);
+        HeapFree(GetProcessHeap(), 0, mem);
+    }
+    else
+        ok(my_free_called == 1 + num_additional_allocs, "%s: my_free got 
called %d times\n", msgpfx, my_free_called);
 
     /* reset the buffer and call with must alloc */
+    my_alloc_called = my_free_called = 0;
     StubMsg.Buffer = StubMsg.BufferStart;
-    if(formattypes[1] & 0x10 /* FC_POINTER_DEREF */)
+    mem_orig = mem = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
+    if (formattypes[1] & FC_POINTER_DEREF)
         *(void**)mem = NULL;
     ptr = NdrPointerUnmarshall( &StubMsg, &mem, formattypes, 1 );
     ok(ptr == NULL, "%s: ret %p\n", msgpfx, ptr);
     /* doesn't allocate mem in this case */
-todo_wine {
     ok(mem == mem_orig, "%s: mem has changed %p %p\n", msgpfx, mem, mem_orig);
- }
     ok(!cmp(mem, memsrc, srcsize), "%s: incorrectly unmarshaled\n", msgpfx);
     ok(StubMsg.Buffer - StubMsg.BufferStart == wiredatalen, "%s: Buffer %p 
Start %p len %d\n", msgpfx, StubMsg.Buffer, StubMsg.BufferStart, wiredatalen);
     ok(StubMsg.MemorySize == 0, "%s: memorysize %d\n", msgpfx, 
StubMsg.MemorySize);
-
-todo_wine {
     ok(my_alloc_called == num_additional_allocs, "%s: my_alloc got called %d 
times\n", msgpfx, my_alloc_called); 
-}
-    my_alloc_called = 0;
-    if(formattypes[0] != 0x11 /* FC_RP */)
+    ok(!my_free_called, "%s: my_free got called %d times\n", msgpfx, 
my_free_called);
+
+    NdrPointerFree(&StubMsg, mem, formattypes);
+    if ((formattypes[1] & FC_ALLOCED_ON_STACK) && (formattypes[1] & 
FC_POINTER_DEREF))
+    {
+        /* In this case the top-level pointer is not freed. */
+        ok(my_free_called == num_additional_allocs, "%s: my_free got called %d 
times\n", msgpfx, my_free_called);
+        HeapFree(GetProcessHeap(), 0, mem);
+    }
+    else
+        ok(my_free_called == 1 + num_additional_allocs, "%s: my_free got 
called %d times\n", msgpfx, my_free_called);
+
+    if (formattypes[0] != FC_RP)
     {
         /* now pass the address of a NULL ptr */
         mem = NULL;
+        my_alloc_called = my_free_called = 0;
         StubMsg.Buffer = StubMsg.BufferStart;
         ptr = NdrPointerUnmarshall( &StubMsg, &mem, formattypes, 0 );
         ok(ptr == NULL, "%s: ret %p\n", msgpfx, ptr);
@@ -314,20 +339,161 @@ todo_wine {
             StubMsg.IsClient = 0;
             ptr = NdrPointerUnmarshall( &StubMsg, &mem, formattypes, 0 );
             ok(ptr == NULL, "%s: ret %p\n", msgpfx, ptr);
-            if (formattypes[2] == 0xd /* FC_ENUM16 */)
+            if (formattypes[2] == FC_ENUM16)
                 ok(mem != StubMsg.BufferStart + wiredatalen - srcsize, "%s: 
mem points to buffer %p %p\n", msgpfx, mem, StubMsg.BufferStart);
             else
                 ok(mem == StubMsg.BufferStart + wiredatalen - srcsize, "%s: 
mem doesn't point to buffer %p %p\n", msgpfx, mem, StubMsg.BufferStart);
             ok(!cmp(mem, memsrc, size), "%s: incorrectly unmarshaled\n", 
msgpfx);
             ok(StubMsg.Buffer - StubMsg.BufferStart == wiredatalen, "%s: 
Buffer %p Start %p len %d\n", msgpfx, StubMsg.Buffer, StubMsg.BufferStart, 
wiredatalen);
             ok(StubMsg.MemorySize == 0, "%s: memorysize %d\n", msgpfx, 
StubMsg.MemorySize);
-            if (formattypes[2] != 0xd /* FC_ENUM16 */) {
+            if (formattypes[2] != FC_ENUM16)
+            {
                 ok(my_alloc_called == num_additional_allocs, "%s: my_alloc got 
called %d times\n", msgpfx, my_alloc_called);
                 my_alloc_called = 0;
             }
         }
     }
-    HeapFree(GetProcessHeap(), 0, mem_orig);
+
+    /* Server */
+    StubMsg.IsClient = 0;
+
+    /* For most basetypes (but not enum16), memory will not be allocated but
+     * instead point directly to the buffer. */
+    my_alloc_called = my_free_called = 0;
+    StubMsg.Buffer = StubMsg.BufferStart;
+    mem = NULL;
+    ptr = NdrPointerUnmarshall( &StubMsg, &mem, formattypes, 0 );
+    ok(ptr == NULL, "%s: ret %p\n", msgpfx, ptr);
+    ok(!!mem, "%s: mem was not allocated\n", msgpfx);
+    ok(!cmp(mem, memsrc, srcsize), "%s: incorrectly unmarshaled\n", msgpfx);
+    ok(StubMsg.Buffer - StubMsg.BufferStart == wiredatalen, "%s: Buffer %p 
Start %p len %d\n", msgpfx, StubMsg.Buffer, StubMsg.BufferStart, wiredatalen);
+    ok(StubMsg.MemorySize == 0, "%s: memorysize %d\n", msgpfx, 
StubMsg.MemorySize);
+    if (formattypes[2] == FC_ENUM16)
+        ok(my_alloc_called == 1, "%s: my_alloc got called %d times\n", msgpfx, 
my_alloc_called);
+    else
+        ok(my_alloc_called == num_additional_allocs, "%s: my_alloc got called 
%d times\n", msgpfx, my_alloc_called);
+    ok(!my_free_called, "%s: my_free got called %d times\n", msgpfx, 
my_free_called);
+
+    NdrPointerFree(&StubMsg, mem, formattypes);
+    if (formattypes[2] == FC_ENUM16)
+        ok(my_free_called == 1, "%s: my_free got called %d times\n", msgpfx, 
my_free_called);
+    else if ((formattypes[1] & FC_ALLOCED_ON_STACK) && (formattypes[1] & 
FC_POINTER_DEREF))
+    {
+        /* In theory this should be freed to correspond with the allocation, 
but
+         * FC_ALLOCED_ON_STACK is set, and NdrPointerFree() has no way of
+         * knowing that the memory allocated by NdrPointerUnmarshall() isn't
+         * stack memory. In practice it always *is* stack memory if ON_STACK is
+         * set, so this leak isn't a concern. */
+        ok(my_free_called == 0, "%s: my_free got called %d times\n", msgpfx, 
my_free_called);
+        HeapFree(GetProcessHeap(), 0, mem);
+    }
+    else
+        ok(my_free_called == num_additional_allocs, "%s: my_free got called %d 
times\n", msgpfx, my_free_called);
+
+    /* reset the buffer and call with must alloc */
+    my_alloc_called = my_free_called = 0;
+    StubMsg.Buffer = StubMsg.BufferStart;
+    mem = NULL;
+    ptr = NdrPointerUnmarshall( &StubMsg, &mem, formattypes, 1 );
+    ok(ptr == NULL, "%s: ret %p\n", msgpfx, ptr);
+    ok(!!mem, "%s: mem was not allocated\n", msgpfx);
+    ok(!cmp(mem, memsrc, srcsize), "%s: incorrectly unmarshaled\n", msgpfx);
+    ok(StubMsg.Buffer - StubMsg.BufferStart == wiredatalen, "%s: Buffer %p 
Start %p len %d\n", msgpfx, StubMsg.Buffer, StubMsg.BufferStart, wiredatalen);
+    ok(StubMsg.MemorySize == 0, "%s: memorysize %d\n", msgpfx, 
StubMsg.MemorySize);
+    if (formattypes[2] == FC_ENUM16)
+        ok(my_alloc_called == 1, "%s: my_alloc got called %d times\n", msgpfx, 
my_alloc_called);
+    else
+        ok(my_alloc_called == num_additional_allocs, "%s: my_alloc got called 
%d times\n", msgpfx, my_alloc_called);
+    ok(!my_free_called, "%s: my_free got called %d times\n", msgpfx, 
my_free_called);
+
+    NdrPointerFree(&StubMsg, mem, formattypes);
+    if (formattypes[2] == FC_ENUM16)
+        ok(my_free_called == 1, "%s: my_free got called %d times\n", msgpfx, 
my_free_called);
+    else if ((formattypes[1] & FC_ALLOCED_ON_STACK) && (formattypes[1] & 
FC_POINTER_DEREF))
+    {
+        ok(my_free_called == 0, "%s: my_free got called %d times\n", msgpfx, 
my_free_called);
+        HeapFree(GetProcessHeap(), 0, mem);
+    }
+    else
+        ok(my_free_called == num_additional_allocs, "%s: my_free got called %d 
times\n", msgpfx, my_free_called);
+
+    /* Ξ€est with an existing pointer. Unless it's a stack pointer (and deref'd)
+     * a new pointer will be allocated anyway (in fact, an invalid pointer 
works
+     * in every such case). */
+
+    my_alloc_called = my_free_called = 0;
+    StubMsg.Buffer = StubMsg.BufferStart;
+    mem_orig = mem = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
+    if (formattypes[1] & FC_POINTER_DEREF)
+        *(void**)mem = NULL;
+    ptr = NdrPointerUnmarshall( &StubMsg, &mem, formattypes, 0 );
+    ok(ptr == NULL, "%s: ret %p\n", msgpfx, ptr);
+    if ((formattypes[1] & FC_ALLOCED_ON_STACK) && (formattypes[1] & 
FC_POINTER_DEREF))
+        ok(mem == mem_orig, "%s: mem has changed %p %p\n", msgpfx, mem, 
mem_orig);
+    else
+    {
+        ok(mem != mem_orig, "%s: mem has not changed\n", msgpfx);
+        HeapFree(GetProcessHeap(), 0, mem_orig);
+    }
+    ok(!cmp(mem, memsrc, srcsize), "%s: incorrectly unmarshaled\n", msgpfx);
+    ok(StubMsg.Buffer - StubMsg.BufferStart == wiredatalen, "%s: Buffer %p 
Start %p len %d\n", msgpfx, StubMsg.Buffer, StubMsg.BufferStart, wiredatalen);
+    ok(StubMsg.MemorySize == 0, "%s: memorysize %d\n", msgpfx, 
StubMsg.MemorySize);
+    if (formattypes[2] == FC_ENUM16)
+        ok(my_alloc_called == 1, "%s: my_alloc got called %d times\n", msgpfx, 
my_alloc_called);
+    else if ((formattypes[1] & FC_ALLOCED_ON_STACK) && (formattypes[1] & 
FC_POINTER_DEREF))
+        ok(my_alloc_called == 0, "%s: my_alloc got called %d times\n", msgpfx, 
my_free_called);
+    else
+        ok(my_alloc_called == num_additional_allocs, "%s: my_alloc got called 
%d times\n", msgpfx, my_alloc_called);
+    ok(!my_free_called, "%s: my_free got called %d times\n", msgpfx, 
my_free_called);
+
+    NdrPointerFree(&StubMsg, mem, formattypes);
+    if (formattypes[2] == FC_ENUM16)
+        ok(my_free_called == 1, "%s: my_free got called %d times\n", msgpfx, 
my_free_called);
+    else if ((formattypes[1] & FC_ALLOCED_ON_STACK) && (formattypes[1] & 
FC_POINTER_DEREF))
+    {
+        ok(my_free_called == 0, "%s: my_free got called %d times\n", msgpfx, 
my_free_called);
+        HeapFree(GetProcessHeap(), 0, mem);
+    }
+    else
+        ok(my_free_called == num_additional_allocs, "%s: my_free got called %d 
times\n", msgpfx, my_free_called);
+
+    /* reset the buffer and call with must alloc */
+    my_alloc_called = my_free_called = 0;
+    StubMsg.Buffer = StubMsg.BufferStart;
+    mem_orig = mem = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
+    if (formattypes[1] & FC_POINTER_DEREF)
+        *(void**)mem = NULL;
+    ptr = NdrPointerUnmarshall( &StubMsg, &mem, formattypes, 1 );
+    ok(ptr == NULL, "%s: ret %p\n", msgpfx, ptr);
+    if ((formattypes[1] & FC_ALLOCED_ON_STACK) && (formattypes[1] & 
FC_POINTER_DEREF))
+        ok(mem == mem_orig, "%s: mem has changed %p %p\n", msgpfx, mem, 
mem_orig);
+    else
+    {
+        ok(mem != mem_orig, "%s: mem has not changed\n", msgpfx);
+        HeapFree(GetProcessHeap(), 0, mem_orig);
+    }
+    ok(!cmp(mem, memsrc, srcsize), "%s: incorrectly unmarshaled\n", msgpfx);
+    ok(StubMsg.Buffer - StubMsg.BufferStart == wiredatalen, "%s: Buffer %p 
Start %p len %d\n", msgpfx, StubMsg.Buffer, StubMsg.BufferStart, wiredatalen);
+    ok(StubMsg.MemorySize == 0, "%s: memorysize %d\n", msgpfx, 
StubMsg.MemorySize);
+    if (formattypes[2] == FC_ENUM16)
+        ok(my_alloc_called == 1, "%s: my_alloc got called %d times\n", msgpfx, 
my_alloc_called);
+    else if ((formattypes[1] & FC_ALLOCED_ON_STACK) && (formattypes[1] & 
FC_POINTER_DEREF))
+        ok(my_alloc_called == 0, "%s: my_alloc got called %d times\n", msgpfx, 
my_free_called);
+    else
+        ok(my_alloc_called == num_additional_allocs, "%s: my_alloc got called 
%d times\n", msgpfx, my_alloc_called);
+    ok(!my_free_called, "%s: my_free got called %d times\n", msgpfx, 
my_free_called);
+
+    NdrPointerFree(&StubMsg, mem, formattypes);
+    if (formattypes[2] == FC_ENUM16)
+        ok(my_free_called == 1, "%s: my_free got called %d times\n", msgpfx, 
my_free_called);
+    else if ((formattypes[1] & FC_ALLOCED_ON_STACK) && (formattypes[1] & 
FC_POINTER_DEREF))
+    {
+        ok(my_free_called == 0, "%s: my_free got called %d times\n", msgpfx, 
my_free_called);
+        HeapFree(GetProcessHeap(), 0, mem);
+    }
+    else
+        ok(my_free_called == num_additional_allocs, "%s: my_free got called %d 
times\n", msgpfx, my_free_called);
+
     HeapFree(GetProcessHeap(), 0, StubMsg.BufferStart);
 }
 
@@ -379,15 +545,15 @@ static void test_simple_types(void)
         0x2,            /* FC_CHAR */
         0x5c,           /* FC_PAD */
     };
-    static const unsigned char fmtstr_rpup_char[] =
+    static const unsigned char fmtstr_rpup_char_onstack_deref[] =
     {
-        0x11, 0x14,     /* FC_RP [alloced_on_stack] */
+        0x11, 0x14,     /* FC_RP [alloced_on_stack] [pointer_deref] */
         NdrFcShort( 0x2 ),      /* Offset= 2 (4) */
         0x12, 0x8,      /* FC_UP [simple_pointer] */
         0x2,            /* FC_CHAR */
         0x5c,           /* FC_PAD */
     };
-    static const unsigned char fmtstr_rpup_char2[] =
+    static const unsigned char fmtstr_rpup_char_onstack[] =
     {
         0x11, 0x04,     /* FC_RP [alloced_on_stack] */
         NdrFcShort( 0x2 ),      /* Offset= 2 (4) */
@@ -395,6 +561,14 @@ static void test_simple_types(void)
         0x2,            /* FC_CHAR */
         0x5c,           /* FC_PAD */
     };
+    static const unsigned char fmtstr_rpup_char_deref[] =
+    {
+        0x11, 0x10,     /* FC_RP [pointer_deref] */
+        NdrFcShort( 0x2 ),      /* Offset= 2 (4) */
+        0x12, 0x8,      /* FC_UP [simple_pointer] */
+        0x2,            /* FC_CHAR */
+        0x5c,           /* FC_PAD */
+    };
 
     static const unsigned char fmtstr_up_wchar[] =
     {
@@ -479,8 +653,9 @@ static void test_simple_types(void)
 
     test_pointer_marshal(fmtstr_rp_char, ch_ptr, 1, &ch, 1, NULL, 0, 
"rp_char");
 
-    test_pointer_marshal(fmtstr_rpup_char, &ch_ptr, 1, wiredata, 5, deref_cmp, 
1, "rpup_char");
-    test_pointer_marshal(fmtstr_rpup_char2, ch_ptr, 1, wiredata, 5, NULL, 0, 
"rpup_char2");
+    test_pointer_marshal(fmtstr_rpup_char_onstack_deref, &ch_ptr, 1, wiredata, 
5, deref_cmp, 1, "rpup_char_onstack_deref");
+    test_pointer_marshal(fmtstr_rpup_char_onstack, ch_ptr, 1, wiredata, 5, 
NULL, 0, "rpup_char_onstack");
+    test_pointer_marshal(fmtstr_rpup_char_deref, &ch_ptr, 1, wiredata, 5, 
deref_cmp, 1, "rpup_char_deref");
 
     s = 0xa597;
     if (use_pointer_ids)
@@ -611,10 +786,8 @@ static void test_nontrivial_pointer_types(void)
     my_alloc_called = 0;
     StubMsg.Buffer = StubMsg.BufferStart;
     NdrPointerUnmarshall( &StubMsg, &mem, &fmtstr_ref_unique_out[4], 1);
-    todo_wine {
-        ok(mem == mem_orig, "mem alloced\n");
-        ok(my_alloc_called == 0, "alloc called %d\n", my_alloc_called);
-    }
+    ok(mem == mem_orig, "mem alloced\n");
+    ok(my_alloc_called == 0, "alloc called %d\n", my_alloc_called);
 
     my_free_called = 0;
     StubMsg.Buffer = StubMsg.BufferStart;
@@ -639,43 +812,43 @@ static void test_nontrivial_pointer_types(void)
 
     /* Server */
     my_alloc_called = 0;
+    my_free_called = 0;
     StubMsg.IsClient = 0;
     mem = NULL;
     StubMsg.Buffer = StubMsg.BufferStart;
     NdrPointerUnmarshall( &StubMsg, &mem, &fmtstr_ref_unique_out[4], 0);
     ok(mem != StubMsg.BufferStart, "mem pointing at buffer\n");
-    todo_wine
     ok(my_alloc_called == 1, "alloc called %d\n", my_alloc_called);
     NdrPointerFree( &StubMsg, mem, &fmtstr_ref_unique_out[4] );
+    ok(my_free_called == 0, "free called %d\n", my_free_called);
+    my_free(mem);
 
     my_alloc_called = 0;
+    my_free_called = 0;
     mem = NULL;
     StubMsg.Buffer = StubMsg.BufferStart;
     NdrPointerUnmarshall( &StubMsg, &mem, &fmtstr_ref_unique_out[4], 1);
     ok(mem != StubMsg.BufferStart, "mem pointing at buffer\n");
-    todo_wine
     ok(my_alloc_called == 1, "alloc called %d\n", my_alloc_called);
     NdrPointerFree( &StubMsg, mem, &fmtstr_ref_unique_out[4] );
+    ok(my_free_called == 0, "free called %d\n", my_free_called);
+    my_free(mem);
 
     my_alloc_called = 0;
     mem = mem_orig;
     *(void **)mem = NULL;
     StubMsg.Buffer = StubMsg.BufferStart;
     NdrPointerUnmarshall( &StubMsg, &mem, &fmtstr_ref_unique_out[4], 0);
-    todo_wine {
-        ok(mem == mem_orig, "mem alloced\n");
-        ok(my_alloc_called == 0, "alloc called %d\n", my_alloc_called);
-    }
+    ok(mem == mem_orig, "mem alloced\n");
+    ok(my_alloc_called == 0, "alloc called %d\n", my_alloc_called);
 
     my_alloc_called = 0;
     mem = mem_orig;
     *(void **)mem = NULL;
     StubMsg.Buffer = StubMsg.BufferStart;
     NdrPointerUnmarshall( &StubMsg, &mem, &fmtstr_ref_unique_out[4], 1);
-    todo_wine {
-        ok(mem == mem_orig, "mem alloced\n");
-        ok(my_alloc_called == 0, "alloc called %d\n", my_alloc_called);
-    }
+    ok(mem == mem_orig, "mem alloced\n");
+    ok(my_alloc_called == 0, "alloc called %d\n", my_alloc_called);
 
     mem = my_alloc(sizeof(void *));
     *(void **)mem = NULL;
@@ -756,7 +929,10 @@ static void test_simple_struct_marshal(const unsigned char 
*formattypes,
     ok(!cmp(mem, memsrc, srcsize), "%s: incorrectly unmarshaled\n", msgpfx);
     ok(my_alloc_called == num_additional_allocs, "%s: my_alloc got called %d 
times\n", msgpfx, my_alloc_called); 
     my_alloc_called = 0;
+    my_free_called = 0;
     ok(StubMsg.MemorySize == 0, "%s: memorysize touched in unmarshal\n", 
msgpfx);
+    NdrSimpleStructFree(&StubMsg, mem, formattypes );
+    ok(my_free_called == num_additional_allocs, "free called %d\n", 
my_free_called);
 
     /* If we're a server we still use the supplied memory */
     StubMsg.Buffer = StubMsg.BufferStart;
@@ -767,7 +943,10 @@ static void test_simple_struct_marshal(const unsigned char 
*formattypes,
     ok(!cmp(mem, memsrc, srcsize), "%s: incorrectly unmarshaled\n", msgpfx); 
     ok(my_alloc_called == num_additional_allocs, "%s: my_alloc got called %d 
times\n", msgpfx, my_alloc_called);
     my_alloc_called = 0;
+    my_free_called = 0;
     ok(StubMsg.MemorySize == 0, "%s: memorysize touched in unmarshal\n", 
msgpfx);
+    NdrSimpleStructFree(&StubMsg, mem, formattypes );
+    ok(my_free_called == num_additional_allocs, "free called %d\n", 
my_free_called);
 
     /* ...unless we pass a NULL ptr, then the buffer is used. 
        Passing a NULL ptr while we're a client && !must_alloc
@@ -785,6 +964,8 @@ static void test_simple_struct_marshal(const unsigned char 
*formattypes,
         ok(my_alloc_called == num_additional_allocs, "%s: my_alloc got called 
%d times\n", msgpfx, my_alloc_called);
         my_alloc_called = 0;
         ok(StubMsg.MemorySize == 0, "%s: memorysize touched in unmarshal\n", 
msgpfx);
+        NdrSimpleStructFree(&StubMsg, mem, formattypes );
+        ok(my_free_called == num_additional_allocs, "free called %d\n", 
my_free_called);
     }
 
     /*** now must_alloc is true ***/
@@ -800,7 +981,11 @@ static void test_simple_struct_marshal(const unsigned char 
*formattypes,
     ok(!cmp(mem, memsrc, srcsize), "incorrectly unmarshaled\n");
     ok(my_alloc_called == num_additional_allocs + 1, "%s: my_alloc got called 
%d times\n", msgpfx, my_alloc_called);
     my_alloc_called = 0;
+    my_free_called = 0;
     ok(StubMsg.MemorySize == 0, "memorysize touched in unmarshal\n");
+    NdrSimpleStructFree(&StubMsg, mem, formattypes );
+    ok(my_free_called == num_additional_allocs, "free called %d\n", 
my_free_called);
+    my_free(mem);
 
     mem = NULL;
     StubMsg.Buffer = StubMsg.BufferStart;
@@ -809,8 +994,12 @@ static void test_simple_struct_marshal(const unsigned char 
*formattypes,
     ok(mem != mem_orig, "mem not changed %p %p\n", mem, mem_orig);
     ok(!cmp(mem, memsrc, srcsize), "incorrectly unmarshaled\n");
     ok(my_alloc_called == num_additional_allocs + 1, "%s: my_alloc got called 
%d times\n", msgpfx, my_alloc_called);
-    my_alloc_called = 0; 
+    my_alloc_called = 0;
+    my_free_called = 0;
     ok(StubMsg.MemorySize == 0, "memorysize touched in unmarshal\n");
+    NdrSimpleStructFree(&StubMsg, mem, formattypes );
+    ok(my_free_called == num_additional_allocs, "free called %d\n", 
my_free_called);
+    my_free(mem);
 
     mem = mem_orig;
     StubMsg.Buffer = StubMsg.BufferStart;
@@ -823,7 +1012,11 @@ static void test_simple_struct_marshal(const unsigned 
char *formattypes,
     ok(!cmp(mem, memsrc, srcsize), "incorrectly unmarshaled\n");
     ok(my_alloc_called == num_additional_allocs + 1, "%s: my_alloc got called 
%d times\n", msgpfx, my_alloc_called);
     my_alloc_called = 0;
+    my_free_called = 0;
     ok(StubMsg.MemorySize == 0, "memorysize touched in unmarshal\n");
+    NdrSimpleStructFree(&StubMsg, mem, formattypes );
+    ok(my_free_called == num_additional_allocs, "free called %d\n", 
my_free_called);
+    my_free(mem);
 
     mem = NULL;
     StubMsg.Buffer = StubMsg.BufferStart;
@@ -835,7 +1028,11 @@ static void test_simple_struct_marshal(const unsigned 
char *formattypes,
     ok(!cmp(mem, memsrc, srcsize), "incorrectly unmarshaled\n"); 
     ok(my_alloc_called == num_additional_allocs + 1, "%s: my_alloc got called 
%d times\n", msgpfx, my_alloc_called);
     my_alloc_called = 0;
+    my_free_called = 0;
     ok(StubMsg.MemorySize == 0, "memorysize touched in unmarshal\n");
+    NdrSimpleStructFree(&StubMsg, mem, formattypes );
+    ok(my_free_called == num_additional_allocs, "free called %d\n", 
my_free_called);
+    my_free(mem);
 
     HeapFree(GetProcessHeap(), 0, mem_orig);
     HeapFree(GetProcessHeap(), 0, StubMsg.BufferStart);
@@ -1015,6 +1212,341 @@ static void test_simple_struct(void)
     test_pointer_marshal(fmtstr_pointer_struct, &ps1, 17, wiredata, 21, 
ps1_cmp, 2, "pointer_struct");
 }
 
+struct aligned
+{
+    int a;
+    LONGLONG b;
+};
+
+static void test_struct_align(void)
+{
+    RPC_MESSAGE RpcMessage;
+    MIDL_STUB_MESSAGE StubMsg;
+    MIDL_STUB_DESC StubDesc;
+    void *ptr;
+    struct aligned *memsrc_orig, *memsrc, *mem;
+
+    /* force bogus struct so that members are marshalled individually */
+    static const unsigned char fmtstr[] =
+    {
+        0x1a,   /* FC_BOGUS_STRUCT */
+        0x7,    /* alignment 8 */
+        NdrFcShort(0x10),   /* memory size 16 */
+        NdrFcShort(0x0),
+        NdrFcShort(0x0),
+        0x08,   /* FC_LONG */
+        0x39,   /* FC_ALIGNM8 */
+        0x0b,   /* FC_HYPER */
+        0x5b,   /* FC_END */
+    };
+
+    memsrc_orig = heap_alloc_zero(sizeof(struct aligned) + 8);
+    /* intentionally mis-align memsrc */
+    memsrc = (struct aligned *)((((ULONG_PTR)memsrc_orig + 7) & ~7) + 4);
+
+    memsrc->a = 0xdeadbeef;
+    memsrc->b = ((ULONGLONG) 0xbadefeed << 32) | 0x2468ace0;
+
+    StubDesc = Object_StubDesc;
+    StubDesc.pFormatTypes = fmtstr;
+    NdrClientInitializeNew(&RpcMessage, &StubMsg, &StubDesc, 0);
+
+    StubMsg.BufferLength = 0;
+    NdrComplexStructBufferSize(&StubMsg, (unsigned char *)memsrc, fmtstr);
+
+    StubMsg.RpcMsg->Buffer = StubMsg.BufferStart = StubMsg.Buffer = 
heap_alloc(StubMsg.BufferLength);
+    StubMsg.BufferEnd = StubMsg.BufferStart + StubMsg.BufferLength;
+
+    ptr = NdrComplexStructMarshall(&StubMsg, (unsigned char *)memsrc, fmtstr);
+    ok(ptr == NULL, "ret %p\n", ptr);
+
+    /* Server */
+    StubMsg.IsClient = 0;
+    mem = NULL;
+    StubMsg.Buffer = StubMsg.BufferStart;
+    ptr = NdrComplexStructUnmarshall(&StubMsg, (unsigned char **)&mem, fmtstr, 
0);
+    ok(ptr == NULL, "ret %p\n", ptr);
+    ok(!memcmp(mem, memsrc, sizeof(*memsrc)), "struct wasn't unmarshalled 
correctly\n");
+    StubMsg.pfnFree(mem);
+
+    heap_free(StubMsg.RpcMsg->Buffer);
+    heap_free(memsrc_orig);
+}
+
+struct testiface
+{
+    IPersist IPersist_iface;
+    LONG ref;
+};
+
+static struct testiface *impl_from_IPersist(IPersist *iface)
+{
+    return CONTAINING_RECORD(iface, struct testiface, IPersist_iface);
+}
+
+static HRESULT WINAPI test_persist_QueryInterface(IPersist *iface, REFIID iid, 
void **out)
+{
+    if (IsEqualGUID(iid, &IID_IUnknown) || IsEqualGUID(iid, &IID_IPersist))
+    {
+        *out = iface;
+        IPersist_AddRef(iface);
+        return S_OK;
+    }
+
+    *out = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI test_persist_AddRef(IPersist *iface)
+{
+    struct testiface *unk = impl_from_IPersist(iface);
+    return ++unk->ref;
+}
+
+static ULONG WINAPI test_persist_Release(IPersist *iface)
+{
+    struct testiface *unk = impl_from_IPersist(iface);
+    return --unk->ref;
+}
+
+static HRESULT WINAPI test_persist_GetClassId(IPersist *iface, GUID *clsid)
+{
+    *clsid = IID_IPersist;
+    return S_OK;
+}
+
+static IPersistVtbl testiface_vtbl = {
+    test_persist_QueryInterface,
+    test_persist_AddRef,
+    test_persist_Release,
+    test_persist_GetClassId,
+};
+
+static void test_iface_ptr(void)
+{
+    struct testiface server_obj = {{&testiface_vtbl}, 1};
+    struct testiface client_obj = {{&testiface_vtbl}, 1};
+
+    MIDL_STUB_MESSAGE StubMsg;
+    MIDL_STUB_DESC StubDesc;
+    RPC_MESSAGE RpcMessage;
+    IPersist *proxy;
+    HRESULT hr;
+    GUID clsid;
+    void *ptr;
+    LONG ref;
+
+    static const unsigned char fmtstr_ip[] =
+    {
+        FC_IP,
+        FC_CONSTANT_IID,
+        NdrFcLong(0x0000010c),
+        NdrFcShort(0x0000),
+        NdrFcShort(0x0000),
+        0xc0,
+        0x00,
+        0x00,
+        0x00,
+        0x00,
+        0x00,
+        0x00,
+        0x46,
+    };
+
+    CoInitialize(NULL);
+
+    StubDesc = Object_StubDesc;
+    StubDesc.pFormatTypes = fmtstr_ip;
+
+    NdrClientInitializeNew(&RpcMessage, &StubMsg, &StubDesc, 0);
+    StubMsg.BufferLength = 0;
+    NdrInterfacePointerBufferSize(&StubMsg, (unsigned char 
*)&client_obj.IPersist_iface, fmtstr_ip);
+
+    StubMsg.RpcMsg->Buffer = StubMsg.BufferStart = StubMsg.Buffer = 
HeapAlloc(GetProcessHeap(), 0, StubMsg.BufferLength);
+    StubMsg.BufferEnd = StubMsg.BufferStart + StubMsg.BufferLength;
+
+    /* server -> client */
+
+    StubMsg.IsClient = 0;
+    my_alloc_called = my_free_called = 0;
+    StubMsg.Buffer = StubMsg.BufferStart;
+    IPersist_AddRef(&server_obj.IPersist_iface);
+    ptr = NdrInterfacePointerMarshall(&StubMsg, (unsigned char 
*)&server_obj.IPersist_iface, fmtstr_ip);
+    ok(!ptr, "ret %p\n", ptr);
+    ok(server_obj.ref > 2, "got %d references\n", server_obj.ref);
+    ok(!my_alloc_called, "alloc called %d\n", my_alloc_called);
+    ok(!my_free_called, "free called %d\n", my_free_called);
+
+    NdrInterfacePointerFree(&StubMsg, (unsigned char 
*)&server_obj.IPersist_iface, fmtstr_ip);
+    ok(server_obj.ref > 1, "got %d references\n", server_obj.ref);
+
+    StubMsg.IsClient = 1;
+    my_alloc_called = my_free_called = 0;
+    StubMsg.Buffer = StubMsg.BufferStart;
+    proxy = NULL;
+    ptr = NdrInterfacePointerUnmarshall(&StubMsg, (unsigned char **)&proxy, 
fmtstr_ip, 0);
+    ok(!ptr, "ret %p\n", ptr);
+    ok(!!proxy, "mem not alloced\n");
+    ok(!my_alloc_called, "alloc called %d\n", my_alloc_called);
+    ok(!my_free_called, "free called %d\n", my_free_called);
+    ok(server_obj.ref > 1, "got %d references\n", server_obj.ref);
+
+    hr = IPersist_GetClassID(proxy, &clsid);
+    ok(hr == S_OK, "got hr %#x\n", hr);
+    ok(IsEqualGUID(&clsid, &IID_IPersist), "wrong clsid %s\n", 
wine_dbgstr_guid(&clsid));
+
+    ref = IPersist_Release(proxy);
+    ok(ref == 1, "got %d references\n", ref);
+    ok(server_obj.ref == 1, "got %d references\n", server_obj.ref);
+
+    /* An existing interface pointer is released; this is necessary so that an
+     * [in, out] pointer which changes does not leak references. */
+
+    StubMsg.IsClient = 0;
+    my_alloc_called = my_free_called = 0;
+    StubMsg.Buffer = StubMsg.BufferStart;
+    IPersist_AddRef(&server_obj.IPersist_iface);
+    ptr = NdrInterfacePointerMarshall(&StubMsg, (unsigned char 
*)&server_obj.IPersist_iface, fmtstr_ip);
+    ok(!ptr, "ret %p\n", ptr);
+    ok(server_obj.ref > 2, "got %d references\n", server_obj.ref);
+    ok(!my_alloc_called, "alloc called %d\n", my_alloc_called);
+    ok(!my_free_called, "free called %d\n", my_free_called);
+
+    NdrInterfacePointerFree(&StubMsg, (unsigned char 
*)&server_obj.IPersist_iface, fmtstr_ip);
+    ok(server_obj.ref > 1, "got %d references\n", server_obj.ref);
+
+    StubMsg.IsClient = 1;
+    my_alloc_called = my_free_called = 0;
+    StubMsg.Buffer = StubMsg.BufferStart;
+    proxy = &client_obj.IPersist_iface;
+    IPersist_AddRef(proxy);
+    ptr = NdrInterfacePointerUnmarshall(&StubMsg, (unsigned char **)&proxy, 
fmtstr_ip, 0);
+    ok(!ptr, "ret %p\n", ptr);
+    ok(!!proxy && proxy != &client_obj.IPersist_iface, "mem not alloced\n");
+    ok(!my_alloc_called, "alloc called %d\n", my_alloc_called);
+    ok(!my_free_called, "free called %d\n", my_free_called);
+    ok(server_obj.ref > 1, "got %d references\n", server_obj.ref);
+    ok(client_obj.ref == 1, "got %d references\n", client_obj.ref);
+
+    hr = IPersist_GetClassID(proxy, &clsid);
+    ok(hr == S_OK, "got hr %#x\n", hr);
+    ok(IsEqualGUID(&clsid, &IID_IPersist), "wrong clsid %s\n", 
wine_dbgstr_guid(&clsid));
+
+    ref = IPersist_Release(proxy);
+    ok(ref == 1, "got %d references\n", ref);
+    ok(server_obj.ref == 1, "got %d references\n", server_obj.ref);
+
+    /* client -> server */
+
+    StubMsg.IsClient = 1;
+    my_alloc_called = my_free_called = 0;
+    StubMsg.Buffer = StubMsg.BufferStart;
+    IPersist_AddRef(&client_obj.IPersist_iface);
+    ptr = NdrInterfacePointerMarshall(&StubMsg, (unsigned char 
*)&client_obj.IPersist_iface, fmtstr_ip);
+    ok(!ptr, "ret %p\n", ptr);
+    ok(client_obj.ref > 2, "got %d references\n", client_obj.ref);
+    ok(!my_alloc_called, "alloc called %d\n", my_alloc_called);
+    ok(!my_free_called, "free called %d\n", my_free_called);
+
+    StubMsg.IsClient = 0;
+    my_alloc_called = my_free_called = 0;
+    StubMsg.Buffer = StubMsg.BufferStart;
+    proxy = NULL;
+    ptr = NdrInterfacePointerUnmarshall(&StubMsg, (unsigned char **)&proxy, 
fmtstr_ip, 0);
+    ok(!ptr, "ret %p\n", ptr);
+    ok(!!proxy, "mem not alloced\n");
+    ok(!my_alloc_called, "alloc called %d\n", my_alloc_called);
+    ok(!my_free_called, "free called %d\n", my_free_called);
+    ok(client_obj.ref > 2, "got %d references\n", client_obj.ref);
+
+    hr = IPersist_GetClassID(proxy, &clsid);
+    ok(hr == S_OK, "got hr %#x\n", hr);
+    ok(IsEqualGUID(&clsid, &IID_IPersist), "wrong clsid %s\n", 
wine_dbgstr_guid(&clsid));
+
+    ref = IPersist_Release(proxy);
+    ok(client_obj.ref > 1, "got %d references\n", client_obj.ref);
+    ok(ref == client_obj.ref, "expected %d references, got %d\n", 
client_obj.ref, ref);
+
+    NdrInterfacePointerFree(&StubMsg, (unsigned char *)proxy, fmtstr_ip);
+    ok(client_obj.ref == 1, "got %d references\n", client_obj.ref);
+
+    /* same, but free the interface after calling NdrInterfacePointerFree */
+
+    StubMsg.IsClient = 1;
+    my_alloc_called = my_free_called = 0;
+    StubMsg.Buffer = StubMsg.BufferStart;
+    IPersist_AddRef(&client_obj.IPersist_iface);
+    ptr = NdrInterfacePointerMarshall(&StubMsg, (unsigned char 
*)&client_obj.IPersist_iface, fmtstr_ip);
+    ok(!ptr, "ret %p\n", ptr);
+    ok(client_obj.ref > 2, "got %d references\n", client_obj.ref);
+    ok(!my_alloc_called, "alloc called %d\n", my_alloc_called);
+    ok(!my_free_called, "free called %d\n", my_free_called);
+
+    StubMsg.IsClient = 0;
+    my_alloc_called = my_free_called = 0;
+    StubMsg.Buffer = StubMsg.BufferStart;
+    proxy = NULL;
+    ptr = NdrInterfacePointerUnmarshall(&StubMsg, (unsigned char **)&proxy, 
fmtstr_ip, 0);
+    ok(!ptr, "ret %p\n", ptr);
+    ok(!!proxy, "mem not alloced\n");
+    ok(!my_alloc_called, "alloc called %d\n", my_alloc_called);
+    ok(!my_free_called, "free called %d\n", my_free_called);
+    ok(client_obj.ref > 2, "got %d references\n", client_obj.ref);
+
+    NdrInterfacePointerFree(&StubMsg, (unsigned char *)proxy, fmtstr_ip);
+    ok(client_obj.ref > 1, "got %d references\n", client_obj.ref);
+
+    hr = IPersist_GetClassID(proxy, &clsid);
+    ok(hr == S_OK, "got hr %#x\n", hr);
+    ok(IsEqualGUID(&clsid, &IID_IPersist), "wrong clsid %s\n", 
wine_dbgstr_guid(&clsid));
+
+    ref = IPersist_Release(proxy);
+    ok(ref == 1, "got %d references\n", ref);
+    ok(client_obj.ref == 1, "got %d references\n", client_obj.ref);
+
+    /* An existing interface pointer is *not* released (in fact, it is ignored
+     * and may be invalid). In practice it will always be NULL anyway. */
+
+    StubMsg.IsClient = 1;
+    my_alloc_called = my_free_called = 0;
+    StubMsg.Buffer = StubMsg.BufferStart;
+    IPersist_AddRef(&client_obj.IPersist_iface);
+    ptr = NdrInterfacePointerMarshall(&StubMsg, (unsigned char 
*)&client_obj.IPersist_iface, fmtstr_ip);
+    ok(!ptr, "ret %p\n", ptr);
+    ok(client_obj.ref > 2, "got %d references\n", client_obj.ref);
+    ok(!my_alloc_called, "alloc called %d\n", my_alloc_called);
+    ok(!my_free_called, "free called %d\n", my_free_called);
+
+    StubMsg.IsClient = 0;
+    my_alloc_called = my_free_called = 0;
+    StubMsg.Buffer = StubMsg.BufferStart;
+    proxy = &server_obj.IPersist_iface;
+    IPersist_AddRef(proxy);
+    ptr = NdrInterfacePointerUnmarshall(&StubMsg, (unsigned char **)&proxy, 
fmtstr_ip, 0);
+    ok(!ptr, "ret %p\n", ptr);
+    ok(!!proxy && proxy != &server_obj.IPersist_iface, "mem not alloced\n");
+    ok(!my_alloc_called, "alloc called %d\n", my_alloc_called);
+    ok(!my_free_called, "free called %d\n", my_free_called);
+    ok(client_obj.ref > 2, "got %d references\n", client_obj.ref);
+    ok(server_obj.ref == 2, "got %d references\n", server_obj.ref);
+    IPersist_Release(&server_obj.IPersist_iface);
+
+    hr = IPersist_GetClassID(proxy, &clsid);
+    ok(hr == S_OK, "got hr %#x\n", hr);
+    ok(IsEqualGUID(&clsid, &IID_IPersist), "wrong clsid %s\n", 
wine_dbgstr_guid(&clsid));
+
+    ref = IPersist_Release(proxy);
+    ok(client_obj.ref > 1, "got %d references\n", client_obj.ref);
+    ok(ref == client_obj.ref, "expected %d references, got %d\n", 
client_obj.ref, ref);
+
+    NdrInterfacePointerFree(&StubMsg, (unsigned char *)proxy, fmtstr_ip);
+    ok(client_obj.ref == 1, "got %d references\n", client_obj.ref);
+
+    HeapFree(GetProcessHeap(), 0, StubMsg.BufferStart);
+
+    CoUninitialize();
+}
+
 static void test_fullpointer_xlat(void)
 {
     PFULL_PTR_XLAT_TABLES pXlatTables;
@@ -1373,6 +1905,8 @@ static void test_ndr_allocate(void)
         else win_skip("v1 mem list format\n");
     }
     /* NdrFree isn't exported so we can't test free'ing */
+    my_free(p1);
+    my_free(p2);
 }
 
 static void test_conformant_array(void)
@@ -1555,10 +2089,8 @@ static void test_conformant_string(void)
     my_alloc_called = 0;
     StubMsg.Buffer = StubMsg.BufferStart;
     NdrPointerUnmarshall( &StubMsg, &mem, fmtstr_conf_str, 1);
-todo_wine {
     ok(mem == mem_orig, "mem not alloced\n");
     ok(my_alloc_called == 0, "alloc called %d\n", my_alloc_called);
-}
 
     /* Prevent a memory leak when running with Wine.
        Remove once the todo_wine block above is fixed. */
@@ -1590,11 +2122,9 @@ todo_wine {
     mem = NULL;
     StubMsg.Buffer = StubMsg.BufferStart;
     NdrPointerUnmarshall( &StubMsg, &mem, fmtstr_conf_str, 1);
-todo_wine {
     ok(mem == StubMsg.BufferStart + 12 || broken(!mem), /* win9x, nt4 */
        "mem not pointing at buffer %p/%p\n", mem, StubMsg.BufferStart + 12 );
     ok(my_alloc_called == 0, "alloc called %d\n", my_alloc_called);
-}
 
     my_alloc_called = 0;
     mem = mem_orig = HeapAlloc(GetProcessHeap(), 0, sizeof(memsrc));
@@ -1608,11 +2138,9 @@ todo_wine {
     mem = mem_orig;
     StubMsg.Buffer = StubMsg.BufferStart;
     NdrPointerUnmarshall( &StubMsg, &mem, fmtstr_conf_str, 1);
-todo_wine {
     ok(mem == StubMsg.BufferStart + 12 || broken(!mem), /* win9x, nt4 */
        "mem not pointing at buffer %p/%p\n", mem, StubMsg.BufferStart + 12 );
     ok(my_alloc_called == 0, "alloc called %d\n", my_alloc_called);
-}
 
     mem = my_alloc(10);
     my_free_called = 0;
@@ -2002,11 +2530,6 @@ static void test_conf_complex_array(void)
 #endif
 
     expected_length = (4 + memsrc.dim1 * (2 + memsrc.dim2)) * 4;
-    if (StubMsg.BufferLength == 96)
-    {
-        win_skip("Tests crash on Win9x, WinMe and NT4\n");
-        goto cleanup;
-    }
     ok(StubMsg.BufferLength >= expected_length, "length %d\n", 
StubMsg.BufferLength);
 
     /*NdrGetBuffer(&_StubMsg, _StubMsg.BufferLength, NULL);*/
@@ -2076,7 +2599,6 @@ static void test_conf_complex_array(void)
 
     HeapFree(GetProcessHeap(), 0, StubMsg.RpcMsg->Buffer);
 
-cleanup:
     for(i = 0; i < memsrc.dim1; i++)
         HeapFree(GetProcessHeap(), 0, memsrc.array[i]);
     HeapFree(GetProcessHeap(), 0, memsrc.array);
@@ -2212,14 +2734,6 @@ static void test_NdrGetUserMarshalInfo(void)
     unsigned char buffer[16];
     void *rpc_channel_buffer = (void *)(ULONG_PTR)0xcafebabe;
     RPC_MESSAGE rpc_msg;
-    RPC_STATUS (RPC_ENTRY *pNdrGetUserMarshalInfo)(ULONG 
*,ULONG,NDR_USER_MARSHAL_INFO *);
-
-    pNdrGetUserMarshalInfo = (void 
*)GetProcAddress(GetModuleHandleA("rpcrt4.dll"), "NdrGetUserMarshalInfo");
-    if (!pNdrGetUserMarshalInfo)
-    {
-        skip("NdrGetUserMarshalInfo not exported\n");
-        return;
-    }
 
     /* unmarshall */
 
@@ -2247,7 +2761,7 @@ static void test_NdrGetUserMarshalInfo(void)
 
     memset(&umi, 0xaa, sizeof(umi));
 
-    status = pNdrGetUserMarshalInfo(&umcb.Flags, 1, &umi);
+    status = NdrGetUserMarshalInfo(&umcb.Flags, 1, &umi);
     ok(status == RPC_S_OK, "NdrGetUserMarshalInfo failed with error %d\n", 
status);
     ok( umi.InformationLevel == 1,
        "umi.InformationLevel was %u instead of 1\n",
@@ -2281,7 +2795,7 @@ static void test_NdrGetUserMarshalInfo(void)
 
     memset(&umi, 0xaa, sizeof(umi));
 
-    status = pNdrGetUserMarshalInfo(&umcb.Flags, 1, &umi);
+    status = NdrGetUserMarshalInfo(&umcb.Flags, 1, &umi);
     ok(status == RPC_S_OK, "NdrGetUserMarshalInfo failed with error %d\n", 
status);
     ok( umi.InformationLevel == 1,
        "umi.InformationLevel was %u instead of 1\n",
@@ -2315,7 +2829,7 @@ static void test_NdrGetUserMarshalInfo(void)
 
     memset(&umi, 0xaa, sizeof(umi));
 
-    status = pNdrGetUserMarshalInfo(&umcb.Flags, 1, &umi);
+    status = NdrGetUserMarshalInfo(&umcb.Flags, 1, &umi);
     ok(status == RPC_S_OK, "NdrGetUserMarshalInfo failed with error %d\n", 
status);
     ok( umi.InformationLevel == 1,
        "umi.InformationLevel was %u instead of 1\n",
@@ -2349,7 +2863,7 @@ static void test_NdrGetUserMarshalInfo(void)
 
     memset(&umi, 0xaa, sizeof(umi));
 
-    status = pNdrGetUserMarshalInfo(&umcb.Flags, 1, &umi);
+    status = NdrGetUserMarshalInfo(&umcb.Flags, 1, &umi);
     ok(status == RPC_S_OK, "NdrGetUserMarshalInfo failed with error %d\n", 
status);
     ok( umi.InformationLevel == 1,
        "umi.InformationLevel was %u instead of 1\n",
@@ -2381,7 +2895,7 @@ static void test_NdrGetUserMarshalInfo(void)
 
     umcb.CBType = USER_MARSHAL_CB_MARSHALL;
 
-    status = pNdrGetUserMarshalInfo(&umcb.Flags, 1, &umi);
+    status = NdrGetUserMarshalInfo(&umcb.Flags, 1, &umi);
     ok(status == RPC_S_OK, "NdrGetUserMarshalInfo failed with error %d\n", 
status);
     ok( U1(umi).Level1.BufferSize == 0,
        "umi.Level1.BufferSize was %u instead of 0\n",
@@ -2390,22 +2904,22 @@ static void test_NdrGetUserMarshalInfo(void)
     /* error conditions */
 
     rpc_msg.BufferLength = 14;
-    status = pNdrGetUserMarshalInfo(&umcb.Flags, 1, &umi);
+    status = NdrGetUserMarshalInfo(&umcb.Flags, 1, &umi);
     ok(status == ERROR_INVALID_USER_BUFFER,
         "NdrGetUserMarshalInfo should have failed with 
ERROR_INVALID_USER_BUFFER instead of %d\n", status);
 
     rpc_msg.BufferLength = 15;
-    status = pNdrGetUserMarshalInfo(&umcb.Flags, 9999, &umi);
+    status = NdrGetUserMarshalInfo(&umcb.Flags, 9999, &umi);
     ok(status == RPC_S_INVALID_ARG,
         "NdrGetUserMarshalInfo should have failed with RPC_S_INVALID_ARG 
instead of %d\n", status);
 
     umcb.CBType = 9999;
-    status = pNdrGetUserMarshalInfo(&umcb.Flags, 1, &umi);
+    status = NdrGetUserMarshalInfo(&umcb.Flags, 1, &umi);
     ok(status == RPC_S_OK, "NdrGetUserMarshalInfo failed with error %d\n", 
status);
 
     umcb.CBType = USER_MARSHAL_CB_MARSHALL;
     umcb.Signature = 0;
-    status = pNdrGetUserMarshalInfo(&umcb.Flags, 1, &umi);
+    status = NdrGetUserMarshalInfo(&umcb.Flags, 1, &umi);
     ok(status == RPC_S_INVALID_ARG,
         "NdrGetUserMarshalInfo should have failed with RPC_S_INVALID_ARG 
instead of %d\n", status);
 }
@@ -2497,6 +3011,8 @@ START_TEST( ndr_marshall )
     test_simple_types();
     test_nontrivial_pointer_types();
     test_simple_struct();
+    test_struct_align();
+    test_iface_ptr();
     test_fullpointer_xlat();
     test_client_init();
     test_server_init();
diff --git a/modules/rostests/winetests/rpcrt4/rpc.c 
b/modules/rostests/winetests/rpcrt4/rpc.c
index f026e99de2..d3a3aee6c8 100644
--- a/modules/rostests/winetests/rpcrt4/rpc.c
+++ b/modules/rostests/winetests/rpcrt4/rpc.c
@@ -149,10 +149,8 @@ static void TestDceErrorInqText (void)
                          */
     DWORD dwCount;
 
-    dwCount = FormatMessageA (FORMAT_MESSAGE_FROM_SYSTEM | 
-              FORMAT_MESSAGE_IGNORE_INSERTS,
-              NULL, RPC_S_NOT_RPC_ERROR, 0, bufferInvalid,
-              sizeof(bufferInvalid)/sizeof(bufferInvalid[0]), NULL);
+    dwCount = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | 
FORMAT_MESSAGE_IGNORE_INSERTS, NULL,
+            RPC_S_NOT_RPC_ERROR, 0, bufferInvalid, ARRAY_SIZE(bufferInvalid), 
NULL);
 
     /* A random sample of DceErrorInqText */
     /* 0 is success */
@@ -651,15 +649,15 @@ static void test_RpcStringBindingParseA(void)
     ok(options == NULL, "options was %p instead of NULL\n", options);
 }
 
-static void test_I_RpcExceptionFilter(void)
+static void test_RpcExceptionFilter(const char *func_name)
 {
     ULONG exception;
     int retval;
-    int (WINAPI *pI_RpcExceptionFilter)(ULONG) = (void 
*)GetProcAddress(GetModuleHandleA("rpcrt4.dll"), "I_RpcExceptionFilter");
+    int (WINAPI *pRpcExceptionFilter)(ULONG) = (void 
*)GetProcAddress(GetModuleHandleA("rpcrt4.dll"), func_name);
 
-    if (!pI_RpcExceptionFilter)
+    if (!pRpcExceptionFilter)
     {
-        win_skip("I_RpcExceptionFilter not exported\n");
+        win_skip("%s not exported\n", func_name);
         return;
     }
 
@@ -670,7 +668,7 @@ static void test_I_RpcExceptionFilter(void)
         if (exception == 0x40000005) exception = 0x80000000;
         if (exception == 0x80000005) exception = 0xc0000000;
 
-        retval = pI_RpcExceptionFilter(exception);
+        retval = pRpcExceptionFilter(exception);
         switch (exception)
         {
         case STATUS_DATATYPE_MISALIGNMENT:
@@ -681,17 +679,17 @@ static void test_I_RpcExceptionFilter(void)
         case STATUS_INSTRUCTION_MISALIGNMENT:
         case STATUS_STACK_OVERFLOW:
         case STATUS_POSSIBLE_DEADLOCK:
-            ok(retval == EXCEPTION_CONTINUE_SEARCH, 
"I_RpcExceptionFilter(0x%x) should have returned %d instead of %d\n",
-               exception, EXCEPTION_CONTINUE_SEARCH, retval);
+            ok(retval == EXCEPTION_CONTINUE_SEARCH, "%s(0x%x) should have 
returned %d instead of %d\n",
+               func_name, exception, EXCEPTION_CONTINUE_SEARCH, retval);
             break;
         case STATUS_GUARD_PAGE_VIOLATION:
         case STATUS_IN_PAGE_ERROR:
         case STATUS_HANDLE_NOT_CLOSABLE:
-            trace("I_RpcExceptionFilter(0x%x) returned %d\n", exception, 
retval);
+            trace("%s(0x%x) returned %d\n", func_name, exception, retval);
             break;
         default:
-            ok(retval == EXCEPTION_EXECUTE_HANDLER, 
"I_RpcExceptionFilter(0x%x) should have returned %d instead of %d\n",
-               exception, EXCEPTION_EXECUTE_HANDLER, retval);
+            ok(retval == EXCEPTION_EXECUTE_HANDLER, "%s(0x%x) should have 
returned %d instead of %d\n",
+               func_name, exception, EXCEPTION_EXECUTE_HANDLER, retval);
         }
     }
 }
@@ -1200,7 +1198,8 @@ START_TEST( rpc )
     test_towers();
     test_I_RpcMapWin32Status();
     test_RpcStringBindingParseA();
-    test_I_RpcExceptionFilter();
+    test_RpcExceptionFilter("I_RpcExceptionFilter");
+    test_RpcExceptionFilter("RpcExceptionFilter");
     test_RpcStringBindingFromBinding();
     test_UuidCreate();
     test_UuidCreateSequential();
diff --git a/modules/rostests/winetests/rpcrt4/rpc_async.c 
b/modules/rostests/winetests/rpcrt4/rpc_async.c
index 555c58f102..15c57fefb5 100644
--- a/modules/rostests/winetests/rpcrt4/rpc_async.c
+++ b/modules/rostests/winetests/rpcrt4/rpc_async.c
@@ -25,9 +25,6 @@
 #include <rpc.h>
 #include <rpcasync.h>
 
-static RPC_STATUS (RPC_ENTRY 
*pRpcAsyncInitializeHandle)(PRPC_ASYNC_STATE,unsigned int);
-static RPC_STATUS (RPC_ENTRY *pRpcAsyncGetCallStatus)(PRPC_ASYNC_STATE);
-
 static void test_RpcAsyncInitializeHandle(void)
 {
     char buffer[256];
@@ -36,15 +33,15 @@ static void test_RpcAsyncInitializeHandle(void)
     int i;
     void *unset_ptr;
 
-    status = pRpcAsyncInitializeHandle((PRPC_ASYNC_STATE)buffer, 
sizeof(buffer));
+    status = RpcAsyncInitializeHandle((PRPC_ASYNC_STATE)buffer, 
sizeof(buffer));
     ok(status == ERROR_INVALID_PARAMETER, "RpcAsyncInitializeHandle with large 
Size should have returned ERROR_INVALID_PARAMETER instead of %d\n", status);
 
-    status = pRpcAsyncInitializeHandle(&async, sizeof(async) - 1);
+    status = RpcAsyncInitializeHandle(&async, sizeof(async) - 1);
     ok(status == ERROR_INVALID_PARAMETER, "RpcAsyncInitializeHandle with small 
Size should have returned ERROR_INVALID_PARAMETER instead of %d\n", status);
 
     memset(&async, 0xcc, sizeof(async));
     memset(&unset_ptr, 0xcc, sizeof(unset_ptr));
-    status = pRpcAsyncInitializeHandle(&async, sizeof(async));
+    status = RpcAsyncInitializeHandle(&async, sizeof(async));
     ok(status == RPC_S_OK, "RpcAsyncInitializeHandle failed with error %d\n", 
status);
 
     ok(async.Size == sizeof(async), "async.Size wrong: %d\n", async.Size);
@@ -65,29 +62,21 @@ static void test_RpcAsyncGetCallStatus(void)
     RPC_ASYNC_STATE async;
     RPC_STATUS status;
 
-    status = pRpcAsyncInitializeHandle(&async, sizeof(async));
+    status = RpcAsyncInitializeHandle(&async, sizeof(async));
     ok(status == RPC_S_OK, "RpcAsyncInitializeHandle failed with error %d\n", 
status);
 
-    status = pRpcAsyncGetCallStatus(&async);
+    status = RpcAsyncGetCallStatus(&async);
     todo_wine
     ok(status == RPC_S_INVALID_BINDING, "RpcAsyncGetCallStatus should have 
returned RPC_S_INVALID_BINDING instead of %d\n", status);
 
     memset(&async, 0, sizeof(async));
-    status = pRpcAsyncGetCallStatus(&async);
+    status = RpcAsyncGetCallStatus(&async);
     todo_wine
     ok(status == RPC_S_INVALID_BINDING, "RpcAsyncGetCallStatus should have 
returned RPC_S_INVALID_BINDING instead of %d\n", status);
 }
 
 START_TEST( rpc_async )
 {
-    HMODULE hRpcRt4 = GetModuleHandleA("rpcrt4.dll");
-    pRpcAsyncInitializeHandle = (void *)GetProcAddress(hRpcRt4, 
"RpcAsyncInitializeHandle");
-    pRpcAsyncGetCallStatus = (void *)GetProcAddress(hRpcRt4, 
"RpcAsyncGetCallStatus");
-    if (!pRpcAsyncInitializeHandle || !pRpcAsyncGetCallStatus)
-    {
-        win_skip("asynchronous functions not available\n");
-        return;
-    }
     test_RpcAsyncInitializeHandle();
     test_RpcAsyncGetCallStatus();
 }
diff --git a/modules/rostests/winetests/rpcrt4/server.c 
b/modules/rostests/winetests/rpcrt4/server.c
index 96cea2c3f6..2a66a38aab 100644
--- a/modules/rostests/winetests/rpcrt4/server.c
+++ b/modules/rostests/winetests/rpcrt4/server.c
@@ -313,7 +313,7 @@ void __cdecl s_get_number_array(int x[20], int *n)
 {
   int c[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
   memcpy(x, c, sizeof(c));
-  *n = sizeof(c)/sizeof(c[0]);
+  *n = ARRAY_SIZE(c);
 }
 
 int __cdecl s_sum_cs(cs_t *cs)
@@ -861,6 +861,16 @@ void __cdecl s_ip_test(ipu_t *a)
     ok(hr == S_OK, "got %#x\n", hr);
 }
 
+int __cdecl s_sum_ptr_array(int *a[2])
+{
+    return *a[0] + *a[1];
+}
+
+int __cdecl s_sum_array_ptr(int (*a)[2])
+{
+    return (*a)[0] + (*a)[1];
+}
+
 static void
 make_cmdline(char buffer[MAX_PATH], const char *test)
 {
@@ -1388,6 +1398,7 @@ array_tests(void)
   pints_t api[5];
   numbers_struct_t *ns;
   refpint_t rpi[5];
+  int i0 = 1, i1 = 2, *ptr_array[2] = {&i0, &i1}, array[2] = {3, 4};
 
   if (!old_windows_version)
   {
@@ -1518,6 +1529,9 @@ array_tests(void)
   pi[4] = -4; rpi[4] = &pi[4];
   ok(sum_complex_array(5, rpi) == 1, "RPC sum_complex_array\n");
   HeapFree(GetProcessHeap(), 0, pi);
+
+  ok(sum_ptr_array(ptr_array) == 3, "RPC sum_ptr_array\n");
+  ok(sum_array_ptr(&array) == 7, "RPC sum_array_ptr\n");
 }
 
 void __cdecl s_authinfo_test(unsigned int protseq, int secure)
@@ -1959,6 +1973,118 @@ static void test_server_listening(void)
     ok(status == RPC_S_OK, "RpcStringFree\n");
 }
 
+static HANDLE create_server_process(void)
+{
+    SECURITY_ATTRIBUTES sec_attr = { sizeof(sec_attr), NULL, TRUE };
+    HANDLE ready_event;
+    char cmdline[MAX_PATH];
+    PROCESS_INFORMATION info;
+    STARTUPINFOA startup;
+    DWORD ret;
+
+    memset(&startup, 0, sizeof startup);
+    startup.cb = sizeof startup;
+
+    ready_event = CreateEventW(&sec_attr, TRUE, FALSE, NULL);
+    ok(ready_event != NULL, "CreateEvent failed: %u\n", GetLastError());
+
+    sprintf(cmdline, "%s server run %lx", progname, (UINT_PTR)ready_event);
+    trace("running server process...\n");
+    ok(CreateProcessA(NULL, cmdline, NULL, NULL, TRUE, 0L, NULL, NULL, 
&startup, &info), "CreateProcess\n");
+    ret = WaitForSingleObject(ready_event, 10000);
+    ok(WAIT_OBJECT_0 == ret, "WaitForSingleObject\n");
+
+    ok(CloseHandle(info.hThread), "CloseHandle\n");
+    ok(CloseHandle(ready_event), "CloseHandle\n");
+    return info.hProcess;
+}
+
+static void run_server(HANDLE ready_event)
+{
+    static unsigned char np[] = "ncacn_np";
+    static unsigned char pipe[] = PIPE "term_test";
+    RPC_STATUS status;
+    BOOL ret;
+
+    status = RpcServerUseProtseqEpA(np, 0, pipe, NULL);
+    ok(status == RPC_S_OK, "RpcServerUseProtseqEp(ncacn_np) failed with status 
%d\n", status);
+
+    status = RpcServerRegisterIf(s_IServer_v0_0_s_ifspec, NULL, NULL);
+    ok(status == RPC_S_OK, "RpcServerRegisterIf failed with status %d\n", 
status);
+
+    test_is_server_listening(NULL, RPC_S_NOT_LISTENING);
+    status = RpcServerListen(1, 20, TRUE);
+    ok(status == RPC_S_OK, "RpcServerListen failed with status %d\n", status);
+
+    stop_event = CreateEventW(NULL, FALSE, FALSE, NULL);
+    ok(stop_event != NULL, "CreateEvent failed with error %d\n", 
GetLastError());
+
+    ret = SetEvent(ready_event);
+    ok(ret, "SetEvent failed: %u\n", GetLastError());
+
+    ret = WaitForSingleObject(stop_event, 1000);
+    ok(WAIT_OBJECT_0 == ret, "WaitForSingleObject\n");
+
+    status = RpcMgmtWaitServerListen();
+    ok(status == RPC_S_OK, "RpcMgmtWaitServerListening failed with status 
%d\n", status);
+
+    CloseHandle(stop_event);
+    stop_event = NULL;
+}
+
+static DWORD WINAPI basic_tests_thread(void *arg)
+{
+    basic_tests();
+    return 0;
+}
+
+static void test_reconnect(void)
+{
+    static unsigned char np[] = "ncacn_np";
+    static unsigned char address_np[] = "\\\\.";
+    static unsigned char pipe[] = PIPE "term_test";
+    unsigned char *binding;
+    HANDLE threads[32];
+    HANDLE server_process;
+    unsigned i;
+    DWORD ret;
+
+    server_process = create_server_process();
+
+    ok(RPC_S_OK == RpcStringBindingComposeA(NULL, np, address_np, pipe, NULL, 
&binding), "RpcStringBindingCompose\n");
+    ok(RPC_S_OK == RpcBindingFromStringBindingA(binding, &IServer_IfHandle), 
"RpcBindingFromStringBinding\n");
+
+    for (i = 0; i < ARRAY_SIZE(threads); i++)
+    {
+        threads[i] = CreateThread(NULL, 0, basic_tests_thread, 0, 0, NULL);
+        ok(threads[i] != NULL, "CreateThread failed: %u\n", GetLastError());
+    }
+
+    for (i = 0; i < ARRAY_SIZE(threads); i++)
+    {
+        ret = WaitForSingleObject(threads[i], 10000);
+        ok(WAIT_OBJECT_0 == ret, "WaitForSingleObject\n");
+        CloseHandle(threads[i]);
+    }
+
+    stop();
+
+    winetest_wait_child_process(server_process);
+    ok(CloseHandle(server_process), "CloseHandle\n");
+
+    /* create new server, rpcrt4 will connect to it once sending to existing 
connection fails
+     * that current connection is broken. */
+    server_process = create_server_process();
+    basic_tests();
+    stop();
+
+    winetest_wait_child_process(server_process);
+    ok(CloseHandle(server_process), "CloseHandle\n");
+
+    ok(RPC_S_OK == RpcStringFreeA(&binding), "RpcStringFree\n");
+    ok(RPC_S_OK == RpcBindingFree(&IServer_IfHandle), "RpcBindingFree\n");
+}
+
 static BOOL is_process_elevated(void)
 {
     HANDLE token;
@@ -2085,16 +2211,10 @@ START_TEST(server)
   ULONG size = 0;
   int argc;
   char **argv;
-  BOOL firewall_enabled = is_firewall_enabled();
+  BOOL firewall_enabled = is_firewall_enabled(), firewall_disabled = FALSE;
 
   InitFunctionPointers();
 
-  if (firewall_enabled && !is_process_elevated())
-  {
-    trace("no privileges, skipping tests to avoid firewall dialog\n");
-    return;
-  }
-
   ok(!GetUserNameExA(NameSamCompatible, NULL, &size), "GetUserNameExA\n");
   domain_and_user = HeapAlloc(GetProcessHeap(), 0, size);
   ok(GetUserNameExA(NameSamCompatible, domain_and_user, &size), 
"GetUserNameExA\n");
@@ -2116,23 +2236,50 @@ START_TEST(server)
   }
   else if (argc == 4)
   {
-    test_server_listening();
+    if (!strcmp(argv[3], "listen"))
+    {
+      test_server_listening();
+    }
+    else if(!strcmp(argv[2], "run"))
+    {
+      UINT_PTR event;
+      sscanf(argv[3], "%lx", &event);
+      run_server((HANDLE)event);
+    }
   }
   else
   {
     if (firewall_enabled)
     {
-      HRESULT hr = set_firewall(APP_ADD);
-      if (hr != S_OK)
+      if (is_process_elevated())
       {
-        skip("can't authorize app in firewall %08x\n", hr);
-        HeapFree(GetProcessHeap(), 0, domain_and_user);
-        return;
+        HRESULT hr = set_firewall(APP_ADD);
+        if (hr == S_OK)
+        {
+          firewall_enabled = FALSE;
+          firewall_disabled = TRUE;
+        }
+        else
+        {
+          skip("can't authorize app in firewall %08x\n", hr);
+        }
+      }
+      else
+      {
+          trace("no privileges, skipping tests to avoid firewall dialog\n");
       }
     }
-    server();
+
+    if (!firewall_enabled) server();
+
+    /* Those tests cause occasional crashes on winxp and win2k3 */
+    if (GetProcAddress(GetModuleHandleA("rpcrt4.dll"), "RpcExceptionFilter"))
+        test_reconnect();
+    else
+        win_skip("Skipping reconnect tests on too old Windows version\n");
+
     run_client("test listen");
-    if (firewall_enabled) set_firewall(APP_REMOVE);
+    if (firewall_disabled) set_firewall(APP_REMOVE);
   }
 
   HeapFree(GetProcessHeap(), 0, domain_and_user);
diff --git a/modules/rostests/winetests/rpcrt4/server.idl 
b/modules/rostests/winetests/rpcrt4/server.idl
index 6aa73823f0..7d89445f72 100644
--- a/modules/rostests/winetests/rpcrt4/server.idl
+++ b/modules/rostests/winetests/rpcrt4/server.idl
@@ -393,4 +393,7 @@ cpp_quote("#endif")
   } ipu_t;
 
   void ip_test([in] ipu_t *a);
+
+  int sum_ptr_array([in] int *a[2]);
+  int sum_array_ptr([in] int (*a)[2]);
 }

Reply via email to