[OLE32_WINETEST] Sync with Wine Staging 3.9. CORE-14656
authorAmine Khaldi <amine.khaldi@reactos.org>
Mon, 4 Jun 2018 02:46:11 +0000 (03:46 +0100)
committerAmine Khaldi <amine.khaldi@reactos.org>
Mon, 4 Jun 2018 02:46:11 +0000 (03:46 +0100)
modules/rostests/winetests/ole32/compobj.c
modules/rostests/winetests/ole32/marshal.c
modules/rostests/winetests/ole32/ole2.c

index 06d1914..aedbc08 100644 (file)
@@ -608,28 +608,8 @@ static void test_StringFromGUID2(void)
   ok(len == 0, "len: %d (expected 0)\n", len);
 }
 
-struct info
-{
-    HANDLE wait, stop;
-};
-
-static DWORD CALLBACK ole_initialize_thread(LPVOID pv)
-{
-    HRESULT hr;
-    struct info *info = pv;
-
-    hr = pCoInitializeEx(NULL, COINIT_MULTITHREADED);
-
-    SetEvent(info->wait);
-    WaitForSingleObject(info->stop, 10000);
-
-    CoUninitialize();
-    return hr;
-}
-
-#define test_apt_type(t, q, t_t, t_q) _test_apt_type(t, q, t_t, t_q, __LINE__)
-static void _test_apt_type(APTTYPE expected_type, APTTYPEQUALIFIER expected_qualifier, BOOL todo_type,
-        BOOL todo_qualifier, int line)
+#define test_apt_type(t, q) _test_apt_type(t, q, __LINE__)
+static void _test_apt_type(APTTYPE expected_type, APTTYPEQUALIFIER expected_qualifier, int line)
 {
     APTTYPEQUALIFIER qualifier = ~0u;
     APTTYPE type = ~0u;
@@ -640,9 +620,7 @@ static void _test_apt_type(APTTYPE expected_type, APTTYPEQUALIFIER expected_qual
 
     hr = pCoGetApartmentType(&type, &qualifier);
     ok_(__FILE__, line)(hr == S_OK || hr == CO_E_NOTINITIALIZED, "Unexpected return code: 0x%08x\n", hr);
-todo_wine_if(todo_type)
     ok_(__FILE__, line)(type == expected_type, "Wrong apartment type %d, expected %d\n", type, expected_type);
-todo_wine_if(todo_qualifier)
     ok_(__FILE__, line)(qualifier == expected_qualifier, "Wrong apartment qualifier %d, expected %d\n", qualifier,
         expected_qualifier);
 }
@@ -650,10 +628,7 @@ todo_wine_if(todo_qualifier)
 static void test_CoCreateInstance(void)
 {
     HRESULT hr;
-    HANDLE thread;
-    DWORD tid, exitcode;
     IUnknown *pUnk;
-    struct info info;
     REFCLSID rclsid = &CLSID_InternetZoneManager;
 
     pUnk = (IUnknown *)0xdeadbeef;
@@ -688,51 +663,15 @@ static void test_CoCreateInstance(void)
     hr = CoCreateInstance(rclsid, NULL, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void **)&pUnk);
     ok(hr == CO_E_NOTINITIALIZED, "CoCreateInstance should have returned CO_E_NOTINITIALIZED instead of 0x%08x\n", hr);
 
-    /* show that COM doesn't have to be initialized for multi-threaded apartments if another
-       thread has already done so */
-
-    test_apt_type(APTTYPE_CURRENT, APTTYPEQUALIFIER_NONE, FALSE, FALSE);
-
-    info.wait = CreateEventA(NULL, TRUE, FALSE, NULL);
-    ok(info.wait != NULL, "CreateEvent failed with error %d\n", GetLastError());
-
-    info.stop = CreateEventA(NULL, TRUE, FALSE, NULL);
-    ok(info.stop != NULL, "CreateEvent failed with error %d\n", GetLastError());
-
-    thread = CreateThread(NULL, 0, ole_initialize_thread, &info, 0, &tid);
-    ok(thread != NULL, "CreateThread failed with error %d\n", GetLastError());
-
-    ok( !WaitForSingleObject(info.wait, 10000 ), "wait timed out\n" );
-
-    test_apt_type(APTTYPE_MTA, APTTYPEQUALIFIER_IMPLICIT_MTA, TRUE, TRUE);
-
-    pUnk = (IUnknown *)0xdeadbeef;
-    hr = CoCreateInstance(rclsid, NULL, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void **)&pUnk);
-    ok(hr == S_OK, "CoCreateInstance should have returned S_OK instead of 0x%08x\n", hr);
-    if (pUnk) IUnknown_Release(pUnk);
-
-    SetEvent(info.stop);
-    ok( !WaitForSingleObject(thread, 10000), "wait timed out\n" );
-
-    GetExitCodeThread(thread, &exitcode);
-    hr = exitcode;
-    ok(hr == S_OK, "thread should have returned S_OK instead of 0x%08x\n", hr);
-
-    CloseHandle(thread);
-    CloseHandle(info.wait);
-    CloseHandle(info.stop);
-
-    test_apt_type(APTTYPE_CURRENT, APTTYPEQUALIFIER_NONE, FALSE, FALSE);
+    test_apt_type(APTTYPE_CURRENT, APTTYPEQUALIFIER_NONE);
 }
 
 static void test_CoGetClassObject(void)
 {
     HRESULT hr;
-    HANDLE thread, handle;
-    DWORD tid, exitcode;
+    HANDLE handle;
     ULONG_PTR cookie;
     IUnknown *pUnk;
-    struct info info;
     REFCLSID rclsid = &CLSID_InternetZoneManager;
     HKEY hkey;
     LONG res;
@@ -746,46 +685,7 @@ static void test_CoGetClassObject(void)
        broken(hr == CO_E_NOTINITIALIZED), /* win9x */
        "CoGetClassObject should have returned E_INVALIDARG instead of 0x%08x\n", hr);
 
