- More tests.
authorRobert Shearman <rob@codeweavers.com>
Fri, 28 Jan 2005 12:39:13 +0000 (12:39 +0000)
committerAlexandre Julliard <julliard@winehq.org>
Fri, 28 Jan 2005 12:39:13 +0000 (12:39 +0000)
- Change return code of CoGetPSClsid to match test result.
- Do a slight hack to make IRemUnknown proxies be added after the
  proxy that uses them to stop them being used after they are
  destroyed.
- Fix multiple local server connections.

dlls/ole32/compobj.c
dlls/ole32/marshal.c
dlls/ole32/rpc.c
dlls/ole32/tests/marshal.c
dlls/ole32/tests/moniker.c

index 60a2485d00b3c8c823f1333a7888fbe360e5e451..06b5c68301475b5243c940b73f799c824dc4a84b 100644 (file)
@@ -1060,8 +1060,8 @@ HRESULT WINAPI CLSIDFromProgID(LPCOLESTR progid, LPCLSID riid)
 /*****************************************************************************
  *             CoGetPSClsid [OLE32.@]
  *
- * This function returns the CLSID of the proxy/stub factory that
- * implements IPSFactoryBuffer for the specified interface.
+ * Retrieves the CLSID of the proxy/stub factory that implements
+ * IPSFactoryBuffer for the specified interface.
  *
  * PARAMS
  *  riid   [I] Interface whose proxy/stub CLSID is to be returned.
@@ -1070,7 +1070,7 @@ HRESULT WINAPI CLSIDFromProgID(LPCOLESTR progid, LPCLSID riid)
  * RETURNS
  *   S_OK
  *   E_OUTOFMEMORY
- *   E_INVALIDARG if no PSFactoryBuffer is associated with the IID, or it could not be parsed
+ *   REGDB_E_IIDNOTREG if no PSFactoryBuffer is associated with the IID, or it could not be parsed
  *
  * NOTES
  *
@@ -1088,6 +1088,9 @@ HRESULT WINAPI CLSIDFromProgID(LPCOLESTR progid, LPCLSID riid)
  *
  * We only search the registry, not ids registered with
  * CoRegisterPSClsid.
+ * Also, native returns S_OK for interfaces with an key in HKCR\Interface, but
+ * without a ProxyStubClsid32 key and leaves garbage in pclsid. This should be
+ * considered a bug in native unless an application depends on this (unlikely).
  */
 HRESULT WINAPI CoGetPSClsid(REFIID riid, CLSID *pclsid)
 {
@@ -1103,9 +1106,7 @@ HRESULT WINAPI CoGetPSClsid(REFIID riid, CLSID *pclsid)
        (length of iid string plus constant length of static text */
     buf = HeapAlloc(GetProcessHeap(), 0, strlen(buf2)+27);
     if (buf == NULL)
-    {
-       return (E_OUTOFMEMORY);
-    }
+        return E_OUTOFMEMORY;
 
     /* Construct the registry key we want */
     sprintf(buf,"Interface\\%s\\ProxyStubClsid32", buf2);
@@ -1113,9 +1114,9 @@ HRESULT WINAPI CoGetPSClsid(REFIID riid, CLSID *pclsid)
     /* Open the key.. */
     if (RegOpenKeyA(HKEY_CLASSES_ROOT, buf, &xhkey))
     {
-       WARN("No PSFactoryBuffer object is registered for this IID\n");
-       HeapFree(GetProcessHeap(),0,buf);
-       return (E_INVALIDARG);
+        WARN("No PSFactoryBuffer object is registered for IID %s\n", debugstr_guid(riid));
+        HeapFree(GetProcessHeap(),0,buf);
+        return REGDB_E_IIDNOTREG;
     }
     HeapFree(GetProcessHeap(),0,buf);
 
@@ -1125,19 +1126,18 @@ HRESULT WINAPI CoGetPSClsid(REFIID riid, CLSID *pclsid)
     buf2len = sizeof(buf2);
     if ( (RegQueryValueA(xhkey,NULL,buf2,&buf2len)) )
     {
-       RegCloseKey(xhkey);
-       return E_INVALIDARG;
+        RegCloseKey(xhkey);
+        return REGDB_E_IIDNOTREG;
     }
     RegCloseKey(xhkey);
 
     /* We have the CLSid we want back from the registry as a string, so
        lets convert it into a CLSID structure */
-    if ( (__CLSIDFromStringA(buf2,pclsid)) != NOERROR) {
-       return E_INVALIDARG;
-    }
+    if ( (__CLSIDFromStringA(buf2,pclsid)) != NOERROR)
+        return REGDB_E_IIDNOTREG;
 
     TRACE ("() Returning CLSID=%s\n", debugstr_guid(pclsid));
-    return (S_OK);
+    return S_OK;
 }
 
 
index d562e0596906d805fa6c7a6c897b594416d7185b..0dbbfbf67de490b8ac90e77feeb25896db9c4962 100644 (file)
@@ -423,13 +423,19 @@ static HRESULT proxy_manager_construct(
      * should store the STDOBJREF flags in the proxy manager. */
     This->sorflags = sorflags;
 
+    assert(channel);
     This->chan = channel; /* FIXME: we should take the binding strings and construct the channel in this function */
 
     /* we create the IRemUnknown proxy on demand */
     This->remunk = NULL;
 
     EnterCriticalSection(&apt->cs);
-    list_add_head(&apt->proxies, &This->entry);
+    /* FIXME: we are dependent on the ordering in here to make sure a proxy's
+     * IRemUnknown proxy doesn't get destroyed before the regual proxy does
+     * because we need the IRemUnknown proxy during the destruction of the
+     * regular proxy. Ideally, we should maintain a separate list for the
+     * IRemUnknown proxies that need late destruction */
+    list_add_tail(&apt->proxies, &This->entry);
     LeaveCriticalSection(&apt->cs);
 
     TRACE("%p created for OXID %s, OID %s\n", This,
@@ -790,7 +796,13 @@ StdMarshalImpl_MarshalInterface(
   start_apartment_listener_thread(); /* just to be sure we have one running. */
   start_apartment_remote_unknown();
 
-  IUnknown_QueryInterface((LPUNKNOWN)pv, riid, (LPVOID*)&pUnk);
+  hres = IUnknown_QueryInterface((LPUNKNOWN)pv, riid, (LPVOID*)&pUnk);
+  if (hres != S_OK)
+  {
+      ERR("object doesn't expose interface %s, failing with error 0x%08lx\n",
+        debugstr_guid(riid), hres);
+      return E_NOINTERFACE;
+  }
 
   if ((manager = get_stub_manager_from_object(apt, pUnk)))
   {
index 9149fb6e66e5b9bf17fb84f23a0f3d75295d5159..bff286a978d75039db8615c2ee69c67c9a91959f 100644 (file)
@@ -991,8 +991,6 @@ static DWORD WINAPI local_server_thread(LPVOID param)
             return hres;
         }
         
-        IStream_Release(pStm);
-
         WriteFile(hPipe,buffer,buflen,&res,NULL);
         FlushFileBuffers(hPipe);
         DisconnectNamedPipe(hPipe);
@@ -1000,6 +998,7 @@ static DWORD WINAPI local_server_thread(LPVOID param)
         TRACE("done marshalling IClassFactory\n");
     }
     CloseHandle(hPipe);
+    IStream_Release(pStm);
     return 0;
 }
 
index f43f93f58a173016f5b4caf3bdcfd6c26b9257e7..e515fb4b85224b6b11f5043e7413b0a360d34bee 100644 (file)
@@ -37,6 +37,26 @@ HRESULT (WINAPI * pCoInitializeEx)(LPVOID lpReserved, DWORD dwCoInit);
 #define ok_no_locks() ok(cLocks == 0, "Number of locks should be 0, but actually is %ld\n", cLocks)
 #define ok_ole_success(hr, func) ok(hr == S_OK, #func " failed with error 0x%08lx\n", hr)
 
+static void test_CoGetPSClsid()
+{
+       HRESULT hr;
+       CLSID clsid;
+       static const CLSID IID_IWineTest = {
+           0x5201163f,
+           0x8164,
+           0x4fd0,
+           {0xa1, 0xa2, 0x5d, 0x5a, 0x36, 0x54, 0xd3, 0xbd}
+       }; /* 5201163f-8164-4fd0-a1a2-5d5a3654d3bd */
+
+       hr = CoGetPSClsid(&IID_IClassFactory, &clsid);
+       ok_ole_success(hr, CoGetPSClsid);
+
+       hr = CoGetPSClsid(&IID_IWineTest, &clsid);
+       ok(hr == REGDB_E_IIDNOTREG,
+          "CoGetPSClsid for random IID returned 0x%08lx instead of REGDB_E_IIDNOTREG\n",
+          hr);
+}
+
 static const LARGE_INTEGER ullZero;
 static LONG cLocks;
 
@@ -251,6 +271,27 @@ static void end_host_object(DWORD tid, HANDLE thread)
     CloseHandle(thread);
 }
 
+/* tests failure case of interface not having a marshaler specified in the
+ * registry */
+static void test_no_marshaler()
+{
+    IStream *pStream;
+    HRESULT hr;
+    static const CLSID IID_IWineTest = {
+        0x5201163f,
+        0x8164,
+        0x4fd0,
+        {0xa1, 0xa2, 0x5d, 0x5a, 0x36, 0x54, 0xd3, 0xbd}
+    }; /* 5201163f-8164-4fd0-a1a2-5d5a3654d3bd */
+
+    hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream);
+    ok_ole_success(hr, CreateStreamOnHGlobal);
+    hr = CoMarshalInterface(pStream, &IID_IWineTest, (IUnknown*)&Test_ClassFactory, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL);
+    ok(hr == E_NOINTERFACE, "CoMarshalInterface should have returned E_NOINTERFACE instead of 0x%08lx\n", hr);
+
+    IStream_Release(pStream);
+}
+
 /* tests normal marshal and then release without unmarshaling */
 static void test_normal_marshal_and_release()
 {
@@ -302,6 +343,49 @@ static void test_normal_marshal_and_unmarshal()
     ok_no_locks();
 }
 
