Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=53176
1) The async test is broken on Windows 10 1507. This appears to be a trend among WinRT dlls. I'm thinking that we can add macro definitions for each testbot VM to avoid having to skip tests that would otherwise be working fine, and just greater control over tests in general.
2) The provider file that contains `interface IWineAsyncInfoImpl` is likely going to be reused again in the future. Perhaps a new file can be added in the include/wine folder to prevent duplicate code? I can do this in a separate merge request and cleanup the existing provider files.
3) All the check_bool_async tests return a random async_id so I skipped them. Testbot example: https://testbot.winehq.org/JobDetails.pl?Key=127232&f208=exe32.report#k2...
-- v2: cryptowinrt: Implement IKeyCredentialManagerStatics_IsSupportedAsync. cryptowinrt/tests: Add IKeyCredentialManagerStatics_IsSupportedAsync tests.
From: Mohamad Al-Jaf mohamadaljaf@gmail.com
--- include/Makefile.in | 1 + include/windows.security.credentials.idl | 125 +++++++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 include/windows.security.credentials.idl
diff --git a/include/Makefile.in b/include/Makefile.in index 27c06a30906..65294fcc509 100644 --- a/include/Makefile.in +++ b/include/Makefile.in @@ -811,6 +811,7 @@ SOURCES = \ windows.media.idl \ windows.media.speechrecognition.idl \ windows.media.speechsynthesis.idl \ + windows.security.credentials.idl \ windows.security.cryptography.idl \ windows.storage.streams.idl \ windows.system.idl \ diff --git a/include/windows.security.credentials.idl b/include/windows.security.credentials.idl new file mode 100644 index 00000000000..02024cc33e6 --- /dev/null +++ b/include/windows.security.credentials.idl @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2022 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.security.cryptography.core.idl"; */ +/* import "windows.storage.streams.idl"; */ +/* import "windows.system.idl"; */ + +namespace Windows.Security.Credentials { + typedef enum KeyCredentialCreationOption KeyCredentialCreationOption; + typedef enum KeyCredentialStatus KeyCredentialStatus; + + interface IKeyCredential; + interface IKeyCredentialManagerStatics; + interface IKeyCredentialRetrievalResult; + + runtimeclass KeyCredential; + runtimeclass KeyCredentialManager; + runtimeclass KeyCredentialRetrievalResult; + + declare { + interface Windows.Foundation.AsyncOperationCompletedHandler<Windows.Security.Credentials.KeyCredentialRetrievalResult *>; + interface Windows.Foundation.IAsyncOperation<Windows.Security.Credentials.KeyCredentialRetrievalResult *>; + } + + [ + contract(Windows.Foundation.UniversalApiContract, 1.0) + ] + enum KeyCredentialCreationOption + { + ReplaceExisting = 0, + FailIfExists = 1, + }; + + [ + contract(Windows.Foundation.UniversalApiContract, 1.0) + ] + enum KeyCredentialStatus + { + Success = 0, + UnknownError = 1, + NotFound = 2, + UserCanceled = 3, + UserPrefersPassword = 4, + CredentialAlreadyExists = 5, + SecurityDeviceLocked = 6, + }; + + [ + contract(Windows.Foundation.UniversalApiContract, 1.0), + exclusiveto(Windows.Security.Credentials.KeyCredentialManager), + uuid(6aac468b-0ef1-4ce0-8290-4106da6a63b5) + ] + interface IKeyCredentialManagerStatics : IInspectable + { + HRESULT IsSupportedAsync([out, retval] Windows.Foundation.IAsyncOperation<boolean> **value); + HRESULT RenewAttestationAsync([out, retval] Windows.Foundation.IAsyncAction **operation); + HRESULT RequestCreateAsync(HSTRING name, Windows.Security.Credentials.KeyCredentialCreationOption option, + [out, retval] Windows.Foundation.IAsyncOperation<Windows.Security.Credentials.KeyCredentialRetrievalResult *> **value); + HRESULT OpenAsync(HSTRING name, [out, retval] Windows.Foundation.IAsyncOperation<Windows.Security.Credentials.KeyCredentialRetrievalResult *> **value); + HRESULT DeleteAsync(HSTRING name, [out, retval] Windows.Foundation.IAsyncAction **operation); + } + + [ + contract(Windows.Foundation.UniversalApiContract, 1.0), + exclusiveto(Windows.Security.Credentials.KeyCredentialRetrievalResult), + uuid(58cd7703-8d87-4249-9b58-f6598cc9644e) + ] + interface IKeyCredentialRetrievalResult : IInspectable + { + [propget] HRESULT Credential([out, retval] Windows.Security.Credentials.KeyCredential **value); + [propget] HRESULT Status([out, retval] Windows.Security.Credentials.KeyCredentialStatus *value); + } + + [ + contract(Windows.Foundation.UniversalApiContract, 1.0), + marshaling_behavior(agile) + ] + runtimeclass KeyCredential + { + [default] interface Windows.Security.Credentials.IKeyCredential; + } + + [ + contract(Windows.Foundation.UniversalApiContract, 1.0), + marshaling_behavior(agile), + static(Windows.Security.Credentials.IKeyCredentialManagerStatics, Windows.Foundation.UniversalApiContract, 1.0), + threading(both) + ] + runtimeclass KeyCredentialManager + { + } + + [ + contract(Windows.Foundation.UniversalApiContract, 1.0), + marshaling_behavior(agile) + ] + runtimeclass KeyCredentialRetrievalResult + { + [default] interface Windows.Security.Credentials.IKeyCredentialRetrievalResult; + } +}
From: Mohamad Al-Jaf mohamadaljaf@gmail.com
--- configure.ac | 1 + dlls/cryptowinrt/tests/Makefile.in | 5 ++ dlls/cryptowinrt/tests/crypto.c | 94 ++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+) create mode 100644 dlls/cryptowinrt/tests/Makefile.in create mode 100644 dlls/cryptowinrt/tests/crypto.c
diff --git a/configure.ac b/configure.ac index 7b3734bd2e9..d47151a2742 100644 --- a/configure.ac +++ b/configure.ac @@ -2426,6 +2426,7 @@ WINE_CONFIG_MAKEFILE(dlls/cryptext) WINE_CONFIG_MAKEFILE(dlls/cryptnet) WINE_CONFIG_MAKEFILE(dlls/cryptnet/tests) WINE_CONFIG_MAKEFILE(dlls/cryptowinrt) +WINE_CONFIG_MAKEFILE(dlls/cryptowinrt/tests) WINE_CONFIG_MAKEFILE(dlls/cryptsp) WINE_CONFIG_MAKEFILE(dlls/cryptui) WINE_CONFIG_MAKEFILE(dlls/cryptui/tests) diff --git a/dlls/cryptowinrt/tests/Makefile.in b/dlls/cryptowinrt/tests/Makefile.in new file mode 100644 index 00000000000..af7cc510dc2 --- /dev/null +++ b/dlls/cryptowinrt/tests/Makefile.in @@ -0,0 +1,5 @@ +TESTDLL = cryptowinrt.dll +IMPORTS = combase + +C_SRCS = \ + crypto.c diff --git a/dlls/cryptowinrt/tests/crypto.c b/dlls/cryptowinrt/tests/crypto.c new file mode 100644 index 00000000000..fc841b2c3c6 --- /dev/null +++ b/dlls/cryptowinrt/tests/crypto.c @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2022 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_Storage_Streams +#include "windows.storage.streams.h" +#define WIDL_using_Windows_Security_Cryptography +#include "windows.security.cryptography.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_CryptobufferStatics(void) +{ + static const WCHAR *cryptobuffer_statics_name = L"Windows.Security.Cryptography.CryptographicBuffer"; + ICryptographicBufferStatics *cryptobuffer_statics; + IActivationFactory *factory; + HSTRING str; + HRESULT hr; + LONG ref; + + hr = WindowsCreateString( cryptobuffer_statics_name, wcslen( cryptobuffer_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( cryptobuffer_statics_name ) ); + return; + } + + check_interface( factory, &IID_IUnknown ); + check_interface( factory, &IID_IInspectable ); + check_interface( factory, &IID_IAgileObject ); + + hr = IActivationFactory_QueryInterface( factory, &IID_ICryptographicBufferStatics, (void **)&cryptobuffer_statics ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + + ref = ICryptographicBufferStatics_Release( cryptobuffer_statics ); + ok( ref == 2, "got ref %ld.\n", ref ); + ref = IActivationFactory_Release( factory ); + ok( ref == 1, "got ref %ld.\n", ref ); +} + +START_TEST(crypto) +{ + HRESULT hr; + + hr = RoInitialize( RO_INIT_MULTITHREADED ); + ok( hr == S_OK, "RoInitialize failed, hr %#lx\n", hr ); + + test_CryptobufferStatics(); + + RoUninitialize(); +}
From: Mohamad Al-Jaf mohamadaljaf@gmail.com
--- dlls/cryptowinrt/main.c | 61 +++--------------------------- dlls/cryptowinrt/private.h | 76 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 56 deletions(-) create mode 100644 dlls/cryptowinrt/private.h
diff --git a/dlls/cryptowinrt/main.c b/dlls/cryptowinrt/main.c index 720b137e4a9..5da2bfd3139 100644 --- a/dlls/cryptowinrt/main.c +++ b/dlls/cryptowinrt/main.c @@ -16,24 +16,16 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
-#include <stdarg.h> +#include "initguid.h" +#include "private.h" + #include <assert.h>
-#define COBJMACROS -#include "windef.h" -#include "winbase.h" -#include "winstring.h" #include "wine/debug.h" #include "objbase.h"
-#include "initguid.h" -#include "activation.h" - #include "bcrypt.h"
-#define WIDL_using_Windows_Foundation -#define WIDL_using_Windows_Foundation_Collections -#include "windows.foundation.h" #define WIDL_using_Windows_Storage_Streams #include "windows.storage.streams.h" #define WIDL_using_Windows_Security_Cryptography @@ -41,7 +33,7 @@
WINE_DEFAULT_DEBUG_CHANNEL(crypto);
-static const char *debugstr_hstring(HSTRING hstr) +const char *debugstr_hstring(HSTRING hstr) { const WCHAR *str; UINT32 len; @@ -62,11 +54,6 @@ static inline struct cryptobuffer_factory *impl_from_IActivationFactory(IActivat return CONTAINING_RECORD(iface, struct cryptobuffer_factory, IActivationFactory_iface); }
-static inline struct cryptobuffer_factory *impl_from_ICryptographicBufferStatics(ICryptographicBufferStatics *iface) -{ - return CONTAINING_RECORD(iface, struct cryptobuffer_factory, ICryptographicBufferStatics_iface); -} - static HRESULT STDMETHODCALLTYPE cryptobuffer_factory_QueryInterface( IActivationFactory *iface, REFIID iid, void **out) { @@ -157,45 +144,7 @@ static const struct IActivationFactoryVtbl cryptobuffer_factory_vtbl = cryptobuffer_factory_ActivateInstance, };
-static HRESULT STDMETHODCALLTYPE cryptobuffer_statics_QueryInterface( - ICryptographicBufferStatics *iface, REFIID iid, void **object) -{ - struct cryptobuffer_factory *factory = impl_from_ICryptographicBufferStatics(iface); - return IActivationFactory_QueryInterface(&factory->IActivationFactory_iface, iid, object); -} - -static ULONG STDMETHODCALLTYPE cryptobuffer_statics_AddRef(ICryptographicBufferStatics *iface) -{ - struct cryptobuffer_factory *factory = impl_from_ICryptographicBufferStatics(iface); - return IActivationFactory_AddRef(&factory->IActivationFactory_iface); -} - -static ULONG STDMETHODCALLTYPE cryptobuffer_statics_Release(ICryptographicBufferStatics *iface) -{ - struct cryptobuffer_factory *factory = impl_from_ICryptographicBufferStatics(iface); - return IActivationFactory_Release(&factory->IActivationFactory_iface); -} - -static HRESULT STDMETHODCALLTYPE cryptobuffer_statics_GetIids( - ICryptographicBufferStatics *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 STDMETHODCALLTYPE cryptobuffer_statics_GetRuntimeClassName( - ICryptographicBufferStatics *iface, HSTRING *class_name) -{ - FIXME("iface %p, class_name %p stub!\n", iface, class_name); - return E_NOTIMPL; -} - -static HRESULT STDMETHODCALLTYPE cryptobuffer_statics_GetTrustLevel( - ICryptographicBufferStatics *iface, TrustLevel *trust_level) -{ - FIXME("iface %p, trust_level %p stub!\n", iface, trust_level); - return E_NOTIMPL; -} +DEFINE_IINSPECTABLE(cryptobuffer_statics, ICryptographicBufferStatics, struct cryptobuffer_factory, IActivationFactory_iface);
static HRESULT STDMETHODCALLTYPE cryptobuffer_statics_Compare( ICryptographicBufferStatics *iface, IBuffer *object1, IBuffer *object2, boolean *is_equal) diff --git a/dlls/cryptowinrt/private.h b/dlls/cryptowinrt/private.h new file mode 100644 index 00000000000..06da75920fe --- /dev/null +++ b/dlls/cryptowinrt/private.h @@ -0,0 +1,76 @@ +/* CryptoWinRT Implementation + * + * Copyright (C) 2022 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_CRYPTOWINRT_PRIVATE_H +#define __WINE_CRYPTOWINRT_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" + +extern const char *debugstr_hstring( HSTRING hstr ); + +#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
From: Mohamad Al-Jaf mohamadaljaf@gmail.com
--- dlls/cryptowinrt/Makefile.in | 1 + dlls/cryptowinrt/classes.idl | 1 + dlls/cryptowinrt/credentials.c | 177 ++++++++++++++++++++++++++++++++ dlls/cryptowinrt/main.c | 9 +- dlls/cryptowinrt/private.h | 4 + dlls/cryptowinrt/tests/crypto.c | 37 +++++++ 6 files changed, 225 insertions(+), 4 deletions(-) create mode 100644 dlls/cryptowinrt/credentials.c
diff --git a/dlls/cryptowinrt/Makefile.in b/dlls/cryptowinrt/Makefile.in index 192ef49f178..39250b66b66 100644 --- a/dlls/cryptowinrt/Makefile.in +++ b/dlls/cryptowinrt/Makefile.in @@ -2,6 +2,7 @@ MODULE = cryptowinrt.dll IMPORTS = combase bcrypt uuid
C_SRCS = \ + credentials.c \ main.c
IDL_SRCS = classes.idl diff --git a/dlls/cryptowinrt/classes.idl b/dlls/cryptowinrt/classes.idl index 4352a8562e5..62272d2f93c 100644 --- a/dlls/cryptowinrt/classes.idl +++ b/dlls/cryptowinrt/classes.idl @@ -19,3 +19,4 @@ #pragma makedep register
#include "windows.security.cryptography.idl" +#include "windows.security.credentials.idl" diff --git a/dlls/cryptowinrt/credentials.c b/dlls/cryptowinrt/credentials.c new file mode 100644 index 00000000000..ba375e77e0c --- /dev/null +++ b/dlls/cryptowinrt/credentials.c @@ -0,0 +1,177 @@ +/* CryptoWinRT Credentials Implementation + * + * Copyright (C) 2022 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 "private.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(crypto); + +struct credentials_statics +{ + IActivationFactory IActivationFactory_iface; + IKeyCredentialManagerStatics IKeyCredentialManagerStatics_iface; + LONG ref; +}; + +static inline struct credentials_statics *impl_from_IActivationFactory( IActivationFactory *iface ) +{ + return CONTAINING_RECORD( iface, struct credentials_statics, IActivationFactory_iface ); +} + +static HRESULT WINAPI credentials_QueryInterface( IActivationFactory *iface, REFIID iid, void **out ) +{ + struct credentials_statics *factory = 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_IAgileObject ) || + IsEqualGUID( iid, &IID_IActivationFactory )) + { + IUnknown_AddRef( iface ); + *out = &factory->IActivationFactory_iface; + return S_OK; + } + + if (IsEqualGUID( iid, &IID_IKeyCredentialManagerStatics )) + { + IUnknown_AddRef( iface ); + *out = &factory->IKeyCredentialManagerStatics_iface; + return S_OK; + } + + FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI credentials_AddRef( IActivationFactory *iface ) +{ + struct credentials_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 credentials_Release( IActivationFactory *iface ) +{ + struct credentials_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 credentials_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 credentials_GetRuntimeClassName( IActivationFactory *iface, HSTRING *class_name ) +{ + FIXME( "iface %p, class_name %p stub!\n", iface, class_name ); + return E_NOTIMPL; +} + +static HRESULT WINAPI credentials_GetTrustLevel( IActivationFactory *iface, TrustLevel *trust_level ) +{ + FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level ); + return E_NOTIMPL; +} + +static HRESULT WINAPI credentials_ActivateInstance( IActivationFactory *iface, IInspectable **instance ) +{ + FIXME( "iface %p, instance %p stub!\n", iface, instance ); + return E_NOTIMPL; +} + +static const struct IActivationFactoryVtbl factory_vtbl = +{ + credentials_QueryInterface, + credentials_AddRef, + credentials_Release, + /* IInspectable methods */ + credentials_GetIids, + credentials_GetRuntimeClassName, + credentials_GetTrustLevel, + /* IActivationFactory methods */ + credentials_ActivateInstance, +}; + +DEFINE_IINSPECTABLE( credentials_statics, IKeyCredentialManagerStatics, struct credentials_statics, IActivationFactory_iface ); + +static HRESULT WINAPI credentials_statics_IsSupportedAsync( IKeyCredentialManagerStatics *iface, IAsyncOperation_boolean **value ) +{ + FIXME( "iface %p, value %p stub!\n", iface, value ); + return E_NOTIMPL; +} + +static HRESULT WINAPI credentials_statics_RenewAttestationAsync( IKeyCredentialManagerStatics *iface, IAsyncAction **operation ) +{ + FIXME( "iface %p, operation %p stub!\n", iface, operation ); + return E_NOTIMPL; +} + +static HRESULT WINAPI credentials_statics_RequestCreateAsync( IKeyCredentialManagerStatics *iface, + HSTRING name, KeyCredentialCreationOption option, IAsyncOperation_KeyCredentialRetrievalResult **value ) +{ + FIXME( "iface %p, name %s, %d option, %p value stub!\n", iface, debugstr_hstring(name), option, value ); + return E_NOTIMPL; +} + +static HRESULT WINAPI credentials_statics_OpenAsync(IKeyCredentialManagerStatics *iface, + HSTRING name, IAsyncOperation_KeyCredentialRetrievalResult **value) +{ + FIXME( "iface %p, name %s, %p value stub!\n", iface, debugstr_hstring(name), value ); + return E_NOTIMPL; +} + +static HRESULT WINAPI credentials_statics_DeleteAsync( IKeyCredentialManagerStatics *iface, HSTRING name, IAsyncAction **operation ) +{ + FIXME( "iface %p, name %s, %p operation stub!\n", iface, debugstr_hstring(name), operation ); + return E_NOTIMPL; +} + +static const struct IKeyCredentialManagerStaticsVtbl credentials_statics_vtbl = +{ + credentials_statics_QueryInterface, + credentials_statics_AddRef, + credentials_statics_Release, + /* IInspectable methods */ + credentials_statics_GetIids, + credentials_statics_GetRuntimeClassName, + credentials_statics_GetTrustLevel, + /* IKeyCredentialManagerStatics methods */ + credentials_statics_IsSupportedAsync, + credentials_statics_RenewAttestationAsync, + credentials_statics_RequestCreateAsync, + credentials_statics_OpenAsync, + credentials_statics_DeleteAsync, +}; + +static struct credentials_statics credentials_statics = +{ + {&factory_vtbl}, + {&credentials_statics_vtbl}, + 1, +}; + +IActivationFactory *credentials_activation_factory = &credentials_statics.IActivationFactory_iface; diff --git a/dlls/cryptowinrt/main.c b/dlls/cryptowinrt/main.c index 5da2bfd3139..e86dc1c3561 100644 --- a/dlls/cryptowinrt/main.c +++ b/dlls/cryptowinrt/main.c @@ -266,6 +266,8 @@ static struct cryptobuffer_factory cryptobuffer_factory = .refcount = 1, };
+IActivationFactory *cryptobuffer_activation_factory = &cryptobuffer_factory.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); @@ -281,10 +283,9 @@ HRESULT WINAPI DllGetActivationFactory(HSTRING classid, IActivationFactory **fac *factory = NULL;
if (!wcscmp(name, RuntimeClass_Windows_Security_Cryptography_CryptographicBuffer)) - { - *factory = &cryptobuffer_factory.IActivationFactory_iface; - IUnknown_AddRef(*factory); - } + IActivationFactory_QueryInterface(cryptobuffer_activation_factory, &IID_IActivationFactory, (void **)factory); + if (!wcscmp(name, RuntimeClass_Windows_Security_Credentials_KeyCredentialManager)) + IActivationFactory_QueryInterface(credentials_activation_factory, &IID_IActivationFactory, (void **)factory);
if (*factory) return S_OK; return CLASS_E_CLASSNOTAVAILABLE; diff --git a/dlls/cryptowinrt/private.h b/dlls/cryptowinrt/private.h index 06da75920fe..fb2e84cbfbf 100644 --- a/dlls/cryptowinrt/private.h +++ b/dlls/cryptowinrt/private.h @@ -32,9 +32,13 @@ #define WIDL_using_Windows_Foundation #define WIDL_using_Windows_Foundation_Collections #include "windows.foundation.h" +#define WIDL_using_Windows_Security_Credentials +#include "windows.security.credentials.h"
extern const char *debugstr_hstring( HSTRING hstr );
+extern IActivationFactory *credentials_activation_factory; + #define DEFINE_IINSPECTABLE_( pfx, iface_type, impl_type, impl_from, iface_mem, expr ) \ static inline impl_type *impl_from( iface_type *iface ) \ { \ diff --git a/dlls/cryptowinrt/tests/crypto.c b/dlls/cryptowinrt/tests/crypto.c index fc841b2c3c6..0ddb1de1823 100644 --- a/dlls/cryptowinrt/tests/crypto.c +++ b/dlls/cryptowinrt/tests/crypto.c @@ -32,6 +32,8 @@ #include "windows.storage.streams.h" #define WIDL_using_Windows_Security_Cryptography #include "windows.security.cryptography.h" +#define WIDL_using_Windows_Security_Credentials +#include "windows.security.credentials.h"
#include "wine/test.h"
@@ -81,6 +83,40 @@ static void test_CryptobufferStatics(void) ok( ref == 1, "got ref %ld.\n", ref ); }
+static void test_Credentials_Statics(void) +{ + static const WCHAR *credentials_statics_name = L"Windows.Security.Credentials.KeyCredentialManager"; + IKeyCredentialManagerStatics *credentials_statics; + IActivationFactory *factory; + HSTRING str; + HRESULT hr; + LONG ref; + + hr = WindowsCreateString( credentials_statics_name, wcslen( credentials_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( credentials_statics_name ) ); + return; + } + + check_interface( factory, &IID_IUnknown ); + check_interface( factory, &IID_IInspectable ); + check_interface( factory, &IID_IAgileObject ); + + hr = IActivationFactory_QueryInterface( factory, &IID_IKeyCredentialManagerStatics, (void **)&credentials_statics ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + + ref = IKeyCredentialManagerStatics_Release( credentials_statics ); + ok( ref == 2, "got ref %ld.\n", ref ); + ref = IActivationFactory_Release( factory ); + ok( ref == 1, "got ref %ld.\n", ref ); +} + START_TEST(crypto) { HRESULT hr; @@ -89,6 +125,7 @@ START_TEST(crypto) ok( hr == S_OK, "RoInitialize failed, hr %#lx\n", hr );
test_CryptobufferStatics(); + test_Credentials_Statics();
RoUninitialize(); }
From: Mohamad Al-Jaf mohamadaljaf@gmail.com
--- dlls/cryptowinrt/Makefile.in | 5 +- dlls/cryptowinrt/async.c | 508 ++++++++++++++++++++++++++++++++++ dlls/cryptowinrt/private.h | 8 + dlls/cryptowinrt/provider.idl | 47 ++++ 4 files changed, 567 insertions(+), 1 deletion(-) create mode 100644 dlls/cryptowinrt/async.c create mode 100644 dlls/cryptowinrt/provider.idl
diff --git a/dlls/cryptowinrt/Makefile.in b/dlls/cryptowinrt/Makefile.in index 39250b66b66..6f11a7cd05d 100644 --- a/dlls/cryptowinrt/Makefile.in +++ b/dlls/cryptowinrt/Makefile.in @@ -2,7 +2,10 @@ MODULE = cryptowinrt.dll IMPORTS = combase bcrypt uuid
C_SRCS = \ + async.c \ credentials.c \ main.c
-IDL_SRCS = classes.idl +IDL_SRCS = \ + classes.idl \ + provider.idl diff --git a/dlls/cryptowinrt/async.c b/dlls/cryptowinrt/async.c new file mode 100644 index 00000000000..dbf111cc880 --- /dev/null +++ b/dlls/cryptowinrt/async.c @@ -0,0 +1,508 @@ +/* CryptoWinRT 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(crypto); + +#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_bool +{ + IAsyncOperation_boolean IAsyncOperation_boolean_iface; + IWineAsyncInfoImpl *IWineAsyncInfoImpl_inner; + LONG ref; +}; + +static inline struct async_bool *impl_from_IAsyncOperation_boolean( IAsyncOperation_boolean *iface ) +{ + return CONTAINING_RECORD( iface, struct async_bool, IAsyncOperation_boolean_iface ); +} + +static HRESULT WINAPI async_bool_QueryInterface( IAsyncOperation_boolean *iface, REFIID iid, void **out ) +{ + struct async_bool *impl = impl_from_IAsyncOperation_boolean( 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_boolean )) + { + IInspectable_AddRef( (*out = &impl->IAsyncOperation_boolean_iface) ); + return S_OK; + } + + return IWineAsyncInfoImpl_QueryInterface( impl->IWineAsyncInfoImpl_inner, iid, out ); +} + +static ULONG WINAPI async_bool_AddRef( IAsyncOperation_boolean *iface ) +{ + struct async_bool *impl = impl_from_IAsyncOperation_boolean( iface ); + ULONG ref = InterlockedIncrement( &impl->ref ); + TRACE( "iface %p, ref %lu.\n", iface, ref ); + return ref; +} + +static ULONG WINAPI async_bool_Release( IAsyncOperation_boolean *iface ) +{ + struct async_bool *impl = impl_from_IAsyncOperation_boolean( 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_bool_GetIids( IAsyncOperation_boolean *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_bool_GetRuntimeClassName( IAsyncOperation_boolean *iface, HSTRING *class_name ) +{ + return WindowsCreateString( L"Windows.Foundation.IAsyncOperation`1<Boolean>", + ARRAY_SIZE(L"Windows.Foundation.IAsyncOperation`1<Boolean>"), + class_name ); +} + +static HRESULT WINAPI async_bool_GetTrustLevel( IAsyncOperation_boolean *iface, TrustLevel *trust_level ) +{ + FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level ); + return E_NOTIMPL; +} + +static HRESULT WINAPI async_bool_put_Completed( IAsyncOperation_boolean *iface, IAsyncOperationCompletedHandler_boolean *bool_handler ) +{ + IWineAsyncOperationCompletedHandler *handler = (IWineAsyncOperationCompletedHandler *)bool_handler; + struct async_bool *impl = impl_from_IAsyncOperation_boolean( iface ); + TRACE( "iface %p, handler %p.\n", iface, handler ); + return IWineAsyncInfoImpl_put_Completed( impl->IWineAsyncInfoImpl_inner, (IWineAsyncOperationCompletedHandler *)handler ); +} + +static HRESULT WINAPI async_bool_get_Completed( IAsyncOperation_boolean *iface, IAsyncOperationCompletedHandler_boolean **bool_handler ) +{ + IWineAsyncOperationCompletedHandler **handler = (IWineAsyncOperationCompletedHandler **)bool_handler; + struct async_bool *impl = impl_from_IAsyncOperation_boolean( iface ); + TRACE( "iface %p, handler %p.\n", iface, handler ); + return IWineAsyncInfoImpl_get_Completed( impl->IWineAsyncInfoImpl_inner, (IWineAsyncOperationCompletedHandler **)handler ); +} + +static HRESULT WINAPI async_bool_GetResults( IAsyncOperation_boolean *iface, BOOLEAN *results ) +{ + struct async_bool *impl = impl_from_IAsyncOperation_boolean( iface ); + PROPVARIANT result = {.vt = VT_BOOL}; + HRESULT hr; + + TRACE( "iface %p, results %p.\n", iface, results ); + + hr = IWineAsyncInfoImpl_get_Result( impl->IWineAsyncInfoImpl_inner, &result ); + + *results = result.boolVal; + PropVariantClear( &result ); + return hr; +} + +static const struct IAsyncOperation_booleanVtbl async_bool_vtbl = +{ + /* IUnknown methods */ + async_bool_QueryInterface, + async_bool_AddRef, + async_bool_Release, + /* IInspectable methods */ + async_bool_GetIids, + async_bool_GetRuntimeClassName, + async_bool_GetTrustLevel, + /* IAsyncOperation<boolean> */ + async_bool_put_Completed, + async_bool_get_Completed, + async_bool_GetResults, +}; + +HRESULT async_operation_boolean_create( IUnknown *invoker, IUnknown *param, async_operation_callback callback, + IAsyncOperation_boolean **out ) +{ + struct async_bool *impl; + HRESULT hr; + + *out = NULL; + if (!(impl = calloc( 1, sizeof(*impl) ))) return E_OUTOFMEMORY; + impl->IAsyncOperation_boolean_iface.lpVtbl = &async_bool_vtbl; + impl->ref = 1; + + if (FAILED(hr = async_info_create( invoker, param, callback, (IInspectable *)&impl->IAsyncOperation_boolean_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_boolean_iface; + TRACE( "created IAsyncOperation_boolean %p\n", *out ); + return S_OK; +} diff --git a/dlls/cryptowinrt/private.h b/dlls/cryptowinrt/private.h index fb2e84cbfbf..4441545740e 100644 --- a/dlls/cryptowinrt/private.h +++ b/dlls/cryptowinrt/private.h @@ -35,10 +35,16 @@ #define WIDL_using_Windows_Security_Credentials #include "windows.security.credentials.h"
+#include "provider.h" + extern const char *debugstr_hstring( HSTRING hstr );
extern IActivationFactory *credentials_activation_factory;
+typedef HRESULT (WINAPI *async_operation_callback)( IUnknown *invoker, IUnknown *param, PROPVARIANT *result ); +extern HRESULT async_operation_boolean_create( IUnknown *invoker, IUnknown *param, async_operation_callback callback, + IAsyncOperation_boolean **out ); + #define DEFINE_IINSPECTABLE_( pfx, iface_type, impl_type, impl_from, iface_mem, expr ) \ static inline impl_type *impl_from( iface_type *iface ) \ { \ @@ -76,5 +82,7 @@ extern IActivationFactory *credentials_activation_factory; } #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/cryptowinrt/provider.idl b/dlls/cryptowinrt/provider.idl new file mode 100644 index 00000000000..7196119dba0 --- /dev/null +++ b/dlls/cryptowinrt/provider.idl @@ -0,0 +1,47 @@ +/* + * Copyright 2022 Mohamad Al-Jaf + * 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 { + /* 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
--- dlls/cryptowinrt/tests/crypto.c | 210 ++++++++++++++++++++++++++++++++ 1 file changed, 210 insertions(+)
diff --git a/dlls/cryptowinrt/tests/crypto.c b/dlls/cryptowinrt/tests/crypto.c index 0ddb1de1823..6c8d03db314 100644 --- a/dlls/cryptowinrt/tests/crypto.c +++ b/dlls/cryptowinrt/tests/crypto.c @@ -37,6 +37,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 ) { @@ -49,6 +68,143 @@ 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_bool_async( a, b, c, d, e ) check_bool_async_( __LINE__, a, b, c, d, e ) +static void check_bool_async_( int line, IAsyncOperation_boolean *async, UINT32 expect_id, AsyncStatus expect_status, + HRESULT expect_hr, BOOLEAN expect_result ) +{ + AsyncStatus async_status; + IAsyncInfo *async_info; + HRESULT hr, async_hr; + UINT32 async_id; + BOOLEAN result; + + hr = IAsyncOperation_boolean_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 ); + if (expect_id) ok_(__FILE__, line)( async_id == expect_id, "got id %u\n", async_id ); + else trace("Skipping async_id check, 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 ); + + result = !expect_result; + hr = IAsyncOperation_boolean_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, "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 bool_async_handler +{ + IAsyncOperationCompletedHandler_boolean IAsyncOperationCompletedHandler_boolean_iface; + IAsyncOperation_boolean *async; + AsyncStatus status; + BOOL invoked; + HANDLE event; +}; + +static inline struct bool_async_handler *impl_from_IAsyncOperationCompletedHandler_boolean( IAsyncOperationCompletedHandler_boolean *iface ) +{ + return CONTAINING_RECORD( iface, struct bool_async_handler, IAsyncOperationCompletedHandler_boolean_iface ); +} + +static HRESULT WINAPI bool_async_handler_QueryInterface( IAsyncOperationCompletedHandler_boolean *iface, REFIID iid, void **out ) +{ + if (IsEqualGUID( iid, &IID_IUnknown ) || + IsEqualGUID( iid, &IID_IAgileObject ) || + IsEqualGUID( iid, &IID_IAsyncOperationCompletedHandler_boolean )) + { + 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 bool_async_handler_AddRef( IAsyncOperationCompletedHandler_boolean *iface ) +{ + return 2; +} + +static ULONG WINAPI bool_async_handler_Release( IAsyncOperationCompletedHandler_boolean *iface ) +{ + return 1; +} + +static HRESULT WINAPI bool_async_handler_Invoke( IAsyncOperationCompletedHandler_boolean *iface, + IAsyncOperation_boolean *async, AsyncStatus status ) +{ + struct bool_async_handler *impl = impl_from_IAsyncOperationCompletedHandler_boolean( 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_booleanVtbl bool_async_handler_vtbl = +{ + /*** IUnknown methods ***/ + bool_async_handler_QueryInterface, + bool_async_handler_AddRef, + bool_async_handler_Release, + /*** IAsyncOperationCompletedHandler<boolean> methods ***/ + bool_async_handler_Invoke, +}; + +static struct bool_async_handler default_bool_async_handler = {{&bool_async_handler_vtbl}}; + static void test_CryptobufferStatics(void) { static const WCHAR *cryptobuffer_statics_name = L"Windows.Security.Cryptography.CryptographicBuffer"; @@ -86,8 +242,12 @@ static void test_CryptobufferStatics(void) static void test_Credentials_Statics(void) { static const WCHAR *credentials_statics_name = L"Windows.Security.Credentials.KeyCredentialManager"; + IAsyncOperationCompletedHandler_boolean *tmp_handler; IKeyCredentialManagerStatics *credentials_statics; + struct bool_async_handler bool_async_handler; + IAsyncOperation_boolean *bool_async; IActivationFactory *factory; + IAsyncInfo *async_info; HSTRING str; HRESULT hr; LONG ref; @@ -111,6 +271,56 @@ static void test_Credentials_Statics(void) hr = IActivationFactory_QueryInterface( factory, &IID_IKeyCredentialManagerStatics, (void **)&credentials_statics ); ok( hr == S_OK, "got hr %#lx.\n", hr );
+ if (!load_combase_functions()) return; + + todo_wine + { + hr = IKeyCredentialManagerStatics_IsSupportedAsync( credentials_statics, &bool_async ); + ok( hr == S_OK, "IsSupportedAsync returned %#lx\n", hr ); + bool_async_handler.event = CreateEventW( NULL, FALSE, FALSE, NULL ); + ok( !!bool_async_handler.event, "CreateEventW failed, error %lu\n", GetLastError() ); + WaitForSingleObject( bool_async_handler.event, 1000 ); + check_bool_async( bool_async, 0, Completed, S_OK, FALSE ); + + check_interface( bool_async, &IID_IUnknown ); + check_interface( bool_async, &IID_IInspectable ); + check_interface( bool_async, &IID_IAgileObject ); + check_interface( bool_async, &IID_IAsyncInfo ); + check_interface( bool_async, &IID_IAsyncOperation_boolean ); + check_runtimeclass( bool_async, L"Windows.Foundation.IAsyncOperation`1<Boolean>" ); + + hr = IAsyncOperation_boolean_get_Completed( bool_async, &tmp_handler ); + ok( hr == S_OK, "get_Completed returned %#lx\n", hr ); + ok( tmp_handler == NULL, "got handler %p\n", tmp_handler ); + bool_async_handler = default_bool_async_handler; + hr = IAsyncOperation_boolean_put_Completed( bool_async, &bool_async_handler.IAsyncOperationCompletedHandler_boolean_iface ); + ok( hr == S_OK, "put_Completed returned %#lx\n", hr ); + ok( bool_async_handler.invoked, "handler not invoked\n" ); + ok( bool_async_handler.async == bool_async, "got async %p\n", bool_async_handler.async ); + ok( bool_async_handler.status == Completed || broken( bool_async_handler.status == Error ), "got status %u\n", bool_async_handler.status ); + hr = IAsyncOperation_boolean_get_Completed( bool_async, &tmp_handler ); + ok( hr == S_OK, "get_Completed returned %#lx\n", hr ); + ok( tmp_handler == NULL, "got handler %p\n", tmp_handler ); + bool_async_handler = default_bool_async_handler; + hr = IAsyncOperation_boolean_put_Completed( bool_async, &bool_async_handler.IAsyncOperationCompletedHandler_boolean_iface ); + ok( hr == E_ILLEGAL_DELEGATE_ASSIGNMENT, "put_Completed returned %#lx\n", hr ); + ok( !bool_async_handler.invoked, "handler invoked\n" ); + ok( bool_async_handler.async == NULL, "got async %p\n", bool_async_handler.async ); + ok( bool_async_handler.status == Started, "got status %u\n", bool_async_handler.status ); + + hr = IAsyncOperation_boolean_QueryInterface( bool_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_bool_async( bool_async, 0, Completed, S_OK, FALSE ); + hr = IAsyncInfo_Close( async_info ); + ok( hr == S_OK, "Close returned %#lx\n", hr ); + check_bool_async( bool_async, 0, 4, S_OK, FALSE ); + IAsyncInfo_Release( async_info ); + + IAsyncOperation_boolean_Release( bool_async ); + } + ref = IKeyCredentialManagerStatics_Release( credentials_statics ); ok( ref == 2, "got ref %ld.\n", ref ); ref = IActivationFactory_Release( factory );
From: Mohamad Al-Jaf mohamadaljaf@gmail.com
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=53176 --- dlls/cryptowinrt/credentials.c | 11 +++- dlls/cryptowinrt/tests/crypto.c | 91 ++++++++++++++++----------------- 2 files changed, 53 insertions(+), 49 deletions(-)
diff --git a/dlls/cryptowinrt/credentials.c b/dlls/cryptowinrt/credentials.c index ba375e77e0c..e23c5d94696 100644 --- a/dlls/cryptowinrt/credentials.c +++ b/dlls/cryptowinrt/credentials.c @@ -118,10 +118,17 @@ static const struct IActivationFactoryVtbl factory_vtbl =
DEFINE_IINSPECTABLE( credentials_statics, IKeyCredentialManagerStatics, struct credentials_statics, IActivationFactory_iface );
+static HRESULT WINAPI is_supported_async( IUnknown *invoker, IUnknown *param, PROPVARIANT *result ) +{ + result->vt = VT_BOOL; + result->boolVal = FALSE; + return S_OK; +} + static HRESULT WINAPI credentials_statics_IsSupportedAsync( IKeyCredentialManagerStatics *iface, IAsyncOperation_boolean **value ) { - FIXME( "iface %p, value %p stub!\n", iface, value ); - return E_NOTIMPL; + TRACE( "iface %p, value %p.\n", iface, value ); + return async_operation_boolean_create( (IUnknown *)iface, NULL, is_supported_async, value ); }
static HRESULT WINAPI credentials_statics_RenewAttestationAsync( IKeyCredentialManagerStatics *iface, IAsyncAction **operation ) diff --git a/dlls/cryptowinrt/tests/crypto.c b/dlls/cryptowinrt/tests/crypto.c index 6c8d03db314..975c06fde94 100644 --- a/dlls/cryptowinrt/tests/crypto.c +++ b/dlls/cryptowinrt/tests/crypto.c @@ -273,53 +273,50 @@ static void test_Credentials_Statics(void)
if (!load_combase_functions()) return;
- todo_wine - { - hr = IKeyCredentialManagerStatics_IsSupportedAsync( credentials_statics, &bool_async ); - ok( hr == S_OK, "IsSupportedAsync returned %#lx\n", hr ); - bool_async_handler.event = CreateEventW( NULL, FALSE, FALSE, NULL ); - ok( !!bool_async_handler.event, "CreateEventW failed, error %lu\n", GetLastError() ); - WaitForSingleObject( bool_async_handler.event, 1000 ); - check_bool_async( bool_async, 0, Completed, S_OK, FALSE ); - - check_interface( bool_async, &IID_IUnknown ); - check_interface( bool_async, &IID_IInspectable ); - check_interface( bool_async, &IID_IAgileObject ); - check_interface( bool_async, &IID_IAsyncInfo ); - check_interface( bool_async, &IID_IAsyncOperation_boolean ); - check_runtimeclass( bool_async, L"Windows.Foundation.IAsyncOperation`1<Boolean>" ); - - hr = IAsyncOperation_boolean_get_Completed( bool_async, &tmp_handler ); - ok( hr == S_OK, "get_Completed returned %#lx\n", hr ); - ok( tmp_handler == NULL, "got handler %p\n", tmp_handler ); - bool_async_handler = default_bool_async_handler; - hr = IAsyncOperation_boolean_put_Completed( bool_async, &bool_async_handler.IAsyncOperationCompletedHandler_boolean_iface ); - ok( hr == S_OK, "put_Completed returned %#lx\n", hr ); - ok( bool_async_handler.invoked, "handler not invoked\n" ); - ok( bool_async_handler.async == bool_async, "got async %p\n", bool_async_handler.async ); - ok( bool_async_handler.status == Completed || broken( bool_async_handler.status == Error ), "got status %u\n", bool_async_handler.status ); - hr = IAsyncOperation_boolean_get_Completed( bool_async, &tmp_handler ); - ok( hr == S_OK, "get_Completed returned %#lx\n", hr ); - ok( tmp_handler == NULL, "got handler %p\n", tmp_handler ); - bool_async_handler = default_bool_async_handler; - hr = IAsyncOperation_boolean_put_Completed( bool_async, &bool_async_handler.IAsyncOperationCompletedHandler_boolean_iface ); - ok( hr == E_ILLEGAL_DELEGATE_ASSIGNMENT, "put_Completed returned %#lx\n", hr ); - ok( !bool_async_handler.invoked, "handler invoked\n" ); - ok( bool_async_handler.async == NULL, "got async %p\n", bool_async_handler.async ); - ok( bool_async_handler.status == Started, "got status %u\n", bool_async_handler.status ); - - hr = IAsyncOperation_boolean_QueryInterface( bool_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_bool_async( bool_async, 0, Completed, S_OK, FALSE ); - hr = IAsyncInfo_Close( async_info ); - ok( hr == S_OK, "Close returned %#lx\n", hr ); - check_bool_async( bool_async, 0, 4, S_OK, FALSE ); - IAsyncInfo_Release( async_info ); - - IAsyncOperation_boolean_Release( bool_async ); - } + hr = IKeyCredentialManagerStatics_IsSupportedAsync( credentials_statics, &bool_async ); + ok( hr == S_OK, "IsSupportedAsync returned %#lx\n", hr ); + bool_async_handler.event = CreateEventW( NULL, FALSE, FALSE, NULL ); + ok( !!bool_async_handler.event, "CreateEventW failed, error %lu\n", GetLastError() ); + WaitForSingleObject( bool_async_handler.event, 1000 ); + check_bool_async( bool_async, 0, Completed, S_OK, FALSE ); + + check_interface( bool_async, &IID_IUnknown ); + check_interface( bool_async, &IID_IInspectable ); + check_interface( bool_async, &IID_IAgileObject ); + check_interface( bool_async, &IID_IAsyncInfo ); + check_interface( bool_async, &IID_IAsyncOperation_boolean ); + check_runtimeclass( bool_async, L"Windows.Foundation.IAsyncOperation`1<Boolean>" ); + + hr = IAsyncOperation_boolean_get_Completed( bool_async, &tmp_handler ); + ok( hr == S_OK, "get_Completed returned %#lx\n", hr ); + ok( tmp_handler == NULL, "got handler %p\n", tmp_handler ); + bool_async_handler = default_bool_async_handler; + hr = IAsyncOperation_boolean_put_Completed( bool_async, &bool_async_handler.IAsyncOperationCompletedHandler_boolean_iface ); + ok( hr == S_OK, "put_Completed returned %#lx\n", hr ); + ok( bool_async_handler.invoked, "handler not invoked\n" ); + ok( bool_async_handler.async == bool_async, "got async %p\n", bool_async_handler.async ); + ok( bool_async_handler.status == Completed || broken( bool_async_handler.status == Error ), "got status %u\n", bool_async_handler.status ); + hr = IAsyncOperation_boolean_get_Completed( bool_async, &tmp_handler ); + ok( hr == S_OK, "get_Completed returned %#lx\n", hr ); + ok( tmp_handler == NULL, "got handler %p\n", tmp_handler ); + bool_async_handler = default_bool_async_handler; + hr = IAsyncOperation_boolean_put_Completed( bool_async, &bool_async_handler.IAsyncOperationCompletedHandler_boolean_iface ); + ok( hr == E_ILLEGAL_DELEGATE_ASSIGNMENT, "put_Completed returned %#lx\n", hr ); + ok( !bool_async_handler.invoked, "handler invoked\n" ); + ok( bool_async_handler.async == NULL, "got async %p\n", bool_async_handler.async ); + ok( bool_async_handler.status == Started, "got status %u\n", bool_async_handler.status ); + + hr = IAsyncOperation_boolean_QueryInterface( bool_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_bool_async( bool_async, 0, Completed, S_OK, FALSE ); + hr = IAsyncInfo_Close( async_info ); + ok( hr == S_OK, "Close returned %#lx\n", hr ); + check_bool_async( bool_async, 0, 4, S_OK, FALSE ); + IAsyncInfo_Release( async_info ); + + IAsyncOperation_boolean_Release( bool_async );
ref = IKeyCredentialManagerStatics_Release( credentials_statics ); ok( ref == 2, "got ref %ld.\n", ref );
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=127291
Your paranoid android.
=== debian11 (32 bit report) ===
winhttp: notification.c:122: Test failed: 943: expected callback to be called from the same thread notification.c:122: Test failed: 948: expected callback to be called from the same thread notification.c:122: Test failed: 953: expected callback to be called from the same thread notification.c:997: Test failed: got 4 notification.c:1006: Test failed: got 0 notification.c:1010: Test failed: unexpected data 0 at 0 notification.c:1017: Test failed: got 0 notification.c:1018: Test failed: got 1002 notification.c:1019: Test failed: got 18 notification.c:1030: Test failed: got 1002 notification.c:122: Test failed: 937: expected callback to be called from the same thread notification.c:122: Test failed: 943: expected callback to be called from the same thread notification.c:122: Test failed: 948: expected callback to be called from the same thread notification.c:122: Test failed: 953: expected callback to be called from the same thread
On Wed Dec 7 02:11:09 2022 +0000, **** wrote:
Marvin replied on the mailing list:
Hi, It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated. The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details: The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=127287 Your paranoid android. === w10pro64 (32 bit report) === cryptowinrt: crypto.c:281: Test failed: got status 0 crypto.c:281: Test failed: GetResults returned 0x8000000e crypto.c:296: Test failed: handler not invoked crypto.c:297: Test failed: got async 00000000 crypto.c:298: Test failed: got status 0 crypto.c:301: Test failed: got handler 0063FC60 crypto.c:305: Test failed: handler invoked crypto.c:306: Test failed: got async 009724A0 crypto.c:307: Test failed: got status 1 === debian11 (32 bit report) === winhttp: notification.c:122: Test failed: 943: expected callback to be called from the same thread notification.c:122: Test failed: 948: expected callback to be called from the same thread notification.c:122: Test failed: 953: expected callback to be called from the same thread notification.c:997: Test failed: got 4 notification.c:1006: Test failed: got 0 notification.c:1010: Test failed: unexpected data 0 at 0 notification.c:1017: Test failed: got 0 notification.c:1018: Test failed: got 1002 notification.c:1019: Test failed: got 18 notification.c:1030: Test failed: got 1002 notification.c:122: Test failed: 937: expected callback to be called from the same thread notification.c:122: Test failed: 943: expected callback to be called from the same thread notification.c:122: Test failed: 948: expected callback to be called from the same thread notification.c:122: Test failed: 953: expected callback to be called from the same thread
Hmm, I thought 500 ms would be enough to avoid this issue.
v2: - Increase timeout before checking bool async from 500 ms to 1000 ms.
Rémi Bernon (@rbernon) commented about include/windows.security.credentials.idl:
- 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.security.cryptography.core.idl"; */ +/* import "windows.storage.streams.idl"; */ +/* import "windows.system.idl"; */
We have `windows.storage.streams.idl` and `windows.system.idl`. You'll also see that you then later need to define `WIDL_using_Windows_Storage_Streams` in `private.h`.
Rémi Bernon (@rbernon) commented about dlls/cryptowinrt/tests/crypto.c:
hr = IActivationFactory_QueryInterface( factory, &IID_IKeyCredentialManagerStatics, (void **)&credentials_statics ); ok( hr == S_OK, "got hr %#lx.\n", hr );
- if (!load_combase_functions()) return;
- todo_wine
- {
hr = IKeyCredentialManagerStatics_IsSupportedAsync( credentials_statics, &bool_async );
In this specific commit, your `IsSupportedAsync` is still stub, so the call returns E_NOTIMPL and a NULL bool_async pointer. This then quickly crashes on the next tests.
Rémi Bernon (@rbernon) commented about dlls/cryptowinrt/tests/crypto.c:
hr = IActivationFactory_QueryInterface( factory, &IID_IKeyCredentialManagerStatics, (void **)&credentials_statics ); ok( hr == S_OK, "got hr %#lx.\n", hr );
- if (!load_combase_functions()) return;
- todo_wine
- {
hr = IKeyCredentialManagerStatics_IsSupportedAsync( credentials_statics, &bool_async );
ok( hr == S_OK, "IsSupportedAsync returned %#lx\n", hr );
bool_async_handler.event = CreateEventW( NULL, FALSE, FALSE, NULL );
ok( !!bool_async_handler.event, "CreateEventW failed, error %lu\n", GetLastError() );
WaitForSingleObject( bool_async_handler.event, 1000 );
Waiting on the event here will never do anything useful. It's just been created, it's not set, and you haven't passed the `bool_async_handler` to anything that could set it asynchronously.
You need to pass `bool_async_handler` first to the async operation through `IAsyncOperation_boolean_put_Completed`, then wait on the event to make sure the operation has completed (and check `WaitForSingleObject` return to be sure).
Rémi Bernon (@rbernon) commented about dlls/cryptowinrt/tests/crypto.c:
hr = IActivationFactory_QueryInterface( factory, &IID_IKeyCredentialManagerStatics, (void **)&credentials_statics ); ok( hr == S_OK, "got hr %#lx.\n", hr );
- if (!load_combase_functions()) return;
- todo_wine
- {
hr = IKeyCredentialManagerStatics_IsSupportedAsync( credentials_statics, &bool_async );
ok( hr == S_OK, "IsSupportedAsync returned %#lx\n", hr );
bool_async_handler.event = CreateEventW( NULL, FALSE, FALSE, NULL );
ok( !!bool_async_handler.event, "CreateEventW failed, error %lu\n", GetLastError() );
WaitForSingleObject( bool_async_handler.event, 1000 );
check_bool_async( bool_async, 0, Completed, S_OK, FALSE );
This call specifically is going to be racy. You haven't passed the completion handler to the async operation, so you cannot wait on its event yet and the async operation may or may not be completed depending on how unfortunate you are.
This is different from Windows.Gaming.Input tests, because in W.G.I. we control both ends of the test and we can block the asynchronous operation until we want it to complete.
You need to remove that call.
Rémi Bernon (@rbernon) commented about dlls/cryptowinrt/tests/crypto.c:
WaitForSingleObject( bool_async_handler.event, 1000 );
check_bool_async( bool_async, 0, Completed, S_OK, FALSE );
check_interface( bool_async, &IID_IUnknown );
check_interface( bool_async, &IID_IInspectable );
check_interface( bool_async, &IID_IAgileObject );
check_interface( bool_async, &IID_IAsyncInfo );
check_interface( bool_async, &IID_IAsyncOperation_boolean );
check_runtimeclass( bool_async, L"Windows.Foundation.IAsyncOperation`1<Boolean>" );
hr = IAsyncOperation_boolean_get_Completed( bool_async, &tmp_handler );
ok( hr == S_OK, "get_Completed returned %#lx\n", hr );
ok( tmp_handler == NULL, "got handler %p\n", tmp_handler );
bool_async_handler = default_bool_async_handler;
hr = IAsyncOperation_boolean_put_Completed( bool_async, &bool_async_handler.IAsyncOperationCompletedHandler_boolean_iface );
ok( hr == S_OK, "put_Completed returned %#lx\n", hr );
Immediately after this call (though after checking hr) you need to wait on the `bool_async_handler.event` to make sure the operation has completed. Otherwise all the checks below will be racy.
Note that you will likely need to move the event creation before the call (and close the handle for cleanup, after the wait), because `bool_async_handler = default_bool_async_handler` will otherwise overwrite the event member.
Rémi Bernon (@rbernon) commented about dlls/cryptowinrt/tests/crypto.c:
ok( bool_async_handler.status == Completed || broken( bool_async_handler.status == Error ), "got status %u\n", bool_async_handler.status );
hr = IAsyncOperation_boolean_get_Completed( bool_async, &tmp_handler );
ok( hr == S_OK, "get_Completed returned %#lx\n", hr );
ok( tmp_handler == NULL, "got handler %p\n", tmp_handler );
bool_async_handler = default_bool_async_handler;
hr = IAsyncOperation_boolean_put_Completed( bool_async, &bool_async_handler.IAsyncOperationCompletedHandler_boolean_iface );
ok( hr == E_ILLEGAL_DELEGATE_ASSIGNMENT, "put_Completed returned %#lx\n", hr );
ok( !bool_async_handler.invoked, "handler invoked\n" );
ok( bool_async_handler.async == NULL, "got async %p\n", bool_async_handler.async );
ok( bool_async_handler.status == Started, "got status %u\n", bool_async_handler.status );
hr = IAsyncOperation_boolean_QueryInterface( bool_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_bool_async( bool_async, 0, Completed, S_OK, FALSE );
Fwiw, this would then be fine to keep because you will then be sure the operation has completed.
- The async test is broken on Windows 10 1507. This appears to be a trend among WinRT dlls. I'm thinking that we can add macro definitions for each testbot VM to avoid having to skip tests that would otherwise be working fine, and just greater control over tests in general.
I think it's alright as long as the test is run at least on some VMs. Having finer version detection and version checks may make things a bit less clear and not necessarily help telling genuine broken results from outdated one: how would we detect that a version is not run anymore?
- The provider file that contains `interface IWineAsyncInfoImpl` is likely going to be reused again in the future. Perhaps a new file can be added in the include/wine folder to prevent duplicate code? I can do this in a separate merge request and cleanup the existing provider files.
Yes, though we need to figure how best we can share code between all the WinRT libraries. I'm thinking maybe a static `win(e)rtbase` could be appropriate, but having global headers also isn't very well appreciated in general, so it needs some thinking. Especially if we want to use IDLs for private helper classes. In the meantime duplicating the code shouldn't be too bad.
Related to https://gitlab.winehq.org/wine/wine/-/merge_requests/1045.