-    /* show that COM doesn't have to be initialized for multi-threaded apartments if another
-       thread has already done so */
-
-    test_apt_type(APTTYPE_CURRENT, APTTYPEQUALIFIER_NONE, FALSE, FALSE);
-
-    info.wait = CreateEventA(NULL, TRUE, FALSE, NULL);
-    ok(info.wait != NULL, "CreateEvent failed with error %d\n", GetLastError());
-
-    info.stop = CreateEventA(NULL, TRUE, FALSE, NULL);
-    ok(info.stop != NULL, "CreateEvent failed with error %d\n", GetLastError());
-
-    thread = CreateThread(NULL, 0, ole_initialize_thread, &info, 0, &tid);
-    ok(thread != NULL, "CreateThread failed with error %d\n", GetLastError());
-
-    ok( !WaitForSingleObject(info.wait, 10000), "wait timed out\n" );
-
-    test_apt_type(APTTYPE_MTA, APTTYPEQUALIFIER_IMPLICIT_MTA, TRUE, TRUE);
-
-    pUnk = (IUnknown *)0xdeadbeef;
-    hr = CoGetClassObject(rclsid, CLSCTX_INPROC_SERVER, NULL, &IID_IUnknown, (void **)&pUnk);
-    if(hr == REGDB_E_CLASSNOTREG)
-        skip("IE not installed so can't test CoGetClassObject\n");
-    else
-    {
-        ok(hr == S_OK, "CoGetClassObject should have returned S_OK instead of 0x%08x\n", hr);
-        if (pUnk) IUnknown_Release(pUnk);
-    }
-
-    SetEvent(info.stop);
-    ok( !WaitForSingleObject(thread, 10000), "wait timed out\n" );
-
-    GetExitCodeThread(thread, &exitcode);
-    hr = exitcode;
-    ok(hr == S_OK, "thread should have returned S_OK instead of 0x%08x\n", hr);
-
-    CloseHandle(thread);
-    CloseHandle(info.wait);
-    CloseHandle(info.stop);
-
-    test_apt_type(APTTYPE_CURRENT, APTTYPEQUALIFIER_NONE, FALSE, FALSE);
+    test_apt_type(APTTYPE_CURRENT, APTTYPEQUALIFIER_NONE);
 
     if (!pRegOverridePredefKey)
     {
@@ -1886,9 +1786,6 @@ static void test_CoGetObjectContext(void)
     IObjContext *pObjContext;
     APTTYPE apttype;
     THDTYPE thdtype;
-    struct info info;
-    HANDLE thread;
-    DWORD tid, exitcode;
     GUID id, id2;
 
     if (!pCoGetObjectContext)
@@ -1901,27 +1798,12 @@ static void test_CoGetObjectContext(void)
     ok(hr == CO_E_NOTINITIALIZED, "CoGetObjectContext should have returned CO_E_NOTINITIALIZED instead of 0x%08x\n", hr);
     ok(pComThreadingInfo == NULL, "pComThreadingInfo should have been set to NULL\n");
 
-    /* show that COM doesn't have to be initialized for multi-threaded apartments if another
-       thread has already done so */
-
-    test_apt_type(APTTYPE_CURRENT, APTTYPEQUALIFIER_NONE, FALSE, FALSE);
-
-    info.wait = CreateEventA(NULL, TRUE, FALSE, NULL);
-    ok(info.wait != NULL, "CreateEvent failed with error %d\n", GetLastError());
-
-    info.stop = CreateEventA(NULL, TRUE, FALSE, NULL);
-    ok(info.stop != NULL, "CreateEvent failed with error %d\n", GetLastError());
-
-    thread = CreateThread(NULL, 0, ole_initialize_thread, &info, 0, &tid);
-    ok(thread != NULL, "CreateThread failed with error %d\n", GetLastError());
-
-    ok( !WaitForSingleObject(info.wait, 10000), "wait timed out\n" );
+    pCoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
 
-    test_apt_type(APTTYPE_MTA, APTTYPEQUALIFIER_IMPLICIT_MTA, TRUE, TRUE);
+    test_apt_type(APTTYPE_MAINSTA, APTTYPEQUALIFIER_NONE);
 
-    pComThreadingInfo = NULL;
     hr = pCoGetObjectContext(&IID_IComThreadingInfo, (void **)&pComThreadingInfo);
-    ok(hr == S_OK, "Expected S_OK, got 0x%08x\n", hr);
+    ok_ole_success(hr, "CoGetObjectContext");
 
     threadinginfo2 = NULL;
     hr = pCoGetObjectContext(&IID_IComThreadingInfo, (void **)&threadinginfo2);
@@ -1939,28 +1821,6 @@ static void test_CoGetObjectContext(void)
     hr = CoGetCurrentLogicalThreadId(&id2);
     ok(IsEqualGUID(&id, &id2), "got %s, expected %s\n", wine_dbgstr_guid(&id), wine_dbgstr_guid(&id2));
 
-    IComThreadingInfo_Release(pComThreadingInfo);
-
-    SetEvent(info.stop);
-    ok( !WaitForSingleObject(thread, 10000), "wait timed out\n" );
-
-    GetExitCodeThread(thread, &exitcode);
-    hr = exitcode;
-    ok(hr == S_OK, "thread should have returned S_OK instead of 0x%08x\n", hr);
-
-    CloseHandle(thread);
-    CloseHandle(info.wait);
-    CloseHandle(info.stop);
-
-    test_apt_type(APTTYPE_CURRENT, APTTYPEQUALIFIER_NONE, FALSE, FALSE);
-
-    pCoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
-
-    test_apt_type(APTTYPE_MAINSTA, APTTYPEQUALIFIER_NONE, FALSE, FALSE);
-
-    hr = pCoGetObjectContext(&IID_IComThreadingInfo, (void **)&pComThreadingInfo);
-    ok_ole_success(hr, "CoGetObjectContext");
-
     hr = IComThreadingInfo_GetCurrentApartmentType(pComThreadingInfo, &apttype);
     ok_ole_success(hr, "IComThreadingInfo_GetCurrentApartmentType");
     ok(apttype == APTTYPE_MAINSTA, "apartment type should be APTTYPE_MAINSTA instead of %d\n", apttype);
@@ -2124,9 +1984,6 @@ static void test_CoGetContextToken(void)
     ULONG refs;
     ULONG_PTR token, token2;
     IObjContext *ctx;
-    struct info info;
-    HANDLE thread;
-    DWORD tid, exitcode;
 
     if (!pCoGetContextToken)
     {
@@ -2139,49 +1996,11 @@ static void test_CoGetContextToken(void)
     ok(hr == CO_E_NOTINITIALIZED, "Expected CO_E_NOTINITIALIZED, got 0x%08x\n", hr);
     ok(token == 0xdeadbeef, "Expected 0, got 0x%lx\n", token);
 
-    /* show that COM doesn't have to be initialized for multi-threaded apartments if another
-       thread has already done so */
-
-    test_apt_type(APTTYPE_CURRENT, APTTYPEQUALIFIER_NONE, FALSE, FALSE);
-
-    info.wait = CreateEventA(NULL, TRUE, FALSE, NULL);
-    ok(info.wait != NULL, "CreateEvent failed with error %d\n", GetLastError());
-
-    info.stop = CreateEventA(NULL, TRUE, FALSE, NULL);
-    ok(info.stop != NULL, "CreateEvent failed with error %d\n", GetLastError());
-
-    thread = CreateThread(NULL, 0, ole_initialize_thread, &info, 0, &tid);
-    ok(thread != NULL, "CreateThread failed with error %d\n", GetLastError());
-
-    ok( !WaitForSingleObject(info.wait, 10000), "wait timed out\n" );
-
-    test_apt_type(APTTYPE_MTA, APTTYPEQUALIFIER_IMPLICIT_MTA, TRUE, TRUE);
-
-    token = 0;
-    hr = pCoGetContextToken(&token);
-    ok(hr == S_OK, "Expected S_OK, got 0x%08x\n", hr);
-
-    token2 = 0;
-    hr = pCoGetContextToken(&token2);
-    ok(hr == S_OK, "Expected S_OK, got 0x%08x\n", hr);
-    ok(token == token2, "got different token\n");
-
-    SetEvent(info.stop);
-    ok( !WaitForSingleObject(thread, 10000), "wait timed out\n" );
-
-    GetExitCodeThread(thread, &exitcode);
-    hr = exitcode;
-    ok(hr == S_OK, "thread should have returned S_OK instead of 0x%08x\n", hr);
-
-    CloseHandle(thread);
-    CloseHandle(info.wait);
-    CloseHandle(info.stop);
-
-    test_apt_type(APTTYPE_CURRENT, APTTYPEQUALIFIER_NONE, FALSE, FALSE);
+    test_apt_type(APTTYPE_CURRENT, APTTYPEQUALIFIER_NONE);
 
     CoInitialize(NULL);
 
-    test_apt_type(APTTYPE_MAINSTA, APTTYPEQUALIFIER_NONE, FALSE, FALSE);
+    test_apt_type(APTTYPE_MAINSTA, APTTYPEQUALIFIER_NONE);
 
     hr = pCoGetContextToken(NULL);
     ok(hr == E_POINTER, "Expected E_POINTER, got 0x%08x\n", hr);
@@ -3185,14 +3004,36 @@ static void test_CoWaitForMultipleHandles(void)
     ok(index == WAIT_OBJECT_0, "WaitForSingleObject failed\n");
     CloseHandle(thread);
 
+    CoUninitialize();
+
+    /* If COM was not initialized, messages are neither pumped nor peeked at */
+    PostMessageA(hWnd, WM_DDE_FIRST, 0, 0);
+    hr = CoWaitForMultipleHandles(0, 100, 2, handles, &index);
+    ok(hr == RPC_S_CALLPENDING, "got %#x\n", hr);
+    success = MsgWaitForMultipleObjectsEx(0, NULL, 2, QS_ALLPOSTMESSAGE, MWMO_ALERTABLE);
+    ok(success == 0, "MsgWaitForMultipleObjects returned %x\n", success);
+    success = PeekMessageA(&msg, hWnd, WM_DDE_FIRST, WM_DDE_FIRST, PM_REMOVE);
+    ok(success, "PeekMessage failed: %u\n", GetLastError());
+
+    /* same in an MTA */
+    CoInitializeEx(NULL, COINIT_MULTITHREADED);
+
+    PostMessageA(hWnd, WM_DDE_FIRST, 0, 0);
+    hr = CoWaitForMultipleHandles(0, 100, 2, handles, &index);
+    ok(hr == RPC_S_CALLPENDING, "got %#x\n", hr);
+    success = MsgWaitForMultipleObjectsEx(0, NULL, 2, QS_ALLPOSTMESSAGE, MWMO_ALERTABLE);
+    ok(success == 0, "MsgWaitForMultipleObjects returned %x\n", success);
+    success = PeekMessageA(&msg, hWnd, WM_DDE_FIRST, WM_DDE_FIRST, PM_REMOVE);
+    ok(success, "PeekMessage failed: %u\n", GetLastError());
+
+    CoUninitialize();
+
     CloseHandle(handles[0]);
     CloseHandle(handles[1]);
     DestroyWindow(hWnd);
 
     success = UnregisterClassA(cls_name, GetModuleHandleA(0));
     ok(success, "UnregisterClass failed %u\n", GetLastError());
-
-    CoUninitialize();
 }
 
 static void test_CoGetMalloc(void)
@@ -3880,6 +3721,71 @@ static void init_funcs(void)
     pReleaseActCtx = (void*)GetProcAddress(hkernel32, "ReleaseActCtx");
 }
 