+/* tests failure case of a unmarshaling an freed object */
+static void test_marshal_and_unmarshal_invalid()
+{
+    HRESULT hr;
+    IStream *pStream = NULL;
+    IClassFactory *pProxy = NULL;
+    DWORD tid;
+    void * dummy;
+    HANDLE thread;
+
+    cLocks = 0;
+
+    hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream);
+    ok_ole_success(hr, CreateStreamOnHGlobal);
+    tid = start_host_object(pStream, &IID_IClassFactory, (IUnknown*)&Test_ClassFactory, MSHLFLAGS_NORMAL, &thread);
+
+    ok_more_than_one_lock();
+       
+    IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL);
+    hr = CoReleaseMarshalData(pStream);
+    ok_ole_success(hr, CoReleaseMarshalData);
+
+    ok_no_locks();
+
+    IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL);
+    hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&pProxy);
+    todo_wine { ok_ole_success(hr, CoUnmarshalInterface); }
+
+    ok_no_locks();
+
+    if (pProxy)
+    {
+        hr = IClassFactory_CreateInstance(pProxy, NULL, &IID_IUnknown, &dummy);
+        ok(hr == RPC_E_DISCONNECTED, "Remote call should have returned RPC_E_DISCONNECTED, instead of 0x%08lx\n", hr);
+
+        IClassFactory_Release(pProxy);
+    }
+
+    IStream_Release(pStream);
+
+    end_host_object(tid, thread);
+}
+
 /* tests success case of an interthread marshal */
 static void test_interthread_marshal_and_unmarshal()
 {
@@ -403,6 +487,118 @@ static void test_marshal_proxy_apartment_shutdown()
     CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
 }
 
