[OLE32_WINETEST] Sync with Wine Staging 1.7.55. CORE-10536
authorAmine Khaldi <amine.khaldi@reactos.org>
Tue, 17 Nov 2015 10:31:59 +0000 (10:31 +0000)
committerAmine Khaldi <amine.khaldi@reactos.org>
Tue, 17 Nov 2015 10:31:59 +0000 (10:31 +0000)
svn path=/trunk/; revision=69909

rostests/winetests/ole32/compobj.c
rostests/winetests/ole32/marshal.c
rostests/winetests/ole32/moniker.c
rostests/winetests/ole32/ole2.c
rostests/winetests/ole32/propvariant.c
rostests/winetests/ole32/storage32.c
rostests/winetests/ole32/usrmarshal.c

index b4fdd47..fa2ef03 100644 (file)
 
 extern const IID GUID_NULL;
 
+#define DEFINE_EXPECT(func) \
+    static BOOL expect_ ## func = FALSE, called_ ## func = FALSE
+
+#define SET_EXPECT(func) \
+    expect_ ## func = TRUE
+
+#define CHECK_EXPECT2(func) \
+    do { \
+        ok(expect_ ##func, "unexpected call " #func "\n"); \
+        called_ ## func = TRUE; \
+    }while(0)
+
+#define CHECK_EXPECT(func) \
+    do { \
+        CHECK_EXPECT2(func); \
+        expect_ ## func = FALSE; \
+    }while(0)
+
+#define CHECK_CALLED(func) \
+    do { \
+        ok(called_ ## func, "expected " #func "\n"); \
+        expect_ ## func = called_ ## func = FALSE; \
+    }while(0)
+
+DEFINE_EXPECT(CreateStub);
+
 /* functions that are not present on all versions of Windows */
 static HRESULT (WINAPI * pCoInitializeEx)(LPVOID lpReserved, DWORD dwCoInit);
 static HRESULT (WINAPI * pCoGetObjectContext)(REFIID riid, LPVOID *ppv);
@@ -135,6 +161,7 @@ static ULONG WINAPI Test_IClassFactory_Release(LPCLASSFACTORY iface)
     return 1; /* non-heap-based object */
 }
 
+static IID create_instance_iid;
 static HRESULT WINAPI Test_IClassFactory_CreateInstance(
     LPCLASSFACTORY iface,
     IUnknown *pUnkOuter,
@@ -142,6 +169,7 @@ static HRESULT WINAPI Test_IClassFactory_CreateInstance(
     LPVOID *ppvObj)
 {
     *ppvObj = NULL;
+    create_instance_iid = *riid;
     if (pUnkOuter) return CLASS_E_NOAGGREGATION;
     return E_NOINTERFACE;
 }
@@ -772,6 +800,30 @@ static void test_CoGetClassObject(void)
     CoUninitialize();
 }
 
+static void test_CoCreateInstanceEx(void)
+{
+    MULTI_QI qi_res = { &IID_IMoniker };
+    DWORD cookie;
+    HRESULT hr;
+
+    CoInitialize(NULL);
+
+    hr = CoRegisterClassObject(&CLSID_WineOOPTest, (IUnknown *)&Test_ClassFactory,
+                               CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &cookie);
+    ok_ole_success(hr, "CoRegisterClassObject");
+
+    create_instance_iid = IID_NULL;
+    hr = CoCreateInstanceEx(&CLSID_WineOOPTest, NULL, CLSCTX_INPROC_SERVER, NULL, 1, &qi_res);
+    ok(hr == E_NOINTERFACE, "CoCreateInstanceEx failed: %08x\n", hr);
+    ok(IsEqualGUID(&create_instance_iid, qi_res.pIID), "Unexpected CreateInstance iid %s\n",
+       wine_dbgstr_guid(&create_instance_iid));
+
+    hr = CoRevokeClassObject(cookie);
+    ok_ole_success(hr, "CoRevokeClassObject");
+
+    CoUninitialize();
+}
+
 static ATOM register_dummy_class(void)
 {
     WNDCLASSA wc =
@@ -912,6 +964,59 @@ static void test_CoRegisterMessageFilter(void)
     CoUninitialize();
 }
 
+static IUnknown Test_Unknown;
+
+static HRESULT WINAPI EnumOLEVERB_QueryInterface(IEnumOLEVERB *iface, REFIID riid, void **ppv)
+{
+    return IUnknown_QueryInterface(&Test_Unknown, riid, ppv);
+}
+
+static ULONG WINAPI EnumOLEVERB_AddRef(IEnumOLEVERB *iface)
+{
+    return 2;
+}
+
+static ULONG WINAPI EnumOLEVERB_Release(IEnumOLEVERB *iface)
+{
+    return 1;
+}
+
+static HRESULT WINAPI EnumOLEVERB_Next(IEnumOLEVERB *iface, ULONG celt, OLEVERB *rgelt, ULONG *fetched)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI EnumOLEVERB_Skip(IEnumOLEVERB *iface, ULONG celt)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI EnumOLEVERB_Reset(IEnumOLEVERB *iface)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI EnumOLEVERB_Clone(IEnumOLEVERB *iface, IEnumOLEVERB **ppenum)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static const IEnumOLEVERBVtbl EnumOLEVERBVtbl = {
+    EnumOLEVERB_QueryInterface,
+    EnumOLEVERB_AddRef,
+    EnumOLEVERB_Release,
+    EnumOLEVERB_Next,
+    EnumOLEVERB_Skip,
+    EnumOLEVERB_Reset,
+    EnumOLEVERB_Clone
+};
+
+static IEnumOLEVERB EnumOLEVERB = { &EnumOLEVERBVtbl };
+
 static HRESULT WINAPI Test_IUnknown_QueryInterface(
     IUnknown *iface,
     REFIID riid,
@@ -919,16 +1024,17 @@ static HRESULT WINAPI Test_IUnknown_QueryInterface(
 {
     if (ppvObj == NULL) return E_POINTER;
 
-    if (IsEqualIID(riid, &IID_IUnknown) ||
-        IsEqualIID(riid, &IID_IWineTest))
-    {
+    if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IWineTest)) {
         *ppvObj = iface;
-        IUnknown_AddRef(iface);
-        return S_OK;
+    }else if(IsEqualIID(riid, &IID_IEnumOLEVERB)) {
+        *ppvObj = &EnumOLEVERB;
+    }else {
+        *ppvObj = NULL;
+        return E_NOINTERFACE;
     }
 
-    *ppvObj = NULL;
-    return E_NOINTERFACE;
+    IUnknown_AddRef((IUnknown*)*ppvObj);
+    return S_OK;
 }
 
 static ULONG WINAPI Test_IUnknown_AddRef(IUnknown *iface)
@@ -950,6 +1056,8 @@ static const IUnknownVtbl TestUnknown_Vtbl =
 
 static IUnknown Test_Unknown = { &TestUnknown_Vtbl };
 
+static IPSFactoryBuffer *ps_factory_buffer;
+
 static HRESULT WINAPI PSFactoryBuffer_QueryInterface(
     IPSFactoryBuffer * This,
     /* [in] */ REFIID riid,
@@ -993,7 +1101,13 @@ static HRESULT WINAPI PSFactoryBuffer_CreateStub(
     /* [unique][in] */ IUnknown *pUnkServer,
     /* [out] */ IRpcStubBuffer **ppStub)
 {
-    return E_NOTIMPL;
+    CHECK_EXPECT(CreateStub);
+
+    ok(pUnkServer == (IUnknown*)&Test_Unknown, "unexpected pUnkServer %p\n", pUnkServer);
+    if(!ps_factory_buffer)
+        return E_NOTIMPL;
+
+    return IPSFactoryBuffer_CreateStub(ps_factory_buffer, &IID_IEnumOLEVERB, pUnkServer, ppStub);
 }
 
 static IPSFactoryBufferVtbl PSFactoryBufferVtbl =
@@ -1037,9 +1151,31 @@ static void test_CoRegisterPSClsid(void)
     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
     ok_ole_success(hr, "CreateStreamOnHGlobal");
 
+    SET_EXPECT(CreateStub);
     hr = CoMarshalInterface(stream, &IID_IWineTest, &Test_Unknown, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL);
     ok(hr == E_NOTIMPL, "CoMarshalInterface should have returned E_NOTIMPL instead of 0x%08x\n", hr);
+    CHECK_CALLED(CreateStub);
+
+    hr = CoGetPSClsid(&IID_IEnumOLEVERB, &clsid);
+    ok_ole_success(hr, "CoGetPSClsid");
+
+    hr = CoGetClassObject(&clsid, CLSCTX_INPROC_SERVER, NULL, &IID_IPSFactoryBuffer, (void **)&ps_factory_buffer);
+    ok_ole_success(hr, "CoGetClassObject");
+
+    hr = CoRegisterPSClsid(&IID_IEnumOLEVERB, &CLSID_WineTestPSFactoryBuffer);
+    ok_ole_success(hr, "CoRegisterPSClsid");
+
+    SET_EXPECT(CreateStub);
+    hr = CoMarshalInterface(stream, &IID_IEnumOLEVERB, (IUnknown*)&EnumOLEVERB, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL);
+    ok(hr == S_OK, "CoMarshalInterface should have returned E_NOTIMPL instead of 0x%08x\n", hr);
+    CHECK_CALLED(CreateStub);
+
+    hr = CoMarshalInterface(stream, &IID_IEnumOLEVERB, &Test_Unknown, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL);
+    ok(hr == S_OK, "CoMarshalInterface should have returned E_NOTIMPL instead of 0x%08x\n", hr);
+
     IStream_Release(stream);
+    IPSFactoryBuffer_Release(ps_factory_buffer);
+    ps_factory_buffer = NULL;
 
     hr = CoRevokeClassObject(dwRegistrationKey);
     ok_ole_success(hr, "CoRevokeClassObject");
@@ -2135,9 +2271,45 @@ static DWORD CALLBACK post_message_thread(LPVOID arg)
     return 0;
 }
 
+static const char cls_name[] = "cowait_test_class";
+static DWORD CALLBACK test_CoWaitForMultipleHandles_thread(LPVOID arg)
+{
+    HANDLE *handles = arg;
+    BOOL success;
+    DWORD index;
+    HRESULT hr;
+    HWND hWnd;
+    MSG msg;
+
+    hr = pCoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+    ok(hr == S_OK, "CoInitializeEx failed with error 0x%08x\n", hr);
+
+    hWnd = CreateWindowExA(0, cls_name, "Test (thread)", WS_TILEDWINDOW, 0, 0, 640, 480, 0, 0, 0, 0);
+    ok(hWnd != 0, "CreateWindowExA failed %u\n", GetLastError());
+
+    index = 0xdeadbeef;
+    PostMessageA(hWnd, WM_DDE_FIRST, 0, 0);
+    hr = CoWaitForMultipleHandles(0, 50, 2, handles, &index);
+    ok(hr == RPC_S_CALLPENDING, "expected RPC_S_CALLPENDING, got 0x%08x\n", hr);
+    ok(index==0 || index==0xdeadbeef/* Win 8 */, "expected index 0, got %u\n", index);
+    success = PeekMessageA(&msg, hWnd, WM_DDE_FIRST, WM_DDE_FIRST, PM_REMOVE);
+    ok(!success, "CoWaitForMultipleHandles didn't pump any messages\n");
+
+    index = 0xdeadbeef;
+    PostMessageA(hWnd, WM_USER, 0, 0);
+    hr = CoWaitForMultipleHandles(0, 50, 2, handles, &index);
+    ok(hr == RPC_S_CALLPENDING, "expected RPC_S_CALLPENDING, got 0x%08x\n", hr);
+    ok(index==0 || index==0xdeadbeef/* Win 8 */, "expected index 0, got %u\n", index);
+    success = PeekMessageA(&msg, hWnd, WM_USER, WM_USER, PM_REMOVE);
+    ok(success, "CoWaitForMultipleHandles unexpectedly pumped messages\n");
+
+    DestroyWindow(hWnd);
+    CoUninitialize();
+    return 0;
+}
+
 static void test_CoWaitForMultipleHandles(void)
 {
-    static const char cls_name[] = "cowait_test_class";
     HANDLE handles[2], thread;
     DWORD index, tid;
     WNDCLASSEXA wc;
@@ -2440,6 +2612,12 @@ static void test_CoWaitForMultipleHandles(void)
         CloseHandle(thread);
     }
 
+    /* test message pumping when CoWaitForMultipleHandles is called from non main apartment thread */
+    thread = CreateThread(NULL, 0, test_CoWaitForMultipleHandles_thread, handles, 0, &tid);
+    index = WaitForSingleObject(thread, 500);
+    ok(index == WAIT_OBJECT_0, "WaitForSingleObject failed\n");
+    CloseHandle(thread);
+
     CloseHandle(handles[0]);
     CloseHandle(handles[1]);
     DestroyWindow(hWnd);
@@ -2493,6 +2671,7 @@ START_TEST(compobj)
     test_CoCreateInstance();
     test_ole_menu();
     test_CoGetClassObject();
+    test_CoCreateInstanceEx();
     test_CoRegisterMessageFilter();
     test_CoRegisterPSClsid();
     test_CoGetPSClsid();
index fd1ddc0..2279fd5 100644 (file)
@@ -122,7 +122,7 @@ static BOOL last_release_closes;
 
 static HRESULT WINAPI ExternalConnection_QueryInterface(IExternalConnection *iface, REFIID riid, void **ppv)
 {
-    ok(0, "unxpected call\n");
+    ok(0, "unexpected call\n");
     *ppv = NULL;
     return E_NOINTERFACE;
 }
@@ -202,6 +202,24 @@ static const IUnknownVtbl TestUnknown_Vtbl =
 
 static IUnknown Test_Unknown = { &TestUnknown_Vtbl };
 
+static ULONG WINAPI TestCrash_IUnknown_Release(LPUNKNOWN iface)
+{
+    UnlockModule();
+    if(!cLocks) {
+        trace("crashing...\n");
+        *(int**)0xc = 0;
+    }
+    return 1; /* non-heap-based object */
+}
+
+static const IUnknownVtbl TestCrashUnknown_Vtbl =
+{
+    Test_IUnknown_QueryInterface,
+    Test_IUnknown_AddRef,
+    TestCrash_IUnknown_Release,
+};
+
+static IUnknown TestCrash_Unknown = { &TestCrashUnknown_Vtbl };
 
 static HRESULT WINAPI Test_IClassFactory_QueryInterface(
     LPCLASSFACTORY iface,
@@ -1089,6 +1107,63 @@ static void test_no_couninitialize_client(void)
     end_host_object(host_tid, host_thread);
 }
 
+static BOOL crash_thread_success;
+
+static DWORD CALLBACK crash_couninitialize_proc(void *p)
+{
+    IStream *stream;
+    HRESULT hr;
+
+    cLocks = 0;
+
+    CoInitialize(NULL);
+
+    hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
+    ok_ole_success(hr, CreateStreamOnHGlobal);
+
+    hr = CoMarshalInterface(stream, &IID_IUnknown, &TestCrash_Unknown, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL);
+    ok_ole_success(hr, CoMarshalInterface);
+
+    IStream_Seek(stream, ullZero, STREAM_SEEK_SET, NULL);
+
+    hr = CoReleaseMarshalData(stream);
+    ok_ole_success(hr, CoReleaseMarshalData);
+
+    ok_no_locks();
+
+    hr = CoMarshalInterface(stream, &IID_IUnknown, &TestCrash_Unknown, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL);
+    ok_ole_success(hr, CoMarshalInterface);
+
+    ok_more_than_one_lock();
+
+    trace("CoUninitialize >>>\n");
+    CoUninitialize();
+    trace("CoUninitialize <<<\n");
+
+    ok_no_locks();
+
+    IStream_Release(stream);
+    crash_thread_success = TRUE;
+    return 0;
+}
+
+static void test_crash_couninitialize(void)
+{
+    HANDLE thread;
+    DWORD tid;
+
+    if(!GetProcAddress(GetModuleHandleA("kernel32.dll"), "CreateActCtxW")) {
+        win_skip("Skipping crash tests on win2k.\n");
+        return;
+    }
+
+    crash_thread_success = FALSE;
+    thread = CreateThread(NULL, 0, crash_couninitialize_proc, NULL, 0, &tid);
+    ok(!WaitForSingleObject(thread, 10000), "wait timed out\n");
+    CloseHandle(thread);
+    ok(crash_thread_success, "Crash thread failed\n");
+}
+
 /* tests success case of a same-thread table-weak marshal, unmarshal, unmarshal */
 static void test_tableweak_marshal_and_unmarshal_twice(void)
 {
@@ -2859,6 +2934,63 @@ static void UnlockModuleOOP(void)
 
 static HWND hwnd_app;
 
+struct local_server
+{
+    IPersist IPersist_iface; /* a nice short interface */
+};
+
+static HRESULT WINAPI local_server_QueryInterface(IPersist *iface, REFIID iid, void **obj)
+{
+    *obj = NULL;
+
+    if (IsEqualGUID(iid, &IID_IUnknown) ||
+        IsEqualGUID(iid, &IID_IPersist))
+        *obj = iface;
+
+    if (*obj)
+    {
+        IPersist_AddRef(iface);
+        return S_OK;
+    }
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI local_server_AddRef(IPersist *iface)
+{
+    return 2;
+}
+
+static ULONG WINAPI local_server_Release(IPersist *iface)
+{
+    return 1;
+}
+
+static HRESULT WINAPI local_server_GetClassID(IPersist *iface, CLSID *clsid)
+{
+    HRESULT hr;
+
+    *clsid = IID_IUnknown;
+
+    /* Test calling CoDisconnectObject within a COM call */
+    hr = CoDisconnectObject((IUnknown *)iface, 0);
+    ok(hr == S_OK, "got %08x\n", hr);
+
+    return S_OK;
+}
+
+static const IPersistVtbl local_server_persist_vtbl =
+{
+    local_server_QueryInterface,
+    local_server_AddRef,
+    local_server_Release,
+    local_server_GetClassID
+};
+
+struct local_server local_server_class =
+{
+    {&local_server_persist_vtbl}
+};
+
 static HRESULT WINAPI TestOOP_IClassFactory_QueryInterface(
     LPCLASSFACTORY iface,
     REFIID riid,
@@ -2893,12 +3025,12 @@ static HRESULT WINAPI TestOOP_IClassFactory_CreateInstance(
     REFIID riid,
     LPVOID *ppvObj)
 {
-    if (IsEqualIID(riid, &IID_IClassFactory) || IsEqualIID(riid, &IID_IUnknown))
-    {
-        *ppvObj = iface;
-        return S_OK;
-    }
-    return CLASS_E_CLASSNOTAVAILABLE;
+    IPersist *persist = &local_server_class.IPersist_iface;
+    HRESULT hr;
+    IPersist_AddRef( persist );
+    hr = IPersist_QueryInterface( persist, riid, ppvObj );
+    IPersist_Release( persist );
+    return hr;
 }
 
 static HRESULT WINAPI TestOOP_IClassFactory_LockServer(
@@ -2928,24 +3060,25 @@ static void test_register_local_server(void)
     DWORD cookie;
     HRESULT hr;
     HANDLE ready_event;
-    HANDLE quit_event;
     DWORD wait;
+    HANDLE handles[2];
 
     heventShutdown = CreateEventA(NULL, TRUE, FALSE, NULL);
+    ready_event = CreateEventA(NULL, FALSE, FALSE, "Wine COM Test Ready Event");
+    handles[0] = CreateEventA(NULL, FALSE, FALSE, "Wine COM Test Quit Event");
+    handles[1] = CreateEventA(NULL, FALSE, FALSE, "Wine COM Test Repeat Event");
 
+again:
     hr = CoRegisterClassObject(&CLSID_WineOOPTest, (IUnknown *)&TestOOP_ClassFactory,
                                CLSCTX_LOCAL_SERVER, REGCLS_SINGLEUSE, &cookie);
     ok_ole_success(hr, CoRegisterClassObject);
 
-    ready_event = CreateEventA(NULL, FALSE, FALSE, "Wine COM Test Ready Event");
     SetEvent(ready_event);
 
-    quit_event = CreateEventA(NULL, FALSE, FALSE, "Wine COM Test Quit Event");
-
     do
     {
-        wait = MsgWaitForMultipleObjects(1, &quit_event, FALSE, 30000, QS_ALLINPUT);
-        if (wait == WAIT_OBJECT_0+1)
+        wait = MsgWaitForMultipleObjects(2, handles, FALSE, 30000, QS_ALLINPUT);
+        if (wait == WAIT_OBJECT_0+2)
         {
             MSG msg;
 
@@ -2956,12 +3089,20 @@ static void test_register_local_server(void)
                 DispatchMessageA(&msg);
             }
         }
+        else if (wait == WAIT_OBJECT_0+1)
+        {
+            hr = CoRevokeClassObject(cookie);
+            ok_ole_success(hr, CoRevokeClassObject);
+            goto again;
+        }
     }
-    while (wait == WAIT_OBJECT_0+1);
+    while (wait == WAIT_OBJECT_0+2);
 
     ok( wait == WAIT_OBJECT_0, "quit event wait timed out\n" );
     hr = CoRevokeClassObject(cookie);
     ok_ole_success(hr, CoRevokeClassObject);
+    CloseHandle(handles[0]);
+    CloseHandle(handles[1]);
 }
 
 static HANDLE create_target_process(const char *arg)
@@ -2976,7 +3117,7 @@ static HANDLE create_target_process(const char *arg)
     pi.hThread = NULL;
     pi.hProcess = NULL;
     winetest_get_mainargs( &argv );
-    sprintf(cmdline, "%s %s %s", argv[0], argv[1], arg);
+    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 with error: %u\n", GetLastError());
     if (pi.hThread) CloseHandle(pi.hThread);
@@ -2989,10 +3130,13 @@ static void test_local_server(void)
     DWORD cookie;
     HRESULT hr;
     IClassFactory * cf;
+    IPersist *persist;
     DWORD ret;
     HANDLE process;
     HANDLE quit_event;
     HANDLE ready_event;
+    HANDLE repeat_event;
+    CLSID clsid;
 
     heventShutdown = CreateEventA(NULL, TRUE, FALSE, NULL);
 
@@ -3059,16 +3203,30 @@ static void test_local_server(void)
 
     ready_event = CreateEventA(NULL, FALSE, FALSE, "Wine COM Test Ready Event");
     ok( !WaitForSingleObject(ready_event, 10000), "wait timed out\n" );
-    CloseHandle(ready_event);
 
-    hr = CoCreateInstance(&CLSID_WineOOPTest, NULL, CLSCTX_LOCAL_SERVER, &IID_IClassFactory, (void **)&cf);
+    hr = CoCreateInstance(&CLSID_WineOOPTest, NULL, CLSCTX_LOCAL_SERVER, &IID_IPersist, (void **)&persist);
     ok_ole_success(hr, CoCreateInstance);
 
-    IClassFactory_Release(cf);
+    IPersist_Release(persist);
 
-    hr = CoCreateInstance(&CLSID_WineOOPTest, NULL, CLSCTX_LOCAL_SERVER, &IID_IClassFactory, (void **)&cf);
+    hr = CoCreateInstance(&CLSID_WineOOPTest, NULL, CLSCTX_LOCAL_SERVER, &IID_IPersist, (void **)&persist);
     ok(hr == REGDB_E_CLASSNOTREG, "Second CoCreateInstance on REGCLS_SINGLEUSE object should have failed\n");
 
+    /* Re-register the class and try calling CoDisconnectObject from within a call to that object */
+    repeat_event = CreateEventA(NULL, FALSE, FALSE, "Wine COM Test Repeat Event");
+    SetEvent(repeat_event);
+    CloseHandle(repeat_event);
+
+    ok( !WaitForSingleObject(ready_event, 10000), "wait timed out\n" );
+    CloseHandle(ready_event);
+
+    hr = CoCreateInstance(&CLSID_WineOOPTest, NULL, CLSCTX_LOCAL_SERVER, &IID_IPersist, (void **)&persist);
+    ok_ole_success(hr, CoCreateInstance);
+
+    /* GetClassID will call CoDisconnectObject */
+    IPersist_GetClassID(persist, &clsid);
+    IPersist_Release(persist);
+
     quit_event = CreateEventA(NULL, FALSE, FALSE, "Wine COM Test Quit Event");
     SetEvent(quit_event);
 
@@ -3556,6 +3714,7 @@ START_TEST(marshal)
 
     test_globalinterfacetable();
     test_manualresetevent();
+    test_crash_couninitialize();
 
     /* must be last test as channel hooks can't be unregistered */
     test_channel_hook();
index 27d93ca..7d0055b 100644 (file)
@@ -99,7 +99,7 @@ static DWORD external_connections;
 
 static HRESULT WINAPI ExternalConnection_QueryInterface(IExternalConnection *iface, REFIID riid, void **ppv)
 {
-    ok(0, "unxpected call\n");
+    ok(0, "unexpected call\n");
     *ppv = NULL;
     return E_NOINTERFACE;
 }
index ef4d0f1..cf3bd1b 100644 (file)
@@ -118,6 +118,32 @@ static HRESULT g_QIFailsWith;
 
 static UINT cf_test_1, cf_test_2, cf_test_3;
 
+/****************************************************************************
+ * PresentationDataHeader
+ *
+ * This structure represents the header of the \002OlePresXXX stream in
+ * the OLE object storage.
+ */
+typedef struct PresentationDataHeader
+{
+    /* clipformat:
+     *  - standard clipformat:
+     *  DWORD length = 0xffffffff;
+     *  DWORD cfFormat;
+     *  - or custom clipformat:
+     *  DWORD length;
+     *  CHAR format_name[length]; (null-terminated)
+     */
+    DWORD unknown3; /* 4, possibly TYMED_ISTREAM */
+    DVASPECT dvAspect;
+    DWORD lindex;
+    DWORD tymed;
+    DWORD unknown7; /* 0 */
+    DWORD dwObjectExtentX;
+    DWORD dwObjectExtentY;
+    DWORD dwSize;
+} PresentationDataHeader;
+
 #define CHECK_EXPECTED_METHOD(method_name) \
     do { \
         trace("%s\n", method_name); \
@@ -1045,6 +1071,7 @@ static void test_OleLoad(IStorage *pStorage)
 {
     HRESULT hr;
     IOleObject *pObject;
+    DWORD fmt;
 
     static const struct expected_method methods_oleload[] =
     {
@@ -1102,6 +1129,105 @@ static void test_OleLoad(IStorage *pStorage)
         IOleObject_Release(pObject);
         CHECK_NO_EXTRA_METHODS();
     }
+
+    for (fmt = CF_TEXT; fmt < CF_MAX; fmt++)
+    {
+        static const WCHAR olrepres[] = { 2,'O','l','e','P','r','e','s','0','0','0',0 };
+        IStorage *stg;
+        IStream *stream;
+        IUnknown *obj;
+        DWORD data, i, tymed, data_size;
+        PresentationDataHeader header;
+        HDC hdc;
+        HGDIOBJ hobj;
+        RECT rc;
+        char buf[256];
+
+        for (i = 0; i < 7; i++)
+        {
+            hr = StgCreateDocfile(NULL, STGM_READWRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_DELETEONRELEASE, 0, &stg);
+            ok(hr == S_OK, "StgCreateDocfile error %#x\n", hr);
+
+            hr = IStorage_SetClass(stg, &CLSID_WineTest);
+            ok(hr == S_OK, "SetClass error %#x\n", hr);
+
+            hr = IStorage_CreateStream(stg, olrepres, STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE, 0, 0, &stream);
+            ok(hr == S_OK, "CreateStream error %#x\n", hr);
+
+            data = ~0;
+            hr = IStream_Write(stream, &data, sizeof(data), NULL);
+            ok(hr == S_OK, "Write error %#x\n", hr);
+
+            data = fmt;
+            hr = IStream_Write(stream, &data, sizeof(data), NULL);
+            ok(hr == S_OK, "Write error %#x\n", hr);
+
+            switch (fmt)
+            {
+            case CF_BITMAP:
+                /* FIXME: figure out stream format */
+                hobj = CreateBitmap(1, 1, 1, 1, NULL);
+                data_size = GetBitmapBits(hobj, sizeof(buf), buf);
+                DeleteObject(hobj);
+                break;
+
+            case CF_METAFILEPICT:
+            case CF_ENHMETAFILE:
+                hdc = CreateMetaFileA(NULL);
+                hobj = CloseMetaFile(hdc);
+                data_size = GetMetaFileBitsEx(hobj, sizeof(buf), buf);
+                DeleteMetaFile(hobj);
+                break;
+
+            default:
+                data_size = sizeof(buf);
+                memset(buf, 'A', sizeof(buf));
+                break;
+            }
+
+            tymed = 1 << i;
+
+            header.unknown3 = 4;
+            header.dvAspect = DVASPECT_CONTENT;
+            header.lindex = -1;
+            header.tymed = tymed;
+            header.unknown7 = 0;
+            header.dwObjectExtentX = 1;
+            header.dwObjectExtentY = 1;
+            header.dwSize = data_size;
+            hr = IStream_Write(stream, &header, sizeof(header), NULL);
+            ok(hr == S_OK, "Write error %#x\n", hr);
+
+            hr = IStream_Write(stream, buf, data_size, NULL);
+            ok(hr == S_OK, "Write error %#x\n", hr);
+
+            IStream_Release(stream);
+
+            hr = OleLoad(stg, &IID_IUnknown, NULL, (void **)&obj);
+            /* FIXME: figure out stream format */
+            if (fmt == CF_BITMAP && hr != S_OK)
+            {
+                IStorage_Release(stg);
+                continue;
+            }
+            ok(hr == S_OK, "OleLoad error %#x: cfFormat = %u, tymed = %u\n", hr, fmt, tymed);
+
+            hdc = CreateCompatibleDC(0);
+            SetRect(&rc, 0, 0, 100, 100);
+            hr = OleDraw(obj, DVASPECT_CONTENT, hdc, &rc);
+            DeleteDC(hdc);
+            if (fmt == CF_METAFILEPICT)
+                ok(hr == S_OK, "OleDraw error %#x: cfFormat = %u, tymed = %u\n", hr, fmt, tymed);
+            else if (fmt == CF_ENHMETAFILE)
+todo_wine
+                ok(hr == S_OK, "OleDraw error %#x: cfFormat = %u, tymed = %u\n", hr, fmt, tymed);
+            else
+                ok(hr == OLE_E_BLANK || hr == OLE_E_NOTRUNNING || hr == E_FAIL, "OleDraw should fail: %#x, cfFormat = %u, tymed = %u\n", hr, fmt, header.tymed);
+
+            IUnknown_Release(obj);
+            IStorage_Release(stg);
+        }
+    }
 }
 
 static BOOL STDMETHODCALLTYPE draw_continue(ULONG_PTR param)
@@ -1936,7 +2062,6 @@ static void test_default_handler(void)
     fmtetc.lindex = -1;
     fmtetc.tymed = TYMED_ENHMF;
     hr = IDataObject_QueryGetData(pDataObject, &fmtetc);
-    todo_wine
     ok(hr == OLE_E_NOTRUNNING, "IDataObject_QueryGetData should have returned OLE_E_NOTRUNNING instead of 0x%08x\n", hr);
 
     fmtetc.cfFormat = CF_TEXT;
@@ -1945,7 +2070,6 @@ static void test_default_handler(void)
     fmtetc.lindex = -1;
     fmtetc.tymed = TYMED_NULL;
     hr = IDataObject_QueryGetData(pDataObject, &fmtetc);
-    todo_wine
     ok(hr == OLE_E_NOTRUNNING, "IDataObject_QueryGetData should have returned OLE_E_NOTRUNNING instead of 0x%08x\n", hr);
 
     hr = IOleObject_QueryInterface(pObject, &IID_IRunnableObject, (void **)&pRunnableObject);
@@ -2107,7 +2231,7 @@ static ULONG WINAPI OleRun_Release(IRunnableObject *iface)
 
 static HRESULT WINAPI OleRun_GetRunningClass(IRunnableObject *iface, CLSID *clsid)
 {
-    ok(0, "unxpected\n");
+    ok(0, "unexpected\n");
     return E_NOTIMPL;
 }
 
@@ -2119,20 +2243,20 @@ static HRESULT WINAPI OleRun_Run(IRunnableObject *iface, LPBINDCTX ctx)
 
 static BOOL WINAPI OleRun_IsRunning(IRunnableObject *iface)
 {
-    ok(0, "unxpected\n");
+    ok(0, "unexpected\n");
     return FALSE;
 }
 
 static HRESULT WINAPI OleRun_LockRunning(IRunnableObject *iface, BOOL lock,
     BOOL last_unlock_closes)
 {
-    ok(0, "unxpected\n");
+    ok(0, "unexpected\n");
     return E_NOTIMPL;
 }
 
 static HRESULT WINAPI OleRun_SetContainedObject(IRunnableObject *iface, BOOL contained)
 {
-    ok(0, "unxpected\n");
+    ok(0, "unexpected\n");
     return E_NOTIMPL;
 }
 
index cc283ff..5a8e81d 100644 (file)
@@ -140,7 +140,7 @@ static const char* wine_vtypes[VT_CLSID+1] =
 };
 
 
-static void expect(HRESULT hr, VARTYPE vt, BOOL copy)
+static void expect(HRESULT hr, VARTYPE vt, BOOL copy, int line)
 {
     int idx = vt & VT_TYPEMASK;
     BYTE flags;
@@ -168,7 +168,12 @@ static void expect(HRESULT hr, VARTYPE vt, BOOL copy)
     }
 
     if(flags == PROP_INV)
-        ok(hr == copy ? DISP_E_BADVARTYPE : STG_E_INVALIDPARAMETER, "%s (%s): got %08x\n", wine_vtypes[idx], modifier, hr);
+    {
+        if (copy && (vt & VT_VECTOR))
+            ok(hr == DISP_E_BADVARTYPE || hr == STG_E_INVALIDPARAMETER, "%s (%s): got %08x (line %d)\n", wine_vtypes[idx], modifier, hr, line);
+        else
+            ok(hr == (copy ? DISP_E_BADVARTYPE : STG_E_INVALIDPARAMETER), "%s (%s): got %08x (line %d)\n", wine_vtypes[idx], modifier, hr, line);
+    }
     else if(flags == PROP_V0)
         ok(hr == S_OK, "%s (%s): got %08x\n", wine_vtypes[idx], modifier, hr);
     else if(flags & PROP_TODO)
@@ -220,7 +225,7 @@ static void test_validtypes(void)
         vt = propvar.vt = i;
         memset(&copy, 0x77, sizeof(copy));
         hr = PropVariantCopy(&copy, &propvar);
-        expect(hr, vt, TRUE);
+        expect(hr, vt, TRUE, __LINE__);
         if (hr == S_OK)
         {
             ok(copy.vt == propvar.vt, "expected %d, got %d\n", propvar.vt, copy.vt);
@@ -234,7 +239,7 @@ static void test_validtypes(void)
             ok(!ret || broken(ret) /* win2000 */, "%d: copy should stay unchanged\n", i);
         }
         hr = PropVariantClear(&propvar);
-        expect(hr, vt, FALSE);
+        expect(hr, vt, FALSE, __LINE__);
         ok(propvar.vt == 0, "expected 0, got %d\n", propvar.vt);
         ok(U(propvar).uhVal.QuadPart == 0, "%u: expected 0, got %#x/%#x\n",
            i, U(propvar).uhVal.u.LowPart, U(propvar).uhVal.u.HighPart);
@@ -244,7 +249,7 @@ static void test_validtypes(void)
         vt = propvar.vt = i | VT_ARRAY;
         memset(&copy, 0x77, sizeof(copy));
         hr = PropVariantCopy(&copy, &propvar);
-        expect(hr, vt, TRUE);
+        expect(hr, vt, TRUE, __LINE__);
         if (hr == S_OK)
         {
             ok(copy.vt == propvar.vt, "expected %d, got %d\n", propvar.vt, copy.vt);
@@ -257,7 +262,7 @@ static void test_validtypes(void)
             ok(!ret || broken(ret) /* win2000 */, "%d: copy should stay unchanged\n", i);
         }
         hr = PropVariantClear(&propvar);
-        expect(hr, vt, FALSE);
+        expect(hr, vt, FALSE, __LINE__);
         ok(propvar.vt == 0, "expected 0, got %d\n", propvar.vt);
         ok(U(propvar).uhVal.QuadPart == 0, "%u: expected 0, got %#x/%#x\n",
            i, U(propvar).uhVal.u.LowPart, U(propvar).uhVal.u.HighPart);
@@ -268,7 +273,7 @@ static void test_validtypes(void)
         vt = propvar.vt = i | VT_VECTOR;
         memset(&copy, 0x77, sizeof(copy));
         hr = PropVariantCopy(&copy, &propvar);
-        expect(hr, vt, TRUE);
+        expect(hr, vt, TRUE, __LINE__);
         if (hr == S_OK)
         {
             ok(copy.vt == propvar.vt, "expected %d, got %d\n", propvar.vt, copy.vt);
@@ -281,7 +286,7 @@ static void test_validtypes(void)
             ok(!ret || broken(ret) /* win2000 */, "%d: copy should stay unchanged\n", i);
         }
         hr = PropVariantClear(&propvar);
-        expect(hr, vt, FALSE);
+        expect(hr, vt, FALSE, __LINE__);
         ok(propvar.vt == 0, "expected 0, got %d\n", propvar.vt);
         ok(U(propvar).uhVal.QuadPart == 0, "%u: expected 0, got %#x/%#x\n",
            i, U(propvar).uhVal.u.LowPart, U(propvar).uhVal.u.HighPart);
@@ -291,7 +296,7 @@ static void test_validtypes(void)
         vt = propvar.vt = i | VT_BYREF;
         memset(&copy, 0x77, sizeof(copy));
         hr = PropVariantCopy(&copy, &propvar);
-        expect(hr, vt, TRUE);
+        expect(hr, vt, TRUE, __LINE__);
         if (hr == S_OK)
         {
             ok(copy.vt == propvar.vt, "expected %d, got %d\n", propvar.vt, copy.vt);
@@ -305,7 +310,7 @@ static void test_validtypes(void)
             ok(!ret || broken(ret) /* win2000 */, "%d: copy should stay unchanged\n", i);
         }
         hr = PropVariantClear(&propvar);
-        expect(hr, vt, FALSE);
+        expect(hr, vt, FALSE, __LINE__);
         ok(propvar.vt == 0, "expected 0, got %d\n", propvar.vt);
         ok(U(propvar).uhVal.QuadPart == 0, "%u: expected 0, got %#x/%#x\n",
            i, U(propvar).uhVal.u.LowPart, U(propvar).uhVal.u.HighPart);
index c3bd25b..5bea5d2 100644 (file)
@@ -224,7 +224,7 @@ static HRESULT WINAPI TestLockBytes_Stat(ILockBytes *iface,
     return S_OK;
 }
 
-static ILockBytesVtbl TestLockBytes_Vtbl = {
+static /* const */ ILockBytesVtbl TestLockBytes_Vtbl = {
     TestLockBytes_QueryInterface,
     TestLockBytes_AddRef,
     TestLockBytes_Release,
@@ -517,6 +517,7 @@ static void test_storage_stream(void)
     ULARGE_INTEGER p;
     unsigned char buffer[0x100];
     IUnknown *unk;
+    BOOL ret;
 
     DeleteFileA(filenameA);
 
@@ -695,8 +696,8 @@ static void test_storage_stream(void)
         IStorage_Release(stg);
     }
 
-    r = DeleteFileA(filenameA);
-    ok(r, "file should exist\n");
+    ret = DeleteFileA(filenameA);
+    ok(ret, "file should exist\n");
 }
 
 static BOOL touch_file(LPCSTR filename)
@@ -743,6 +744,7 @@ static void test_open_storage(void)
     IStorage *stg = NULL, *stg2 = NULL;
     HRESULT r;
     DWORD stgm;
+    BOOL ret;
 
     /* try opening a zero length file - it should stay zero length */
     DeleteFileA(filenameA);
@@ -902,8 +904,8 @@ static void test_open_storage(void)
     r = StgOpenStorage( filename, NULL, STGM_NOSNAPSHOT | STGM_PRIORITY, NULL, 0, &stg);
     ok(r == STG_E_INVALIDFLAG, "should fail\n");
 
-    r = DeleteFileA(filenameA);
-    ok(r, "file didn't exist\n");
+    ret = DeleteFileA(filenameA);
+    ok(ret, "file didn't exist\n");
 }
 
 static void test_storage_suminfo(void)
@@ -1226,6 +1228,7 @@ static void test_streamenum(void)
     static const WCHAR stmname[] = { 'C','O','N','T','E','N','T','S',0 };
     static const WCHAR stmname2[] = { 'A','B','C','D','E','F','G','H','I',0 };
     static const WCHAR stmname3[] = { 'A','B','C','D','E','F','G','H','I','J',0 };
+    static const STATSTG stat_null;
     STATSTG stat;
     IEnumSTATSTG *ee = NULL;
     ULONG count;
@@ -1271,10 +1274,12 @@ static void test_streamenum(void)
     r = IStorage_DestroyElement(stg, stmname);
     ok(r==S_OK, "IStorage->DestroyElement failed\n");
 
+    memset(&stat, 0xad, sizeof(stat));
     count = 0xf00;
     r = IEnumSTATSTG_Next(ee, 1, &stat, &count);
     ok(r==S_FALSE, "IEnumSTATSTG->Next failed\n");
     ok(count == 0, "count wrong\n");
+    ok(memcmp(&stat, &stat_null, sizeof(stat)) == 0, "stat is not zeroed\n");
 
     /* reset and try again */
     r = IEnumSTATSTG_Reset(ee);
@@ -1401,6 +1406,7 @@ static void test_transact(void)
     static const WCHAR stmname2[] = { 'F','O','O',0 };
     static const WCHAR stgname[] = { 'P','E','R','M','S','T','G',0 };
     static const WCHAR stgname2[] = { 'T','E','M','P','S','T','G',0 };
+    BOOL ret;
 
     DeleteFileA(filenameA);
 
@@ -1521,8 +1527,8 @@ static void test_transact(void)
 
     IStorage_Release(stg);
 
-    r = DeleteFileA(filenameA);
-    ok( r == TRUE, "deleted file\n");
+    ret = DeleteFileA(filenameA);
+    ok(ret, "deleted file\n");
 }
 
 static void test_substorage_share(void)
@@ -1533,6 +1539,7 @@ static void test_substorage_share(void)
     static const WCHAR stgname[] = { 'P','E','R','M','S','T','G',0 };
     static const WCHAR stmname[] = { 'C','O','N','T','E','N','T','S',0 };
     static const WCHAR othername[] = { 'N','E','W','N','A','M','E',0 };
+    BOOL ret;
 
     DeleteFileA(filenameA);
 
@@ -1612,8 +1619,8 @@ static void test_substorage_share(void)
 
     IStorage_Release(stg);
 
-    r = DeleteFileA(filenameA);
-    ok( r == TRUE, "deleted file\n");
+    ret = DeleteFileA(filenameA);
+    ok(ret, "deleted file\n");
 }
 
 static void test_revert(void)
@@ -1626,6 +1633,7 @@ static void test_revert(void)
     static const WCHAR stgname[] = { 'P','E','R','M','S','T','G',0 };
     static const WCHAR stgname2[] = { 'T','E','M','P','S','T','G',0 };
     STATSTG statstg;
+    BOOL ret;
 
     DeleteFileA(filenameA);
 
@@ -1743,8 +1751,8 @@ static void test_revert(void)
 
     IStorage_Release(stg);
 
-    r = DeleteFileA(filenameA);
-    ok( r == TRUE, "deleted file\n");
+    ret = DeleteFileA(filenameA);
+    ok(ret, "deleted file\n");
 
     /* Revert only invalidates objects in transacted mode */
     r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE |
@@ -1763,8 +1771,8 @@ static void test_revert(void)
     IStream_Release(stm);
     IStorage_Release(stg);
 
-    r = DeleteFileA(filenameA);
-    ok( r == TRUE, "deleted file\n");
+    ret = DeleteFileA(filenameA);
+    ok(ret, "deleted file\n");
 }
 
 static void test_parent_free(void)