+static DWORD CALLBACK implicit_mta_proc(void *param)
+{
+    IComThreadingInfo *threading_info;
+    ULONG_PTR token;
+    IUnknown *unk;
+    DWORD cookie;
+    CLSID clsid;
+    HRESULT hr;
+
+    test_apt_type(APTTYPE_MTA, APTTYPEQUALIFIER_IMPLICIT_MTA);
+
+    hr = CoCreateInstance(&CLSID_InternetZoneManager, NULL, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void **)&unk);
+    ok_ole_success(hr, "CoCreateInstance");
+    IUnknown_Release(unk);
+
+    hr = CoGetClassObject(&CLSID_InternetZoneManager, CLSCTX_INPROC_SERVER, NULL, &IID_IUnknown, (void **)&unk);
+    ok_ole_success(hr, "CoGetClassObject");
+    IUnknown_Release(unk);
+
+    hr = CoGetObjectContext(&IID_IComThreadingInfo, (void **)&threading_info);
+    ok_ole_success(hr, "CoGetObjectContext");
+    IComThreadingInfo_Release(threading_info);
+
+    hr = CoGetContextToken(&token);
+    ok_ole_success(hr, "CoGetContextToken");
+
+    hr = CoRegisterPSClsid(&IID_IWineTest, &CLSID_WineTestPSFactoryBuffer);
+    ok_ole_success(hr, "CoRegisterPSClsid");
+
+    hr = CoGetPSClsid(&IID_IClassFactory, &clsid);
+    ok_ole_success(hr, "CoGetPSClsid");
+
+    hr = CoRegisterClassObject(&CLSID_WineOOPTest, (IUnknown *)&Test_ClassFactory,
+                               CLSCTX_INPROC_SERVER, REGCLS_SINGLEUSE, &cookie);
+    ok_ole_success(hr, "CoRegisterClassObject");
+
+    hr = CoRevokeClassObject(cookie);
+    ok_ole_success(hr, "CoRevokeClassObject");
+
+    hr = CoRegisterMessageFilter(NULL, NULL);
+    ok(hr == CO_E_NOT_SUPPORTED, "got %#x\n", hr);
+
+    hr = CoLockObjectExternal((IUnknown *)&Test_Unknown, TRUE, TRUE);
+    ok_ole_success(hr, "CoLockObjectExternal");
+
+    hr = CoDisconnectObject((IUnknown *)&Test_Unknown, 0);
+    ok_ole_success(hr, "CoDisconnectObject");
+
+    return 0;
+}
+
+/* Some COM functions (perhaps even all of them?) can make use of an "implicit"
+ * multi-threaded apartment created by another thread in the same process. */
+static void test_implicit_mta(void)
+{
+    HANDLE thread;
+
+    CoInitializeEx(NULL, COINIT_MULTITHREADED);
+
+    thread = CreateThread(NULL, 0, implicit_mta_proc, NULL, 0, NULL);
+    ok(!WaitForSingleObject(thread, 1000), "wait failed\n");
+
+    CoUninitialize();
+}
+
 START_TEST(compobj)
 {
     init_funcs();
@@ -3929,4 +3835,5 @@ START_TEST(compobj)
     test_IInitializeSpy();
     test_CoGetInstanceFromFile();
     test_GlobalOptions();
+    test_implicit_mta();
 }
