| /* | |
| * Copyright (c) 2018 Alistair Leslie-Hughes | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | |
| */ | |
| #define COBJMACROS | |
| #include <stdarg.h> | |
| #include "windef.h" | |
| #include "winbase.h" | |
| #include "winerror.h" | |
| #include "winstring.h" | |
| #include "winternl.h" | |
| #include "initguid.h" | |
| #include "roapi.h" | |
| #include "roerrorapi.h" | |
| #include "wine/test.h" | |
| #define EXPECT_REF(obj,ref) _expect_ref((IUnknown*)obj, ref, __LINE__) | |
| static void _expect_ref(IUnknown* obj, ULONG ref, int line) | |
| { | |
| ULONG rc; | |
| IUnknown_AddRef(obj); | |
| rc = IUnknown_Release(obj); | |
| ok_(__FILE__,line)(rc == ref, "expected refcount %ld, got %ld\n", ref, rc); | |
| } | |
| static void flush_events(void) | |
| { | |
| int diff = 200; | |
| DWORD time; | |
| MSG msg; | |
| time = GetTickCount() + diff; | |
| while (diff > 0) | |
| { | |
| if (MsgWaitForMultipleObjects(0, NULL, FALSE, 100, QS_ALLINPUT) == WAIT_TIMEOUT) | |
| break; | |
| while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) | |
| DispatchMessageA(&msg); | |
| diff = time - GetTickCount(); | |
| } | |
| } | |
| static void load_resource(const WCHAR *filename) | |
| { | |
| DWORD written; | |
| HANDLE file; | |
| HRSRC res; | |
| void *ptr; | |
| file = CreateFileW(filename, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0); | |
| ok(file != INVALID_HANDLE_VALUE, "failed to create %s, error %lu\n", debugstr_w(filename), GetLastError()); | |
| res = FindResourceW(NULL, filename, L"TESTDLL"); | |
| ok(res != 0, "couldn't find resource\n"); | |
| ptr = LockResource(LoadResource(GetModuleHandleW(NULL), res)); | |
| WriteFile(file, ptr, SizeofResource(GetModuleHandleW(NULL), res), &written, NULL); | |
| ok(written == SizeofResource(GetModuleHandleW(NULL), res), "couldn't write resource\n"); | |
| CloseHandle(file); | |
| } | |
| static void test_ActivationFactories(void) | |
| { | |
| HRESULT hr; | |
| HSTRING str, str2; | |
| IActivationFactory *factory = NULL; | |
| IInspectable *inspect = NULL; | |
| ULONG ref; | |
| hr = WindowsCreateString(L"Windows.Data.Xml.Dom.XmlDocument", | |
| ARRAY_SIZE(L"Windows.Data.Xml.Dom.XmlDocument") - 1, &str); | |
| ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); | |
| hr = WindowsCreateString(L"Does.Not.Exist", ARRAY_SIZE(L"Does.Not.Exist") - 1, &str2); | |
| ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); | |
| hr = RoInitialize(RO_INIT_MULTITHREADED); | |
| ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); | |
| hr = RoGetActivationFactory(str2, &IID_IActivationFactory, (void **)&factory); | |
| ok(hr == REGDB_E_CLASSNOTREG, "Unexpected hr %#lx.\n", hr); | |
| hr = RoGetActivationFactory(str, &IID_IActivationFactory, (void **)&factory); | |
| todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); | |
| if(factory) | |
| IActivationFactory_Release(factory); | |
| hr = RoActivateInstance(str2, &inspect); | |
| ok(hr == REGDB_E_CLASSNOTREG, "Unexpected hr %#lx.\n", hr); | |
| hr = RoActivateInstance(str, &inspect); | |
| todo_wine ok(hr == S_OK, "UNexpected hr %#lx.\n", hr); | |
| if(inspect) | |
| IInspectable_Release(inspect); | |
| WindowsDeleteString(str2); | |
| WindowsDeleteString(str); | |
| hr = WindowsCreateString(L"Wine.Test.Missing", ARRAY_SIZE(L"Wine.Test.Missing") - 1, &str); | |
| ok(hr == S_OK, "WindowsCreateString returned %#lx.\n", hr); | |
| hr = RoGetActivationFactory(str, &IID_IActivationFactory, (void **)&factory); | |
| ok(hr == REGDB_E_CLASSNOTREG, "RoGetActivationFactory returned %#lx.\n", hr); | |
| ok(factory == NULL, "got factory %p.\n", factory); | |
| WindowsDeleteString(str); | |
| hr = WindowsCreateString(L"Wine.Test.Class", ARRAY_SIZE(L"Wine.Test.Class") - 1, &str); | |
| ok(hr == S_OK, "WindowsCreateString returned %#lx.\n", hr); | |
| hr = RoGetActivationFactory(str, &IID_IActivationFactory, (void **)&factory); | |
| todo_wine | |
| ok(hr == E_NOTIMPL || broken(hr == REGDB_E_CLASSNOTREG) /* <= w1064v1809 */, | |
| "RoGetActivationFactory returned %#lx.\n", hr); | |
| todo_wine | |
| ok(factory == NULL, "got factory %p.\n", factory); | |
| if (factory) IActivationFactory_Release(factory); | |
| WindowsDeleteString(str); | |
| hr = WindowsCreateString(L"Wine.Test.Trusted", ARRAY_SIZE(L"Wine.Test.Trusted") - 1, &str); | |
| ok(hr == S_OK, "WindowsCreateString returned %#lx.\n", hr); | |
| hr = RoGetActivationFactory(str, &IID_IActivationFactory, (void **)&factory); | |
| ok(hr == S_OK || broken(hr == REGDB_E_CLASSNOTREG) /* <= w1064v1809 */, | |
| "RoGetActivationFactory returned %#lx.\n", hr); | |
| if (hr == REGDB_E_CLASSNOTREG) | |
| ok(!factory, "got factory %p.\n", factory); | |
| else | |
| ok(!!factory, "got factory %p.\n", factory); | |
| if (!factory) ref = 0; | |
| else ref = IActivationFactory_Release(factory); | |
| ok(ref == 0, "Release returned %lu\n", ref); | |
| WindowsDeleteString(str); | |
| RoUninitialize(); | |
| } | |
| static APTTYPE check_thread_apttype; | |
| static APTTYPEQUALIFIER check_thread_aptqualifier; | |
| static HRESULT check_thread_hr; | |
| static DWORD WINAPI check_apartment_thread(void *dummy) | |
| { | |
| check_thread_apttype = 0xdeadbeef; | |
| check_thread_aptqualifier = 0xdeadbeef; | |
| check_thread_hr = CoGetApartmentType(&check_thread_apttype, &check_thread_aptqualifier); | |
| return 0; | |
| } | |
| #define check_thread_apartment(a) check_thread_apartment_(__LINE__, FALSE, a) | |
| #define check_thread_apartment_broken(a) check_thread_apartment_(__LINE__, TRUE, a) | |
| static void check_thread_apartment_(unsigned int line, BOOL broken_fail, HRESULT expected_hr_thread) | |
| { | |
| HANDLE thread; | |
| check_thread_hr = 0xdeadbeef; | |
| thread = CreateThread(NULL, 0, check_apartment_thread, NULL, 0, NULL); | |
| WaitForSingleObject(thread, INFINITE); | |
| CloseHandle(thread); | |
| ok_(__FILE__, line)(check_thread_hr == expected_hr_thread | |
| || broken(broken_fail && expected_hr_thread == S_OK && check_thread_hr == CO_E_NOTINITIALIZED), | |
| "got %#lx, expected %#lx.\n", check_thread_hr, expected_hr_thread); | |
| if (SUCCEEDED(check_thread_hr)) | |
| { | |
| ok_(__FILE__, line)(check_thread_apttype == APTTYPE_MTA, "got %d.\n", check_thread_apttype); | |
| ok_(__FILE__, line)(check_thread_aptqualifier == APTTYPEQUALIFIER_IMPLICIT_MTA, "got %d.\n", check_thread_aptqualifier); | |
| } | |
| } | |
| static HANDLE mta_init_thread_init_done_event, mta_init_thread_done_event; | |
| static DWORD WINAPI mta_init_thread(void *dummy) | |
| { | |
| HRESULT hr; | |
| hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); | |
| ok(hr == S_OK, "got %#lx.\n", hr); | |
| SetEvent(mta_init_thread_init_done_event); | |
| WaitForSingleObject(mta_init_thread_done_event, INFINITE); | |
| CoUninitialize(); | |
| return 0; | |
| } | |
| static DWORD WINAPI mta_init_implicit_thread(void *dummy) | |
| { | |
| IActivationFactory *factory; | |
| HSTRING str; | |
| HRESULT hr; | |
| hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); | |
| ok(hr == S_OK, "got %#lx.\n", hr); | |
| hr = WindowsCreateString(L"Does.Not.Exist", ARRAY_SIZE(L"Does.Not.Exist") - 1, &str); | |
| ok(hr == S_OK, "got %#lx.\n", hr); | |
| hr = RoGetActivationFactory(str, &IID_IActivationFactory, (void **)&factory); | |
| ok(hr == REGDB_E_CLASSNOTREG, "got %#lx.\n", hr); | |
| WindowsDeleteString(str); | |
| SetEvent(mta_init_thread_init_done_event); | |
| WaitForSingleObject(mta_init_thread_done_event, INFINITE); | |
| /* No CoUninitialize(), testing cleanup on thread exit. */ | |
| return 0; | |
| } | |
| enum oletlsflags | |
| { | |
| OLETLS_UUIDINITIALIZED = 0x2, | |
| OLETLS_DISABLE_OLE1DDE = 0x40, | |
| OLETLS_APARTMENTTHREADED = 0x80, | |
| OLETLS_MULTITHREADED = 0x100, | |
| }; | |
| struct oletlsdata | |
| { | |
| void *threadbase; | |
| void *smallocator; | |
| DWORD id; | |
| DWORD flags; | |
| }; | |
| static DWORD get_oletlsflags(void) | |
| { | |
| struct oletlsdata *data = NtCurrentTeb()->ReservedForOle; | |
| return data ? data->flags : 0; | |
| } | |
| static void test_implicit_mta(void) | |
| { | |
| static const struct | |
| { | |
| BOOL ro_init; | |
| BOOL mta; | |
| } | |
| tests[] = | |
| { | |
| { FALSE, FALSE }, | |
| { FALSE, TRUE }, | |
| { TRUE, FALSE }, | |
| { TRUE, TRUE }, | |
| }; | |
| APTTYPEQUALIFIER aptqualifier; | |
| IActivationFactory *factory; | |
| APTTYPE apttype; | |
| unsigned int i; | |
| HANDLE thread; | |
| HSTRING str; | |
| HRESULT hr; | |
| hr = WindowsCreateString(L"Does.Not.Exist", ARRAY_SIZE(L"Does.Not.Exist") - 1, &str); | |
| ok(hr == S_OK, "got %#lx.\n", hr); | |
| /* RoGetActivationFactory doesn't implicitly initialize COM. */ | |
| hr = RoGetActivationFactory(str, &IID_IActivationFactory, (void **)&factory); | |
| ok(hr == CO_E_NOTINITIALIZED, "got %#lx.\n", hr); | |
| check_thread_apartment(CO_E_NOTINITIALIZED); | |
| /* RoGetActivationFactory initializes implicit MTA. */ | |
| for (i = 0; i < ARRAY_SIZE(tests); ++i) | |
| { | |
| winetest_push_context("test %u", i); | |
| if (tests[i].ro_init) | |
| { | |
| DWORD flags = tests[i].mta ? OLETLS_MULTITHREADED : OLETLS_APARTMENTTHREADED; | |
| hr = RoInitialize(tests[i].mta ? RO_INIT_MULTITHREADED : RO_INIT_SINGLETHREADED); | |
| flags |= OLETLS_DISABLE_OLE1DDE; | |
| ok((get_oletlsflags() & flags) == flags, "get_oletlsflags() = %lx\n", get_oletlsflags()); | |
| } | |
| else | |
| hr = CoInitializeEx(NULL, tests[i].mta ? COINIT_MULTITHREADED : COINIT_APARTMENTTHREADED); | |
| ok(hr == S_OK, "got %#lx.\n", hr); | |
| check_thread_apartment(tests[i].mta ? S_OK : CO_E_NOTINITIALIZED); | |
| hr = RoGetActivationFactory(str, &IID_IActivationFactory, (void **)&factory); | |
| ok(hr == REGDB_E_CLASSNOTREG, "got %#lx.\n", hr); | |
| check_thread_apartment_broken(S_OK); /* Broken on Win8. */ | |
| hr = RoGetActivationFactory(str, &IID_IActivationFactory, (void **)&factory); | |
| ok(hr == REGDB_E_CLASSNOTREG, "got %#lx.\n", hr); | |
| check_thread_apartment_broken(S_OK); /* Broken on Win8. */ | |
| if (tests[i].ro_init) | |
| RoUninitialize(); | |
| else | |
| CoUninitialize(); | |
| check_thread_apartment(CO_E_NOTINITIALIZED); | |
| winetest_pop_context(); | |
| } | |
| mta_init_thread_init_done_event = CreateEventW(NULL, FALSE, FALSE, NULL); | |
| mta_init_thread_done_event = CreateEventW(NULL, FALSE, FALSE, NULL); | |
| /* RoGetActivationFactory references implicit MTA in a current thread | |
| * even if implicit MTA was already initialized: check with STA init | |
| * after RoGetActivationFactory(). */ | |
| thread = CreateThread(NULL, 0, mta_init_thread, NULL, 0, NULL); | |
| ok(!!thread, "failed.\n"); | |
| WaitForSingleObject(mta_init_thread_init_done_event, INFINITE); | |
| check_thread_apartment(S_OK); | |
| hr = RoGetActivationFactory(str, &IID_IActivationFactory, (void **)&factory); | |
| ok(hr == REGDB_E_CLASSNOTREG, "got %#lx.\n", hr); | |
| check_thread_apartment(S_OK); | |
| hr = CoGetApartmentType(&apttype, &aptqualifier); | |
| ok(hr == S_OK, "got %#lx.\n", hr); | |
| ok(apttype == APTTYPE_MTA, "got %d.\n", apttype); | |
| ok(aptqualifier == APTTYPEQUALIFIER_IMPLICIT_MTA, "got %d.\n", aptqualifier); | |
| hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); | |
| ok(hr == S_OK, "got %#lx.\n", hr); | |
| hr = CoGetApartmentType(&apttype, &aptqualifier); | |
| ok(hr == S_OK, "got %#lx.\n", hr); | |
| ok(apttype == APTTYPE_MAINSTA, "got %d.\n", apttype); | |
| ok(aptqualifier == APTTYPEQUALIFIER_NONE, "got %d.\n", aptqualifier); | |
| SetEvent(mta_init_thread_done_event); | |
| WaitForSingleObject(thread, INFINITE); | |
| CloseHandle(thread); | |
| check_thread_apartment_broken(S_OK); /* Broken on Win8. */ | |
| CoUninitialize(); | |
| check_thread_apartment(CO_E_NOTINITIALIZED); | |
| /* RoGetActivationFactory references implicit MTA in a current thread | |
| * even if implicit MTA was already initialized: check with STA init | |
| * before RoGetActivationFactory(). */ | |
| thread = CreateThread(NULL, 0, mta_init_thread, NULL, 0, NULL); | |
| ok(!!thread, "failed.\n"); | |
| WaitForSingleObject(mta_init_thread_init_done_event, INFINITE); | |
| check_thread_apartment(S_OK); | |
| hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); | |
| ok(hr == S_OK, "got %#lx.\n", hr); | |
| hr = RoGetActivationFactory(str, &IID_IActivationFactory, (void **)&factory); | |
| ok(hr == REGDB_E_CLASSNOTREG, "got %#lx.\n", hr); | |
| check_thread_apartment(S_OK); | |
| SetEvent(mta_init_thread_done_event); | |
| WaitForSingleObject(thread, INFINITE); | |
| CloseHandle(thread); | |
| check_thread_apartment_broken(S_OK); /* Broken on Win8. */ | |
| CoUninitialize(); | |
| check_thread_apartment(CO_E_NOTINITIALIZED); | |
| /* Test implicit MTA apartment thread exit. */ | |
| thread = CreateThread(NULL, 0, mta_init_implicit_thread, NULL, 0, NULL); | |
| ok(!!thread, "failed.\n"); | |
| WaitForSingleObject(mta_init_thread_init_done_event, INFINITE); | |
| check_thread_apartment_broken(S_OK); /* Broken on Win8. */ | |
| SetEvent(mta_init_thread_done_event); | |
| WaitForSingleObject(thread, INFINITE); | |
| CloseHandle(thread); | |
| check_thread_apartment(CO_E_NOTINITIALIZED); | |
| CloseHandle(mta_init_thread_init_done_event); | |
| CloseHandle(mta_init_thread_done_event); | |
| WindowsDeleteString(str); | |
| } | |
| struct unk_impl | |
| { | |
| IUnknown IUnknown_iface; | |
| LONG ref; | |
| }; | |
| static inline struct unk_impl *impl_from_IUnknown(IUnknown *iface) | |
| { | |
| return CONTAINING_RECORD(iface, struct unk_impl, IUnknown_iface); | |
| } | |
| static HRESULT WINAPI unk_QueryInterface(IUnknown *iface, REFIID riid, void **ppv) | |
| { | |
| if (IsEqualGUID(riid, &IID_IUnknown)) | |
| { | |
| *ppv = iface; | |
| IUnknown_AddRef(iface); | |
| return S_OK; | |
| } | |
| return E_NOINTERFACE; | |
| } | |
| static HRESULT WINAPI unk_no_marshal_QueryInterface(IUnknown *iface, REFIID riid, void **ppv) | |
| { | |
| if (IsEqualGUID(riid, &IID_IUnknown) | |
| || IsEqualGUID(riid, &IID_INoMarshal)) | |
| { | |
| *ppv = iface; | |
| IUnknown_AddRef(iface); | |
| return S_OK; | |
| } | |
| return E_NOINTERFACE; | |
| } | |
| static HRESULT WINAPI unk_agile_QueryInterface(IUnknown *iface, REFIID riid, void **ppv) | |
| { | |
| if (IsEqualGUID(riid, &IID_IUnknown) | |
| || IsEqualGUID(riid, &IID_IAgileObject)) | |
| { | |
| *ppv = iface; | |
| IUnknown_AddRef(iface); | |
| return S_OK; | |
| } | |
| return E_NOINTERFACE; | |
| } | |
| static ULONG WINAPI unk_AddRef(IUnknown *iface) | |
| { | |
| struct unk_impl *impl = impl_from_IUnknown(iface); | |
| return InterlockedIncrement(&impl->ref); | |
| } | |
| static ULONG WINAPI unk_Release(IUnknown *iface) | |
| { | |
| struct unk_impl *impl = impl_from_IUnknown(iface); | |
| return InterlockedDecrement(&impl->ref); | |
| } | |
| static const IUnknownVtbl unk_vtbl = | |
| { | |
| unk_QueryInterface, | |
| unk_AddRef, | |
| unk_Release | |
| }; | |
| static const IUnknownVtbl unk_no_marshal_vtbl = | |
| { | |
| unk_no_marshal_QueryInterface, | |
| unk_AddRef, | |
| unk_Release | |
| }; | |
| static const IUnknownVtbl unk_agile_vtbl = | |
| { | |
| unk_agile_QueryInterface, | |
| unk_AddRef, | |
| unk_Release | |
| }; | |
| struct test_RoGetAgileReference_thread_param | |
| { | |
| enum AgileReferenceOptions option; | |
| RO_INIT_TYPE from_type; | |
| RO_INIT_TYPE to_type; | |
| IAgileReference *agile_reference; | |
| IUnknown *unk_obj; | |
| BOOLEAN obj_is_agile; | |
| }; | |
| static DWORD CALLBACK test_RoGetAgileReference_thread_proc(void *arg) | |
| { | |
| struct test_RoGetAgileReference_thread_param *param = (struct test_RoGetAgileReference_thread_param *)arg; | |
| IUnknown *unknown; | |
| HRESULT hr; | |
| winetest_push_context("%d %d %d", param->option, param->from_type, param->to_type); | |
| RoInitialize(param->to_type); | |
| unknown = NULL; | |
| hr = IAgileReference_Resolve(param->agile_reference, &IID_IUnknown, (void **)&unknown); | |
| ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); | |
| ok(!!unknown, "Expected pointer not NULL.\n"); | |
| if (param->obj_is_agile) | |
| { | |
| ok(unknown == param->unk_obj, "Expected the same object.\n"); | |
| EXPECT_REF(param->unk_obj, 4); | |
| } | |
| else if (param->from_type == RO_INIT_MULTITHREADED && param->to_type == RO_INIT_MULTITHREADED) | |
| { | |
| ok(unknown == param->unk_obj, "Expected the same object.\n"); | |
| todo_wine_if(param->option == AGILEREFERENCE_DEFAULT) | |
| EXPECT_REF(param->unk_obj, param->option == AGILEREFERENCE_DEFAULT ? 5 : 6); | |
| } | |
| else | |
| { | |
| ok(unknown != param->unk_obj, "Expected a different object pointer.\n"); | |
| todo_wine_if(param->option == AGILEREFERENCE_DEFAULT) | |
| EXPECT_REF(param->unk_obj, param->option == AGILEREFERENCE_DEFAULT ? 4 : 5); | |
| EXPECT_REF(unknown, 1); | |
| } | |
| IUnknown_Release(unknown); | |
| RoUninitialize(); | |
| winetest_pop_context(); | |
| return 0; | |
| } | |
| static void test_RoGetAgileReference(void) | |
| { | |
| struct test_RoGetAgileReference_thread_param param; | |
| struct unk_impl unk_no_marshal_obj = {{&unk_no_marshal_vtbl}, 1}; | |
| struct unk_impl unk_obj = {{&unk_vtbl}, 1}; | |
| struct unk_impl unk_agile_obj = {{&unk_agile_vtbl}, 1}; | |
| enum AgileReferenceOptions option; | |
| IAgileReference *agile_reference; | |
| RO_INIT_TYPE from_type, to_type; | |
| IUnknown *unknown, *unknown2; | |
| IAgileObject *agile_object; | |
| HANDLE thread; | |
| HRESULT hr; | |
| DWORD ret; | |
| for (option = AGILEREFERENCE_DEFAULT; option <= AGILEREFERENCE_DELAYEDMARSHAL; option++) | |
| { | |
| for (from_type = RO_INIT_SINGLETHREADED; from_type <= RO_INIT_MULTITHREADED; from_type++) | |
| { | |
| winetest_push_context("%d %d", option, from_type); | |
| hr = RoGetAgileReference(option, &IID_IUnknown, &unk_obj.IUnknown_iface, &agile_reference); | |
| ok(hr == CO_E_NOTINITIALIZED, "Got unexpected hr %#lx.\n", hr); | |
| RoInitialize(from_type); | |
| agile_reference = NULL; | |
| EXPECT_REF(&unk_obj, 1); | |
| /* Invalid option */ | |
| hr = RoGetAgileReference(AGILEREFERENCE_DELAYEDMARSHAL + 1, &IID_IUnknown, &unk_obj.IUnknown_iface, &agile_reference); | |
| ok(hr == E_INVALIDARG, "Got unexpected hr %#lx.\n", hr); | |
| /* Non-existent interface for the object */ | |
| hr = RoGetAgileReference(option, &IID_IActivationFactory, &unk_obj.IUnknown_iface, &agile_reference); | |
| ok(hr == E_NOINTERFACE, "Got unexpected hr %#lx.\n", hr); | |
| /* Objects that implements INoMarshal */ | |
| hr = RoGetAgileReference(option, &IID_IUnknown, &unk_no_marshal_obj.IUnknown_iface, &agile_reference); | |
| ok(hr == CO_E_NOT_SUPPORTED, "Got unexpected hr %#lx.\n", hr); | |
| /* Create agile reference object */ | |
| hr = RoGetAgileReference(option, &IID_IUnknown, &unk_obj.IUnknown_iface, &agile_reference); | |
| ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); | |
| ok(!!agile_reference, "Got unexpected agile_reference.\n"); | |
| todo_wine_if(option == AGILEREFERENCE_DEFAULT) | |
| EXPECT_REF(&unk_obj, 2); | |
| /* Check the created agile reference object has IAgileObject */ | |
| hr = IAgileReference_QueryInterface(agile_reference, &IID_IAgileObject, (void **)&agile_object); | |
| ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); | |
| IAgileObject_Release(agile_object); | |
| /* Resolve once */ | |
| unknown = NULL; | |
| hr = IAgileReference_Resolve(agile_reference, &IID_IUnknown, (void **)&unknown); | |
| ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); | |
| ok(!!unknown, "Expected pointer not NULL.\n"); | |
| ok(unknown == &unk_obj.IUnknown_iface, "Expected the same object.\n"); | |
| todo_wine | |
| EXPECT_REF(&unk_obj, 3); | |
| /* Resolve twice */ | |
| unknown2 = NULL; | |
| hr = IAgileReference_Resolve(agile_reference, &IID_IUnknown, (void **)&unknown2); | |
| ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); | |
| ok(!!unknown2, "Expected pointer not NULL.\n"); | |
| ok(unknown2 == &unk_obj.IUnknown_iface, "Expected the same object.\n"); | |
| todo_wine | |
| EXPECT_REF(&unk_obj, 4); | |
| /* Resolve in another apartment */ | |
| for (to_type = RO_INIT_SINGLETHREADED; to_type <= RO_INIT_MULTITHREADED; to_type++) | |
| { | |
| param.option = option; | |
| param.from_type = from_type; | |
| param.to_type = to_type; | |
| param.agile_reference = agile_reference; | |
| param.unk_obj = &unk_obj.IUnknown_iface; | |
| param.obj_is_agile = FALSE; | |
| thread = CreateThread(NULL, 0, test_RoGetAgileReference_thread_proc, ¶m, 0, NULL); | |
| flush_events(); | |
| ret = WaitForSingleObject(thread, 100); | |
| ok(!ret, "WaitForSingleObject failed, error %ld.\n", GetLastError()); | |
| } | |
| IUnknown_Release(unknown2); | |
| IUnknown_Release(unknown); | |
| IAgileReference_Release(agile_reference); | |
| EXPECT_REF(&unk_obj, 1); | |
| hr = RoGetAgileReference(option, &IID_IUnknown, &unk_agile_obj.IUnknown_iface, &agile_reference); | |
| ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); | |
| ok(!!agile_reference, "Got unexpected agile_reference.\n"); | |
| EXPECT_REF(&unk_agile_obj, 2); | |
| unknown = NULL; | |
| hr = IAgileReference_Resolve(agile_reference, &IID_IUnknown, (void **)&unknown); | |
| ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); | |
| ok(!!unknown, "Expected pointer not NULL.\n"); | |
| ok(unknown == &unk_agile_obj.IUnknown_iface, "Expected the same object.\n"); | |
| EXPECT_REF(&unk_agile_obj, 3); | |
| for (to_type = RO_INIT_SINGLETHREADED; to_type <= RO_INIT_MULTITHREADED; to_type++) | |
| { | |
| param.option = option; | |
| param.from_type = from_type; | |
| param.to_type = to_type; | |
| param.agile_reference = agile_reference; | |
| param.unk_obj = &unk_agile_obj.IUnknown_iface; | |
| param.obj_is_agile = TRUE; | |
| thread = CreateThread(NULL, 0, test_RoGetAgileReference_thread_proc, ¶m, 0, NULL); | |
| flush_events(); | |
| ret = WaitForSingleObject(thread, 100); | |
| ok(!ret, "WaitForSingleObject failed, error %ld.\n", GetLastError()); | |
| } | |
| IUnknown_Release(unknown); | |
| IAgileReference_Release(agile_reference); | |
| EXPECT_REF(&unk_obj, 1); | |
| RoUninitialize(); | |
| winetest_pop_context(); | |
| } | |
| } | |
| } | |
| static void test_RoGetErrorReportingFlags(void) | |
| { | |
| UINT32 flags; | |
| HRESULT hr; | |
| hr = RoGetErrorReportingFlags(NULL); | |
| ok(hr == E_POINTER, "Got unexpected hr %#lx.\n", hr); | |
| hr = RoGetErrorReportingFlags(&flags); | |
| ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); | |
| ok(flags == RO_ERROR_REPORTING_USESETERRORINFO, "Got unexpected flag %#x.\n", flags); | |
| } | |
| START_TEST(roapi) | |
| { | |
| BOOL ret; | |
| load_resource(L"wine.combase.test.dll"); | |
| test_implicit_mta(); | |
| test_ActivationFactories(); | |
| test_RoGetAgileReference(); | |
| test_RoGetErrorReportingFlags(); | |
| SetLastError(0xdeadbeef); | |
| ret = DeleteFileW(L"wine.combase.test.dll"); | |
| todo_wine_if(!ret && GetLastError() == ERROR_ACCESS_DENIED) | |
| ok(ret, "Failed to delete file, error %lu\n", GetLastError()); | |
| } |