@@ -1776,6 +1784,7 @@ static void test_parent_free(void)
     static const WCHAR stgname[] = { 'P','E','R','M','S','T','G',0 };
     ULONG ref;
     STATSTG statstg;
+    BOOL ret;
 
     DeleteFileA(filenameA);
 
@@ -1825,8 +1834,8 @@ static void test_parent_free(void)
 
     IStorage_Release(stg);
 
-    r = DeleteFileA(filenameA);
-    ok( r == TRUE, "deleted file\n");
+    ret = DeleteFileA(filenameA);
+    ok(ret, "deleted file\n");
 }
 
 static void test_nonroot_transacted(void)
@@ -1837,6 +1846,7 @@ static void test_nonroot_transacted(void)
     static const WCHAR stgname[] = { 'P','E','R','M','S','T','G',0 };
     static const WCHAR stmname[] = { 'C','O','N','T','E','N','T','S',0 };
     static const WCHAR stmname2[] = { 'F','O','O',0 };
+    BOOL ret;
 
     DeleteFileA(filenameA);
 
@@ -1945,8 +1955,8 @@ static void test_nonroot_transacted(void)
 
     IStorage_Release(stg);
 
-    r = DeleteFileA(filenameA);
-    ok( r == TRUE, "deleted file\n");
+    ret = DeleteFileA(filenameA);
+    ok(ret, "deleted file\n");
 }
 
 static void test_ReadClassStm(void)
