/* * 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 #include "windef.h" #include "winbase.h" #include "winerror.h" #include "winstring.h" #include "initguid.h" #include "roapi.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; } 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) hr = RoInitialize(tests[i].mta ? RO_INIT_MULTITHREADED : RO_INIT_SINGLETHREADED); 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 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 }; struct test_RoGetAgileReference_thread_param { enum AgileReferenceOptions option; RO_INIT_TYPE from_type; RO_INIT_TYPE to_type; IAgileReference *agile_reference; IUnknown *unk_obj; }; 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->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}; 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; 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); RoUninitialize(); winetest_pop_context(); } } } START_TEST(roapi) { BOOL ret; load_resource(L"wine.combase.test.dll"); test_implicit_mta(); test_ActivationFactories(); test_RoGetAgileReference(); 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()); }