From: Amine Khaldi Date: Mon, 4 Jun 2018 02:46:11 +0000 (+0100) Subject: [OLE32_WINETEST] Sync with Wine Staging 3.9. CORE-14656 X-Git-Tag: 0.4.11-dev~469 X-Git-Url: https://git.reactos.org/?p=reactos.git;a=commitdiff_plain;h=a6cdf4ff0885008788ea2ea876a14edad7fd512e [OLE32_WINETEST] Sync with Wine Staging 3.9. CORE-14656 --- diff --git a/modules/rostests/winetests/ole32/compobj.c b/modules/rostests/winetests/ole32/compobj.c index 06d19142406..aedbc08f07a 100644 --- a/modules/rostests/winetests/ole32/compobj.c +++ b/modules/rostests/winetests/ole32/compobj.c @@ -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(); } diff --git a/modules/rostests/winetests/ole32/marshal.c b/modules/rostests/winetests/ole32/marshal.c index 12c46e92df5..1fcf9a392a1 100644 --- a/modules/rostests/winetests/ole32/marshal.c +++ b/modules/rostests/winetests/ole32/marshal.c @@ -34,10 +34,37 @@ #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, ®istration_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, ®istration_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(); diff --git a/modules/rostests/winetests/ole32/ole2.c b/modules/rostests/winetests/ole32/ole2.c index 231f9d2531c..dd9257dff85 100644 --- a/modules/rostests/winetests/ole32/ole2.c +++ b/modules/rostests/winetests/ole32/ole2.c @@ -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); + } }