@@ -2415,6 +2425,7 @@ static void test_fmtusertypestg(void)
     static const WCHAR fileW[] = {'f','m','t','t','e','s','t',0};
     static WCHAR userTypeW[] = {'S','t','g','U','s','r','T','y','p','e',0};
     static const WCHAR strmNameW[] = {1,'C','o','m','p','O','b','j',0};
+    static const STATSTG statstg_null;
 
     hr = StgCreateDocfile( fileW, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, &stg);
     ok(hr == S_OK, "should succeed, res=%x\n", hr);
@@ -2433,6 +2444,7 @@ static void test_fmtusertypestg(void)
             BOOL found = FALSE;
             STATSTG statstg;
             DWORD got;
+            memset(&statstg, 0xad, sizeof(statstg));
             while ((hr = IEnumSTATSTG_Next(stat, 1, &statstg, &got)) == S_OK && got == 1)
             {
                 if (strcmp_ww(statstg.pwcsName, strmNameW) == 0)
@@ -2441,6 +2453,7 @@ static void test_fmtusertypestg(void)
                     ok(0, "found unexpected stream or storage\n");
                 CoTaskMemFree(statstg.pwcsName);
             }
+            ok(memcmp(&statstg, &statstg_null, sizeof(statstg)) == 0, "statstg is not zeroed\n");
             ok(found == TRUE, "expected storage to contain stream \\0001CompObj\n");
             IEnumSTATSTG_Release(stat);
         }
