From 9b634b97aee24367f4ed1925c4b6820fb3e2893e Mon Sep 17 00:00:00 2001 From: Robert Shearman Date: Fri, 28 Jan 2005 12:39:13 +0000 Subject: [PATCH] - More tests. - 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 | 30 ++-- dlls/ole32/marshal.c | 16 +- dlls/ole32/rpc.c | 3 +- dlls/ole32/tests/marshal.c | 331 ++++++++++++++++++++++++++++++++++++- dlls/ole32/tests/moniker.c | 84 +++++++++- 5 files changed, 443 insertions(+), 21 deletions(-) diff --git a/dlls/ole32/compobj.c b/dlls/ole32/compobj.c index 60a2485d00b..06b5c683014 100644 --- a/dlls/ole32/compobj.c +++ b/dlls/ole32/compobj.c @@ -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; } diff --git a/dlls/ole32/marshal.c b/dlls/ole32/marshal.c index d562e059690..0dbbfbf67de 100644 --- a/dlls/ole32/marshal.c +++ b/dlls/ole32/marshal.c @@ -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))) { diff --git a/dlls/ole32/rpc.c b/dlls/ole32/rpc.c index 9149fb6e66e..bff286a978d 100644 --- a/dlls/ole32/rpc.c +++ b/dlls/ole32/rpc.c @@ -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; } diff --git a/dlls/ole32/tests/marshal.c b/dlls/ole32/tests/marshal.c index f43f93f58a1..e515fb4b852 100644 --- a/dlls/ole32/tests/marshal.c +++ b/dlls/ole32/tests/marshal.c @@ -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 */ diff --git a/dlls/ole32/tests/moniker.c b/dlls/ole32/tests/moniker.c index d9f2879c824..21302f673de 100644 --- a/dlls/ole32/tests/moniker.c +++ b/dlls/ole32/tests/moniker.c @@ -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(); }