+/* tests that proxies are released when the containing mta apartment is destroyed */
+static void test_marshal_proxy_mta_apartment_shutdown()
+{
+    HRESULT hr;
+    IStream *pStream = NULL;
+    IUnknown *pProxy = NULL;
+    DWORD tid;
+    HANDLE thread;
+
+    CoUninitialize();
+    CoInitializeEx(NULL, COINIT_MULTITHREADED);
+
+    cLocks = 0;
+
+    hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream);
+    ok_ole_success(hr, CreateStreamOnHGlobal);
+    tid = start_host_object(pStream, &IID_IClassFactory, (IUnknown*)&Test_ClassFactory, MSHLFLAGS_NORMAL, &thread);
+
+    ok_more_than_one_lock();
+       
+    IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL);
+    hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&pProxy);
+    ok_ole_success(hr, CoReleaseMarshalData);
+    IStream_Release(pStream);
+
+    ok_more_than_one_lock();
+
+    CoUninitialize();
+
+    ok_no_locks();
+
+    IUnknown_Release(pProxy);
+
+    ok_no_locks();
+
+    end_host_object(tid, thread);
+
+    CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+}
+
+struct ncu_params
+{
+       LPSTREAM stream;
+       HANDLE marshal_event;
+       HANDLE unmarshal_event;
+};
+
+/* helper for test_proxy_used_in_wrong_thread */
+static DWORD CALLBACK no_couninitialize_proc(LPVOID p)
+{
+       struct ncu_params *ncu_params = (struct ncu_params *)p;
+       HRESULT hr;
+
+       CoInitializeEx(NULL, COINIT_MULTITHREADED);
+
+       hr = CoMarshalInterface(ncu_params->stream, &IID_IClassFactory, (IUnknown*)&Test_ClassFactory, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL);
+       ok_ole_success(hr, CoMarshalInterface);
+
+       SetEvent(ncu_params->marshal_event);
+
+       WaitForSingleObject(ncu_params->unmarshal_event, INFINITE);
+
+       /* die without calling CoUninitialize */
+
+       return 0;
+}
+
+/* tests apartment that an apartment is released if the owning thread exits */
+static void test_no_couninitialize()
+{
+    HRESULT hr;
+    IStream *pStream = NULL;
+    IUnknown *pProxy = NULL;
+    DWORD tid;
+    HANDLE thread;
+    struct ncu_params ncu_params;
+
+    cLocks = 0;
+
+    ncu_params.marshal_event = CreateEvent(NULL, TRUE, FALSE, NULL);
+    ncu_params.unmarshal_event = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+    hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream);
+    ok_ole_success(hr, CreateStreamOnHGlobal);
+    ncu_params.stream = pStream;
+
+    thread = CreateThread(NULL, 0, no_couninitialize_proc, &ncu_params, 0, &tid);
+
+    WaitForSingleObject(ncu_params.marshal_event, INFINITE);
+    ok_more_than_one_lock();
+       
+    IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL);
+    hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&pProxy);
+    ok_ole_success(hr, CoUnmarshalInterface);
+    IStream_Release(pStream);
+
+    ok_more_than_one_lock();
+
+    SetEvent(ncu_params.unmarshal_event);
+    WaitForSingleObject(thread, INFINITE);
+
+    todo_wine { ok_no_locks(); }
+
+    CloseHandle(thread);
+    CloseHandle(ncu_params.marshal_event);
+    CloseHandle(ncu_params.unmarshal_event);
+
+    IUnknown_Release(pProxy);
+
+    ok_no_locks();
+}
+
 /* tests success case of a same-thread table-weak marshal, unmarshal, unmarshal */
 static void test_tableweak_marshal_and_unmarshal_twice()
 {
@@ -444,6 +640,96 @@ static void test_tableweak_marshal_and_unmarshal_twice()
     end_host_object(tid, thread);
 }
 