@@ -2457,6 +2470,7 @@ static void test_fmtusertypestg(void)
             BOOL found = FALSE;
             STATSTG statstg;
             DWORD got;
+            memset(&statstg, 0xad, sizeof(statstg));
             while ((hr = IEnumSTATSTG_Next(stat, 1, &statstg, &got)) == S_OK && got == 1)
             {
                 if (strcmp_ww(statstg.pwcsName, strmNameW) == 0)
@@ -2465,6 +2479,7 @@ static void test_fmtusertypestg(void)
                     ok(0, "found unexpected stream or storage\n");
                 CoTaskMemFree(statstg.pwcsName);
             }
+            ok(memcmp(&statstg, &statstg_null, sizeof(statstg)) == 0, "statstg is not zeroed\n");
             ok(found == TRUE, "expected storage to contain stream \\0001CompObj\n");
             IEnumSTATSTG_Release(stat);
         }
@@ -2929,6 +2944,7 @@ static void test_rename(void)
     static const WCHAR stgname2[] = { 'S','T','G',0 };
     static const WCHAR stmname[] = { 'C','O','N','T','E','N','T','S',0 };
     static const WCHAR stmname2[] = { 'E','N','T','S',0 };
+    BOOL ret;
 
     DeleteFileA(filenameA);
 
