From 4335c64f7b944b195c33b54daac070cf87519015 Mon Sep 17 00:00:00 2001 From: Robert Shearman Date: Wed, 24 May 2006 23:01:41 +0100 Subject: [PATCH] ole32: Add a rough implementation of the free-threaded marshaler MarshalInterface and UnmarshalInterface methods, with test cases. --- dlls/ole32/ftmarshal.c | 75 ++++++++++++++-- dlls/ole32/tests/marshal.c | 179 +++++++++++++++++++++++++++++++++++++ 2 files changed, 249 insertions(+), 5 deletions(-) diff --git a/dlls/ole32/ftmarshal.c b/dlls/ole32/ftmarshal.c index 8a776f14e38..8749c3c238f 100644 --- a/dlls/ole32/ftmarshal.c +++ b/dlls/ole32/ftmarshal.c @@ -34,6 +34,8 @@ #include "wine/debug.h" +#include "compobj_private.h" + WINE_DEFAULT_DEBUG_CHANNEL(ole); typedef struct _FTMarshalImpl { @@ -173,17 +175,44 @@ FTMarshalImpl_MarshalInterface (LPMARSHAL iface, IStream * pStm, REFIID riid, vo IMarshal *pMarshal = NULL; HRESULT hres; + DWORD magic = 0x57dfd54d /* MEOW */; - FTMarshalImpl *This = impl_from_IMarshal(iface); - - FIXME ("(), stub!\n"); + TRACE("(%p, %s, %p, 0x%lx, %p, 0x%lx)\n", pStm, debugstr_guid(riid), pv, + dwDestContext, pvDestContext, mshlflags); /* if the marshalling happens inside the same process the interface pointer is copied between the apartments */ if (dwDestContext == MSHCTX_INPROC || dwDestContext == MSHCTX_CROSSCTX) { - return IStream_Write (pStm, This, sizeof (This), 0); + void *object; + DWORD constant = 0; + GUID unknown_guid = { 0 }; + + hres = IUnknown_QueryInterface((IUnknown *)pv, riid, &object); + if (FAILED(hres)) + return hres; + + hres = IStream_Write (pStm, &mshlflags, sizeof (mshlflags), NULL); + if (hres != S_OK) return STG_E_MEDIUMFULL; + + hres = IStream_Write (pStm, &object, sizeof (object), NULL); + if (hres != S_OK) return STG_E_MEDIUMFULL; + + hres = IStream_Write (pStm, &constant, sizeof (constant), NULL); + if (hres != S_OK) return STG_E_MEDIUMFULL; + + hres = IStream_Write (pStm, &unknown_guid, sizeof (unknown_guid), NULL); + if (hres != S_OK) return STG_E_MEDIUMFULL; + + return S_OK; } + /* FIXME: this isn't exactly corret. it looks like the standard marshaler + * for native writes all of the OBJREF data into the stream, so we should + * really rely on it to write this constant for us. however, we need a + * constant to differentiate the outofproc data from the inproc data */ + hres = IStream_Write (pStm, &magic, sizeof (magic), NULL); + if (hres != S_OK) return STG_E_MEDIUMFULL; + /* use the standard marshaler to handle all other cases */ CoGetStandardMarshal (riid, pv, dwDestContext, pvDestContext, mshlflags, &pMarshal); hres = IMarshal_MarshalInterface (pMarshal, pStm, riid, pv, dwDestContext, pvDestContext, mshlflags); @@ -194,7 +223,43 @@ FTMarshalImpl_MarshalInterface (LPMARSHAL iface, IStream * pStm, REFIID riid, vo static HRESULT WINAPI FTMarshalImpl_UnmarshalInterface (LPMARSHAL iface, IStream * pStm, REFIID riid, void **ppv) { - FIXME ("(), stub!\n"); + DWORD mshlflags; + HRESULT hres; + + TRACE ("(%p, %s, %p)\n", pStm, debugstr_guid(riid), ppv); + + hres = IStream_Read (pStm, &mshlflags, sizeof (mshlflags), NULL); + if (hres != S_OK) return STG_E_READFAULT; + + if (mshlflags == 0x57dfd54d /* MEOW */) { + IMarshal *pMarshal; + + hres = CoCreateInstance (&CLSID_DfMarshal, NULL, CLSCTX_INPROC, &IID_IMarshal, (void **)&pMarshal); + if (FAILED(hres)) return hres; + + hres = IMarshal_UnmarshalInterface (pMarshal, pStm, riid, ppv); + } + else { + IUnknown *object; + DWORD constant; + GUID unknown_guid; + + hres = IStream_Read (pStm, &object, sizeof (object), NULL); + if (hres != S_OK) return STG_E_READFAULT; + + hres = IStream_Read (pStm, &constant, sizeof (constant), NULL); + if (hres != S_OK) return STG_E_READFAULT; + if (constant != 0) + FIXME("constant is 0x%lx instead of 0\n", constant); + + hres = IStream_Read (pStm, &unknown_guid, sizeof (unknown_guid), NULL); + if (hres != S_OK) return STG_E_READFAULT; + + hres = IUnknown_QueryInterface(object, riid, ppv); + IUnknown_Release(object); + return hres; + } + return S_OK; } diff --git a/dlls/ole32/tests/marshal.c b/dlls/ole32/tests/marshal.c index 600abe57c15..e2eb472f751 100644 --- a/dlls/ole32/tests/marshal.c +++ b/dlls/ole32/tests/marshal.c @@ -1603,6 +1603,184 @@ static void test_WM_QUIT_handling(void) } } +static void test_freethreadedmarshaldata(IStream *pStream, MSHCTX mshctx, void *ptr, DWORD mshlflags) +{ + HGLOBAL hglobal; + DWORD size; + char *marshal_data; + HRESULT hr; + + hr = GetHGlobalFromStream(pStream, &hglobal); + ok_ole_success(hr, GetHGlobalFromStream); + + size = GlobalSize(hglobal); + + marshal_data = (char *)GlobalLock(hglobal); + + if (mshctx == MSHCTX_INPROC) + { + DWORD expected_size = sizeof(DWORD) + sizeof(void *) + sizeof(DWORD) + sizeof(GUID); + ok(size == expected_size, "size should have been %ld instead of %ld\n", expected_size, size); + + ok(*(DWORD *)marshal_data == mshlflags, "expected 0x%lx, but got 0x%lx for mshctx\n", mshlflags, *(DWORD *)marshal_data); + marshal_data += sizeof(DWORD); + ok(*(void **)marshal_data == ptr, "expected %p, but got %p for mshctx\n", ptr, *(void **)marshal_data); + marshal_data += sizeof(void *); + ok(*(DWORD *)marshal_data == 0, "expected 0x0, but got 0x%lx\n", *(DWORD *)marshal_data); + marshal_data += sizeof(DWORD); + trace("got guid data: {%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n", + ((GUID *)marshal_data)->Data1, ((GUID *)marshal_data)->Data2, ((GUID *)marshal_data)->Data3, + ((GUID *)marshal_data)->Data4[0], ((GUID *)marshal_data)->Data4[1], ((GUID *)marshal_data)->Data4[2], ((GUID *)marshal_data)->Data4[3], + ((GUID *)marshal_data)->Data4[4], ((GUID *)marshal_data)->Data4[5], ((GUID *)marshal_data)->Data4[6], ((GUID *)marshal_data)->Data4[7]); + } + else + { + ok(size > sizeof(DWORD), "size should have been > sizeof(DWORD), not %ld\n", size); + ok(*(DWORD *)marshal_data == 0x574f454d /* MEOW */, + "marshal data should be filled by standard marshal and start with MEOW signature\n"); + } + + GlobalUnlock(hglobal); +} + +static void test_freethreadedmarshaler(void) +{ + HRESULT hr; + IUnknown *pFTUnknown; + IMarshal *pFTMarshal; + IStream *pStream; + IUnknown *pProxy; + static const LARGE_INTEGER llZero; + + cLocks = 0; + hr = CoCreateFreeThreadedMarshaler(NULL, &pFTUnknown); + ok_ole_success(hr, CoCreateFreeThreadedMarshaler); + hr = IUnknown_QueryInterface(pFTUnknown, &IID_IMarshal, (void **)&pFTMarshal); + ok_ole_success(hr, IUnknown_QueryInterface); + IUnknown_Release(pFTUnknown); + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, CreateStreamOnHGlobal); + + /* inproc normal marshaling */ + + hr = IMarshal_MarshalInterface(pFTMarshal, pStream, &IID_IClassFactory, + (IUnknown*)&Test_ClassFactory, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL); + ok_ole_success(hr, IMarshal_MarshalInterface); + + ok_more_than_one_lock(); + + test_freethreadedmarshaldata(pStream, MSHCTX_INPROC, &Test_ClassFactory, MSHLFLAGS_NORMAL); + + IStream_Seek(pStream, llZero, STREAM_SEEK_SET, NULL); + hr = IMarshal_UnmarshalInterface(pFTMarshal, pStream, &IID_IUnknown, (void **)&pProxy); + ok_ole_success(hr, IMarshal_UnmarshalInterface); + + IUnknown_Release(pProxy); + + ok_no_locks(); + +/* native doesn't allow us to unmarshal or release the stream data, + * presumably because it wants us to call CoMarshalInterface instead */ +#if 0 + /* local normal marshaling */ + + IStream_Seek(pStream, llZero, STREAM_SEEK_SET, NULL); + hr = IMarshal_MarshalInterface(pFTMarshal, pStream, IID_IClassFactory, (IUnknown*)&Test_ClassFactory, MSHCTX_LOCAL, NULL, MSHLFLAGS_NORMAL); + ok_ole_success(hr, IMarshal_MarshalInterface); + + ok_more_than_one_lock(); + + test_freethreadedmarshaldata(pStream, MSHCTX_LOCAL, &Test_ClassFactory, MSHLFLAGS_NORMAL); + + IStream_Seek(pStream, llZero, STREAM_SEEK_SET, NULL); + hr = IMarshal_ReleaseMarshalData(pFTMarshal, pStream); + ok_ole_success(hr, IMarshal_ReleaseMarshalData); + + ok_no_locks(); +#endif + + /* inproc table-strong marshaling */ + + IStream_Seek(pStream, llZero, STREAM_SEEK_SET, NULL); + hr = IMarshal_MarshalInterface(pFTMarshal, pStream, &IID_IClassFactory, + (IUnknown*)&Test_ClassFactory, MSHCTX_INPROC, (void *)0xdeadbeef, + MSHLFLAGS_TABLESTRONG); + ok_ole_success(hr, IMarshal_MarshalInterface); + + ok_more_than_one_lock(); + + test_freethreadedmarshaldata(pStream, MSHCTX_INPROC, &Test_ClassFactory, MSHLFLAGS_TABLESTRONG); + + IStream_Seek(pStream, llZero, STREAM_SEEK_SET, NULL); + hr = IMarshal_UnmarshalInterface(pFTMarshal, pStream, &IID_IUnknown, (void **)&pProxy); + ok_ole_success(hr, IMarshal_UnmarshalInterface); + + IUnknown_Release(pProxy); + + todo_wine ok_more_than_one_lock(); + + IStream_Seek(pStream, llZero, STREAM_SEEK_SET, NULL); + hr = IMarshal_ReleaseMarshalData(pFTMarshal, pStream); + ok_ole_success(hr, IMarshal_ReleaseMarshalData); + + ok_no_locks(); + + /* inproc table-weak marshaling */ + + IStream_Seek(pStream, llZero, STREAM_SEEK_SET, NULL); + hr = IMarshal_MarshalInterface(pFTMarshal, pStream, &IID_IClassFactory, + (IUnknown*)&Test_ClassFactory, MSHCTX_INPROC, (void *)0xdeadbeef, + MSHLFLAGS_TABLEWEAK); + ok_ole_success(hr, IMarshal_MarshalInterface); + + todo_wine ok_no_locks(); + + test_freethreadedmarshaldata(pStream, MSHCTX_INPROC, &Test_ClassFactory, MSHLFLAGS_TABLEWEAK); + + IStream_Seek(pStream, llZero, STREAM_SEEK_SET, NULL); + hr = IMarshal_UnmarshalInterface(pFTMarshal, pStream, &IID_IUnknown, (void **)&pProxy); + ok_ole_success(hr, IMarshal_UnmarshalInterface); + + ok_more_than_one_lock(); + + IUnknown_Release(pProxy); + + ok_no_locks(); + + /* inproc normal marshaling (for extraordinary cases) */ + + IStream_Seek(pStream, llZero, STREAM_SEEK_SET, NULL); + hr = IMarshal_MarshalInterface(pFTMarshal, pStream, &IID_IClassFactory, + (IUnknown*)&Test_ClassFactory, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL); + ok_ole_success(hr, IMarshal_MarshalInterface); + + ok_more_than_one_lock(); + + /* this call shows that DisconnectObject does nothing */ + hr = IMarshal_DisconnectObject(pFTMarshal, 0); + ok_ole_success(hr, IMarshal_DisconnectObject); + + ok_more_than_one_lock(); + + IStream_Seek(pStream, llZero, STREAM_SEEK_SET, NULL); + hr = IMarshal_ReleaseMarshalData(pFTMarshal, pStream); + ok_ole_success(hr, IMarshal_ReleaseMarshalData); + + todo_wine ok_no_locks(); + + /* doesn't enforce marshaling rules here and allows us to unmarshal the + * interface, even though it was freed above */ + IStream_Seek(pStream, llZero, STREAM_SEEK_SET, NULL); + hr = IMarshal_UnmarshalInterface(pFTMarshal, pStream, &IID_IUnknown, (void **)&pProxy); + ok_ole_success(hr, IMarshal_UnmarshalInterface); + + todo_wine ok_no_locks(); + + IStream_Release(pStream); + IMarshal_Release(pFTMarshal); +} + static HANDLE heventShutdown; static void LockModuleOOP(void) @@ -2013,6 +2191,7 @@ START_TEST(marshal) test_proxybuffer(&IID_IClassFactory); test_message_reentrancy(); test_WM_QUIT_handling(); + test_freethreadedmarshaler(); /* doesn't pass with Win9x COM DLLs (even though Essential COM says it should) */ if (0) test_out_of_process_com();