+/* tests releasing after unmarshaling one object */
+static void test_tableweak_marshal_releasedata1()
+{
+    HRESULT hr;
+    IStream *pStream = NULL;
+    IUnknown *pProxy1 = NULL;
+    IUnknown *pProxy2 = NULL;
+    DWORD tid;
+    HANDLE thread;
+
+    cLocks = 0;
+
+    hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream);
+    ok_ole_success(hr, CreateStreamOnHGlobal);
+    tid = start_host_object(pStream, &IID_IClassFactory, (IUnknown*)&Test_ClassFactory, MSHLFLAGS_TABLEWEAK, &thread);
+
+    ok_more_than_one_lock();
+
+    IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL);
+    hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&pProxy1);
+    ok_ole_success(hr, CoUnmarshalInterface);
+
+    ok_more_than_one_lock();
+
+    /* release the remaining reference on the object by calling
+     * CoReleaseMarshalData in the hosting thread */
+    IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL);
+    release_host_object(tid);
+
+    todo_wine { ok_more_than_one_lock(); }
+
+    IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL);
+    hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&pProxy2);
+    todo_wine { ok_ole_success(hr, CoUnmarshalInterface); }
+    IStream_Release(pStream);
+
+    todo_wine { ok_more_than_one_lock(); }
+
+    IUnknown_Release(pProxy1);
+    if (pProxy2)
+        IUnknown_Release(pProxy2);
+
+    /* this line is shows the difference between weak and strong table marshaling:
+     *  weak has cLocks == 0
+     *  strong has cLocks > 0 */
+    ok_no_locks();
+
+    end_host_object(tid, thread);
+}
+
+/* tests releasing after unmarshaling one object */
+static void test_tableweak_marshal_releasedata2()
+{
+    HRESULT hr;
+    IStream *pStream = NULL;
+    IUnknown *pProxy = NULL;
+    DWORD tid;
+    HANDLE thread;
+
+    cLocks = 0;
+
+    hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream);
+    ok_ole_success(hr, CreateStreamOnHGlobal);
+    tid = start_host_object(pStream, &IID_IClassFactory, (IUnknown*)&Test_ClassFactory, MSHLFLAGS_TABLEWEAK, &thread);
+
+    ok_more_than_one_lock();
+
+    /* release the remaining reference on the object by calling
+     * CoReleaseMarshalData in the hosting thread */
+    IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL);
+    release_host_object(tid);
+
+    todo_wine
+    {
+
+    ok_no_locks();
+
+    IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL);
+    hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&pProxy);
+    ok(hr == CO_E_OBJNOTREG,
+       "CoUnmarshalInterface should have failed with CO_E_OBJNOTREG, but returned 0x%08lx instead\n",
+       hr);
+    IStream_Release(pStream);
+
+    ok_no_locks();
+    }
+
+    end_host_object(tid, thread);
+}
+
 /* tests success case of a same-thread table-strong marshal, unmarshal, unmarshal */
 static void test_tablestrong_marshal_and_unmarshal_twice()
 {
@@ -892,6 +1178,40 @@ static void test_proxy_interfaces()
     end_host_object(tid, thread);
 }
 