@@ -2985,8 +3001,8 @@ static void test_rename(void)
 
     IStorage_Release(stg);
 
-    r = DeleteFileA(filenameA);
-    ok( r == TRUE, "deleted file\n");
+    ret = DeleteFileA(filenameA);
+    ok(ret, "deleted file\n");
 }
 
 static void test_toplevel_stat(void)
@@ -3072,6 +3088,7 @@ static void test_substorage_enum(void)
     HRESULT r;
     ULONG ref;
     static const WCHAR stgname[] = { 'P','E','R','M','S','T','G',0 };
+    BOOL ret;
 
     DeleteFileA(filenameA);
 
@@ -3107,8 +3124,8 @@ static void test_substorage_enum(void)
 
     IStorage_Release(stg);
 
-    r = DeleteFileA(filenameA);
-    ok( r == TRUE, "deleted file\n");
+    ret = DeleteFileA(filenameA);
+    ok(ret, "deleted file\n");
 }
 
 static void test_copyto_locking(void)
@@ -3119,6 +3136,7 @@ static void test_copyto_locking(void)
     static const WCHAR stgname[] = { 'S','T','G','1',0 };
     static const WCHAR stgname2[] = { 'S','T','G','2',0 };
     static const WCHAR stmname[] = { 'C','O','N','T','E','N','T','S',0 };