index 12c46e9..1fcf9a3 100644 (file)
 #include "initguid.h"
 
 #include "wine/test.h"
+#include "wine/heap.h"
+
+#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_GUID(CLSID_StdGlobalInterfaceTable,0x00000323,0x0000,0x0000,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
 DEFINE_GUID(CLSID_ManualResetEvent,       0x0000032c,0x0000,0x0000,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
 
+static const GUID CLSID_WineTestPSFactoryBuffer = { 0x22222222, 0x1234, 0x1234, { 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0 } };
+
 /* functions that are not present on all versions of Windows */
 static HRESULT (WINAPI * pCoInitializeEx)(LPVOID lpReserved, DWORD dwCoInit);
 static HRESULT (WINAPI *pDllGetClassObject)(REFCLSID,REFIID,LPVOID);
@@ -281,6 +308,253 @@ static const IClassFactoryVtbl TestClassFactory_Vtbl =
 
 static IClassFactory Test_ClassFactory = { &TestClassFactory_Vtbl };
 
+DEFINE_EXPECT(Invoke);
+DEFINE_EXPECT(CreateStub);
+DEFINE_EXPECT(CreateProxy);
+DEFINE_EXPECT(GetWindow);
+DEFINE_EXPECT(Disconnect);
+
+static HRESULT WINAPI OleWindow_QueryInterface(IOleWindow *iface, REFIID riid, void **ppv)
+{
+    ok(0, "unexpected call %s\n", wine_dbgstr_guid(riid));
+    *ppv = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI OleWindow_AddRef(IOleWindow *iface)
+{
+    return 2;
+}
+
+static ULONG WINAPI OleWindow_Release(IOleWindow *iface)
+{
+    return 1;
+}
+
+static HRESULT WINAPI OleWindow_GetWindow(IOleWindow *iface, HWND *hwnd)
+{
+    CHECK_EXPECT(GetWindow);
+    *hwnd = (HWND)0xdeadbeef;
+    return S_OK;
+}
+
+static const IOleWindowVtbl OleWindowVtbl = {
+    OleWindow_QueryInterface,
+    OleWindow_AddRef,
+    OleWindow_Release,
+    OleWindow_GetWindow,
+    /* not needed */
+};
+
+static IOleWindow Test_OleWindow = { &OleWindowVtbl };
+
+static HRESULT WINAPI OleClientSite_QueryInterface(IOleClientSite *iface, REFIID riid, void **ppv)
+{
+    if (IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_IOleClientSite))
+        *ppv = iface;
+    else if (IsEqualGUID(riid, &IID_IOleWindow))
+        *ppv = &Test_OleWindow;
+    else
+    {
+        *ppv = NULL;
+        return E_NOINTERFACE;
+    }
+
+    IUnknown_AddRef((IUnknown*)*ppv);
+    return S_OK;
+}
+
+static ULONG WINAPI OleClientSite_AddRef(IOleClientSite *iface)
+{
+    return 2;
+}
+
+static ULONG WINAPI OleClientSite_Release(IOleClientSite *iface)
+{
+    return 1;
+}
+
+static const IOleClientSiteVtbl OleClientSiteVtbl = {
+    OleClientSite_QueryInterface,
+    OleClientSite_AddRef,
+    OleClientSite_Release,
+    /* we don't need the rest, we never call it */
+};
+
+static IOleClientSite Test_OleClientSite = { &OleClientSiteVtbl };
+
+typedef struct {
+    IRpcStubBuffer IRpcStubBuffer_iface;
+    LONG ref;
+    IRpcStubBuffer *buffer;
+} StubBufferWrapper;
+
+static StubBufferWrapper *impl_from_IRpcStubBuffer(IRpcStubBuffer *iface)
+{
+    return CONTAINING_RECORD(iface, StubBufferWrapper, IRpcStubBuffer_iface);
+}
+
+static HRESULT WINAPI RpcStubBuffer_QueryInterface(IRpcStubBuffer *iface, REFIID riid, void **ppv)
+{
+    StubBufferWrapper *This = impl_from_IRpcStubBuffer(iface);
+
+    if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IRpcStubBuffer, riid)) {
+        *ppv = &This->IRpcStubBuffer_iface;
+    }else {
+        *ppv = NULL;
+        return E_NOINTERFACE;
+    }
+
+    IUnknown_AddRef((IUnknown*)*ppv);
+    return S_OK;
+}
+
+static ULONG WINAPI RpcStubBuffer_AddRef(IRpcStubBuffer *iface)
+{
+    StubBufferWrapper *This = impl_from_IRpcStubBuffer(iface);
+    return InterlockedIncrement(&This->ref);
+}
+
+static ULONG WINAPI RpcStubBuffer_Release(IRpcStubBuffer *iface)
+{
+    StubBufferWrapper *This = impl_from_IRpcStubBuffer(iface);
+    LONG ref = InterlockedDecrement(&This->ref);
+    if(!ref) {
+        IRpcStubBuffer_Release(This->buffer);
+        heap_free(This);
+    }
+    return ref;
+}
+
+static HRESULT WINAPI RpcStubBuffer_Connect(IRpcStubBuffer *iface, IUnknown *pUnkServer)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static void WINAPI RpcStubBuffer_Disconnect(IRpcStubBuffer *iface)
+{
+    CHECK_EXPECT(Disconnect);
+}
+
+static HRESULT WINAPI RpcStubBuffer_Invoke(IRpcStubBuffer *iface, RPCOLEMESSAGE *_prpcmsg,
+    IRpcChannelBuffer *_pRpcChannelBuffer)
+{
+    StubBufferWrapper *This = impl_from_IRpcStubBuffer(iface);
+    void *dest_context_data;
+    DWORD dest_context;
+    HRESULT hr;
+
+    CHECK_EXPECT(Invoke);
+
+    hr = IRpcChannelBuffer_GetDestCtx(_pRpcChannelBuffer, &dest_context, &dest_context_data);
+    ok(hr == S_OK, "GetDestCtx failed: %08x\n", hr);
+    ok(dest_context == MSHCTX_INPROC, "desc_context = %x\n", dest_context);
+    ok(!dest_context_data, "desc_context_data = %p\n", dest_context_data);
+
+    return IRpcStubBuffer_Invoke(This->buffer, _prpcmsg, _pRpcChannelBuffer);
+}
+
+static IRpcStubBuffer *WINAPI RpcStubBuffer_IsIIDSupported(IRpcStubBuffer *iface, REFIID riid)
+{
+    ok(0, "unexpected call\n");
+    return NULL;
+}
+
+static ULONG WINAPI RpcStubBuffer_CountRefs(IRpcStubBuffer *iface)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI RpcStubBuffer_DebugServerQueryInterface(IRpcStubBuffer *iface, void **ppv)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static void WINAPI RpcStubBuffer_DebugServerRelease(IRpcStubBuffer *iface, void *pv)
+{
+    ok(0, "unexpected call\n");
+}
+
+static const IRpcStubBufferVtbl RpcStubBufferVtbl = {
+    RpcStubBuffer_QueryInterface,
+    RpcStubBuffer_AddRef,
+    RpcStubBuffer_Release,
+    RpcStubBuffer_Connect,
+    RpcStubBuffer_Disconnect,
+    RpcStubBuffer_Invoke,
+    RpcStubBuffer_IsIIDSupported,
+    RpcStubBuffer_CountRefs,
+    RpcStubBuffer_DebugServerQueryInterface,
+    RpcStubBuffer_DebugServerRelease
+};
+
+static IPSFactoryBuffer *ps_factory_buffer;
+
+static HRESULT WINAPI PSFactoryBuffer_QueryInterface(IPSFactoryBuffer *iface, REFIID riid, void **ppv)
+{
+    if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IPSFactoryBuffer))
+        *ppv = iface;
+    else
+    {
+        *ppv = NULL;
+        return E_NOINTERFACE;
+    }
+    IUnknown_AddRef((IUnknown*)*ppv);
+    return S_OK;
+}
+
+static ULONG WINAPI PSFactoryBuffer_AddRef(IPSFactoryBuffer *iface)
+{
+    return 2;
+}
+
+static ULONG WINAPI PSFactoryBuffer_Release(IPSFactoryBuffer *iface)
+{
+    return 1;
+}
+
+static HRESULT WINAPI PSFactoryBuffer_CreateProxy(IPSFactoryBuffer *iface, IUnknown *outer,
+    REFIID riid, IRpcProxyBuffer **ppProxy, void **ppv)
+{
+    CHECK_EXPECT(CreateProxy);
+    return IPSFactoryBuffer_CreateProxy(ps_factory_buffer, outer, riid, ppProxy, ppv);
+}
+
+static HRESULT WINAPI PSFactoryBuffer_CreateStub(IPSFactoryBuffer *iface, REFIID riid,
+    IUnknown *server, IRpcStubBuffer **ppStub)
+{
+    StubBufferWrapper *stub;
+    HRESULT hr;
+
+    CHECK_EXPECT(CreateStub);
+
+    ok(server == (IUnknown*)&Test_OleClientSite, "unexpected server %p\n", server);
+
+    stub = heap_alloc(sizeof(*stub));
+    stub->IRpcStubBuffer_iface.lpVtbl = &RpcStubBufferVtbl;
+    stub->ref = 1;
+
+    hr = IPSFactoryBuffer_CreateStub(ps_factory_buffer, riid, server, &stub->buffer);
+    ok(hr == S_OK, "CreateStub failed: %08x\n", hr);
+
+    *ppStub = &stub->IRpcStubBuffer_iface;
+    return S_OK;
+}
+
+static IPSFactoryBufferVtbl PSFactoryBufferVtbl =
+{
+    PSFactoryBuffer_QueryInterface,
+    PSFactoryBuffer_AddRef,
+    PSFactoryBuffer_Release,
+    PSFactoryBuffer_CreateProxy,
+    PSFactoryBuffer_CreateStub
+};
+
+static IPSFactoryBuffer PSFactoryBuffer = { &PSFactoryBufferVtbl };
+
 #define RELEASEMARSHALDATA WM_USER
 
 struct host_object_data