+static void test_stubbuffer(REFIID riid)
+{
+    HRESULT hr;
+    IPSFactoryBuffer *psfb;
+    IRpcStubBuffer *stub;
+    ULONG refs;
+    CLSID clsid;
+
+    cLocks = 0;
+
+    hr = CoGetPSClsid(riid, &clsid);
+    ok_ole_success(hr, CoGetPSClsid);
+
+    hr = CoGetClassObject(&clsid, CLSCTX_INPROC_SERVER, NULL, &IID_IPSFactoryBuffer, (LPVOID*)&psfb);
+    ok_ole_success(hr, CoGetClassObject);
+
+    hr = IPSFactoryBuffer_CreateStub(psfb, riid, (IUnknown*)&Test_ClassFactory, &stub);
+    ok_ole_success(hr, IPSFactoryBuffer_CreateStub);
+
+    refs = IPSFactoryBuffer_Release(psfb);
+#if 0 /* not reliable on native. maybe it leaks references */
+    ok(refs == 0, "Ref-count leak of %ld on IPSFactoryBuffer\n", refs);
+#endif
+
+    ok_more_than_one_lock();
+
+    IRpcStubBuffer_Disconnect(stub);
+
+    ok_no_locks();
+
+    refs = IRpcStubBuffer_Release(stub);
+    ok(refs == 0, "Ref-count leak of %ld on IRpcProxyBuffer\n", refs);
+}
+
 
 /* doesn't pass with Win9x COM DLLs (even though Essential COM says it should) */
 #if 0
@@ -1056,13 +1376,22 @@ START_TEST(marshal)
 
     /* FIXME: test CoCreateInstanceEx */
 
+    /* helper function tests */
+    test_CoGetPSClsid();
+
     /* lifecycle management and marshaling tests */
+    test_no_marshaler();
     test_normal_marshal_and_release();
     test_normal_marshal_and_unmarshal();
+    test_marshal_and_unmarshal_invalid();
     test_interthread_marshal_and_unmarshal();
     test_marshal_stub_apartment_shutdown();
     test_marshal_proxy_apartment_shutdown();