+    BOOL ret;
 
     DeleteFileA(filenameA);
 
@@ -3158,8 +3176,8 @@ static void test_copyto_locking(void)
     IStorage_Release(stg2);
     IStorage_Release(stg);
 
-    r = DeleteFileA(filenameA);
-    ok( r == TRUE, "deleted file\n");
+    ret = DeleteFileA(filenameA);
+    ok(ret, "deleted file\n");
 }
 
 static void test_copyto_recursive(void)
@@ -3168,6 +3186,7 @@ static void test_copyto_recursive(void)
     HRESULT r;
     static const WCHAR stgname[] = { 'S','T','G','1',0 };
     static const WCHAR stgname2[] = { 'S','T','G','2',0 };
+    BOOL ret;
 
     DeleteFileA(filenameA);
 
@@ -3209,8 +3228,8 @@ static void test_copyto_recursive(void)
     IStorage_Release(stg2);
     IStorage_Release(stg);
 
-    r = DeleteFileA(filenameA);
-    ok( r == TRUE, "deleted file\n");
+    ret = DeleteFileA(filenameA);
+    ok(ret, "deleted file\n");
 }
 
 static void test_hglobal_storage_creation(void)
index 395ea4e..8cce648 100644 (file)
@@ -91,6 +91,91 @@ static void init_user_marshal_cb(USER_MARSHAL_CB *umcb,
     umcb->CBType = buffer ? USER_MARSHAL_CB_UNMARSHALL : USER_MARSHAL_CB_BUFFER_SIZE;
 }
 
+#define RELEASEMARSHALDATA WM_USER
+
+struct host_object_data
+{
+    IStream *stream;
+    IID iid;
+    IUnknown *object;
+    MSHLFLAGS marshal_flags;
+    HANDLE marshal_event;
+    IMessageFilter *filter;
+};
+
+static DWORD CALLBACK host_object_proc(LPVOID p)
+{
+    struct host_object_data *data = p;
+    HRESULT hr;
+    MSG msg;
+
+    CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+
+    if (data->filter)
+    {
+        IMessageFilter * prev_filter = NULL;
+        hr = CoRegisterMessageFilter(data->filter, &prev_filter);
+        if (prev_filter) IMessageFilter_Release(prev_filter);
+        ok(hr == S_OK, "got %08x\n", hr);
+    }
+
+    hr = CoMarshalInterface(data->stream, &data->iid, data->object, MSHCTX_INPROC, NULL, data->marshal_flags);
+    ok(hr == S_OK, "got %08x\n", hr);
+
+    /* force the message queue to be created before signaling parent thread */
+    PeekMessageA(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
+
+    SetEvent(data->marshal_event);
+
+    while (GetMessageA(&msg, NULL, 0, 0))
+    {
+        if (msg.hwnd == NULL && msg.message == RELEASEMARSHALDATA)
+        {
+            CoReleaseMarshalData(data->stream);
+            SetEvent((HANDLE)msg.lParam);
+        }
+        else
+            DispatchMessageA(&msg);
+    }
+
+    HeapFree(GetProcessHeap(), 0, data);
+
+    CoUninitialize();
+
+    return hr;
+}
+
+static DWORD start_host_object2(IStream *stream, REFIID riid, IUnknown *object, MSHLFLAGS marshal_flags, IMessageFilter *filter, HANDLE *thread)
+{
+    DWORD tid = 0;
+    HANDLE marshal_event = CreateEventA(NULL, FALSE, FALSE, NULL);
+    struct host_object_data *data = HeapAlloc(GetProcessHeap(), 0, sizeof(*data));
+
+    data->stream = stream;
+    data->iid = *riid;
+    data->object = object;
+    data->marshal_flags = marshal_flags;
+    data->marshal_event = marshal_event;
+    data->filter = filter;
+
+    *thread = CreateThread(NULL, 0, host_object_proc, data, 0, &tid);
+
+    /* wait for marshaling to complete before returning */
+    ok( !WaitForSingleObject(marshal_event, 10000), "wait timed out\n" );
+    CloseHandle(marshal_event);
+
+    return tid;
+}
+
+static void end_host_object(DWORD tid, HANDLE thread)
+{
+    BOOL ret = PostThreadMessageA(tid, WM_QUIT, 0, 0);
+    ok(ret, "PostThreadMessage failed with error %d\n", GetLastError());
+    /* be careful of races - don't return until hosting thread has terminated */
+    ok( !WaitForSingleObject(thread, 10000), "wait timed out\n" );
+    CloseHandle(thread);
+}
+
 static const char cf_marshaled[] =
 {
     0x9, 0x0, 0x0, 0x0,
@@ -533,6 +618,17 @@ static const IUnknownVtbl TestUnknown_Vtbl =
     Test_IUnknown_Release,
 };
 
+struct test_stream
+{
+    IStream IStream_iface;
+    LONG refs;
+};
+
+static inline struct test_stream *impl_from_IStream(IStream *iface)
+{
+    return CONTAINING_RECORD(iface, struct test_stream, IStream_iface);
+}
+
 static HRESULT WINAPI Test_IStream_QueryInterface(IStream *iface,
                                                   REFIID riid, LPVOID *ppvObj)
 {
@@ -552,12 +648,14 @@ static HRESULT WINAPI Test_IStream_QueryInterface(IStream *iface,
 
 static ULONG WINAPI Test_IStream_AddRef(IStream *iface)
 {
-    return 2; /* non-heap-based object */
+    struct test_stream *This = impl_from_IStream(iface);
+    return InterlockedIncrement(&This->refs);
 }
 
 static ULONG WINAPI Test_IStream_Release(IStream *iface)
 {
-    return 1; /* non-heap-based object */
+    struct test_stream *This = impl_from_IStream(iface);
+    return InterlockedDecrement(&This->refs);
 }
 
 static const IStreamVtbl TestStream_Vtbl =
@@ -569,13 +667,15 @@ static const IStreamVtbl TestStream_Vtbl =
 };
 
 static TestUnknown Test_Unknown = { {&TestUnknown_Vtbl}, 1 };
-static IStream Test_Stream = { &TestStream_Vtbl };
+static TestUnknown Test_Unknown2 = { {&TestUnknown_Vtbl}, 1 };
+static struct test_stream Test_Stream = { {&TestStream_Vtbl}, 1 };
+static struct test_stream Test_Stream2 = { {&TestStream_Vtbl}, 1 };
 
 ULONG __RPC_USER WdtpInterfacePointer_UserSize(ULONG *, ULONG, ULONG, IUnknown *, REFIID);
 unsigned char * __RPC_USER WdtpInterfacePointer_UserMarshal(ULONG *, ULONG, unsigned char *, IUnknown *, REFIID);
 unsigned char * __RPC_USER WdtpInterfacePointer_UserUnmarshal(ULONG *, unsigned char *, IUnknown **, REFIID);
 
-static void marshal_WdtpInterfacePointer(DWORD umcb_ctx, DWORD ctx)
+static void marshal_WdtpInterfacePointer(DWORD umcb_ctx, DWORD ctx, BOOL client, BOOL in, BOOL out)
 {
     USER_MARSHAL_CB umcb;
     MIDL_STUB_MESSAGE stub_msg;
@@ -644,11 +744,17 @@ todo_wine
     CoReleaseMarshalData(stm);
     IStream_Release(stm);
 
-    unk2 = NULL;
+    Test_Unknown2.refs = 1;
+    unk2 = &Test_Unknown2.IUnknown_iface;
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, umcb_ctx);
+    umcb.pStubMsg->IsClient = client;
+    umcb.pStubMsg->fIsIn = in;
+    umcb.pStubMsg->fIsOut = out;
+
     WdtpInterfacePointer_UserUnmarshal(&umcb.Flags, buffer, &unk2, &IID_IUnknown);
     ok(unk2 != NULL, "IUnknown object didn't unmarshal properly\n");
     ok(Test_Unknown.refs == 2, "got %d\n", Test_Unknown.refs);
+    ok(Test_Unknown2.refs == 0, "got %d\n", Test_Unknown2.refs);
     HeapFree(GetProcessHeap(), 0, buffer);
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_INPROC);
     IUnknown_Release(unk2);
@@ -663,17 +769,26 @@ static void test_marshal_WdtpInterfacePointer(void)
      */
 
     /* All three are marshalled as inproc */
-    marshal_WdtpInterfacePointer(MSHCTX_INPROC, MSHCTX_INPROC);
-    marshal_WdtpInterfacePointer(MSHCTX_DIFFERENTMACHINE, MSHCTX_INPROC);
-    marshal_WdtpInterfacePointer(MSHCTX_INPROC, MAKELONG(MSHCTX_INPROC, 0xffff));
+    marshal_WdtpInterfacePointer(MSHCTX_INPROC, MSHCTX_INPROC, 0,0,0);
+    marshal_WdtpInterfacePointer(MSHCTX_DIFFERENTMACHINE, MSHCTX_INPROC,0,0,0);
+    marshal_WdtpInterfacePointer(MSHCTX_INPROC, MAKELONG(MSHCTX_INPROC, 0xffff),0,0,0);
 
     /* All three are marshalled as remote */
-    marshal_WdtpInterfacePointer(MSHCTX_INPROC, MSHCTX_DIFFERENTMACHINE);
-    marshal_WdtpInterfacePointer(MSHCTX_DIFFERENTMACHINE, MSHCTX_DIFFERENTMACHINE);
-    marshal_WdtpInterfacePointer(MSHCTX_INPROC, MAKELONG(MSHCTX_DIFFERENTMACHINE, 0xffff));
+    marshal_WdtpInterfacePointer(MSHCTX_INPROC, MSHCTX_DIFFERENTMACHINE,0,0,0);
+    marshal_WdtpInterfacePointer(MSHCTX_DIFFERENTMACHINE, MSHCTX_DIFFERENTMACHINE,0,0,0);
+    marshal_WdtpInterfacePointer(MSHCTX_INPROC, MAKELONG(MSHCTX_DIFFERENTMACHINE, 0xffff),0,0,0);
+
+    /* Test different combinations of client, in and out */
+    marshal_WdtpInterfacePointer(MSHCTX_INPROC, MSHCTX_DIFFERENTMACHINE,0,0,1);
+    marshal_WdtpInterfacePointer(MSHCTX_INPROC, MSHCTX_DIFFERENTMACHINE,0,1,0);
+    marshal_WdtpInterfacePointer(MSHCTX_INPROC, MSHCTX_DIFFERENTMACHINE,0,1,1);
+    marshal_WdtpInterfacePointer(MSHCTX_INPROC, MSHCTX_DIFFERENTMACHINE,1,0,0);
+    marshal_WdtpInterfacePointer(MSHCTX_INPROC, MSHCTX_DIFFERENTMACHINE,1,0,1);
+    marshal_WdtpInterfacePointer(MSHCTX_INPROC, MSHCTX_DIFFERENTMACHINE,1,1,0);
+    marshal_WdtpInterfacePointer(MSHCTX_INPROC, MSHCTX_DIFFERENTMACHINE,1,1,1);
 }
 