@@ -289,18 +563,31 @@ struct host_object_data
     IID iid;
     IUnknown *object;
     MSHLFLAGS marshal_flags;
-    HANDLE marshal_event;
     IMessageFilter *filter;
+    IUnknown *register_object;
+    CLSID register_clsid;
+    HANDLE marshal_event;
 };
 
+#ifndef __REACTOS__ /* FIXME: Inspect */
+static IPSFactoryBuffer PSFactoryBuffer;
+#endif
+
 static DWORD CALLBACK host_object_proc(LPVOID p)
 {
     struct host_object_data *data = p;
+    DWORD registration_key;
     HRESULT hr;
     MSG msg;
 
     pCoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
 
+    if(data->register_object) {
+        hr = CoRegisterClassObject(&data->register_clsid, data->register_object,
+            CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &registration_key);
+        ok(hr == S_OK, "CoRegisterClassObject failed: %08x\n", hr);
+    }
+
     if (data->filter)
     {
         IMessageFilter * prev_filter = NULL;
@@ -335,31 +622,27 @@ static DWORD CALLBACK host_object_proc(LPVOID p)
     return hr;
 }
 
-static DWORD start_host_object2(IStream *stream, REFIID riid, IUnknown *object, MSHLFLAGS marshal_flags, IMessageFilter *filter, HANDLE *thread)
+static DWORD start_host_object2(struct host_object_data *object_data, 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;
+    struct host_object_data *data;
 
+    data = HeapAlloc(GetProcessHeap(), 0, sizeof(*data));
+    *data = *object_data;
+    data->marshal_event = CreateEventA(NULL, FALSE, FALSE, NULL);
     *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);
+    ok( !WaitForSingleObject(data->marshal_event, 10000), "wait timed out\n" );
+    CloseHandle(data->marshal_event);
 
     return tid;
 }
 
 static DWORD start_host_object(IStream *stream, REFIID riid, IUnknown *object, MSHLFLAGS marshal_flags, HANDLE *thread)
 {
-    return start_host_object2(stream, riid, object, marshal_flags, NULL, thread);
+    struct host_object_data object_data = { stream, *riid, object, marshal_flags };
+    return start_host_object2(&object_data, thread);
 }
 
 /* asks thread to release the marshal data because it has to be done by the
@@ -962,6 +1245,75 @@ static void test_marshal_proxy_mta_apartment_shutdown(void)
     pCoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
 }
 
+static void test_marshal_channel_buffer(void)
+{
+    DWORD registration_key;
+    IUnknown *proxy = NULL;
+    IOleWindow *ole_window;
+    HWND hwnd;
+    CLSID clsid;
+    DWORD tid;
+    HANDLE thread;
+    HRESULT hr;
+
+    struct host_object_data object_data = { NULL, IID_IOleClientSite, (IUnknown*)&Test_OleClientSite,
+                                            MSHLFLAGS_NORMAL, NULL, (IUnknown*)&PSFactoryBuffer,
+                                            CLSID_WineTestPSFactoryBuffer };
+
+    cLocks = 0;
+    external_connections = 0;
+
+    hr = CoGetPSClsid(&IID_IOleWindow, &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 = CreateStreamOnHGlobal(NULL, TRUE, &object_data.stream);
+    ok_ole_success(hr, CreateStreamOnHGlobal);
+    tid = start_host_object2(&object_data, &thread);
+
+    IStream_Seek(object_data.stream, ullZero, STREAM_SEEK_SET, NULL);
+    hr = CoUnmarshalInterface(object_data.stream, &IID_IUnknown, (void **)&proxy);
+    ok_ole_success(hr, CoUnmarshalInterface);
+    IStream_Release(object_data.stream);
+
+    hr = CoRegisterClassObject(&CLSID_WineTestPSFactoryBuffer, (IUnknown *)&PSFactoryBuffer,
+        CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &registration_key);
+    ok(hr == S_OK, "CoRegisterClassObject failed: %08x\n", hr);
+
+    hr = CoRegisterPSClsid(&IID_IOleWindow, &CLSID_WineTestPSFactoryBuffer);
+    ok(hr == S_OK, "CoRegisterPSClsid failed: %08x\n", hr);
+
+    SET_EXPECT(CreateStub);
+    SET_EXPECT(CreateProxy);
+    hr = IUnknown_QueryInterface(proxy, &IID_IOleWindow, (void**)&ole_window);
+    ok(hr == S_OK, "Could not get IOleWindow iface: %08x\n", hr);
+    CHECK_CALLED(CreateStub);
+    CHECK_CALLED(CreateProxy);
+
+    SET_EXPECT(Invoke);
+    SET_EXPECT(GetWindow);
+    hr = IOleWindow_GetWindow(ole_window, &hwnd);
+    ok(hr == S_OK, "GetWindow failed: %08x\n", hr);
+    ok(hwnd == (HWND)0xdeadbeef, "hwnd = %p\n", hwnd);
+    CHECK_CALLED(Invoke);
+    CHECK_CALLED(GetWindow);
+
+    IOleWindow_Release(ole_window);
+
+    SET_EXPECT(Disconnect);
+    IUnknown_Release(proxy);
+todo_wine
+    CHECK_CALLED(Disconnect);
+
+    hr = CoRevokeClassObject(registration_key);
+    ok(hr == S_OK, "CoRevokeClassObject failed: %08x\n", hr);
+
+    end_host_object(tid, thread);
+}
+
 struct ncu_params
 {
     LPSTREAM stream;
@@ -1965,25 +2317,27 @@ static IMessageFilter MessageFilter = { &MessageFilter_Vtbl };
 static void test_message_filter(void)
 {
     HRESULT hr;
-    IStream *pStream = NULL;
     IClassFactory *cf = NULL;
     DWORD tid;
     IUnknown *proxy = NULL;
     IMessageFilter *prev_filter = NULL;
     HANDLE thread;
 
+    struct host_object_data object_data = { NULL, IID_IClassFactory, (IUnknown*)&Test_ClassFactory,
+                                            MSHLFLAGS_NORMAL, &MessageFilter };
+
     cLocks = 0;
 
-    hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream);
+    hr = CreateStreamOnHGlobal(NULL, TRUE, &object_data.stream);
     ok_ole_success(hr, CreateStreamOnHGlobal);
-    tid = start_host_object2(pStream, &IID_IClassFactory, (IUnknown*)&Test_ClassFactory, MSHLFLAGS_NORMAL, &MessageFilter, &thread);
+    tid = start_host_object2(&object_data, &thread);
 
     ok_more_than_one_lock();
 
-    IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL);
-    hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&cf);
+    IStream_Seek(object_data.stream, ullZero, STREAM_SEEK_SET, NULL);
+    hr = CoUnmarshalInterface(object_data.stream, &IID_IClassFactory, (void **)&cf);
     ok_ole_success(hr, CoUnmarshalInterface);
-    IStream_Release(pStream);
+    IStream_Release(object_data.stream);
 
     ok_more_than_one_lock();
 
@@ -3419,6 +3773,169 @@ static void test_manualresetevent(void)
     ok(!ref, "Got nonzero ref: %d\n", ref);
 }
 
+static DWORD CALLBACK implicit_mta_unmarshal_proc(void *param)
+{
+    IStream *stream = param;
+    IClassFactory *cf;
+    IUnknown *proxy;
+    HRESULT hr;
+
+    IStream_Seek(stream, ullZero, STREAM_SEEK_SET, NULL);
+    hr = CoUnmarshalInterface(stream, &IID_IClassFactory, (void **)&cf);
+    ok_ole_success(hr, CoUnmarshalInterface);
+
+    hr = IClassFactory_CreateInstance(cf, NULL, &IID_IUnknown, (void **)&proxy);
+    ok_ole_success(hr, IClassFactory_CreateInstance);
+
+    IUnknown_Release(proxy);
+
+    /* But if we initialize an STA in this apartment, it becomes the wrong one. */
+    CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+
+    hr = IClassFactory_CreateInstance(cf, NULL, &IID_IUnknown, (void **)&proxy);
+    ok(hr == RPC_E_WRONG_THREAD, "got %#x\n", hr);
+
+    CoUninitialize();
+
+    ok_more_than_one_lock();
+    ok_non_zero_external_conn();
+
+    IClassFactory_Release(cf);
+
+    ok_no_locks();
+    ok_zero_external_conn();
+    ok_last_release_closes(TRUE);
+    return 0;
+}
+
+static DWORD CALLBACK implicit_mta_use_proc(void *param)
+{
+    IClassFactory *cf = param;
+    IUnknown *proxy;
+    HRESULT hr;
+
+    hr = IClassFactory_CreateInstance(cf, NULL, &IID_IUnknown, (void **)&proxy);
+    ok_ole_success(hr, IClassFactory_CreateInstance);
+
+    IUnknown_Release(proxy);
+
+    /* But if we initialize an STA in this apartment, it becomes the wrong one. */
+    CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+
+    hr = IClassFactory_CreateInstance(cf, NULL, &IID_IUnknown, (void **)&proxy);
+    ok(hr == RPC_E_WRONG_THREAD, "got %#x\n", hr);
+
+    CoUninitialize();
+    return 0;
+}
+
+struct implicit_mta_marshal_data
+{
+    IStream *stream;
+    HANDLE start;
+    HANDLE stop;
+};
+
+static DWORD CALLBACK implicit_mta_marshal_proc(void *param)
+{
+    struct implicit_mta_marshal_data *data = param;
+    HRESULT hr;
+
+    hr = CoMarshalInterface(data->stream, &IID_IClassFactory,
+        (IUnknown *)&Test_ClassFactory, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL);
+    ok_ole_success(hr, CoMarshalInterface);
+
+    SetEvent(data->start);
+
+    ok(!WaitForSingleObject(data->stop, 1000), "wait failed\n");
+    return 0;
+}
+
+static void test_implicit_mta(void)
+{
+    struct implicit_mta_marshal_data data;
+    HANDLE host_thread, thread;
+    IClassFactory *cf;
+    IUnknown *proxy;
+    IStream *stream;
+    HRESULT hr;
+    DWORD tid;
+
+    cLocks = 0;
+    external_connections = 0;
+
+    CoInitializeEx(NULL, COINIT_MULTITHREADED);
+
+    /* Firstly: we can unmarshal and use an object while in the implicit MTA. */
+    hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
+    ok_ole_success(hr, CreateStreamOnHGlobal);
+    tid = start_host_object(stream, &IID_IClassFactory, (IUnknown *)&Test_ClassFactory, MSHLFLAGS_NORMAL, &host_thread);
+
+    ok_more_than_one_lock();
+    ok_non_zero_external_conn();
+
+    thread = CreateThread(NULL, 0, implicit_mta_unmarshal_proc, stream, 0, NULL);
+    ok(!WaitForSingleObject(thread, 1000), "wait failed\n");
+    CloseHandle(thread);
+
+    IStream_Release(stream);
+    end_host_object(tid, host_thread);
+
+    /* Secondly: we can unmarshal an object into the real MTA and then use it
+     * from the implicit MTA. */
+    hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
+    ok_ole_success(hr, CreateStreamOnHGlobal);
+    tid = start_host_object(stream, &IID_IClassFactory, (IUnknown *)&Test_ClassFactory, MSHLFLAGS_NORMAL, &host_thread);
+
+    ok_more_than_one_lock();
+    ok_non_zero_external_conn();
+
+    IStream_Seek(stream, ullZero, STREAM_SEEK_SET, NULL);
+    hr = CoUnmarshalInterface(stream, &IID_IClassFactory, (void **)&cf);
+    ok_ole_success(hr, CoUnmarshalInterface);
+
+    thread = CreateThread(NULL, 0, implicit_mta_use_proc, cf, 0, NULL);
+    ok(!WaitForSingleObject(thread, 1000), "wait failed\n");
+    CloseHandle(thread);
+
+    IClassFactory_Release(cf);
+    IStream_Release(stream);
+
+    ok_no_locks();
+    ok_non_zero_external_conn();
+    ok_last_release_closes(TRUE);
+
+    end_host_object(tid, host_thread);
+
+    /* Thirdly: we can marshal an object from the implicit MTA and then
+     * unmarshal it into the real one. */
+    data.start = CreateEventA(NULL, FALSE, FALSE, NULL);
+    data.stop  = CreateEventA(NULL, FALSE, FALSE, NULL);
+
+    hr = CreateStreamOnHGlobal(NULL, TRUE, &data.stream);
+    ok_ole_success(hr, CreateStreamOnHGlobal);
+
+    thread = CreateThread(NULL, 0, implicit_mta_marshal_proc, &data, 0, NULL);
+    ok(!WaitForSingleObject(data.start, 1000), "wait failed\n");
+
+    IStream_Seek(data.stream, ullZero, STREAM_SEEK_SET, NULL);
+    hr = CoUnmarshalInterface(data.stream, &IID_IClassFactory, (void **)&cf);
+    ok_ole_success(hr, CoUnmarshalInterface);
+
+    hr = IClassFactory_CreateInstance(cf, NULL, &IID_IUnknown, (void **)&proxy);
+    ok_ole_success(hr, IClassFactory_CreateInstance);
+
+    IUnknown_Release(proxy);
+
+    SetEvent(data.stop);
+    ok(!WaitForSingleObject(thread, 1000), "wait failed\n");
+    CloseHandle(thread);
+
+    IStream_Release(data.stream);
+
+    CoUninitialize();
+}
+
 static const char *debugstr_iid(REFIID riid)
 {
     static char name[256];
@@ -3692,13 +4209,15 @@ static IChannelHook TestChannelHook = { &TestChannelHookVtbl };
 
 static void test_channel_hook(void)
 {
-    IStream *pStream = NULL;
     IClassFactory *cf = NULL;
     DWORD tid;
     IUnknown *proxy = NULL;
     HANDLE thread;
     HRESULT hr;
 
+    struct host_object_data object_data = { NULL, IID_IClassFactory, (IUnknown*)&Test_ClassFactory,
+                                            MSHLFLAGS_NORMAL, &MessageFilter };
+
     hr = CoRegisterChannelHook(&EXTENTID_WineTest, &TestChannelHook);
     ok_ole_success(hr, CoRegisterChannelHook);
 
@@ -3707,17 +4226,17 @@ static void test_channel_hook(void)
 
     cLocks = 0;
 
-    hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream);
+    hr = CreateStreamOnHGlobal(NULL, TRUE, &object_data.stream);
     ok_ole_success(hr, CreateStreamOnHGlobal);
-    tid = start_host_object2(pStream, &IID_IClassFactory, (IUnknown*)&Test_ClassFactory, MSHLFLAGS_NORMAL, &MessageFilter, &thread);
+    tid = start_host_object2(&object_data, &thread);
     server_tid = tid;
 
     ok_more_than_one_lock();
 
-    IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL);
-    hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&cf);
+    IStream_Seek(object_data.stream, ullZero, STREAM_SEEK_SET, NULL);
+    hr = CoUnmarshalInterface(object_data.stream, &IID_IClassFactory, (void **)&cf);
     ok_ole_success(hr, CoUnmarshalInterface);
