Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=55709
There appears to be a race condition happening. When it doesn't work, it looks like the apartment is not initialized, output:
``` 01e8:fixme:combase:RoGetActivationFactory (L"Windows.Security.Credentials.UI.UserConsentVerifier", {af4f3f91-564c-4ddc-b8b5-973447627c65}, 000000000011E890): semi-stub 01e8:fixme:credentials:factory_QueryInterface {94ea2b94-e9cc-49e0-c0ff-ee64ca8f5b90} not implemented, returning E_NOINTERFACE. 0220:err:ole:CoGetContextToken apartment not initialised 0220:fixme:ole:thread_context_callback_ContextCallback 000000000347E4A8, 00007FFFFE10D510, 0000000005C7FBA0, {0a299774-3e4e-fc42-1d9d-72cee105ca57}, 5, 0000000000000000 0220:fixme:combase:RoOriginateLanguageException 0x80004001, L"", 0000000000000000: stub wine: Call from 0000000174013C17 to unimplemented function combase.dll.RoFailFastWithErrorContext, aborting wine: Unimplemented function combase.dll.RoFailFastWithErrorContext called at address 0000000174013C17 (thread 0220), starting debugger. ```
Adding in WINEDEBUG=+credentials makes it consistently work:
``` 0154:fixme:combase:RoGetActivationFactory (L"Windows.Security.Credentials.UI.UserConsentVerifier", {af4f3f91-564c-4ddc-b8b5-973447627c65}, 000000000011E890): semi-stub 0154:trace:credentials:DllGetActivationFactory class L"Windows.Security.Credentials.UI.UserConsentVerifier", factory 000000000011E6C0. 0154:trace:credentials:factory_QueryInterface iface 00000001F3B04040, iid {00000035-0000-0000-c000-000000000046}, out 000000000011E6C0. 0154:trace:credentials:factory_AddRef iface 00000001F3B04040 increasing refcount to 2. 0154:trace:credentials:factory_QueryInterface iface 00000001F3B04040, iid {af4f3f91-564c-4ddc-b8b5-973447627c65}, out 000000000011E890. 0154:trace:credentials:factory_AddRef iface 00000001F3B04040 increasing refcount to 3. 0154:trace:credentials:factory_Release iface 00000001F3B04040 decreasing refcount to 2. 0154:trace:credentials:factory_QueryInterface iface 00000001F3B04040, iid {94ea2b94-e9cc-49e0-c0ff-ee64ca8f5b90}, out 000000000011E898. 0154:fixme:credentials:factory_QueryInterface {94ea2b94-e9cc-49e0-c0ff-ee64ca8f5b90} not implemented, returning E_NOINTERFACE. 0154:trace:credentials:user_consent_verifier_statics_CheckAvailabilityAsync iface 00000001F3B04048, result 000000000011E868 0154:trace:credentials:factory_AddRef iface 00000001F3B04040 increasing refcount to 3. 0154:trace:credentials:async_impl_Start iface 0000000000791260. 0154:trace:credentials:async_UserConsentVerifierAvailability_AddRef iface 0000000003461650, ref 2. 0154:trace:credentials:async_operation_UserConsentVerifierAvailability_create created IAsyncOperation_UserConsentVerifierAvailability 0000000003461650 0154:trace:credentials:factory_Release iface 00000001F3B04040 decreasing refcount to 2. 0154:trace:credentials:async_UserConsentVerifierAvailability_AddRef iface 0000000003461650, ref 3. 0154:trace:credentials:async_UserConsentVerifierAvailability_put_Completed iface 0000000003461650, handler 00000000033E2410. 0154:trace:credentials:async_impl_put_Completed iface 0000000000791260, handler 00000000033E2410. 0154:trace:credentials:async_UserConsentVerifierAvailability_GetResults iface 0000000003461650, results 000000000011E790. 0154:trace:credentials:async_impl_get_Result iface 0000000000791260, result 000000000011E700. 018c:trace:credentials:async_UserConsentVerifierAvailability_Release iface 0000000003461650, ref 2. 0154:trace:credentials:async_UserConsentVerifierAvailability_Release iface 0000000003461650, ref 1. 0154:trace:credentials:async_UserConsentVerifierAvailability_Release iface 0000000003461650, ref 0. 0154:trace:credentials:async_impl_Release iface 0000000000791260, ref 0. 0154:trace:credentials:async_info_Close iface 0000000000791268. 0154:trace:credentials:factory_Release iface 00000001F3B04040 decreasing refcount to 1. ```
-- v2: windows.security.credentials.ui.userconsentverifier/tests: Add IUserConsentVerifierStatics::CheckAvailabilityAsync() tests. windows.security.credentials.ui.userconsentverifier: Implement IUserConsentVerifierStatics::CheckAvailabilityAsync(). windows.security.credentials.ui.userconsentverifier: Import and modify IAsyncOperation from windows.gaming.input.
From: Mohamad Al-Jaf mohamadaljaf@gmail.com
--- include/Makefile.in | 1 + include/windows.security.credentials.ui.idl | 91 +++++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 include/windows.security.credentials.ui.idl
diff --git a/include/Makefile.in b/include/Makefile.in index 5f61344324d..ef0c48ed9ef 100644 --- a/include/Makefile.in +++ b/include/Makefile.in @@ -855,6 +855,7 @@ SOURCES = \ windows.perception.spatial.idl \ windows.perception.spatial.surfaces.idl \ windows.security.credentials.idl \ + windows.security.credentials.ui.idl \ windows.security.cryptography.idl \ windows.security.isolation.idl \ windows.storage.fileproperties.idl \ diff --git a/include/windows.security.credentials.ui.idl b/include/windows.security.credentials.ui.idl new file mode 100644 index 00000000000..3457ea1ec33 --- /dev/null +++ b/include/windows.security.credentials.ui.idl @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2023 Mohamad Al-Jaf + * + * 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 + */ + +#ifdef __WIDL__ +#pragma winrt ns_prefix +#endif + +import "inspectable.idl"; +import "asyncinfo.idl"; +import "eventtoken.idl"; +import "windowscontracts.idl"; +import "windows.foundation.idl"; +import "windows.storage.streams.idl"; + +namespace Windows.Security.Credentials.UI { + typedef enum UserConsentVerificationResult UserConsentVerificationResult; + typedef enum UserConsentVerifierAvailability UserConsentVerifierAvailability; + + interface IUserConsentVerifierStatics; + + runtimeclass UserConsentVerifier; + + declare { + interface Windows.Foundation.AsyncOperationCompletedHandler<Windows.Security.Credentials.UI.UserConsentVerificationResult>; + interface Windows.Foundation.AsyncOperationCompletedHandler<Windows.Security.Credentials.UI.UserConsentVerifierAvailability>; + interface Windows.Foundation.IAsyncOperation<Windows.Security.Credentials.UI.UserConsentVerificationResult>; + interface Windows.Foundation.IAsyncOperation<Windows.Security.Credentials.UI.UserConsentVerifierAvailability>; + } + + [ + contract(Windows.Foundation.UniversalApiContract, 1.0) + ] + enum UserConsentVerificationResult + { + Verified = 0, + DeviceNotPresent = 1, + NotConfiguredForUser = 2, + DisabledByPolicy = 3, + DeviceBusy = 4, + RetriesExhausted = 5, + Canceled = 6, + }; + + [ + contract(Windows.Foundation.UniversalApiContract, 1.0) + ] + enum UserConsentVerifierAvailability + { + Available = 0, + DeviceNotPresent = 1, + NotConfiguredForUser = 2, + DisabledByPolicy = 3, + DeviceBusy = 4, + }; + + [ + contract(Windows.Foundation.UniversalApiContract, 1.0), + exclusiveto(Windows.Security.Credentials.UI.UserConsentVerifier), + uuid(af4f3f91-564c-4ddc-b8b5-973447627c65) + ] + interface IUserConsentVerifierStatics : IInspectable + { + HRESULT CheckAvailabilityAsync([out, retval] Windows.Foundation.IAsyncOperation<Windows.Security.Credentials.UI.UserConsentVerifierAvailability> **result); + HRESULT RequestVerificationAsync([in] HSTRING message, [out, retval] Windows.Foundation.IAsyncOperation<Windows.Security.Credentials.UI.UserConsentVerificationResult> **result); + } + + [ + contract(Windows.Foundation.UniversalApiContract, 1.0), + marshaling_behavior(agile), + static(Windows.Security.Credentials.UI.IUserConsentVerifierStatics, Windows.Foundation.UniversalApiContract, 1.0), + threading(both) + ] + runtimeclass UserConsentVerifier + { + } +}
From: Mohamad Al-Jaf mohamadaljaf@gmail.com
--- configure.ac | 2 + .../Makefile.in | 8 + .../classes.idl | 23 +++ .../main.c | 138 ++++++++++++++++++ .../private.h | 38 +++++ .../tests/Makefile.in | 5 + .../tests/verifier.c | 85 +++++++++++ ...ty.credentials.ui.userconsentverifier.spec | 3 + 8 files changed, 302 insertions(+) create mode 100644 dlls/windows.security.credentials.ui.userconsentverifier/Makefile.in create mode 100644 dlls/windows.security.credentials.ui.userconsentverifier/classes.idl create mode 100644 dlls/windows.security.credentials.ui.userconsentverifier/main.c create mode 100644 dlls/windows.security.credentials.ui.userconsentverifier/private.h create mode 100644 dlls/windows.security.credentials.ui.userconsentverifier/tests/Makefile.in create mode 100644 dlls/windows.security.credentials.ui.userconsentverifier/tests/verifier.c create mode 100644 dlls/windows.security.credentials.ui.userconsentverifier/windows.security.credentials.ui.userconsentverifier.spec
diff --git a/configure.ac b/configure.ac index 5676e35f2b9..f64f9274b24 100644 --- a/configure.ac +++ b/configure.ac @@ -3174,6 +3174,8 @@ WINE_CONFIG_MAKEFILE(dlls/windows.networking.hostname/tests) WINE_CONFIG_MAKEFILE(dlls/windows.networking) WINE_CONFIG_MAKEFILE(dlls/windows.perception.stub) WINE_CONFIG_MAKEFILE(dlls/windows.perception.stub/tests) +WINE_CONFIG_MAKEFILE(dlls/windows.security.credentials.ui.userconsentverifier) +WINE_CONFIG_MAKEFILE(dlls/windows.security.credentials.ui.userconsentverifier/tests) WINE_CONFIG_MAKEFILE(dlls/windows.storage.applicationdata) WINE_CONFIG_MAKEFILE(dlls/windows.storage.applicationdata/tests) WINE_CONFIG_MAKEFILE(dlls/windows.system.profile.systemmanufacturers) diff --git a/dlls/windows.security.credentials.ui.userconsentverifier/Makefile.in b/dlls/windows.security.credentials.ui.userconsentverifier/Makefile.in new file mode 100644 index 00000000000..286ba63de01 --- /dev/null +++ b/dlls/windows.security.credentials.ui.userconsentverifier/Makefile.in @@ -0,0 +1,8 @@ +MODULE = windows.security.credentials.ui.userconsentverifier.dll +IMPORTS = combase + +C_SRCS = \ + main.c + +IDL_SRCS = \ + classes.idl diff --git a/dlls/windows.security.credentials.ui.userconsentverifier/classes.idl b/dlls/windows.security.credentials.ui.userconsentverifier/classes.idl new file mode 100644 index 00000000000..fa573197a25 --- /dev/null +++ b/dlls/windows.security.credentials.ui.userconsentverifier/classes.idl @@ -0,0 +1,23 @@ +/* + * Runtime Classes for windows.security.credentials.ui.userconsentverifier.dll + * + * Copyright (C) 2023 Mohamad Al-Jaf + * + * 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 + */ + +#pragma makedep register + +#include "windows.security.credentials.ui.idl" diff --git a/dlls/windows.security.credentials.ui.userconsentverifier/main.c b/dlls/windows.security.credentials.ui.userconsentverifier/main.c new file mode 100644 index 00000000000..159d58d3a4f --- /dev/null +++ b/dlls/windows.security.credentials.ui.userconsentverifier/main.c @@ -0,0 +1,138 @@ +/* WinRT Windows.Security.Credentials.UI Implementation + * + * Copyright (C) 2023 Mohamad Al-Jaf + * + * 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 + */ + +#include "initguid.h" +#include "private.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(credentials); + +struct user_consent_verifier_statics +{ + IActivationFactory IActivationFactory_iface; + LONG ref; +}; + +static inline struct user_consent_verifier_statics *impl_from_IActivationFactory( IActivationFactory *iface ) +{ + return CONTAINING_RECORD( iface, struct user_consent_verifier_statics, IActivationFactory_iface ); +} + +static HRESULT WINAPI factory_QueryInterface( IActivationFactory *iface, REFIID iid, void **out ) +{ + struct user_consent_verifier_statics *impl = impl_from_IActivationFactory( iface ); + + TRACE( "iface %p, iid %s, out %p.\n", iface, debugstr_guid( iid ), out ); + + if (IsEqualGUID( iid, &IID_IUnknown ) || + IsEqualGUID( iid, &IID_IInspectable ) || + IsEqualGUID( iid, &IID_IActivationFactory )) + { + *out = &impl->IActivationFactory_iface; + IInspectable_AddRef( *out ); + return S_OK; + } + + FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI factory_AddRef( IActivationFactory *iface ) +{ + struct user_consent_verifier_statics *impl = impl_from_IActivationFactory( iface ); + ULONG ref = InterlockedIncrement( &impl->ref ); + TRACE( "iface %p increasing refcount to %lu.\n", iface, ref ); + return ref; +} + +static ULONG WINAPI factory_Release( IActivationFactory *iface ) +{ + struct user_consent_verifier_statics *impl = impl_from_IActivationFactory( iface ); + ULONG ref = InterlockedDecrement( &impl->ref ); + TRACE( "iface %p decreasing refcount to %lu.\n", iface, ref ); + return ref; +} + +static HRESULT WINAPI factory_GetIids( IActivationFactory *iface, ULONG *iid_count, IID **iids ) +{ + FIXME( "iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids ); + return E_NOTIMPL; +} + +static HRESULT WINAPI factory_GetRuntimeClassName( IActivationFactory *iface, HSTRING *class_name ) +{ + FIXME( "iface %p, class_name %p stub!\n", iface, class_name ); + return E_NOTIMPL; +} + +static HRESULT WINAPI factory_GetTrustLevel( IActivationFactory *iface, TrustLevel *trust_level ) +{ + FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level ); + return E_NOTIMPL; +} + +static HRESULT WINAPI factory_ActivateInstance( IActivationFactory *iface, IInspectable **instance ) +{ + FIXME( "iface %p, instance %p stub!\n", iface, instance ); + return E_NOTIMPL; +} + +static const struct IActivationFactoryVtbl factory_vtbl = +{ + factory_QueryInterface, + factory_AddRef, + factory_Release, + /* IInspectable methods */ + factory_GetIids, + factory_GetRuntimeClassName, + factory_GetTrustLevel, + /* IActivationFactory methods */ + factory_ActivateInstance, +}; + +static struct user_consent_verifier_statics user_consent_verifier_statics = +{ + {&factory_vtbl}, + 1, +}; + +static IActivationFactory *user_consent_verifier_factory = &user_consent_verifier_statics.IActivationFactory_iface; + +HRESULT WINAPI DllGetClassObject( REFCLSID clsid, REFIID riid, void **out ) +{ + FIXME( "clsid %s, riid %s, out %p stub!\n", debugstr_guid( clsid ), debugstr_guid( riid ), out ); + return CLASS_E_CLASSNOTAVAILABLE; +} + +HRESULT WINAPI DllGetActivationFactory( HSTRING classid, IActivationFactory **factory ) +{ + const WCHAR *buffer = WindowsGetStringRawBuffer( classid, NULL ); + + TRACE( "class %s, factory %p.\n", debugstr_hstring( classid ), factory ); + + *factory = NULL; + + if (!wcscmp( buffer, RuntimeClass_Windows_Security_Credentials_UI_UserConsentVerifier )) + IActivationFactory_QueryInterface( user_consent_verifier_factory, &IID_IActivationFactory, (void **)factory ); + + if (*factory) return S_OK; + return CLASS_E_CLASSNOTAVAILABLE; +} diff --git a/dlls/windows.security.credentials.ui.userconsentverifier/private.h b/dlls/windows.security.credentials.ui.userconsentverifier/private.h new file mode 100644 index 00000000000..e9aa9d1cd96 --- /dev/null +++ b/dlls/windows.security.credentials.ui.userconsentverifier/private.h @@ -0,0 +1,38 @@ +/* WinRT Windows.Security.Credentials.UI Implementation + * + * Copyright (C) 2023 Mohamad Al-Jaf + * + * 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 + */ + +#ifndef __WINE_WINDOWS_SECURITY_CREDENTIALS_UI_PRIVATE_H +#define __WINE_WINDOWS_SECURITY_CREDENTIALS_UI_PRIVATE_H + +#include <stdarg.h> + +#define COBJMACROS +#include "windef.h" +#include "winbase.h" +#include "winstring.h" + +#include "activation.h" + +#define WIDL_using_Windows_Foundation +#define WIDL_using_Windows_Foundation_Collections +#include "windows.foundation.h" +#define WIDL_using_Windows_Security_Credentials_UI +#include "windows.security.credentials.ui.h" + +#endif diff --git a/dlls/windows.security.credentials.ui.userconsentverifier/tests/Makefile.in b/dlls/windows.security.credentials.ui.userconsentverifier/tests/Makefile.in new file mode 100644 index 00000000000..d7c4e3f507b --- /dev/null +++ b/dlls/windows.security.credentials.ui.userconsentverifier/tests/Makefile.in @@ -0,0 +1,5 @@ +TESTDLL = windows.security.credentials.ui.userconsentverifier.dll +IMPORTS = combase + +C_SRCS = \ + verifier.c diff --git a/dlls/windows.security.credentials.ui.userconsentverifier/tests/verifier.c b/dlls/windows.security.credentials.ui.userconsentverifier/tests/verifier.c new file mode 100644 index 00000000000..096696a2a70 --- /dev/null +++ b/dlls/windows.security.credentials.ui.userconsentverifier/tests/verifier.c @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2023 Mohamad Al-Jaf + * + * 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 "initguid.h" +#include <stdarg.h> + +#include "windef.h" +#include "winbase.h" +#include "winstring.h" + +#include "roapi.h" + +#define WIDL_using_Windows_Foundation +#define WIDL_using_Windows_Foundation_Collections +#include "windows.foundation.h" +#define WIDL_using_Windows_Security_Credentials_UI +#include "windows.security.credentials.ui.h" + +#include "wine/test.h" + +#define check_interface( obj, iid ) check_interface_( __LINE__, obj, iid ) +static void check_interface_( unsigned int line, void *obj, const IID *iid ) +{ + IUnknown *iface = obj; + IUnknown *unk; + HRESULT hr; + + hr = IUnknown_QueryInterface( iface, iid, (void **)&unk ); + ok_(__FILE__, line)( hr == S_OK, "got hr %#lx.\n", hr ); + IUnknown_Release( unk ); +} + +static void test_UserConsentVerifierStatics(void) +{ + static const WCHAR *user_consent_verifier_statics_name = L"Windows.Security.Credentials.UI.UserConsentVerifier"; + IActivationFactory *factory; + HSTRING str; + HRESULT hr; + LONG ref; + + hr = WindowsCreateString( user_consent_verifier_statics_name, wcslen( user_consent_verifier_statics_name ), &str ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + + hr = RoGetActivationFactory( str, &IID_IActivationFactory, (void **)&factory ); + WindowsDeleteString( str ); + ok( hr == S_OK || broken( hr == REGDB_E_CLASSNOTREG ), "got hr %#lx.\n", hr ); + if (hr == REGDB_E_CLASSNOTREG) + { + win_skip( "%s runtimeclass not registered, skipping tests.\n", wine_dbgstr_w( user_consent_verifier_statics_name ) ); + return; + } + + check_interface( factory, &IID_IUnknown ); + check_interface( factory, &IID_IInspectable ); + + ref = IActivationFactory_Release( factory ); + ok( ref == 1, "got ref %ld.\n", ref ); +} + +START_TEST(verifier) +{ + HRESULT hr; + + hr = RoInitialize( RO_INIT_MULTITHREADED ); + ok( hr == S_OK, "RoInitialize failed, hr %#lx\n", hr ); + + test_UserConsentVerifierStatics(); + + RoUninitialize(); +} diff --git a/dlls/windows.security.credentials.ui.userconsentverifier/windows.security.credentials.ui.userconsentverifier.spec b/dlls/windows.security.credentials.ui.userconsentverifier/windows.security.credentials.ui.userconsentverifier.spec new file mode 100644 index 00000000000..20a8bfa98ea --- /dev/null +++ b/dlls/windows.security.credentials.ui.userconsentverifier/windows.security.credentials.ui.userconsentverifier.spec @@ -0,0 +1,3 @@ +@ stdcall -private DllCanUnloadNow() +@ stdcall -private DllGetActivationFactory(ptr ptr) +@ stdcall -private DllGetClassObject(ptr ptr ptr)
From: Mohamad Al-Jaf mohamadaljaf@gmail.com
--- .../main.c | 38 +++++++++++++++++++ .../private.h | 38 +++++++++++++++++++ .../tests/verifier.c | 6 +++ 3 files changed, 82 insertions(+)
diff --git a/dlls/windows.security.credentials.ui.userconsentverifier/main.c b/dlls/windows.security.credentials.ui.userconsentverifier/main.c index 159d58d3a4f..d86601331fb 100644 --- a/dlls/windows.security.credentials.ui.userconsentverifier/main.c +++ b/dlls/windows.security.credentials.ui.userconsentverifier/main.c @@ -27,6 +27,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(credentials); struct user_consent_verifier_statics { IActivationFactory IActivationFactory_iface; + IUserConsentVerifierStatics IUserConsentVerifierStatics_iface; LONG ref; };
@@ -50,6 +51,13 @@ static HRESULT WINAPI factory_QueryInterface( IActivationFactory *iface, REFIID return S_OK; }
+ if (IsEqualGUID( iid, &IID_IUserConsentVerifierStatics )) + { + *out = &impl->IUserConsentVerifierStatics_iface; + IInspectable_AddRef( *out ); + return S_OK; + } + FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); *out = NULL; return E_NOINTERFACE; @@ -108,9 +116,39 @@ static const struct IActivationFactoryVtbl factory_vtbl = factory_ActivateInstance, };
+DEFINE_IINSPECTABLE( user_consent_verifier_statics, IUserConsentVerifierStatics, struct user_consent_verifier_statics, IActivationFactory_iface ) + +static HRESULT WINAPI user_consent_verifier_statics_CheckAvailabilityAsync( IUserConsentVerifierStatics *iface, IAsyncOperation_UserConsentVerifierAvailability **result ) +{ + FIXME( "iface %p, result %p stub!\n", iface, result ); + return E_NOTIMPL; +} + +static HRESULT WINAPI user_consent_verifier_statics_RequestVerificationAsync( IUserConsentVerifierStatics *iface, HSTRING message, + IAsyncOperation_UserConsentVerificationResult **result ) +{ + FIXME( "iface %p, message %s, result %p stub!\n", iface, debugstr_hstring( message ), result ); + return E_NOTIMPL; +} + +static const struct IUserConsentVerifierStaticsVtbl user_consent_verifier_statics_vtbl = +{ + user_consent_verifier_statics_QueryInterface, + user_consent_verifier_statics_AddRef, + user_consent_verifier_statics_Release, + /* IInspectable methods */ + user_consent_verifier_statics_GetIids, + user_consent_verifier_statics_GetRuntimeClassName, + user_consent_verifier_statics_GetTrustLevel, + /* IUserConsentVerifierStatics methods */ + user_consent_verifier_statics_CheckAvailabilityAsync, + user_consent_verifier_statics_RequestVerificationAsync, +}; + static struct user_consent_verifier_statics user_consent_verifier_statics = { {&factory_vtbl}, + {&user_consent_verifier_statics_vtbl}, 1, };
diff --git a/dlls/windows.security.credentials.ui.userconsentverifier/private.h b/dlls/windows.security.credentials.ui.userconsentverifier/private.h index e9aa9d1cd96..dc22b85ffab 100644 --- a/dlls/windows.security.credentials.ui.userconsentverifier/private.h +++ b/dlls/windows.security.credentials.ui.userconsentverifier/private.h @@ -35,4 +35,42 @@ #define WIDL_using_Windows_Security_Credentials_UI #include "windows.security.credentials.ui.h"
+#define DEFINE_IINSPECTABLE_( pfx, iface_type, impl_type, impl_from, iface_mem, expr ) \ + static inline impl_type *impl_from( iface_type *iface ) \ + { \ + return CONTAINING_RECORD( iface, impl_type, iface_mem ); \ + } \ + static HRESULT WINAPI pfx##_QueryInterface( iface_type *iface, REFIID iid, void **out ) \ + { \ + impl_type *impl = impl_from( iface ); \ + return IInspectable_QueryInterface( (IInspectable *)(expr), iid, out ); \ + } \ + static ULONG WINAPI pfx##_AddRef( iface_type *iface ) \ + { \ + impl_type *impl = impl_from( iface ); \ + return IInspectable_AddRef( (IInspectable *)(expr) ); \ + } \ + static ULONG WINAPI pfx##_Release( iface_type *iface ) \ + { \ + impl_type *impl = impl_from( iface ); \ + return IInspectable_Release( (IInspectable *)(expr) ); \ + } \ + static HRESULT WINAPI pfx##_GetIids( iface_type *iface, ULONG *iid_count, IID **iids ) \ + { \ + impl_type *impl = impl_from( iface ); \ + return IInspectable_GetIids( (IInspectable *)(expr), iid_count, iids ); \ + } \ + static HRESULT WINAPI pfx##_GetRuntimeClassName( iface_type *iface, HSTRING *class_name ) \ + { \ + impl_type *impl = impl_from( iface ); \ + return IInspectable_GetRuntimeClassName( (IInspectable *)(expr), class_name ); \ + } \ + static HRESULT WINAPI pfx##_GetTrustLevel( iface_type *iface, TrustLevel *trust_level ) \ + { \ + impl_type *impl = impl_from( iface ); \ + return IInspectable_GetTrustLevel( (IInspectable *)(expr), trust_level ); \ + } +#define DEFINE_IINSPECTABLE( pfx, iface_type, impl_type, base_iface ) \ + DEFINE_IINSPECTABLE_( pfx, iface_type, impl_type, impl_from_##iface_type, iface_type##_iface, &impl->base_iface ) + #endif diff --git a/dlls/windows.security.credentials.ui.userconsentverifier/tests/verifier.c b/dlls/windows.security.credentials.ui.userconsentverifier/tests/verifier.c index 096696a2a70..39245fd2b14 100644 --- a/dlls/windows.security.credentials.ui.userconsentverifier/tests/verifier.c +++ b/dlls/windows.security.credentials.ui.userconsentverifier/tests/verifier.c @@ -48,6 +48,7 @@ static void check_interface_( unsigned int line, void *obj, const IID *iid ) static void test_UserConsentVerifierStatics(void) { static const WCHAR *user_consent_verifier_statics_name = L"Windows.Security.Credentials.UI.UserConsentVerifier"; + IUserConsentVerifierStatics *user_consent_verifier_statics = NULL; IActivationFactory *factory; HSTRING str; HRESULT hr; @@ -68,6 +69,11 @@ static void test_UserConsentVerifierStatics(void) check_interface( factory, &IID_IUnknown ); check_interface( factory, &IID_IInspectable );
+ hr = IActivationFactory_QueryInterface( factory, &IID_IUserConsentVerifierStatics, (void **)&user_consent_verifier_statics ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + + ref = IUserConsentVerifierStatics_Release( user_consent_verifier_statics ); + ok( ref == 1, "got ref %ld.\n", ref ); ref = IActivationFactory_Release( factory ); ok( ref == 1, "got ref %ld.\n", ref ); }
From: Mohamad Al-Jaf mohamadaljaf@gmail.com
--- .../Makefile.in | 4 +- .../async.c | 509 ++++++++++++++++++ .../private.h | 8 + .../provider.idl | 46 ++ 4 files changed, 566 insertions(+), 1 deletion(-) create mode 100644 dlls/windows.security.credentials.ui.userconsentverifier/async.c create mode 100644 dlls/windows.security.credentials.ui.userconsentverifier/provider.idl
diff --git a/dlls/windows.security.credentials.ui.userconsentverifier/Makefile.in b/dlls/windows.security.credentials.ui.userconsentverifier/Makefile.in index 286ba63de01..3794a442baa 100644 --- a/dlls/windows.security.credentials.ui.userconsentverifier/Makefile.in +++ b/dlls/windows.security.credentials.ui.userconsentverifier/Makefile.in @@ -2,7 +2,9 @@ MODULE = windows.security.credentials.ui.userconsentverifier.dll IMPORTS = combase
C_SRCS = \ + async.c \ main.c
IDL_SRCS = \ - classes.idl + classes.idl \ + provider.idl diff --git a/dlls/windows.security.credentials.ui.userconsentverifier/async.c b/dlls/windows.security.credentials.ui.userconsentverifier/async.c new file mode 100644 index 00000000000..aba1a02dc96 --- /dev/null +++ b/dlls/windows.security.credentials.ui.userconsentverifier/async.c @@ -0,0 +1,509 @@ +/* WinRT Windows.Security.Credentials.UI Implementation + * + * Copyright 2022 Bernhard Kölbl for CodeWeavers + * Copyright 2022 Rémi Bernon for CodeWeavers + * + * 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 + */ + +#include "private.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(credentials); + +#define Closed 4 +#define HANDLER_NOT_SET ((void *)~(ULONG_PTR)0) + +struct async_info +{ + IWineAsyncInfoImpl IWineAsyncInfoImpl_iface; + IAsyncInfo IAsyncInfo_iface; + IInspectable *IInspectable_outer; + LONG ref; + + async_operation_callback callback; + TP_WORK *async_run_work; + IUnknown *invoker; + IUnknown *param; + + CRITICAL_SECTION cs; + IWineAsyncOperationCompletedHandler *handler; + PROPVARIANT result; + AsyncStatus status; + HRESULT hr; +}; + +static inline struct async_info *impl_from_IWineAsyncInfoImpl( IWineAsyncInfoImpl *iface ) +{ + return CONTAINING_RECORD( iface, struct async_info, IWineAsyncInfoImpl_iface ); +} + +static HRESULT WINAPI async_impl_QueryInterface( IWineAsyncInfoImpl *iface, REFIID iid, void **out ) +{ + struct async_info *impl = impl_from_IWineAsyncInfoImpl( iface ); + + TRACE( "iface %p, iid %s, out %p.\n", iface, debugstr_guid( iid ), out ); + + if (IsEqualGUID( iid, &IID_IUnknown ) || + IsEqualGUID( iid, &IID_IInspectable ) || + IsEqualGUID( iid, &IID_IAgileObject ) || + IsEqualGUID( iid, &IID_IWineAsyncInfoImpl )) + { + IInspectable_AddRef( (*out = &impl->IWineAsyncInfoImpl_iface) ); + return S_OK; + } + + if (IsEqualGUID( iid, &IID_IAsyncInfo )) + { + IInspectable_AddRef( (*out = &impl->IAsyncInfo_iface) ); + return S_OK; + } + + FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI async_impl_AddRef( IWineAsyncInfoImpl *iface ) +{ + struct async_info *impl = impl_from_IWineAsyncInfoImpl( iface ); + ULONG ref = InterlockedIncrement( &impl->ref ); + TRACE( "iface %p, ref %lu.\n", iface, ref ); + return ref; +} + +static ULONG WINAPI async_impl_Release( IWineAsyncInfoImpl *iface ) +{ + struct async_info *impl = impl_from_IWineAsyncInfoImpl( iface ); + ULONG ref = InterlockedDecrement( &impl->ref ); + TRACE( "iface %p, ref %lu.\n", iface, ref ); + + if (!ref) + { + if (impl->handler && impl->handler != HANDLER_NOT_SET) IWineAsyncOperationCompletedHandler_Release( impl->handler ); + IAsyncInfo_Close( &impl->IAsyncInfo_iface ); + if (impl->param) IUnknown_Release( impl->param ); + if (impl->invoker) IUnknown_Release( impl->invoker ); + impl->cs.DebugInfo->Spare[0] = 0; + DeleteCriticalSection( &impl->cs ); + free( impl ); + } + + return ref; +} + +static HRESULT WINAPI async_impl_put_Completed( IWineAsyncInfoImpl *iface, IWineAsyncOperationCompletedHandler *handler ) +{ + struct async_info *impl = impl_from_IWineAsyncInfoImpl( iface ); + HRESULT hr = S_OK; + + TRACE( "iface %p, handler %p.\n", iface, handler ); + + EnterCriticalSection( &impl->cs ); + if (impl->status == Closed) hr = E_ILLEGAL_METHOD_CALL; + else if (impl->handler != HANDLER_NOT_SET) hr = E_ILLEGAL_DELEGATE_ASSIGNMENT; + else if ((impl->handler = handler)) + { + IWineAsyncOperationCompletedHandler_AddRef( impl->handler ); + + if (impl->status > Started) + { + IInspectable *operation = impl->IInspectable_outer; + AsyncStatus status = impl->status; + impl->handler = NULL; /* Prevent concurrent invoke. */ + LeaveCriticalSection( &impl->cs ); + + IWineAsyncOperationCompletedHandler_Invoke( handler, operation, status ); + IWineAsyncOperationCompletedHandler_Release( handler ); + + return S_OK; + } + } + LeaveCriticalSection( &impl->cs ); + + return hr; +} + +static HRESULT WINAPI async_impl_get_Completed( IWineAsyncInfoImpl *iface, IWineAsyncOperationCompletedHandler **handler ) +{ + struct async_info *impl = impl_from_IWineAsyncInfoImpl( iface ); + HRESULT hr = S_OK; + + TRACE( "iface %p, handler %p.\n", iface, handler ); + + EnterCriticalSection( &impl->cs ); + if (impl->status == Closed) hr = E_ILLEGAL_METHOD_CALL; + if (impl->handler == NULL || impl->handler == HANDLER_NOT_SET) *handler = NULL; + else IWineAsyncOperationCompletedHandler_AddRef( (*handler = impl->handler) ); + LeaveCriticalSection( &impl->cs ); + + return hr; +} + +static HRESULT WINAPI async_impl_get_Result( IWineAsyncInfoImpl *iface, PROPVARIANT *result ) +{ + struct async_info *impl = impl_from_IWineAsyncInfoImpl( iface ); + HRESULT hr = E_ILLEGAL_METHOD_CALL; + + TRACE( "iface %p, result %p.\n", iface, result ); + + EnterCriticalSection( &impl->cs ); + if (impl->status == Completed || impl->status == Error) + { + PropVariantCopy( result, &impl->result ); + hr = impl->hr; + } + LeaveCriticalSection( &impl->cs ); + + return hr; +} + +static HRESULT WINAPI async_impl_Start( IWineAsyncInfoImpl *iface ) +{ + struct async_info *impl = impl_from_IWineAsyncInfoImpl( iface ); + + TRACE( "iface %p.\n", iface ); + + /* keep the async alive in the callback */ + IInspectable_AddRef( impl->IInspectable_outer ); + SubmitThreadpoolWork( impl->async_run_work ); + + return S_OK; +} + +static const struct IWineAsyncInfoImplVtbl async_impl_vtbl = +{ + /* IUnknown methods */ + async_impl_QueryInterface, + async_impl_AddRef, + async_impl_Release, + /* IWineAsyncInfoImpl */ + async_impl_put_Completed, + async_impl_get_Completed, + async_impl_get_Result, + async_impl_Start, +}; + +DEFINE_IINSPECTABLE_OUTER( async_info, IAsyncInfo, struct async_info, IInspectable_outer ) + +static HRESULT WINAPI async_info_get_Id( IAsyncInfo *iface, UINT32 *id ) +{ + struct async_info *impl = impl_from_IAsyncInfo( iface ); + HRESULT hr = S_OK; + + TRACE( "iface %p, id %p.\n", iface, id ); + + EnterCriticalSection( &impl->cs ); + if (impl->status == Closed) hr = E_ILLEGAL_METHOD_CALL; + *id = 1; + LeaveCriticalSection( &impl->cs ); + + return hr; +} + +static HRESULT WINAPI async_info_get_Status( IAsyncInfo *iface, AsyncStatus *status ) +{ + struct async_info *impl = impl_from_IAsyncInfo( iface ); + HRESULT hr = S_OK; + + TRACE( "iface %p, status %p.\n", iface, status ); + + EnterCriticalSection( &impl->cs ); + if (impl->status == Closed) hr = E_ILLEGAL_METHOD_CALL; + *status = impl->status; + LeaveCriticalSection( &impl->cs ); + + return hr; +} + +static HRESULT WINAPI async_info_get_ErrorCode( IAsyncInfo *iface, HRESULT *error_code ) +{ + struct async_info *impl = impl_from_IAsyncInfo( iface ); + HRESULT hr = S_OK; + + TRACE( "iface %p, error_code %p.\n", iface, error_code ); + + EnterCriticalSection( &impl->cs ); + if (impl->status == Closed) *error_code = hr = E_ILLEGAL_METHOD_CALL; + else *error_code = impl->hr; + LeaveCriticalSection( &impl->cs ); + + return hr; +} + +static HRESULT WINAPI async_info_Cancel( IAsyncInfo *iface ) +{ + struct async_info *impl = impl_from_IAsyncInfo( iface ); + HRESULT hr = S_OK; + + TRACE( "iface %p.\n", iface ); + + EnterCriticalSection( &impl->cs ); + if (impl->status == Closed) hr = E_ILLEGAL_METHOD_CALL; + else if (impl->status == Started) impl->status = Canceled; + LeaveCriticalSection( &impl->cs ); + + return hr; +} + +static HRESULT WINAPI async_info_Close( IAsyncInfo *iface ) +{ + struct async_info *impl = impl_from_IAsyncInfo( iface ); + HRESULT hr = S_OK; + + TRACE( "iface %p.\n", iface ); + + EnterCriticalSection( &impl->cs ); + if (impl->status == Started) + hr = E_ILLEGAL_STATE_CHANGE; + else if (impl->status != Closed) + { + CloseThreadpoolWork( impl->async_run_work ); + impl->async_run_work = NULL; + impl->status = Closed; + } + LeaveCriticalSection( &impl->cs ); + + return hr; +} + +static const struct IAsyncInfoVtbl async_info_vtbl = +{ + /* IUnknown methods */ + async_info_QueryInterface, + async_info_AddRef, + async_info_Release, + /* IInspectable methods */ + async_info_GetIids, + async_info_GetRuntimeClassName, + async_info_GetTrustLevel, + /* IAsyncInfo */ + async_info_get_Id, + async_info_get_Status, + async_info_get_ErrorCode, + async_info_Cancel, + async_info_Close, +}; + +static void CALLBACK async_info_callback( TP_CALLBACK_INSTANCE *instance, void *iface, TP_WORK *work ) +{ + struct async_info *impl = impl_from_IWineAsyncInfoImpl( iface ); + IInspectable *operation = impl->IInspectable_outer; + PROPVARIANT result; + HRESULT hr; + + hr = impl->callback( impl->invoker, impl->param, &result ); + + EnterCriticalSection( &impl->cs ); + if (impl->status != Closed) impl->status = FAILED(hr) ? Error : Completed; + PropVariantCopy( &impl->result, &result ); + impl->hr = hr; + + if (impl->handler != NULL && impl->handler != HANDLER_NOT_SET) + { + IWineAsyncOperationCompletedHandler *handler = impl->handler; + AsyncStatus status = impl->status; + impl->handler = NULL; /* Prevent concurrent invoke. */ + LeaveCriticalSection( &impl->cs ); + + IWineAsyncOperationCompletedHandler_Invoke( handler, operation, status ); + IWineAsyncOperationCompletedHandler_Release( handler ); + } + else LeaveCriticalSection( &impl->cs ); + + /* release refcount acquired in Start */ + IInspectable_Release( operation ); + + PropVariantClear( &result ); +} + +static HRESULT async_info_create( IUnknown *invoker, IUnknown *param, async_operation_callback callback, + IInspectable *outer, IWineAsyncInfoImpl **out ) +{ + struct async_info *impl; + HRESULT hr; + + if (!(impl = calloc( 1, sizeof(struct async_info) ))) return E_OUTOFMEMORY; + impl->IWineAsyncInfoImpl_iface.lpVtbl = &async_impl_vtbl; + impl->IAsyncInfo_iface.lpVtbl = &async_info_vtbl; + impl->IInspectable_outer = outer; + impl->ref = 1; + + impl->callback = callback; + impl->handler = HANDLER_NOT_SET; + impl->status = Started; + if (!(impl->async_run_work = CreateThreadpoolWork( async_info_callback, &impl->IWineAsyncInfoImpl_iface, NULL ))) + { + hr = HRESULT_FROM_WIN32( GetLastError() ); + free( impl ); + return hr; + } + + if ((impl->invoker = invoker)) IUnknown_AddRef( impl->invoker ); + if ((impl->param = param)) IUnknown_AddRef( impl->param ); + + InitializeCriticalSection( &impl->cs ); + impl->cs.DebugInfo->Spare[0] = (DWORD_PTR)( __FILE__ ": async_info.cs" ); + + *out = &impl->IWineAsyncInfoImpl_iface; + return S_OK; +} + +struct async_user_consent_verifier_availability +{ + IAsyncOperation_UserConsentVerifierAvailability IAsyncOperation_UserConsentVerifierAvailability_iface; + IWineAsyncInfoImpl *IWineAsyncInfoImpl_inner; + LONG ref; +}; + +static inline struct async_user_consent_verifier_availability *impl_from_IAsyncOperation_UserConsentVerifierAvailability( IAsyncOperation_UserConsentVerifierAvailability *iface ) +{ + return CONTAINING_RECORD( iface, struct async_user_consent_verifier_availability, IAsyncOperation_UserConsentVerifierAvailability_iface ); +} + +static HRESULT WINAPI async_user_consent_verifier_availability_QueryInterface( IAsyncOperation_UserConsentVerifierAvailability *iface, REFIID iid, void **out ) +{ + struct async_user_consent_verifier_availability *impl = impl_from_IAsyncOperation_UserConsentVerifierAvailability( iface ); + + TRACE( "iface %p, iid %s, out %p.\n", iface, debugstr_guid( iid ), out ); + + if (IsEqualGUID( iid, &IID_IUnknown ) || + IsEqualGUID( iid, &IID_IInspectable ) || + IsEqualGUID( iid, &IID_IAgileObject ) || + IsEqualGUID( iid, &IID_IAsyncOperation_UserConsentVerifierAvailability )) + { + IInspectable_AddRef( (*out = &impl->IAsyncOperation_UserConsentVerifierAvailability_iface) ); + return S_OK; + } + + return IWineAsyncInfoImpl_QueryInterface( impl->IWineAsyncInfoImpl_inner, iid, out ); +} + +static ULONG WINAPI async_user_consent_verifier_availability_AddRef( IAsyncOperation_UserConsentVerifierAvailability *iface ) +{ + struct async_user_consent_verifier_availability *impl = impl_from_IAsyncOperation_UserConsentVerifierAvailability( iface ); + ULONG ref = InterlockedIncrement( &impl->ref ); + TRACE( "iface %p, ref %lu.\n", iface, ref ); + return ref; +} + +static ULONG WINAPI async_user_consent_verifier_availability_Release( IAsyncOperation_UserConsentVerifierAvailability *iface ) +{ + struct async_user_consent_verifier_availability *impl = impl_from_IAsyncOperation_UserConsentVerifierAvailability( iface ); + ULONG ref = InterlockedDecrement( &impl->ref ); + TRACE( "iface %p, ref %lu.\n", iface, ref ); + + if (!ref) + { + /* guard against re-entry if inner releases an outer iface */ + InterlockedIncrement( &impl->ref ); + IWineAsyncInfoImpl_Release( impl->IWineAsyncInfoImpl_inner ); + free( impl ); + } + + return ref; +} + +static HRESULT WINAPI async_user_consent_verifier_availability_GetIids( IAsyncOperation_UserConsentVerifierAvailability *iface, ULONG *iid_count, IID **iids ) +{ + FIXME( "iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids ); + return E_NOTIMPL; +} + +static HRESULT WINAPI async_user_consent_verifier_availability_GetRuntimeClassName( IAsyncOperation_UserConsentVerifierAvailability *iface, HSTRING *class_name ) +{ + return WindowsCreateString( L"Windows.Foundation.IAsyncOperation`1<Windows.Security.Credentials.UI.UserConsentVerifierAvailability>", + ARRAY_SIZE(L"Windows.Foundation.IAsyncOperation`1<Windows.Security.Credentials.UI.UserConsentVerifierAvailability>"), + class_name ); +} + +static HRESULT WINAPI async_user_consent_verifier_availability_GetTrustLevel( IAsyncOperation_UserConsentVerifierAvailability *iface, TrustLevel *trust_level ) +{ + FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level ); + return E_NOTIMPL; +} + +static HRESULT WINAPI async_user_consent_verifier_availability_put_Completed( IAsyncOperation_UserConsentVerifierAvailability *iface, + IAsyncOperationCompletedHandler_UserConsentVerifierAvailability *UserConsentVerifierAvailability_handler ) +{ + IWineAsyncOperationCompletedHandler *handler = (IWineAsyncOperationCompletedHandler *)UserConsentVerifierAvailability_handler; + struct async_user_consent_verifier_availability *impl = impl_from_IAsyncOperation_UserConsentVerifierAvailability( iface ); + TRACE( "iface %p, handler %p.\n", iface, handler ); + return IWineAsyncInfoImpl_put_Completed( impl->IWineAsyncInfoImpl_inner, (IWineAsyncOperationCompletedHandler *)handler ); +} + +static HRESULT WINAPI async_user_consent_verifier_availability_get_Completed( IAsyncOperation_UserConsentVerifierAvailability *iface, + IAsyncOperationCompletedHandler_UserConsentVerifierAvailability **UserConsentVerifierAvailability_handler ) +{ + IWineAsyncOperationCompletedHandler **handler = (IWineAsyncOperationCompletedHandler **)UserConsentVerifierAvailability_handler; + struct async_user_consent_verifier_availability *impl = impl_from_IAsyncOperation_UserConsentVerifierAvailability( iface ); + TRACE( "iface %p, handler %p.\n", iface, handler ); + return IWineAsyncInfoImpl_get_Completed( impl->IWineAsyncInfoImpl_inner, (IWineAsyncOperationCompletedHandler **)handler ); +} + +static HRESULT WINAPI async_user_consent_verifier_availability_GetResults( IAsyncOperation_UserConsentVerifierAvailability *iface, UserConsentVerifierAvailability *results ) +{ + struct async_user_consent_verifier_availability *impl = impl_from_IAsyncOperation_UserConsentVerifierAvailability( iface ); + PROPVARIANT result = {.vt = VT_UI4}; + HRESULT hr; + + TRACE( "iface %p, results %p.\n", iface, results ); + + hr = IWineAsyncInfoImpl_get_Result( impl->IWineAsyncInfoImpl_inner, &result ); + + *results = result.ulVal; + PropVariantClear( &result ); + return hr; +} + +static const struct IAsyncOperation_UserConsentVerifierAvailabilityVtbl async_user_consent_verifier_availability_vtbl = +{ + /* IUnknown methods */ + async_user_consent_verifier_availability_QueryInterface, + async_user_consent_verifier_availability_AddRef, + async_user_consent_verifier_availability_Release, + /* IInspectable methods */ + async_user_consent_verifier_availability_GetIids, + async_user_consent_verifier_availability_GetRuntimeClassName, + async_user_consent_verifier_availability_GetTrustLevel, + /* IAsyncOperation<UserConsentVerifierAvailability> */ + async_user_consent_verifier_availability_put_Completed, + async_user_consent_verifier_availability_get_Completed, + async_user_consent_verifier_availability_GetResults, +}; + +HRESULT async_operation_user_consent_verifier_availability_create( IUnknown *invoker, IUnknown *param, async_operation_callback callback, + IAsyncOperation_UserConsentVerifierAvailability **out ) +{ + struct async_user_consent_verifier_availability *impl; + HRESULT hr; + + *out = NULL; + if (!(impl = calloc( 1, sizeof(*impl) ))) return E_OUTOFMEMORY; + impl->IAsyncOperation_UserConsentVerifierAvailability_iface.lpVtbl = &async_user_consent_verifier_availability_vtbl; + impl->ref = 1; + + if (FAILED(hr = async_info_create( invoker, param, callback, (IInspectable *)&impl->IAsyncOperation_UserConsentVerifierAvailability_iface, &impl->IWineAsyncInfoImpl_inner )) || + FAILED(hr = IWineAsyncInfoImpl_Start( impl->IWineAsyncInfoImpl_inner ))) + { + if (impl->IWineAsyncInfoImpl_inner) IWineAsyncInfoImpl_Release( impl->IWineAsyncInfoImpl_inner ); + free( impl ); + return hr; + } + + *out = &impl->IAsyncOperation_UserConsentVerifierAvailability_iface; + TRACE( "created IAsyncOperation_UserConsentVerifierAvailability %p\n", *out ); + return S_OK; +} diff --git a/dlls/windows.security.credentials.ui.userconsentverifier/private.h b/dlls/windows.security.credentials.ui.userconsentverifier/private.h index dc22b85ffab..5edeb299792 100644 --- a/dlls/windows.security.credentials.ui.userconsentverifier/private.h +++ b/dlls/windows.security.credentials.ui.userconsentverifier/private.h @@ -35,6 +35,12 @@ #define WIDL_using_Windows_Security_Credentials_UI #include "windows.security.credentials.ui.h"
+#include "provider.h" + +typedef HRESULT (WINAPI *async_operation_callback)( IUnknown *invoker, IUnknown *param, PROPVARIANT *result ); +extern HRESULT async_operation_user_consent_verifier_availability_create( IUnknown *invoker, IUnknown *param, async_operation_callback callback, + IAsyncOperation_UserConsentVerifierAvailability **out ); + #define DEFINE_IINSPECTABLE_( pfx, iface_type, impl_type, impl_from, iface_mem, expr ) \ static inline impl_type *impl_from( iface_type *iface ) \ { \ @@ -72,5 +78,7 @@ } #define DEFINE_IINSPECTABLE( pfx, iface_type, impl_type, base_iface ) \ DEFINE_IINSPECTABLE_( pfx, iface_type, impl_type, impl_from_##iface_type, iface_type##_iface, &impl->base_iface ) +#define DEFINE_IINSPECTABLE_OUTER( pfx, iface_type, impl_type, outer_iface ) \ + DEFINE_IINSPECTABLE_( pfx, iface_type, impl_type, impl_from_##iface_type, iface_type##_iface, impl->outer_iface )
#endif diff --git a/dlls/windows.security.credentials.ui.userconsentverifier/provider.idl b/dlls/windows.security.credentials.ui.userconsentverifier/provider.idl new file mode 100644 index 00000000000..36ffe4e174d --- /dev/null +++ b/dlls/windows.security.credentials.ui.userconsentverifier/provider.idl @@ -0,0 +1,46 @@ +/* + * Copyright 2022 Rémi Bernon for CodeWeavers + * + * 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 + */ + +#pragma makedep header + +#ifdef __WIDL__ +#pragma winrt ns_prefix +#endif + +import "propidl.idl"; +import "inspectable.idl"; +import "asyncinfo.idl"; +import "eventtoken.idl"; +import "windowscontracts.idl"; +import "windows.foundation.idl"; + +namespace Windows.Security.Credentials.UI { + /* type-pruning version of AsyncOperationCompletedHandler<T> */ + delegate HRESULT WineAsyncOperationCompletedHandler([in] IInspectable *async, [in] AsyncStatus status); + + [ + uuid(83f377ee-c799-11ec-9d64-0242ac120002) + ] + interface IWineAsyncInfoImpl : IUnknown + { + [propput] HRESULT Completed([in] WineAsyncOperationCompletedHandler *handler); + [propget] HRESULT Completed([out, retval] WineAsyncOperationCompletedHandler **handler); + [propget] HRESULT Result([out, retval] PROPVARIANT *result); + HRESULT Start(); + } +}
From: Mohamad Al-Jaf mohamadaljaf@gmail.com
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=55709 --- .../main.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/dlls/windows.security.credentials.ui.userconsentverifier/main.c b/dlls/windows.security.credentials.ui.userconsentverifier/main.c index d86601331fb..352515a1848 100644 --- a/dlls/windows.security.credentials.ui.userconsentverifier/main.c +++ b/dlls/windows.security.credentials.ui.userconsentverifier/main.c @@ -118,10 +118,17 @@ static const struct IActivationFactoryVtbl factory_vtbl =
DEFINE_IINSPECTABLE( user_consent_verifier_statics, IUserConsentVerifierStatics, struct user_consent_verifier_statics, IActivationFactory_iface )
+static HRESULT WINAPI check_availability_async( IUnknown *invoker, IUnknown *param, PROPVARIANT *result ) +{ + result->vt = VT_UI4; + result->ulVal = UserConsentVerifierAvailability_DeviceNotPresent; + return S_OK; +} + static HRESULT WINAPI user_consent_verifier_statics_CheckAvailabilityAsync( IUserConsentVerifierStatics *iface, IAsyncOperation_UserConsentVerifierAvailability **result ) { - FIXME( "iface %p, result %p stub!\n", iface, result ); - return E_NOTIMPL; + TRACE( "iface %p, result %p\n", iface, result ); + return async_operation_user_consent_verifier_availability_create( (IUnknown *)iface, NULL, check_availability_async, result ); }
static HRESULT WINAPI user_consent_verifier_statics_RequestVerificationAsync( IUserConsentVerifierStatics *iface, HSTRING message,
From: Mohamad Al-Jaf mohamadaljaf@gmail.com
--- .../tests/verifier.c | 211 +++++++++++++++++- 1 file changed, 206 insertions(+), 5 deletions(-)
diff --git a/dlls/windows.security.credentials.ui.userconsentverifier/tests/verifier.c b/dlls/windows.security.credentials.ui.userconsentverifier/tests/verifier.c index 39245fd2b14..0a02579083b 100644 --- a/dlls/windows.security.credentials.ui.userconsentverifier/tests/verifier.c +++ b/dlls/windows.security.credentials.ui.userconsentverifier/tests/verifier.c @@ -33,6 +33,25 @@
#include "wine/test.h"
+static HRESULT (WINAPI *pWindowsDeleteString)( HSTRING str ); +static const WCHAR* (WINAPI *pWindowsGetStringRawBuffer)( HSTRING, UINT32* ); + +static BOOL load_combase_functions(void) +{ + HMODULE combase = GetModuleHandleW( L"combase.dll" ); + +#define LOAD_FUNC(m, f) if (!(p ## f = (void *)GetProcAddress( m, #f ))) goto failed; + LOAD_FUNC( combase, WindowsDeleteString ); + LOAD_FUNC( combase, WindowsGetStringRawBuffer ); +#undef LOAD_FUNC + + return TRUE; + +failed: + win_skip("Failed to load combase.dll functions, skipping tests\n"); + return FALSE; +} + #define check_interface( obj, iid ) check_interface_( __LINE__, obj, iid ) static void check_interface_( unsigned int line, void *obj, const IID *iid ) { @@ -45,14 +64,154 @@ static void check_interface_( unsigned int line, void *obj, const IID *iid ) IUnknown_Release( unk ); }
+#define check_runtimeclass( a, b ) check_runtimeclass_( __LINE__, (IInspectable *)a, b ) +static void check_runtimeclass_( int line, IInspectable *inspectable, const WCHAR *class_name ) +{ + const WCHAR *buffer; + UINT32 length; + HSTRING str; + HRESULT hr; + + hr = IInspectable_GetRuntimeClassName( inspectable, &str ); + ok_(__FILE__, line)( hr == S_OK, "GetRuntimeClassName returned %#lx\n", hr ); + buffer = pWindowsGetStringRawBuffer( str, &length ); + ok_(__FILE__, line)( !wcscmp( buffer, class_name ), "got class name %s\n", debugstr_w(buffer) ); + pWindowsDeleteString( str ); +} + +#define check_verifier_async( a, b, c, d, e ) check_verifier_async_( __LINE__, a, b, c, d, e ) +static void check_verifier_async_( int line, IAsyncOperation_UserConsentVerifierAvailability *async, UINT32 expect_id, AsyncStatus expect_status, + HRESULT expect_hr, UserConsentVerifierAvailability expect_result ) +{ + UserConsentVerifierAvailability result; + AsyncStatus async_status; + IAsyncInfo *async_info; + HRESULT hr, async_hr; + UINT32 async_id; + + hr = IAsyncOperation_UserConsentVerifierAvailability_QueryInterface( async, &IID_IAsyncInfo, (void **)&async_info ); + ok_(__FILE__, line)( hr == S_OK, "QueryInterface returned %#lx\n", hr ); + + async_id = 0xdeadbeef; + hr = IAsyncInfo_get_Id( async_info, &async_id ); + if (expect_status < 4) ok_(__FILE__, line)( hr == S_OK, "get_Id returned %#lx\n", hr ); + else ok_(__FILE__, line)( hr == E_ILLEGAL_METHOD_CALL, "get_Id returned %#lx\n", hr ); + ok_(__FILE__, line)( async_id == expect_id, "got id %u\n", async_id ); + + async_status = 0xdeadbeef; + hr = IAsyncInfo_get_Status( async_info, &async_status ); + if (expect_status < 4) ok_(__FILE__, line)( hr == S_OK, "get_Status returned %#lx\n", hr ); + else ok_(__FILE__, line)( hr == E_ILLEGAL_METHOD_CALL, "get_Status returned %#lx\n", hr ); + ok_(__FILE__, line)( async_status == expect_status || broken( async_status == Error ), "got status %u\n", async_status ); + + async_hr = 0xdeadbeef; + hr = IAsyncInfo_get_ErrorCode( async_info, &async_hr ); + if (expect_status < 4) ok_(__FILE__, line)( hr == S_OK, "get_ErrorCode returned %#lx\n", hr ); + else ok_(__FILE__, line)( hr == E_ILLEGAL_METHOD_CALL, "get_ErrorCode returned %#lx\n", hr ); + if (expect_status < 4) + todo_wine_if(FAILED(expect_hr)) ok_(__FILE__, line)( async_hr == expect_hr || broken( async_hr == E_INVALIDARG), "got error %#lx\n", async_hr ); + else ok_(__FILE__, line)( async_hr == E_ILLEGAL_METHOD_CALL, "got error %#lx\n", async_hr ); + + IAsyncInfo_Release( async_info ); + + hr = IAsyncOperation_UserConsentVerifierAvailability_GetResults( async, &result ); + switch (expect_status) + { + case Completed: + case Error: + todo_wine_if(FAILED(expect_hr)) + ok_(__FILE__, line)( hr == expect_hr || broken( hr == E_INVALIDARG ), "GetResults returned %#lx\n", hr ); + ok_(__FILE__, line)( result == expect_result || broken(result == UserConsentVerifierAvailability_Available /*Win10 1507 */), "got result %u\n", result ); + break; + case Canceled: + case Started: + default: + ok_(__FILE__, line)( hr == E_ILLEGAL_METHOD_CALL, "GetResults returned %#lx\n", hr ); + break; + } +} + +struct verifier_async_handler +{ + IAsyncOperationCompletedHandler_UserConsentVerifierAvailability IAsyncOperationCompletedHandler_UserConsentVerifierAvailability_iface; + IAsyncOperation_UserConsentVerifierAvailability *async; + AsyncStatus status; + BOOL invoked; + HANDLE event; +}; + +static inline struct verifier_async_handler *impl_from_IAsyncOperationCompletedHandler_UserConsentVerifierAvailability( + IAsyncOperationCompletedHandler_UserConsentVerifierAvailability *iface ) +{ + return CONTAINING_RECORD( iface, struct verifier_async_handler, IAsyncOperationCompletedHandler_UserConsentVerifierAvailability_iface ); +} + +static HRESULT WINAPI verifier_async_handler_QueryInterface( IAsyncOperationCompletedHandler_UserConsentVerifierAvailability *iface, REFIID iid, void **out ) +{ + if (IsEqualGUID( iid, &IID_IUnknown ) || + IsEqualGUID( iid, &IID_IAgileObject ) || + IsEqualGUID( iid, &IID_IAsyncOperationCompletedHandler_UserConsentVerifierAvailability )) + { + IUnknown_AddRef( iface ); + *out = iface; + return S_OK; + } + + trace( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI verifier_async_handler_AddRef( IAsyncOperationCompletedHandler_UserConsentVerifierAvailability *iface ) +{ + return 2; +} + +static ULONG WINAPI verifier_async_handler_Release( IAsyncOperationCompletedHandler_UserConsentVerifierAvailability *iface ) +{ + return 1; +} + +static HRESULT WINAPI verifier_async_handler_Invoke( IAsyncOperationCompletedHandler_UserConsentVerifierAvailability *iface, + IAsyncOperation_UserConsentVerifierAvailability *async, AsyncStatus status ) +{ + struct verifier_async_handler *impl = impl_from_IAsyncOperationCompletedHandler_UserConsentVerifierAvailability( iface ); + + trace( "iface %p, async %p, status %u\n", iface, async, status ); + + ok( !impl->invoked, "invoked twice\n" ); + impl->invoked = TRUE; + impl->async = async; + impl->status = status; + if (impl->event) SetEvent( impl->event ); + + return S_OK; +} + +static IAsyncOperationCompletedHandler_UserConsentVerifierAvailabilityVtbl verifier_async_handler_vtbl = +{ + /*** IUnknown methods ***/ + verifier_async_handler_QueryInterface, + verifier_async_handler_AddRef, + verifier_async_handler_Release, + /*** IAsyncOperationCompletedHandler<UserConsentVerifierAvailability> methods ***/ + verifier_async_handler_Invoke, +}; + +static struct verifier_async_handler default_verifier_async_handler = {{&verifier_async_handler_vtbl}}; + static void test_UserConsentVerifierStatics(void) { static const WCHAR *user_consent_verifier_statics_name = L"Windows.Security.Credentials.UI.UserConsentVerifier"; + IAsyncOperationCompletedHandler_UserConsentVerifierAvailability *tmp_handler = NULL; + IAsyncOperation_UserConsentVerifierAvailability *verifier_async = NULL; IUserConsentVerifierStatics *user_consent_verifier_statics = NULL; + struct verifier_async_handler verifier_async_handler; + IAsyncInfo *async_info = NULL; IActivationFactory *factory; HSTRING str; HRESULT hr; - LONG ref; + DWORD ret;
hr = WindowsCreateString( user_consent_verifier_statics_name, wcslen( user_consent_verifier_statics_name ), &str ); ok( hr == S_OK, "got hr %#lx.\n", hr ); @@ -72,10 +231,52 @@ static void test_UserConsentVerifierStatics(void) hr = IActivationFactory_QueryInterface( factory, &IID_IUserConsentVerifierStatics, (void **)&user_consent_verifier_statics ); ok( hr == S_OK, "got hr %#lx.\n", hr );
- ref = IUserConsentVerifierStatics_Release( user_consent_verifier_statics ); - ok( ref == 1, "got ref %ld.\n", ref ); - ref = IActivationFactory_Release( factory ); - ok( ref == 1, "got ref %ld.\n", ref ); + if (!load_combase_functions()) return; + + hr = IUserConsentVerifierStatics_CheckAvailabilityAsync( user_consent_verifier_statics, &verifier_async ); + ok( hr == S_OK, "CheckAvailabilityAsync returned %#lx\n", hr ); + + check_interface( verifier_async, &IID_IUnknown ); + check_interface( verifier_async, &IID_IInspectable ); + check_interface( verifier_async, &IID_IAgileObject ); + check_interface( verifier_async, &IID_IAsyncInfo ); + check_interface( verifier_async, &IID_IAsyncOperation_UserConsentVerifierAvailability ); + check_runtimeclass( verifier_async, L"Windows.Foundation.IAsyncOperation`1<Windows.Security.Credentials.UI.UserConsentVerifierAvailability>" ); + + hr = IAsyncOperation_UserConsentVerifierAvailability_get_Completed( verifier_async, &tmp_handler ); + ok( hr == S_OK, "get_Completed returned %#lx\n", hr ); + ok( tmp_handler == NULL, "got handler %p\n", tmp_handler ); + verifier_async_handler = default_verifier_async_handler; + verifier_async_handler.event = CreateEventW( NULL, FALSE, FALSE, NULL ); + hr = IAsyncOperation_UserConsentVerifierAvailability_put_Completed( verifier_async, &verifier_async_handler.IAsyncOperationCompletedHandler_UserConsentVerifierAvailability_iface ); + ok( hr == S_OK, "put_Completed returned %#lx\n", hr ); + ret = WaitForSingleObject( verifier_async_handler.event, 1000 ); + ok( !ret, "WaitForSingleObject returned %#lx\n", ret ); + ret = CloseHandle( verifier_async_handler.event ); + ok( ret, "CloseHandle failed, error %lu\n", GetLastError() ); + ok( verifier_async_handler.invoked, "handler not invoked\n" ); + ok( verifier_async_handler.async == verifier_async, "got async %p\n", verifier_async_handler.async ); + ok( verifier_async_handler.status == Completed, "got status %u\n", verifier_async_handler.status ); + verifier_async_handler = default_verifier_async_handler; + hr = IAsyncOperation_UserConsentVerifierAvailability_put_Completed( verifier_async, &verifier_async_handler.IAsyncOperationCompletedHandler_UserConsentVerifierAvailability_iface ); + ok( hr == E_ILLEGAL_DELEGATE_ASSIGNMENT, "put_Completed returned %#lx\n", hr ); + ok( !verifier_async_handler.invoked, "handler invoked\n" ); + ok( verifier_async_handler.async == NULL, "got async %p\n", verifier_async_handler.async ); + ok( verifier_async_handler.status == Started, "got status %u\n", verifier_async_handler.status ); + + hr = IAsyncOperation_UserConsentVerifierAvailability_QueryInterface( verifier_async, &IID_IAsyncInfo, (void **)&async_info ); + ok( hr == S_OK, "QueryInterface returned %#lx\n", hr ); + hr = IAsyncInfo_Cancel( async_info ); + ok( hr == S_OK, "Cancel returned %#lx\n", hr ); + check_verifier_async( verifier_async, 1, Completed, S_OK, UserConsentVerifierAvailability_DeviceNotPresent ); + hr = IAsyncInfo_Close( async_info ); + ok( hr == S_OK, "Close returned %#lx\n", hr ); + check_verifier_async( verifier_async, 1, 4, S_OK, FALSE ); + + IAsyncInfo_Release( async_info ); + IAsyncOperation_UserConsentVerifierAvailability_Release( verifier_async ); + IUserConsentVerifierStatics_Release( user_consent_verifier_statics ); + IActivationFactory_Release( factory ); }
START_TEST(verifier)
aa20e3f9 is dead code as the async aren't created anywhere yet, please squash it with the next change.
Regarding the apartment issue, maybe it is related to https://gitlab.winehq.org/wine/wine/-/merge_requests/3806?
On Mon Oct 9 19:41:48 2023 +0000, Rémi Bernon wrote:
aa20e3f9 is dead code as the async aren't created anywhere yet, please squash it with the next change. Regarding the apartment issue, maybe it is related to https://gitlab.winehq.org/wine/wine/-/merge_requests/3806?
Unfortunately flutter_local_auth.exe crashes on me every time with or without WINEDEBUG=+credentials and with or without the patch from !3806. Mohamad's code looks good to me (thank you Mohamad!) so I suspect the race condition is an unrelated bug that shouldn't hold up this MR.