-static void test_marshal_STGMEDIUM(void)
+static void marshal_STGMEDIUM(BOOL client, BOOL in, BOOL out)
 {
     USER_MARSHAL_CB umcb;
     MIDL_STUB_MESSAGE stub_msg;
@@ -682,13 +797,17 @@ static void test_marshal_STGMEDIUM(void)
     ULONG size, expect_size;
     STGMEDIUM med, med2;
     IUnknown *unk = &Test_Unknown.IUnknown_iface;
-    IStream *stm = &Test_Stream;
+    IStream *stm = &Test_Stream.IStream_iface;
 
     /* TYMED_NULL with pUnkForRelease */
 
+    Test_Unknown.refs = 1;
+
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_DIFFERENTMACHINE);
     expect_size = WdtpInterfacePointer_UserSize(&umcb.Flags, umcb.Flags, 2 * sizeof(DWORD), unk, &IID_IUnknown);
     expect_buffer = HeapAlloc(GetProcessHeap(), 0, expect_size);
+    *(DWORD*)expect_buffer = TYMED_NULL;
+    *((DWORD*)expect_buffer + 1) = 0xdeadbeef;
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, expect_buffer, expect_size, MSHCTX_DIFFERENTMACHINE);
     expect_buffer_end = WdtpInterfacePointer_UserMarshal(&umcb.Flags, umcb.Flags, expect_buffer + 2 * sizeof(DWORD), unk, &IID_IUnknown);
 
@@ -709,26 +828,42 @@ static void test_marshal_STGMEDIUM(void)
     ok(!memcmp(buffer+8, expect_buffer + 8, expect_buffer_end - expect_buffer - 8), "buffer mismatch\n");
 
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_DIFFERENTMACHINE);
+    umcb.pStubMsg->IsClient = client;
+    umcb.pStubMsg->fIsIn = in;
+    umcb.pStubMsg->fIsOut = out;
 
-    /* native crashes if this is uninitialised, presumably because it
-       tries to release it */
+    Test_Unknown2.refs = 1;
     med2.tymed = TYMED_NULL;
     U(med2).pstm = NULL;
-    med2.pUnkForRelease = NULL;
+    med2.pUnkForRelease = &Test_Unknown2.IUnknown_iface;
 
     STGMEDIUM_UserUnmarshal(&umcb.Flags, buffer, &med2);
 
     ok(med2.tymed == TYMED_NULL, "got tymed %x\n", med2.tymed);
     ok(med2.pUnkForRelease != NULL, "Incorrectly unmarshalled\n");
+    ok(Test_Unknown2.refs == 0, "got %d\n", Test_Unknown2.refs);
 
     HeapFree(GetProcessHeap(), 0, buffer);
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_DIFFERENTMACHINE);
     STGMEDIUM_UserFree(&umcb.Flags, &med2);
 
+    init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, expect_buffer, expect_size, MSHCTX_DIFFERENTMACHINE);
+    med2.tymed = TYMED_NULL;
+    U(med2).pstm = NULL;
+    med2.pUnkForRelease = NULL;
+    STGMEDIUM_UserUnmarshal(&umcb.Flags, expect_buffer, &med2);
+    init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_DIFFERENTMACHINE);
+    STGMEDIUM_UserFree(&umcb.Flags, &med2);
+
+    ok(Test_Unknown.refs == 1, "got %d\n", Test_Unknown.refs);
+
     HeapFree(GetProcessHeap(), 0, expect_buffer);
 
     /* TYMED_ISTREAM with pUnkForRelease */
 
+    Test_Unknown.refs = 1;
+    Test_Stream.refs = 1;
+
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_DIFFERENTMACHINE);
     expect_size = WdtpInterfacePointer_UserSize(&umcb.Flags, umcb.Flags, 3 * sizeof(DWORD), (IUnknown*)stm, &IID_IStream);
     expect_size = WdtpInterfacePointer_UserSize(&umcb.Flags, umcb.Flags, expect_size, unk, &IID_IUnknown);
@@ -736,6 +871,9 @@ static void test_marshal_STGMEDIUM(void)
     expect_buffer = HeapAlloc(GetProcessHeap(), 0, expect_size);
     /* There may be a hole between the two interfaces so init the buffer to something */
     memset(expect_buffer, 0xcc, expect_size);
+    *(DWORD*)expect_buffer = TYMED_ISTREAM;
+    *((DWORD*)expect_buffer + 1) = 0xdeadbeef;
+    *((DWORD*)expect_buffer + 2) = 0xcafe;
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, expect_buffer, expect_size, MSHCTX_DIFFERENTMACHINE);
     expect_buffer_end = WdtpInterfacePointer_UserMarshal(&umcb.Flags, umcb.Flags, expect_buffer + 3 * sizeof(DWORD), (IUnknown*)stm, &IID_IStream);
     expect_buffer_end = WdtpInterfacePointer_UserMarshal(&umcb.Flags, umcb.Flags, expect_buffer_end, unk, &IID_IUnknown);
@@ -759,24 +897,98 @@ static void test_marshal_STGMEDIUM(void)
     ok(!memcmp(buffer + 12, expect_buffer + 12, (buffer_end - buffer) - 12), "buffer mismatch\n");
 
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_DIFFERENTMACHINE);
+    umcb.pStubMsg->IsClient = client;
+    umcb.pStubMsg->fIsIn = in;
+    umcb.pStubMsg->fIsOut = out;
 
-    /* native crashes if this is uninitialised, presumably because it
-       tries to release it */
-    med2.tymed = TYMED_NULL;
-    U(med2).pstm = NULL;
-    med2.pUnkForRelease = NULL;
+    Test_Stream2.refs = 1;
+    Test_Unknown2.refs = 1;
+    med2.tymed = TYMED_ISTREAM;
+    U(med2).pstm = &Test_Stream2.IStream_iface;
+    med2.pUnkForRelease = &Test_Unknown2.IUnknown_iface;
 
     STGMEDIUM_UserUnmarshal(&umcb.Flags, buffer, &med2);
 
     ok(med2.tymed == TYMED_ISTREAM, "got tymed %x\n", med2.tymed);
     ok(U(med2).pstm != NULL, "Incorrectly unmarshalled\n");
     ok(med2.pUnkForRelease != NULL, "Incorrectly unmarshalled\n");
+    ok(Test_Stream2.refs == 0, "got %d\n", Test_Stream2.refs);
+    ok(Test_Unknown2.refs == 0, "got %d\n", Test_Unknown2.refs);
 
     HeapFree(GetProcessHeap(), 0, buffer);
     init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_DIFFERENTMACHINE);
     STGMEDIUM_UserFree(&umcb.Flags, &med2);
 
+    init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, expect_buffer, expect_size, MSHCTX_DIFFERENTMACHINE);
+    med2.tymed = TYMED_NULL;
+    U(med2).pstm = NULL;
+    med2.pUnkForRelease = NULL;
+    STGMEDIUM_UserUnmarshal(&umcb.Flags, expect_buffer, &med2);
+    init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_DIFFERENTMACHINE);
+    STGMEDIUM_UserFree(&umcb.Flags, &med2);
+
+    ok(Test_Unknown.refs == 1, "got %d\n", Test_Unknown.refs);
+    ok(Test_Stream.refs == 1, "got %d\n", Test_Stream.refs);
+
     HeapFree(GetProcessHeap(), 0, expect_buffer);