-    IStream_Release(pStream);
+    IStream_Release(object_data.stream);
 
     ok_more_than_one_lock();
 
@@ -3765,6 +4284,7 @@ START_TEST(marshal)
     register_test_window();
 
     test_cocreateinstance_proxy();
+    test_implicit_mta();
 
     pCoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
 
@@ -3802,6 +4322,7 @@ START_TEST(marshal)
         with_external_conn = !with_external_conn;
     } while (with_external_conn);
 
+    test_marshal_channel_buffer();
     test_hresult_marshaling();
     test_proxy_used_in_wrong_thread();
     test_message_filter();
index 231f9d2..dd9257d 100644 (file)
@@ -4082,6 +4082,83 @@ static void check_storage_contents(IStorage *stg, const struct storage_def *stg_
     }
 }
 
+static HRESULT stgmedium_cmp(const STGMEDIUM *med1, STGMEDIUM *med2)
+{
+    BYTE *data1, *data2;
+    ULONG datasize1, datasize2;
+
+    if (med1->tymed != med2->tymed)
+        return E_FAIL;
+
+    if (med1->tymed == TYMED_MFPICT)
+    {
+        METAFILEPICT *mfpict1 = GlobalLock(U(med1)->hMetaFilePict);
+        METAFILEPICT *mfpict2 = GlobalLock(U(med2)->hMetaFilePict);
+
+        datasize1 = GetMetaFileBitsEx(mfpict1->hMF, 0, NULL);
+        datasize2 = GetMetaFileBitsEx(mfpict2->hMF, 0, NULL);
+        if (datasize1 == datasize2)
+        {
+            data1 = HeapAlloc(GetProcessHeap(), 0, datasize1);
+            data2 = HeapAlloc(GetProcessHeap(), 0, datasize2);
+            GetMetaFileBitsEx(mfpict1->hMF, datasize1, data1);
+            GetMetaFileBitsEx(mfpict2->hMF, datasize2, data2);
+        }
+        else return E_FAIL;
+    }
+    else if (med1->tymed == TYMED_ENHMF)
+    {
+        datasize1 = GetEnhMetaFileBits(med1->hEnhMetaFile, 0, NULL);
+        datasize2 = GetEnhMetaFileBits(med2->hEnhMetaFile, 0, NULL);
+        if (datasize1 == datasize2)
+        {
+            data1 = HeapAlloc(GetProcessHeap(), 0, datasize1);
+            data2 = HeapAlloc(GetProcessHeap(), 0, datasize2);
+            GetEnhMetaFileBits(med1->hEnhMetaFile, datasize1, data1);
+            GetEnhMetaFileBits(med2->hEnhMetaFile, datasize2, data2);
+        }
+        else return E_FAIL;
+    }
+    else if (med1->tymed == TYMED_HGLOBAL)
+    {
+        datasize1 = GlobalSize(med1->hGlobal);
+        datasize2 = GlobalSize(med2->hGlobal);
+
+        if (datasize1 == datasize2)
+        {
+            data1 = GlobalLock(med1->hGlobal);
+            data2 = GlobalLock(med2->hGlobal);
+        }
+        else
+            return E_FAIL;
+    }
+    else
+        return E_NOTIMPL;
+
+    if (memcmp(data1, data2, datasize1) != 0)
+        return E_FAIL;
+
+    if (med1->tymed == TYMED_HGLOBAL)
+    {
+        GlobalUnlock(U(med1)->hGlobal);
+        GlobalUnlock(U(med2)->hGlobal);
+    }
+    else if (med1->tymed == TYMED_MFPICT)
+    {
+        HeapFree(GetProcessHeap(), 0, data1);
+        HeapFree(GetProcessHeap(), 0, data2);
+        GlobalUnlock(U(med1)->hMetaFilePict);
+        GlobalUnlock(U(med2)->hMetaFilePict);
+    }
+    else
+    {
+        HeapFree(GetProcessHeap(), 0, data1);
+        HeapFree(GetProcessHeap(), 0, data2);
+    }
+
+    return S_OK;
+}
+
 static IStorage *create_storage_from_def(const struct storage_def *stg_def)
 {
     HRESULT hr;
@@ -4254,8 +4331,10 @@ static void test_data_cache_save_data(void)
     IStorage *doc;
     IOleCache2 *cache;
     IPersistStorage *persist;
+    IDataObject *odata;
     int enumerated_streams, matched_streams, i;
     DWORD dummy;
+    STGMEDIUM stgmeds[MAX_FMTS];
     struct tests_data_cache
     {
         FORMATETC fmts[MAX_FMTS];
@@ -4370,9 +4449,9 @@ static void test_data_cache_save_data(void)
             ok(SUCCEEDED(hr), "unexpected %#x\n", hr);
             if (i < pdata->num_set)
             {
-                get_stgmedium(pdata->fmts[i].cfFormat, &stgmed);
-                get_stgdef(&pdata->stg_def, pdata->fmts[i].cfFormat, &stgmed, i);
-                hr = IOleCache2_SetData(cache, &pdata->fmts[i], &stgmed, TRUE);
+                get_stgmedium(pdata->fmts[i].cfFormat, &stgmeds[i]);
+                get_stgdef(&pdata->stg_def, pdata->fmts[i].cfFormat, &stgmeds[i], i);
+                hr = IOleCache2_SetData(cache, &pdata->fmts[i], &stgmeds[i], FALSE);
                 ok(hr == S_OK, "unexpected %#x\n", hr);
             }
         }
@@ -4403,12 +4482,41 @@ static void test_data_cache_save_data(void)
         ok(enumerated_streams == pdata->stg_def.stream_count, "created %d != def streams %d\n",
            enumerated_streams, pdata->stg_def.stream_count);
 
+        IPersistStorage_Release(persist);
+        IOleCache2_Release(cache);
+
+        /* now test _Load/_GetData using the storage we used for _Save */
+        hr = CreateDataCache(NULL, pdata->clsid, &IID_IOleCache2, (void **)&cache);
+        ok(hr == S_OK, "unexpected %#x\n", hr);
+        hr = IOleCache2_QueryInterface(cache, &IID_IPersistStorage, (void **)&persist);
+        ok(hr == S_OK, "unexpected %#x\n", hr);
+
+        hr = IStorage_SetClass(doc, pdata->clsid);
+        ok(hr == S_OK, "unexpected %#x\n", hr);
+        trace("IPersistStorage_Load\n");
+        hr = IPersistStorage_Load(persist, doc);
+        ok(hr == S_OK, "unexpected %#x\n", hr);
+
+        hr = IOleCache2_QueryInterface(cache, &IID_IDataObject, (void **)&odata);
+        ok(hr == S_OK, "unexpected %#x\n", hr);
         for (i = 0; i < pdata->num_set; i++)
-            HeapFree(GetProcessHeap(), 0, (void *)pdata->stg_def.stream[i].data);
+        {
+            hr = IDataObject_GetData(odata, &pdata->fmts[i], &stgmed);
+            ok(hr == S_OK, "unexpected %#x\n", hr);
 
+            hr = stgmedium_cmp(&stgmeds[i], &stgmed);
+            ok(hr == S_OK, "unexpected %#x\n", hr);
+            ReleaseStgMedium(&stgmed);
+            ReleaseStgMedium(&stgmeds[i]);
+        }
+
+        IDataObject_Release(odata);
         IPersistStorage_Release(persist);
         IStorage_Release(doc);
         IOleCache2_Release(cache);
+        for (i = 0; i < pdata->num_set; i++)
+            HeapFree(GetProcessHeap(), 0, (void *)pdata->stg_def.stream[i].data);
+
     }
 }