+    test_marshal_proxy_mta_apartment_shutdown();
+    test_no_couninitialize();
     test_tableweak_marshal_and_unmarshal_twice();
+    test_tableweak_marshal_releasedata1();
+    test_tableweak_marshal_releasedata2();
     test_tablestrong_marshal_and_unmarshal_twice();
     test_lock_object_external();
     test_disconnect_stub();
@@ -1072,7 +1401,7 @@ START_TEST(marshal)
     test_message_filter();
     test_bad_marshal_stream();
     test_proxy_interfaces();
-    /* FIXME: test custom marshaling */
+    test_stubbuffer(&IID_IClassFactory);
     /* FIXME: test GIT */
     /* FIXME: test COM re-entrancy */
 
index d9f2879c82408844a16425037abab5487385a4fd..21302f673de851bf93eabfaa6d04dfed295c6b57 100644 (file)
@@ -26,6 +26,7 @@
 #include "windef.h"
 #include "winbase.h"
 #include "objbase.h"
+#include "comcat.h"
 
 #include "wine/test.h"
 
@@ -58,12 +59,93 @@ static void test_MkParseDisplayName()
     IBindCtx_Release(pbc);
 }
 
+static const BYTE expected_moniker_data[] =
+{
+       0x4d,0x45,0x4f,0x57,0x04,0x00,0x00,0x00,
+       0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+       0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46,
+       0x1a,0x03,0x00,0x00,0x00,0x00,0x00,0x00,
+       0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46,
+       0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,
+       0x05,0xe0,0x02,0x00,0x00,0x00,0x00,0x00,
+       0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46,
+       0x00,0x00,0x00,0x00,
+};
+
+static const LARGE_INTEGER llZero;
+
+static void test_class_moniker()
+{
+    IStream * stream;
+    IMoniker * moniker;
+    HRESULT hr;
+    HGLOBAL hglobal;
+    LPBYTE moniker_data;
+    DWORD moniker_size;
+    DWORD i;
+    BOOL same = TRUE;
+
+    hr = CreateClassMoniker(&CLSID_StdComponentCategoriesMgr, &moniker);
+    todo_wine { ok_ole_success(hr, CreateClassMoniker); }
+
+    hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
+
+    hr = CoMarshalInterface(stream, &IID_IMoniker, (IUnknown *)moniker, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL);
+    todo_wine { ok_ole_success(hr, CoMarshalInterface); }
+
+    hr = GetHGlobalFromStream(stream, &hglobal);
+    ok_ole_success(hr, GetHGlobalFromStream);
+
+    moniker_size = GlobalSize(hglobal);
+
+    moniker_data = GlobalLock(hglobal);
+
+    /* first check we have the right amount of data */
+    todo_wine {
+    ok(moniker_size == sizeof(expected_moniker_data),
+        "Size of marshaled data differs (expected %d, actual %ld)\n",
+        sizeof(expected_moniker_data), moniker_size);
+    }
+
+    /* then do a byte-by-byte comparison */
+    for (i = 0; i < min(moniker_size, sizeof(expected_moniker_data)); i++)
+    {
+        if (expected_moniker_data[i] != moniker_data[i])
+        {
+            same = FALSE;
+            break;
+        }
+    }
+
+    ok(same, "Marshaled data differs\n");
+    if (!same)
+    {
+        trace("Dumping marshaled moniker data:\n");
+        for (i = 0; i < moniker_size; i++)
+        {
+            trace("0x%02x,", moniker_data[i]);
+            if (i % 8 == 7) trace("\n");
+            if (i % 8 == 0) trace("     ");
+        }
+    }
+
+    GlobalUnlock(hglobal);
+
+    IStream_Seek(stream, llZero, STREAM_SEEK_SET, NULL);
+    hr = CoReleaseMarshalData(stream);
+    todo_wine { ok_ole_success(hr, CoReleaseMarshalData); }
+
+    IStream_Release(stream);
+    if (moniker) IMoniker_Release(moniker);
+}
+
 START_TEST(moniker)
 {
     CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
 
-    /* FIXME: test moniker creation funcs and parsing other moniker formats */
     test_MkParseDisplayName();
+    test_class_moniker();
+    /* FIXME: test moniker creation funcs and parsing other moniker formats */
 
     CoUninitialize();
 }