+
+    /* TYMED_ISTREAM = NULL with pUnkForRelease = NULL */
+
+    init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_DIFFERENTMACHINE);
+    expect_size = 3 * sizeof(DWORD);
+
+    med.tymed = TYMED_ISTREAM;
+    U(med).pstm = NULL;
+    med.pUnkForRelease = NULL;
+
+    init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_DIFFERENTMACHINE);
+    size = STGMEDIUM_UserSize(&umcb.Flags, 0, &med);
+    ok(size == expect_size, "size %d should be %d bytes\n", size, expect_size);
+
+    buffer = HeapAlloc(GetProcessHeap(), 0, size);
+    memset(buffer, 0xcc, size);
+    init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_DIFFERENTMACHINE);
+    buffer_end = STGMEDIUM_UserMarshal(&umcb.Flags, buffer, &med);
+    ok(buffer_end - buffer == expect_size, "buffer size mismatch\n");
+    ok(*(DWORD*)buffer == TYMED_ISTREAM, "got %08x\n", *(DWORD*)buffer);
+    ok(*((DWORD*)buffer+1) == 0, "got %08x\n", *((DWORD*)buffer+1));
+    ok(*((DWORD*)buffer+2) == 0, "got %08x\n", *((DWORD*)buffer+2));
+
+    init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, buffer, size, MSHCTX_DIFFERENTMACHINE);
+    umcb.pStubMsg->IsClient = client;
+    umcb.pStubMsg->fIsIn = in;
+    umcb.pStubMsg->fIsOut = out;
+
+    Test_Stream2.refs = 1;
+    Test_Unknown2.refs = 1;
+    med2.tymed = TYMED_ISTREAM;
+    U(med2).pstm = &Test_Stream2.IStream_iface;
+    med2.pUnkForRelease = &Test_Unknown2.IUnknown_iface;
+
+    STGMEDIUM_UserUnmarshal(&umcb.Flags, buffer, &med2);
+
+    ok(med2.tymed == TYMED_ISTREAM, "got tymed %x\n", med2.tymed);
+    ok(U(med2).pstm == NULL, "Incorrectly unmarshalled\n");
+    ok(med2.pUnkForRelease == &Test_Unknown2.IUnknown_iface, "Incorrectly unmarshalled\n");
+    ok(Test_Stream2.refs == 0, "got %d\n", Test_Stream2.refs);
+    ok(Test_Unknown2.refs == 1, "got %d\n", Test_Unknown2.refs);
+
+    HeapFree(GetProcessHeap(), 0, buffer);
+    init_user_marshal_cb(&umcb, &stub_msg, &rpc_msg, NULL, 0, MSHCTX_DIFFERENTMACHINE);
+    STGMEDIUM_UserFree(&umcb.Flags, &med2);
+}
+
+static void test_marshal_STGMEDIUM(void)
+{
+    marshal_STGMEDIUM(0, 0, 0);
+    marshal_STGMEDIUM(0, 0, 1);
+    marshal_STGMEDIUM(0, 1, 0);
+    marshal_STGMEDIUM(0, 1, 1);
+    /* For Windows versions post 2003, client side, non-[in,out] STGMEDIUMs get zero-initialised.
+       However since inline stubs don't set fIsIn or fIsOut this behaviour would break
+       ref counting in GetDataHere_Proxy for example, as we'd end up not releasing the original
+       interface.  For simplicity we don't test or implement this. */
+    marshal_STGMEDIUM(1, 1, 1);
 }
 
 static void test_marshal_SNB(void)
@@ -984,9 +1196,156 @@ static void test_marshal_HBRUSH(void)
     DeleteObject(hBrush);
 }
 
+struct obj
+{
+    IDataObject IDataObject_iface;
+};
+
+static HRESULT WINAPI obj_QueryInterface(IDataObject *iface, REFIID iid, void **obj)
+{
+    *obj = NULL;
+
+    if (IsEqualGUID(iid, &IID_IUnknown) ||
+        IsEqualGUID(iid, &IID_IDataObject))
+        *obj = iface;
+
+    if (*obj)
+    {
+        IDataObject_AddRef(iface);
+        return S_OK;
+    }
+
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI obj_AddRef(IDataObject *iface)
+{
+    return 2;
+}
+
+static ULONG WINAPI obj_Release(IDataObject *iface)
+{
+    return 1;
+}
+
+static HRESULT WINAPI obj_DO_GetDataHere(IDataObject *iface, FORMATETC *fmt,
+                                         STGMEDIUM *med)
+{
+    ok( med->pUnkForRelease == NULL, "got %p\n", med->pUnkForRelease );
+
+    if (fmt->cfFormat == 2)
+    {
+        IStream_Release(U(med)->pstm);
+        U(med)->pstm = &Test_Stream2.IStream_iface;
+    }
+
+    return S_OK;
+}
+
+static const IDataObjectVtbl obj_data_object_vtbl =
+{
+    obj_QueryInterface,
+    obj_AddRef,
+    obj_Release,
+    NULL, /* GetData */
+    obj_DO_GetDataHere,
+    NULL, /* QueryGetData */
+    NULL, /* GetCanonicalFormatEtc */
+    NULL, /* SetData */
+    NULL, /* EnumFormatEtc */
+    NULL, /* DAdvise */
+    NULL, /* DUnadvise */
+    NULL  /* EnumDAdvise */
+};
+
+static struct obj obj =
+{
+    {&obj_data_object_vtbl}
+};
+
+static void test_GetDataHere_Proxy(void)
+{
+    HRESULT hr;
+    IStream *stm;
+    HANDLE thread;
+    DWORD tid;
+    static const LARGE_INTEGER zero;
+    IDataObject *data;
+    FORMATETC fmt;
+    STGMEDIUM med;
+
+    hr = CreateStreamOnHGlobal( NULL, TRUE, &stm );
+    ok( hr == S_OK, "got %08x\n", hr );
+    tid = start_host_object2( stm, &IID_IDataObject, (IUnknown *)&obj.IDataObject_iface, MSHLFLAGS_NORMAL, NULL, &thread );
+
+    IStream_Seek( stm, zero, STREAM_SEEK_SET, NULL );
+    hr = CoUnmarshalInterface( stm, &IID_IDataObject, (void **)&data );
+    ok( hr == S_OK, "got %08x\n", hr );
+    IStream_Release( stm );
+
+    Test_Stream.refs = 1;
+    Test_Stream2.refs = 1;
+    Test_Unknown.refs = 1;
+
+    fmt.cfFormat = 1;
+    fmt.ptd = NULL;
+    fmt.dwAspect = DVASPECT_CONTENT;
+    fmt.lindex = -1;
+    U(med).pstm = NULL;
+    med.pUnkForRelease = &Test_Unknown.IUnknown_iface;
+
+    fmt.tymed = med.tymed = TYMED_NULL;
+    hr = IDataObject_GetDataHere( data, &fmt, &med );
+    ok( hr == DV_E_TYMED, "got %08x\n", hr );
+
+    for (fmt.tymed = TYMED_HGLOBAL; fmt.tymed <= TYMED_ENHMF; fmt.tymed <<= 1)
+    {
+        med.tymed = fmt.tymed;
+        hr = IDataObject_GetDataHere( data, &fmt, &med );
+        ok( hr == (fmt.tymed <= TYMED_ISTORAGE ? S_OK : DV_E_TYMED), "got %08x for tymed %d\n", hr, fmt.tymed );
+        ok( Test_Unknown.refs == 1, "got %d\n", Test_Unknown.refs );
+    }
+
+    fmt.tymed = TYMED_ISTREAM;
+    med.tymed = TYMED_ISTORAGE;
+    hr = IDataObject_GetDataHere( data, &fmt, &med );
+    ok( hr == DV_E_TYMED, "got %08x\n", hr );
+
+    fmt.tymed = med.tymed = TYMED_ISTREAM;
+    U(med).pstm = &Test_Stream.IStream_iface;
+    med.pUnkForRelease = &Test_Unknown.IUnknown_iface;
+
+    hr = IDataObject_GetDataHere( data, &fmt, &med );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    ok( U(med).pstm == &Test_Stream.IStream_iface, "stm changed\n" );
+    ok( med.pUnkForRelease == &Test_Unknown.IUnknown_iface, "punk changed\n" );
+
+    ok( Test_Stream.refs == 1, "got %d\n", Test_Stream.refs );
+    ok( Test_Unknown.refs == 1, "got %d\n", Test_Unknown.refs );
+
+    fmt.cfFormat = 2;
+    fmt.tymed = med.tymed = TYMED_ISTREAM;
+    U(med).pstm = &Test_Stream.IStream_iface;
+    med.pUnkForRelease = &Test_Unknown.IUnknown_iface;
+
+    hr = IDataObject_GetDataHere( data, &fmt, &med );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    ok( U(med).pstm == &Test_Stream.IStream_iface, "stm changed\n" );
+    ok( med.pUnkForRelease == &Test_Unknown.IUnknown_iface, "punk changed\n" );
+
+    ok( Test_Stream.refs == 1, "got %d\n", Test_Stream.refs );
+    ok( Test_Unknown.refs == 1, "got %d\n", Test_Unknown.refs );
+    ok( Test_Stream2.refs == 0, "got %d\n", Test_Stream2.refs );
+
+    IDataObject_Release( data );
+    end_host_object( tid, thread );
+}
+
 START_TEST(usrmarshal)
 {
-    CoInitialize(NULL);
+    CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
 
     test_marshal_CLIPFORMAT();
     test_marshal_HWND();
@@ -1001,5 +1360,7 @@ START_TEST(usrmarshal)
     test_marshal_HICON();
     test_marshal_HBRUSH();
 
+    test_GetDataHere_Proxy();
+
     CoUninitialize();
 }