Needed by Paint.NET installer.
-- v8: coremessaging: Partially implement CreateDispatcherQueueController(). coremessaging/tests: Add CreateDispatcherQueueController() tests. coremessaging/tests: Add IDispatcherQueueControllerStatics::CreateOnDedicatedThread() tests.
From: Mohamad Al-Jaf mohamadaljaf@gmail.com
--- configure.ac | 1 + dlls/coremessaging/Makefile.in | 4 +- dlls/coremessaging/classes.idl | 24 ++++ dlls/coremessaging/coremessaging.spec | 2 +- dlls/coremessaging/main.c | 141 ++++++++++++++++++++++- dlls/coremessaging/private.h | 79 +++++++++++++ dlls/coremessaging/tests/Makefile.in | 5 + dlls/coremessaging/tests/coremessaging.c | 90 +++++++++++++++ include/windows.system.idl | 2 + 9 files changed, 345 insertions(+), 3 deletions(-) create mode 100644 dlls/coremessaging/classes.idl create mode 100644 dlls/coremessaging/private.h create mode 100644 dlls/coremessaging/tests/Makefile.in create mode 100644 dlls/coremessaging/tests/coremessaging.c
diff --git a/configure.ac b/configure.ac index d1d07bc8f37..06da342bd4f 100644 --- a/configure.ac +++ b/configure.ac @@ -2495,6 +2495,7 @@ WINE_CONFIG_MAKEFILE(dlls/concrt140) WINE_CONFIG_MAKEFILE(dlls/concrt140/tests) WINE_CONFIG_MAKEFILE(dlls/connect) WINE_CONFIG_MAKEFILE(dlls/coremessaging) +WINE_CONFIG_MAKEFILE(dlls/coremessaging/tests) WINE_CONFIG_MAKEFILE(dlls/credui) WINE_CONFIG_MAKEFILE(dlls/credui/tests) WINE_CONFIG_MAKEFILE(dlls/crtdll) diff --git a/dlls/coremessaging/Makefile.in b/dlls/coremessaging/Makefile.in index 0aa0fc43a9b..049d9d7e0a3 100644 --- a/dlls/coremessaging/Makefile.in +++ b/dlls/coremessaging/Makefile.in @@ -1,4 +1,6 @@ -MODULE = coremessaging.dll +MODULE = coremessaging.dll +IMPORTS = combase
SOURCES = \ + classes.idl \ main.c diff --git a/dlls/coremessaging/classes.idl b/dlls/coremessaging/classes.idl new file mode 100644 index 00000000000..a7509d2a658 --- /dev/null +++ b/dlls/coremessaging/classes.idl @@ -0,0 +1,24 @@ +/* + * Runtime Classes for coremessaging.dll + * + * Copyright (C) 2025 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 + +#define _CORE_MESSAGING +#include "windows.system.idl" diff --git a/dlls/coremessaging/coremessaging.spec b/dlls/coremessaging/coremessaging.spec index 58f2be2dc8e..fb98c802077 100644 --- a/dlls/coremessaging/coremessaging.spec +++ b/dlls/coremessaging/coremessaging.spec @@ -18,7 +18,7 @@ @ stdcall CreateDispatcherQueueController(long long long ptr) @ stub CreateDispatcherQueueForCurrentThread @ stdcall -private DllCanUnloadNow() -@ stub DllGetActivationFactory +@ stdcall -private DllGetActivationFactory(ptr ptr) @ stub DllGetClassObject @ stub GetDispatcherQueueForCurrentThread @ stub MsgBlobCreateShared diff --git a/dlls/coremessaging/main.c b/dlls/coremessaging/main.c index 4944f28a49a..5bb4a99ee57 100644 --- a/dlls/coremessaging/main.c +++ b/dlls/coremessaging/main.c @@ -17,11 +17,150 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#include "initguid.h" +#include "private.h" #include "dispatcherqueue.h" -#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(messaging);
+struct dispatcher_queue_controller_statics +{ + IActivationFactory IActivationFactory_iface; + IDispatcherQueueControllerStatics IDispatcherQueueControllerStatics_iface; + LONG ref; +}; + +static inline struct dispatcher_queue_controller_statics *impl_from_IActivationFactory( IActivationFactory *iface ) +{ + return CONTAINING_RECORD( iface, struct dispatcher_queue_controller_statics, IActivationFactory_iface ); +} + +static HRESULT WINAPI factory_QueryInterface( IActivationFactory *iface, REFIID iid, void **out ) +{ + struct dispatcher_queue_controller_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_IAgileObject ) || + IsEqualGUID( iid, &IID_IActivationFactory )) + { + *out = &impl->IActivationFactory_iface; + IInspectable_AddRef( *out ); + return S_OK; + } + + if (IsEqualGUID( iid, &IID_IDispatcherQueueControllerStatics )) + { + *out = &impl->IDispatcherQueueControllerStatics_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 dispatcher_queue_controller_statics *impl = impl_from_IActivationFactory( iface ); + ULONG ref = InterlockedIncrement( &impl->ref ); + TRACE( "iface %p increasing ref to %lu.\n", iface, ref ); + return ref; +} + +static ULONG WINAPI factory_Release( IActivationFactory *iface ) +{ + struct dispatcher_queue_controller_statics *impl = impl_from_IActivationFactory( iface ); + ULONG ref = InterlockedDecrement( &impl->ref ); + TRACE( "iface %p decreasing ref 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, +}; + +DEFINE_IINSPECTABLE( dispatcher_queue_controller_statics, IDispatcherQueueControllerStatics, struct dispatcher_queue_controller_statics, IActivationFactory_iface ) + +static HRESULT WINAPI dispatcher_queue_controller_statics_CreateOnDedicatedThread( IDispatcherQueueControllerStatics *iface, IDispatcherQueueController **result ) +{ + FIXME( "iface %p, result %p stub!\n", iface, result ); + return E_NOTIMPL; +} + +static const struct IDispatcherQueueControllerStaticsVtbl dispatcher_queue_controller_statics_vtbl = +{ + dispatcher_queue_controller_statics_QueryInterface, + dispatcher_queue_controller_statics_AddRef, + dispatcher_queue_controller_statics_Release, + /* IInspectable methods */ + dispatcher_queue_controller_statics_GetIids, + dispatcher_queue_controller_statics_GetRuntimeClassName, + dispatcher_queue_controller_statics_GetTrustLevel, + /* IDispatcherQueueControllerStatics methods */ + dispatcher_queue_controller_statics_CreateOnDedicatedThread, +}; + +static struct dispatcher_queue_controller_statics dispatcher_queue_controller_statics = +{ + {&factory_vtbl}, + {&dispatcher_queue_controller_statics_vtbl}, + 1, +}; + +static IActivationFactory *dispatcher_queue_controller_factory = &dispatcher_queue_controller_statics.IActivationFactory_iface; + +HRESULT WINAPI DllGetActivationFactory( HSTRING classid, IActivationFactory **factory ) +{ + const WCHAR *name = WindowsGetStringRawBuffer( classid, NULL ); + + TRACE( "classid %s, factory %p.\n", debugstr_hstring( classid ), factory ); + + *factory = NULL; + + if (!wcscmp( name, RuntimeClass_Windows_System_DispatcherQueueController )) + IActivationFactory_QueryInterface( dispatcher_queue_controller_factory, &IID_IActivationFactory, (void **)factory ); + + if (*factory) return S_OK; + return CLASS_E_CLASSNOTAVAILABLE; +} + HRESULT WINAPI CreateDispatcherQueueController( DispatcherQueueOptions options, PDISPATCHERQUEUECONTROLLER *queue_controller ) { FIXME( "options.dwSize = %lu, options.threadType = %d, options.apartmentType = %d, queue_controller %p stub!\n", diff --git a/dlls/coremessaging/private.h b/dlls/coremessaging/private.h new file mode 100644 index 00000000000..09567dedc1c --- /dev/null +++ b/dlls/coremessaging/private.h @@ -0,0 +1,79 @@ +/* WinRT coremessaging.dll Implementation + * + * Copyright (C) 2025 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_CORE_MESSAGING_PRIVATE_H +#define __WINE_CORE_MESSAGING_PRIVATE_H + +#include <stdarg.h> +#include <stddef.h> + +#define COBJMACROS +#include "windef.h" +#include "winbase.h" +#include "winstring.h" + +#include "activation.h" +#include "roapi.h" + +#include "wine/debug.h" + +#define WIDL_using_Windows_Foundation +#include "windows.foundation.h" +#define WIDL_using_Windows_System +#include "windows.system.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/coremessaging/tests/Makefile.in b/dlls/coremessaging/tests/Makefile.in new file mode 100644 index 00000000000..b048cbe204e --- /dev/null +++ b/dlls/coremessaging/tests/Makefile.in @@ -0,0 +1,5 @@ +TESTDLL = coremessaging.dll +IMPORTS = combase + +SOURCES = \ + coremessaging.c diff --git a/dlls/coremessaging/tests/coremessaging.c b/dlls/coremessaging/tests/coremessaging.c new file mode 100644 index 00000000000..83d00b6261f --- /dev/null +++ b/dlls/coremessaging/tests/coremessaging.c @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2025 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 +#include "windows.foundation.h" +#define WIDL_using_Windows_System +#include "windows.system.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_DispatcherQueueController_Statics(void) +{ + static const WCHAR *dispatcher_queue_controller_statics_name = L"Windows.System.DispatcherQueueController"; + IDispatcherQueueControllerStatics *dispatcher_queue_controller_statics = (void *)0xdeadbeef; + IActivationFactory *factory = (void *)0xdeadbeef; + HSTRING str = NULL; + HRESULT hr; + LONG ref; + + hr = WindowsCreateString( dispatcher_queue_controller_statics_name, wcslen( dispatcher_queue_controller_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( dispatcher_queue_controller_statics_name ) ); + return; + } + + check_interface( factory, &IID_IUnknown ); + check_interface( factory, &IID_IInspectable ); + check_interface( factory, &IID_IAgileObject ); + + hr = IActivationFactory_QueryInterface( factory, &IID_IDispatcherQueueControllerStatics, (void **)&dispatcher_queue_controller_statics ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + + ref = IDispatcherQueueControllerStatics_Release( dispatcher_queue_controller_statics ); + ok( ref == 2, "got ref %ld.\n", ref ); + ref = IActivationFactory_Release( factory ); + ok( ref == 1, "got ref %ld.\n", ref ); +} + +START_TEST(coremessaging) +{ + HRESULT hr; + + hr = RoInitialize( RO_INIT_MULTITHREADED ); + ok( hr == S_OK, "RoInitialize failed, hr %#lx\n", hr ); + + test_DispatcherQueueController_Statics(); + + RoUninitialize(); +} diff --git a/include/windows.system.idl b/include/windows.system.idl index 3a349edd4f8..152d4b6d901 100644 --- a/include/windows.system.idl +++ b/include/windows.system.idl @@ -462,6 +462,7 @@ namespace Windows.System [default] interface Windows.System.IDispatcherQueueTimer; }
+#ifndef _CORE_MESSAGING [ contract(Windows.Foundation.UniversalApiContract, 1.0), marshaling_behavior(agile), @@ -472,6 +473,7 @@ namespace Windows.System { [default] interface Windows.System.IUser; } +#endif
[ contract(Windows.Foundation.UniversalApiContract, 1.0),
From: Mohamad Al-Jaf mohamadaljaf@gmail.com
--- dlls/coremessaging/Makefile.in | 5 +- dlls/coremessaging/tests/Makefile.in | 2 +- dlls/coremessaging/tests/coremessaging.c | 269 +++++++++++++++++++++++ 3 files changed, 273 insertions(+), 3 deletions(-)
diff --git a/dlls/coremessaging/Makefile.in b/dlls/coremessaging/Makefile.in index 049d9d7e0a3..25de74385c5 100644 --- a/dlls/coremessaging/Makefile.in +++ b/dlls/coremessaging/Makefile.in @@ -1,5 +1,6 @@ -MODULE = coremessaging.dll -IMPORTS = combase +MODULE = coremessaging.dll +IMPORTS = combase +IMPORTLIB = coremessaging
SOURCES = \ classes.idl \ diff --git a/dlls/coremessaging/tests/Makefile.in b/dlls/coremessaging/tests/Makefile.in index b048cbe204e..c8947c28c50 100644 --- a/dlls/coremessaging/tests/Makefile.in +++ b/dlls/coremessaging/tests/Makefile.in @@ -1,5 +1,5 @@ TESTDLL = coremessaging.dll -IMPORTS = combase +IMPORTS = combase coremessaging user32
SOURCES = \ coremessaging.c diff --git a/dlls/coremessaging/tests/coremessaging.c b/dlls/coremessaging/tests/coremessaging.c index 83d00b6261f..b02d206985a 100644 --- a/dlls/coremessaging/tests/coremessaging.c +++ b/dlls/coremessaging/tests/coremessaging.c @@ -30,6 +30,8 @@ #define WIDL_using_Windows_System #include "windows.system.h"
+#include "dispatcherqueue.h" + #include "wine/test.h"
#define check_interface( obj, iid ) check_interface_( __LINE__, obj, iid ) @@ -44,14 +46,189 @@ static void check_interface_( unsigned int line, void *obj, const IID *iid ) IUnknown_Release( unk ); }
+struct typed_event_handler_dispatcher_queue +{ + ITypedEventHandler_DispatcherQueue_IInspectable ITypedEventHandler_DispatcherQueue_IInspectable_iface; + LONG ref; + + HANDLE event; +}; + +static struct typed_event_handler_dispatcher_queue *impl_from_ITypedEventHandler_DispatcherQueue_IInspectable( ITypedEventHandler_DispatcherQueue_IInspectable *iface ) +{ + return CONTAINING_RECORD( iface, struct typed_event_handler_dispatcher_queue, ITypedEventHandler_DispatcherQueue_IInspectable_iface ); +} + +static HRESULT WINAPI typed_event_handler_dispatcher_queue_QueryInterface( ITypedEventHandler_DispatcherQueue_IInspectable *iface, REFIID iid, void **out ) +{ + if (IsEqualGUID( iid, &IID_ITypedEventHandler_DispatcherQueue_IInspectable ) || + IsEqualGUID( iid, &IID_IAgileObject ) || + IsEqualGUID( iid, &IID_IInspectable ) || + IsEqualGUID( iid, &IID_IUnknown )) + { + *out = iface; + ITypedEventHandler_DispatcherQueue_IInspectable_AddRef( iface ); + return S_OK; + } + + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI typed_event_handler_dispatcher_queue_AddRef( ITypedEventHandler_DispatcherQueue_IInspectable *iface ) +{ + struct typed_event_handler_dispatcher_queue *handler = impl_from_ITypedEventHandler_DispatcherQueue_IInspectable( iface ); + return InterlockedIncrement( &handler->ref ); +} + +static ULONG WINAPI typed_event_handler_dispatcher_queue_Release( ITypedEventHandler_DispatcherQueue_IInspectable *iface ) +{ + struct typed_event_handler_dispatcher_queue *handler = impl_from_ITypedEventHandler_DispatcherQueue_IInspectable( iface ); + ULONG ref = InterlockedDecrement( &handler->ref ); + + if (!ref) + { + CloseHandle( handler->event ); + free( handler ); + } + + return ref; +} + +static HRESULT WINAPI typed_event_handler_dispatcher_queue_Invoke( ITypedEventHandler_DispatcherQueue_IInspectable *iface, IDispatcherQueue *queue, IInspectable *inspectable ) +{ + struct typed_event_handler_dispatcher_queue *handler = impl_from_ITypedEventHandler_DispatcherQueue_IInspectable( iface ); + + SetEvent( handler->event ); + return S_OK; +} + +static const ITypedEventHandler_DispatcherQueue_IInspectableVtbl typed_event_handler_dispatcher_queue_vtbl = +{ + typed_event_handler_dispatcher_queue_QueryInterface, + typed_event_handler_dispatcher_queue_AddRef, + typed_event_handler_dispatcher_queue_Release, + typed_event_handler_dispatcher_queue_Invoke, +}; + +static HRESULT create_typed_event_handler_dispatcher_queue( ITypedEventHandler_DispatcherQueue_IInspectable **handler ) +{ + struct typed_event_handler_dispatcher_queue *impl; + + *handler = NULL; + + if (!(impl = calloc( 1, sizeof( *impl ) ))) return E_OUTOFMEMORY; + + impl->ITypedEventHandler_DispatcherQueue_IInspectable_iface.lpVtbl = &typed_event_handler_dispatcher_queue_vtbl; + impl->ref = 1; + impl->event = CreateEventW( NULL, FALSE, FALSE, NULL ); + + *handler = &impl->ITypedEventHandler_DispatcherQueue_IInspectable_iface; + return S_OK; +} + +struct dispatcher_queue_handler +{ + IDispatcherQueueHandler IDispatcherQueueHandler_iface; + LONG ref; + + HANDLE event; +}; + +static struct dispatcher_queue_handler *impl_from_IDispatcherQueueHandler( IDispatcherQueueHandler *iface ) +{ + return CONTAINING_RECORD( iface, struct dispatcher_queue_handler, IDispatcherQueueHandler_iface ); +} + +static HRESULT WINAPI dispatcher_queue_handler_QueryInterface( IDispatcherQueueHandler *iface, REFIID iid, void **out ) +{ + if (IsEqualGUID( iid, &IID_IDispatcherQueueHandler ) || + IsEqualGUID( iid, &IID_IAgileObject ) || + IsEqualGUID( iid, &IID_IInspectable ) || + IsEqualGUID( iid, &IID_IUnknown )) + { + *out = iface; + IDispatcherQueueHandler_AddRef( iface ); + return S_OK; + } + + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI dispatcher_queue_handler_AddRef( IDispatcherQueueHandler *iface ) +{ + struct dispatcher_queue_handler *handler = impl_from_IDispatcherQueueHandler( iface ); + return InterlockedIncrement( &handler->ref ); +} + +static ULONG WINAPI dispatcher_queue_handler_Release( IDispatcherQueueHandler *iface ) +{ + struct dispatcher_queue_handler *handler = impl_from_IDispatcherQueueHandler( iface ); + ULONG ref = InterlockedDecrement( &handler->ref ); + + if (!ref) + { + CloseHandle( handler->event ); + free( handler ); + } + + return ref; +} + +static HRESULT WINAPI dispatcher_queue_handler_Invoke( IDispatcherQueueHandler *iface ) +{ + struct dispatcher_queue_handler *handler = impl_from_IDispatcherQueueHandler( iface ); + + SetEvent( handler->event ); + return S_OK; +} + +static const IDispatcherQueueHandlerVtbl dispatcher_queue_handler_vtbl = +{ + dispatcher_queue_handler_QueryInterface, + dispatcher_queue_handler_AddRef, + dispatcher_queue_handler_Release, + dispatcher_queue_handler_Invoke, +}; + +static HRESULT create_dispatcher_queue_handler( IDispatcherQueueHandler **handler ) +{ + struct dispatcher_queue_handler *impl; + + *handler = NULL; + + if (!(impl = calloc( 1, sizeof( *impl ) ))) return E_OUTOFMEMORY; + + impl->IDispatcherQueueHandler_iface.lpVtbl = &dispatcher_queue_handler_vtbl; + impl->ref = 1; + impl->event = CreateEventW( NULL, FALSE, FALSE, NULL ); + + *handler = &impl->IDispatcherQueueHandler_iface; + return S_OK; +} + static void test_DispatcherQueueController_Statics(void) { static const WCHAR *dispatcher_queue_controller_statics_name = L"Windows.System.DispatcherQueueController"; IDispatcherQueueControllerStatics *dispatcher_queue_controller_statics = (void *)0xdeadbeef; + ITypedEventHandler_DispatcherQueue_IInspectable *event_handler_iface = (void *)0xdeadbeef; + struct typed_event_handler_dispatcher_queue *event_handler = (void *)0xdeadbeef; + IDispatcherQueueController *dispatcher_queue_controller = (void *)0xdeadbeef; + IDispatcherQueueHandler *handler_iface = (void *)0xdeadbeef; + IDispatcherQueue2 *dispatcher_queue2 = (void *)0xdeadbeef; + IDispatcherQueue *dispatcher_queue = (void *)0xdeadbeef; IActivationFactory *factory = (void *)0xdeadbeef; + IAsyncAction *operation = (void *)0xdeadbeef; + IAsyncInfo *async_info = (void *)0xdeadbeef; + EventRegistrationToken token; + AsyncStatus status; HSTRING str = NULL; + boolean result; HRESULT hr; + DWORD ret; LONG ref; + MSG msg;
hr = WindowsCreateString( dispatcher_queue_controller_statics_name, wcslen( dispatcher_queue_controller_statics_name ), &str ); ok( hr == S_OK, "got hr %#lx.\n", hr ); @@ -71,6 +248,98 @@ static void test_DispatcherQueueController_Statics(void) hr = IActivationFactory_QueryInterface( factory, &IID_IDispatcherQueueControllerStatics, (void **)&dispatcher_queue_controller_statics ); ok( hr == S_OK, "got hr %#lx.\n", hr );
+ hr = IDispatcherQueueControllerStatics_CreateOnDedicatedThread( dispatcher_queue_controller_statics, NULL ); + todo_wine + ok( hr == E_POINTER || hr == 0x80000005 /* E_POINTER #if !defined(_WIN32) || defined(_MAC) */, "got hr %#lx.\n", hr ); + hr = IDispatcherQueueControllerStatics_CreateOnDedicatedThread( dispatcher_queue_controller_statics, &dispatcher_queue_controller ); + todo_wine + ok( hr == S_OK, "got hr %#lx.\n", hr ); + if (FAILED(hr)) goto done; + + hr = IDispatcherQueueController_get_DispatcherQueue( dispatcher_queue_controller, NULL ); + todo_wine + ok( hr == E_POINTER || hr == 0x80000005 /* E_POINTER #if !defined(_WIN32) || defined(_MAC) */, "got hr %#lx.\n", hr ); + hr = IDispatcherQueueController_get_DispatcherQueue( dispatcher_queue_controller, &dispatcher_queue ); + todo_wine + ok( hr == S_OK, "got hr %#lx.\n", hr ); + + check_interface( dispatcher_queue, &IID_IUnknown ); + check_interface( dispatcher_queue, &IID_IInspectable ); + check_interface( dispatcher_queue, &IID_IAgileObject ); + + hr = create_dispatcher_queue_handler( &handler_iface ); + ok( hr == S_OK, "Unexpected hr %#lx.\n", hr ); + + hr = IDispatcherQueue_TryEnqueue( dispatcher_queue, handler_iface, &result ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + ok( result == TRUE, "got result %d.\n", result ); + + hr = IDispatcherQueue_QueryInterface( dispatcher_queue, &IID_IDispatcherQueue2, (void **)&dispatcher_queue2 ); + ok( hr == S_OK || broken(hr == E_NOINTERFACE) /* w1064v1809 */, "got hr %#lx.\n", hr ); + if (SUCCEEDED(hr)) + { + hr = IDispatcherQueue2_get_HasThreadAccess( dispatcher_queue2, &result ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + ok( result == FALSE, "got result %d.\n", result ); + ref = IDispatcherQueue2_Release( dispatcher_queue2 ); + ok( ref == 3, "got ref %ld.\n", ref ); + } + + hr = create_typed_event_handler_dispatcher_queue( &event_handler_iface ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + event_handler = impl_from_ITypedEventHandler_DispatcherQueue_IInspectable( event_handler_iface ); + hr = IDispatcherQueue_add_ShutdownCompleted( dispatcher_queue, event_handler_iface, &token ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + + hr = IDispatcherQueueController_ShutdownQueueAsync( dispatcher_queue_controller, NULL ); + ok( hr == E_POINTER || hr == 0x80000005 /* E_POINTER #if !defined(_WIN32) || defined(_MAC) */, "got hr %#lx.\n", hr ); + hr = IDispatcherQueueController_ShutdownQueueAsync( dispatcher_queue_controller, &operation ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + + check_interface( operation, &IID_IInspectable ); + check_interface( operation, &IID_IAgileObject ); + check_interface( operation, &IID_IAsyncAction ); + + hr = IAsyncAction_QueryInterface( operation, &IID_IAsyncInfo, (void **)&async_info ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + + hr = IAsyncInfo_get_Status( async_info, &status ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + ok( status == Started, "got status %d.\n", status ); + while (PeekMessageW( &msg, NULL, 0, 0, PM_REMOVE )) + { + TranslateMessage( &msg ); + DispatchMessageW( &msg ); + } + Sleep( 100 ); + ret = WaitForSingleObject( event_handler->event, INFINITE ); + ok( !ret, "Unexpected wait result %lu.\n", ret ); + hr = IAsyncInfo_get_Status( async_info, &status ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + ok( status == Completed, "got status %d.\n", status ); + + hr = IAsyncInfo_Close( async_info ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + ref = IAsyncInfo_Release( async_info ); + ok( ref == 2, "got ref %ld.\n", ref ); + ref = IAsyncAction_Release( operation ); + ok( ref == 1, "got ref %ld.\n", ref ); + hr = IDispatcherQueue_remove_ShutdownCompleted( dispatcher_queue, token ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + + hr = IDispatcherQueue_TryEnqueue( dispatcher_queue, handler_iface, &result ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + ok( result == FALSE, "got result %d.\n", result ); + + ref = ITypedEventHandler_DispatcherQueue_IInspectable_Release( event_handler_iface ); + ok( ref == 0, "got ref %ld.\n", ref ); + ref = IDispatcherQueueHandler_Release( handler_iface ); + ok( ref == 0, "got ref %ld.\n", ref ); + ref = IDispatcherQueue_Release( dispatcher_queue ); + ok( ref == 1, "got ref %ld.\n", ref ); + ref = IDispatcherQueueController_Release( dispatcher_queue_controller ); + ok( ref == 0, "got ref %ld.\n", ref ); +done: ref = IDispatcherQueueControllerStatics_Release( dispatcher_queue_controller_statics ); ok( ref == 2, "got ref %ld.\n", ref ); ref = IActivationFactory_Release( factory );
From: Mohamad Al-Jaf mohamadaljaf@gmail.com
--- dlls/coremessaging/tests/coremessaging.c | 153 +++++++++++++++++++++++ 1 file changed, 153 insertions(+)
diff --git a/dlls/coremessaging/tests/coremessaging.c b/dlls/coremessaging/tests/coremessaging.c index b02d206985a..ab3daecb2ca 100644 --- a/dlls/coremessaging/tests/coremessaging.c +++ b/dlls/coremessaging/tests/coremessaging.c @@ -208,6 +208,158 @@ static HRESULT create_dispatcher_queue_handler( IDispatcherQueueHandler **handle return S_OK; }
+#define check_create_dispatcher_queue_controller( size, thread_type, apartment_type, expected_hr ) \ + check_create_dispatcher_queue_controller_( __LINE__, size, thread_type, apartment_type, expected_hr ) +static void check_create_dispatcher_queue_controller_( unsigned int line, DWORD size, DISPATCHERQUEUE_THREAD_TYPE thread_type, + DISPATCHERQUEUE_THREAD_APARTMENTTYPE apartment_type, HRESULT expected_hr ) +{ + ITypedEventHandler_DispatcherQueue_IInspectable *event_handler_iface = NULL; + struct typed_event_handler_dispatcher_queue *event_handler = NULL; + IDispatcherQueueController *dispatcher_queue_controller = NULL; + IDispatcherQueueHandler *handler_iface = NULL; + struct DispatcherQueueOptions options = { 0 }; + IDispatcherQueue2 *dispatcher_queue2 = NULL; + IDispatcherQueue *dispatcher_queue = NULL; + IAsyncAction *operation = NULL; + IAsyncInfo *async_info = NULL; + EventRegistrationToken token; + AsyncStatus status; + boolean result; + HRESULT hr; + DWORD ret; + LONG ref; + MSG msg; + + options.dwSize = size; + options.threadType = thread_type; + options.apartmentType = apartment_type; + + hr = CreateDispatcherQueueController( options, &dispatcher_queue_controller ); + todo_wine + ok_(__FILE__, line)( hr == expected_hr, "got CreateDispatcherQueueController hr %#lx.\n", hr ); + if (FAILED(hr)) return; + + hr = IDispatcherQueueController_get_DispatcherQueue( dispatcher_queue_controller, &dispatcher_queue ); + todo_wine + ok_(__FILE__, line)( hr == S_OK, "got IDispatcherQueueController_get_DispatcherQueue hr %#lx.\n", hr ); + if (FAILED(hr)) goto done; + + hr = IDispatcherQueue_QueryInterface( dispatcher_queue, &IID_IDispatcherQueue2, (void **)&dispatcher_queue2 ); + ok_(__FILE__, line)( hr == S_OK || broken(hr == E_NOINTERFACE) /* w1064v1809 */, "got IDispatcherQueue_QueryInterface hr %#lx.\n", hr ); + if (SUCCEEDED(hr)) + { + hr = IDispatcherQueue2_get_HasThreadAccess( dispatcher_queue2, &result ); + ok_(__FILE__, line)( hr == S_OK, "got IDispatcherQueue2_get_HasThreadAccess hr %#lx.\n", hr ); + ok_(__FILE__, line)( result == (thread_type == DQTYPE_THREAD_CURRENT ? TRUE : FALSE), "got IDispatcherQueue2_get_HasThreadAccess result %d.\n", result ); + ref = IDispatcherQueue2_Release( dispatcher_queue2 ); + ok_(__FILE__, line)( ref == (thread_type == DQTYPE_THREAD_CURRENT ? 2 : 3), "got IDispatcherQueue2_Release ref %ld.\n", ref ); + } + + hr = create_dispatcher_queue_handler( &handler_iface ); + ok_(__FILE__, line)( hr == S_OK, "create_dispatcher_queue_handler failed, hr %#lx.\n", hr ); + + hr = IDispatcherQueue_TryEnqueue( dispatcher_queue, handler_iface, &result ); + ok_(__FILE__, line)( hr == S_OK, "got IDispatcherQueue_TryEnqueue hr %#lx.\n", hr ); + ok_(__FILE__, line)( result == TRUE, "got IDispatcherQueue_TryEnqueue result %d.\n", result ); + + hr = create_typed_event_handler_dispatcher_queue( &event_handler_iface ); + ok_(__FILE__, line)( hr == S_OK, "create_typed_event_handler_dispatcher_queue failed, hr %#lx.\n", hr ); + event_handler = impl_from_ITypedEventHandler_DispatcherQueue_IInspectable( event_handler_iface ); + + hr = IDispatcherQueue_add_ShutdownCompleted( dispatcher_queue, event_handler_iface, &token ); + ok_(__FILE__, line)( hr == S_OK, "got IDispatcherQueue_add_ShutdownCompleted hr %#lx.\n", hr ); + hr = IDispatcherQueueController_ShutdownQueueAsync( dispatcher_queue_controller, &operation ); + ok_(__FILE__, line)( hr == S_OK, "got IDispatcherQueueController_ShutdownQueueAsync hr %#lx.\n", hr ); + + hr = IAsyncAction_QueryInterface( operation, &IID_IAsyncInfo, (void **)&async_info ); + ok_(__FILE__, line)( hr == S_OK, "got IAsyncAction_QueryInterface hr %#lx.\n", hr ); + + hr = IAsyncInfo_get_Status( async_info, &status ); + ok_(__FILE__, line)( hr == S_OK, "got IAsyncInfo_get_Status hr %#lx.\n", hr ); + ok_(__FILE__, line)( status == Started, "got IAsyncInfo_get_Status status %d.\n", status ); + while (PeekMessageW( &msg, NULL, 0, 0, PM_REMOVE )) + { + TranslateMessage( &msg ); + DispatchMessageW( &msg ); + } + Sleep( 100 ); + ret = WaitForSingleObject( event_handler->event, INFINITE ); + ok_(__FILE__, line)( !ret, "Unexpected wait result %lu.\n", ret ); + hr = IAsyncInfo_get_Status( async_info, &status ); + ok_(__FILE__, line)( hr == S_OK, "got IAsyncInfo_get_Status hr %#lx.\n", hr ); + ok_(__FILE__, line)( status == Completed, "got IAsyncInfo_get_Status status %d.\n", status ); + + hr = IAsyncInfo_Close( async_info ); + ok_(__FILE__, line)( hr == S_OK, "got IAsyncInfo_Close hr %#lx.\n", hr ); + ref = IAsyncInfo_Release( async_info ); + ok_(__FILE__, line)( ref == 2, "got IAsyncInfo_Release ref %ld.\n", ref ); + ref = IAsyncAction_Release( operation ); + ok_(__FILE__, line)( ref == 1, "got IAsyncAction_Release ref %ld.\n", ref ); + hr = IDispatcherQueue_remove_ShutdownCompleted( dispatcher_queue, token ); + ok_(__FILE__, line)( hr == S_OK, "got IDispatcherQueue_remove_ShutdownCompleted hr %#lx.\n", hr ); + + ref = ITypedEventHandler_DispatcherQueue_IInspectable_Release( event_handler_iface ); + ok_(__FILE__, line)( ref == 0, "got ITypedEventHandler_DispatcherQueue_IInspectable_Release ref %ld.\n", ref ); + ref = IDispatcherQueueHandler_Release( handler_iface ); + ok_(__FILE__, line)( ref == 0, "got IDispatcherQueueHandler_Release ref %ld.\n", ref ); + ref = IDispatcherQueue_Release( dispatcher_queue ); + ok_(__FILE__, line)( ref == 1, "got IDispatcherQueue_Release ref %ld.\n", ref ); +done: + ref = IDispatcherQueueController_Release( dispatcher_queue_controller ); + ok_(__FILE__, line)( ref == 0, "got IDispatcherQueueController_Release ref %ld.\n", ref ); +} + +static void test_CreateDispatcherQueueController(void) +{ + IDispatcherQueueController *dispatcher_queue_controller = (void *)0xdeadbeef; + struct DispatcherQueueOptions options = { 0 }; + HRESULT hr; + + hr = CreateDispatcherQueueController( options, NULL ); + todo_wine + ok( hr == E_POINTER || hr == 0x80000005 /* E_POINTER #if !defined(_WIN32) || defined(_MAC) */, "got hr %#lx.\n", hr ); + hr = CreateDispatcherQueueController( options, &dispatcher_queue_controller ); + todo_wine + ok( hr == E_INVALIDARG, "got hr %#lx.\n", hr ); + ok( dispatcher_queue_controller == (void *)0xdeadbeef, "got dispatcher_queue_controller %p.\n", dispatcher_queue_controller ); + + /* Invalid args */ + + check_create_dispatcher_queue_controller( 0, 0, DQTAT_COM_NONE, E_INVALIDARG ); + check_create_dispatcher_queue_controller( 0, 0, DQTAT_COM_ASTA, E_INVALIDARG ); + check_create_dispatcher_queue_controller( 0, 0, DQTAT_COM_STA, E_INVALIDARG ); + check_create_dispatcher_queue_controller( 0, DQTYPE_THREAD_CURRENT, DQTAT_COM_NONE, E_INVALIDARG ); + check_create_dispatcher_queue_controller( 0, DQTYPE_THREAD_CURRENT, DQTAT_COM_ASTA, E_INVALIDARG ); + check_create_dispatcher_queue_controller( 0, DQTYPE_THREAD_CURRENT, DQTAT_COM_STA, E_INVALIDARG ); + check_create_dispatcher_queue_controller( 0, DQTYPE_THREAD_DEDICATED, DQTAT_COM_NONE, E_INVALIDARG ); + check_create_dispatcher_queue_controller( 0, DQTYPE_THREAD_DEDICATED, DQTAT_COM_ASTA, E_INVALIDARG ); + check_create_dispatcher_queue_controller( 0, DQTYPE_THREAD_DEDICATED, DQTAT_COM_STA, E_INVALIDARG ); + check_create_dispatcher_queue_controller( sizeof( DispatcherQueueOptions ), 0, DQTAT_COM_NONE, E_INVALIDARG ); + check_create_dispatcher_queue_controller( sizeof( DispatcherQueueOptions ), 0, DQTAT_COM_ASTA, E_INVALIDARG ); + check_create_dispatcher_queue_controller( sizeof( DispatcherQueueOptions ), 0, DQTAT_COM_STA, E_INVALIDARG ); + check_create_dispatcher_queue_controller( sizeof( DispatcherQueueOptions ), 0xdeadbeef, DQTAT_COM_NONE, E_INVALIDARG ); + check_create_dispatcher_queue_controller( sizeof( DispatcherQueueOptions ), 0xdeadbeef, DQTAT_COM_ASTA, E_INVALIDARG ); + check_create_dispatcher_queue_controller( sizeof( DispatcherQueueOptions ), 0xdeadbeef, DQTAT_COM_STA, E_INVALIDARG ); + check_create_dispatcher_queue_controller( sizeof( DispatcherQueueOptions ) - 1, DQTYPE_THREAD_CURRENT, DQTAT_COM_NONE, E_INVALIDARG ); + check_create_dispatcher_queue_controller( sizeof( DispatcherQueueOptions ) + 1, DQTYPE_THREAD_CURRENT, DQTAT_COM_NONE, E_INVALIDARG ); + + if (0) /* Silently crashes in Windows */ + { + check_create_dispatcher_queue_controller( sizeof( DispatcherQueueOptions ), DQTYPE_THREAD_DEDICATED, 0xdeadbeef, S_OK ); + check_create_dispatcher_queue_controller( sizeof( DispatcherQueueOptions ), DQTYPE_THREAD_DEDICATED, DQTAT_COM_NONE, S_OK ); + } + + /* Valid args */ + + check_create_dispatcher_queue_controller( sizeof( DispatcherQueueOptions ), DQTYPE_THREAD_CURRENT, 0xdeadbeef, S_OK ); + check_create_dispatcher_queue_controller( sizeof( DispatcherQueueOptions ), DQTYPE_THREAD_CURRENT, DQTAT_COM_NONE, S_OK ); + check_create_dispatcher_queue_controller( sizeof( DispatcherQueueOptions ), DQTYPE_THREAD_CURRENT, DQTAT_COM_ASTA, S_OK ); + check_create_dispatcher_queue_controller( sizeof( DispatcherQueueOptions ), DQTYPE_THREAD_CURRENT, DQTAT_COM_STA, S_OK ); + + check_create_dispatcher_queue_controller( sizeof( DispatcherQueueOptions ), DQTYPE_THREAD_DEDICATED, DQTAT_COM_ASTA, S_OK ); + check_create_dispatcher_queue_controller( sizeof( DispatcherQueueOptions ), DQTYPE_THREAD_DEDICATED, DQTAT_COM_STA, S_OK ); +} + static void test_DispatcherQueueController_Statics(void) { static const WCHAR *dispatcher_queue_controller_statics_name = L"Windows.System.DispatcherQueueController"; @@ -353,6 +505,7 @@ START_TEST(coremessaging) hr = RoInitialize( RO_INIT_MULTITHREADED ); ok( hr == S_OK, "RoInitialize failed, hr %#lx\n", hr );
+ test_CreateDispatcherQueueController(); test_DispatcherQueueController_Statics();
RoUninitialize();
From: Mohamad Al-Jaf mohamadaljaf@gmail.com
Needed by Paint.NET installer. --- dlls/coremessaging/Makefile.in | 2 + dlls/coremessaging/async.c | 655 +++++++++++++++++++++++ dlls/coremessaging/async_private.idl | 46 ++ dlls/coremessaging/main.c | 121 ++++- dlls/coremessaging/private.h | 6 + dlls/coremessaging/tests/coremessaging.c | 5 +- 6 files changed, 829 insertions(+), 6 deletions(-) create mode 100644 dlls/coremessaging/async.c create mode 100644 dlls/coremessaging/async_private.idl
diff --git a/dlls/coremessaging/Makefile.in b/dlls/coremessaging/Makefile.in index 25de74385c5..4df45c482ef 100644 --- a/dlls/coremessaging/Makefile.in +++ b/dlls/coremessaging/Makefile.in @@ -3,5 +3,7 @@ IMPORTS = combase IMPORTLIB = coremessaging
SOURCES = \ + async.c \ + async_private.idl \ classes.idl \ main.c diff --git a/dlls/coremessaging/async.c b/dlls/coremessaging/async.c new file mode 100644 index 00000000000..8f3869da5c7 --- /dev/null +++ b/dlls/coremessaging/async.c @@ -0,0 +1,655 @@ +/* WinRT IAsync* 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 + */ + +#define WIDL_using_Wine_Internal +#include "private.h" +#include "initguid.h" +#include "async_private.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(combase); + +#define Closed 4 +#define HANDLER_NOT_SET ((void *)~(ULONG_PTR)0) + +struct async_info +{ + IAsyncInfoImpl IAsyncInfoImpl_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; + IAsyncOperationCompletedHandlerImpl *handler; + PROPVARIANT result; + AsyncStatus status; + HRESULT hr; +}; + +static inline struct async_info *impl_from_IAsyncInfoImpl( IAsyncInfoImpl *iface ) +{ + return CONTAINING_RECORD( iface, struct async_info, IAsyncInfoImpl_iface ); +} + +static HRESULT WINAPI async_impl_QueryInterface( IAsyncInfoImpl *iface, REFIID iid, void **out ) +{ + struct async_info *impl = impl_from_IAsyncInfoImpl( 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_IAsyncInfoImpl )) + { + IInspectable_AddRef( (*out = &impl->IAsyncInfoImpl_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( IAsyncInfoImpl *iface ) +{ + struct async_info *impl = impl_from_IAsyncInfoImpl( iface ); + ULONG ref = InterlockedIncrement( &impl->ref ); + TRACE( "iface %p, ref %lu.\n", iface, ref ); + return ref; +} + +static ULONG WINAPI async_impl_Release( IAsyncInfoImpl *iface ) +{ + struct async_info *impl = impl_from_IAsyncInfoImpl( iface ); + ULONG ref = InterlockedDecrement( &impl->ref ); + TRACE( "iface %p, ref %lu.\n", iface, ref ); + + if (!ref) + { + if (impl->handler && impl->handler != HANDLER_NOT_SET) IAsyncOperationCompletedHandlerImpl_Release( impl->handler ); + IAsyncInfo_Close( &impl->IAsyncInfo_iface ); + if (impl->param) IUnknown_Release( impl->param ); + if (impl->invoker) IUnknown_Release( impl->invoker ); + PropVariantClear( &impl->result ); + impl->cs.DebugInfo->Spare[0] = 0; + DeleteCriticalSection( &impl->cs ); + free( impl ); + } + + return ref; +} + +static HRESULT WINAPI async_impl_put_Completed( IAsyncInfoImpl *iface, IAsyncOperationCompletedHandlerImpl *handler ) +{ + struct async_info *impl = impl_from_IAsyncInfoImpl( 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)) + { + IAsyncOperationCompletedHandlerImpl_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 ); + + IAsyncOperationCompletedHandlerImpl_Invoke( handler, operation, status ); + IAsyncOperationCompletedHandlerImpl_Release( handler ); + + return S_OK; + } + } + LeaveCriticalSection( &impl->cs ); + + return hr; +} + +static HRESULT WINAPI async_impl_get_Completed( IAsyncInfoImpl *iface, IAsyncOperationCompletedHandlerImpl **handler ) +{ + struct async_info *impl = impl_from_IAsyncInfoImpl( 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 IAsyncOperationCompletedHandlerImpl_AddRef( (*handler = impl->handler) ); + LeaveCriticalSection( &impl->cs ); + + return hr; +} + +static HRESULT WINAPI async_impl_get_Result( IAsyncInfoImpl *iface, PROPVARIANT *result ) +{ + struct async_info *impl = impl_from_IAsyncInfoImpl( 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( IAsyncInfoImpl *iface ) +{ + struct async_info *impl = impl_from_IAsyncInfoImpl( 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 IAsyncInfoImplVtbl async_impl_vtbl = +{ + /* IUnknown methods */ + async_impl_QueryInterface, + async_impl_AddRef, + async_impl_Release, + /* IAsyncInfoImpl */ + 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_IAsyncInfoImpl( iface ); + IInspectable *operation = impl->IInspectable_outer; + PROPVARIANT result = {0}; + 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) + { + IAsyncOperationCompletedHandlerImpl *handler = impl->handler; + AsyncStatus status = impl->status; + impl->handler = NULL; /* Prevent concurrent invoke. */ + LeaveCriticalSection( &impl->cs ); + + IAsyncOperationCompletedHandlerImpl_Invoke( handler, operation, status ); + IAsyncOperationCompletedHandlerImpl_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, IAsyncInfoImpl **out ) +{ + struct async_info *impl; + HRESULT hr; + + if (!(impl = calloc( 1, sizeof(struct async_info) ))) return E_OUTOFMEMORY; + impl->IAsyncInfoImpl_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->IAsyncInfoImpl_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 ); + + InitializeCriticalSectionEx( &impl->cs, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO ); + impl->cs.DebugInfo->Spare[0] = (DWORD_PTR)( __FILE__ ": async_info.cs" ); + + *out = &impl->IAsyncInfoImpl_iface; + return S_OK; +} + +struct async_bool +{ + IAsyncOperation_boolean IAsyncOperation_boolean_iface; + IAsyncInfoImpl *IAsyncInfoImpl_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 IAsyncInfoImpl_QueryInterface( impl->IAsyncInfoImpl_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 ); + IAsyncInfoImpl_Release( impl->IAsyncInfoImpl_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 ) +{ + IAsyncOperationCompletedHandlerImpl *handler = (IAsyncOperationCompletedHandlerImpl *)bool_handler; + struct async_bool *impl = impl_from_IAsyncOperation_boolean( iface ); + TRACE( "iface %p, handler %p.\n", iface, handler ); + return IAsyncInfoImpl_put_Completed( impl->IAsyncInfoImpl_inner, handler ); +} + +static HRESULT WINAPI async_bool_get_Completed( IAsyncOperation_boolean *iface, IAsyncOperationCompletedHandler_boolean **bool_handler ) +{ + IAsyncOperationCompletedHandlerImpl **handler = (IAsyncOperationCompletedHandlerImpl **)bool_handler; + struct async_bool *impl = impl_from_IAsyncOperation_boolean( iface ); + TRACE( "iface %p, handler %p.\n", iface, handler ); + return IAsyncInfoImpl_get_Completed( impl->IAsyncInfoImpl_inner, 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 = IAsyncInfoImpl_get_Result( impl->IAsyncInfoImpl_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->IAsyncInfoImpl_inner )) || + FAILED(hr = IAsyncInfoImpl_Start( impl->IAsyncInfoImpl_inner ))) + { + if (impl->IAsyncInfoImpl_inner) IAsyncInfoImpl_Release( impl->IAsyncInfoImpl_inner ); + free( impl ); + return hr; + } + + *out = &impl->IAsyncOperation_boolean_iface; + TRACE( "created IAsyncOperation_boolean %p\n", *out ); + return S_OK; +} + +struct async_action +{ + IAsyncAction IAsyncAction_iface; + IAsyncInfoImpl *IAsyncInfoImpl_inner; + LONG ref; +}; + +static inline struct async_action *impl_from_IAsyncAction( IAsyncAction *iface ) +{ + return CONTAINING_RECORD( iface, struct async_action, IAsyncAction_iface ); +} + +static HRESULT WINAPI async_action_QueryInterface( IAsyncAction *iface, REFIID iid, void **out ) +{ + struct async_action *impl = impl_from_IAsyncAction( 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_IAsyncAction )) + { + IInspectable_AddRef( (*out = &impl->IAsyncAction_iface) ); + return S_OK; + } + + return IAsyncInfoImpl_QueryInterface( impl->IAsyncInfoImpl_inner, iid, out ); +} + +static ULONG WINAPI async_action_AddRef( IAsyncAction *iface ) +{ + struct async_action *impl = impl_from_IAsyncAction( iface ); + ULONG ref = InterlockedIncrement( &impl->ref ); + TRACE( "iface %p, ref %lu.\n", iface, ref ); + return ref; +} + +static ULONG WINAPI async_action_Release( IAsyncAction *iface ) +{ + struct async_action *impl = impl_from_IAsyncAction( 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 ); + IAsyncInfoImpl_Release( impl->IAsyncInfoImpl_inner ); + free( impl ); + } + + return ref; +} + +static HRESULT WINAPI async_action_GetIids( IAsyncAction *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_action_GetRuntimeClassName( IAsyncAction *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_action_GetTrustLevel( IAsyncAction *iface, TrustLevel *trust_level ) +{ + FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level ); + return E_NOTIMPL; +} + +static HRESULT WINAPI async_action_put_Completed( IAsyncAction *iface, IAsyncActionCompletedHandler *bool_handler ) +{ + IAsyncOperationCompletedHandlerImpl *handler = (IAsyncOperationCompletedHandlerImpl *)bool_handler; + struct async_action *impl = impl_from_IAsyncAction( iface ); + TRACE( "iface %p, handler %p.\n", iface, handler ); + return IAsyncInfoImpl_put_Completed( impl->IAsyncInfoImpl_inner, handler ); +} + +static HRESULT WINAPI async_action_get_Completed( IAsyncAction *iface, IAsyncActionCompletedHandler **bool_handler ) +{ + IAsyncOperationCompletedHandlerImpl **handler = (IAsyncOperationCompletedHandlerImpl **)bool_handler; + struct async_action *impl = impl_from_IAsyncAction( iface ); + TRACE( "iface %p, handler %p.\n", iface, handler ); + return IAsyncInfoImpl_get_Completed( impl->IAsyncInfoImpl_inner, handler ); +} + +static HRESULT WINAPI async_action_GetResults( IAsyncAction *iface ) +{ + struct async_action *impl = impl_from_IAsyncAction( iface ); + PROPVARIANT result; + HRESULT hr; + + TRACE( "iface %p.\n", iface ); + + PropVariantInit( &result ); + hr = IAsyncInfoImpl_get_Result( impl->IAsyncInfoImpl_inner, &result ); + PropVariantClear( &result ); + return hr; +} + +static const struct IAsyncActionVtbl async_action_vtbl = +{ + /* IUnknown methods */ + async_action_QueryInterface, + async_action_AddRef, + async_action_Release, + /* IInspectable methods */ + async_action_GetIids, + async_action_GetRuntimeClassName, + async_action_GetTrustLevel, + /* IAsyncOperation<boolean> */ + async_action_put_Completed, + async_action_get_Completed, + async_action_GetResults, +}; + +HRESULT async_action_create( IUnknown *invoker, async_operation_callback callback, IAsyncAction **out ) +{ + struct async_action *impl; + HRESULT hr; + + *out = NULL; + if (!(impl = calloc( 1, sizeof(*impl) ))) return E_OUTOFMEMORY; + impl->IAsyncAction_iface.lpVtbl = &async_action_vtbl; + impl->ref = 1; + + if (FAILED(hr = async_info_create( invoker, NULL, callback, (IInspectable *)&impl->IAsyncAction_iface, &impl->IAsyncInfoImpl_inner )) || + FAILED(hr = IAsyncInfoImpl_Start( impl->IAsyncInfoImpl_inner ))) + { + if (impl->IAsyncInfoImpl_inner) IAsyncInfoImpl_Release( impl->IAsyncInfoImpl_inner ); + free( impl ); + return hr; + } + + *out = &impl->IAsyncAction_iface; + TRACE( "created IAsyncAction %p\n", *out ); + return S_OK; +} diff --git a/dlls/coremessaging/async_private.idl b/dlls/coremessaging/async_private.idl new file mode 100644 index 00000000000..10741572601 --- /dev/null +++ b/dlls/coremessaging/async_private.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 "windowscontracts.idl"; + +namespace Wine.Internal { + /* type-pruning version of AsyncOperationCompletedHandlerImpl<T> */ + delegate HRESULT AsyncOperationCompletedHandlerImpl([in] IInspectable *async, [in] AsyncStatus status); + + [ + uuid(d81ab70d-82e0-481c-983d-401225d98a2c) + ] + interface IAsyncInfoImpl : IUnknown + { + [propput] HRESULT Completed([in] AsyncOperationCompletedHandlerImpl *handler); + [propget] HRESULT Completed([out, retval] AsyncOperationCompletedHandlerImpl **handler); + [propget] HRESULT Result([out, retval] PROPVARIANT *result); + HRESULT Start(); + } + + typedef HRESULT (*async_operation_callback)( IUnknown *invoker, IUnknown *param, PROPVARIANT *result ); +} diff --git a/dlls/coremessaging/main.c b/dlls/coremessaging/main.c index 5bb4a99ee57..0cb8380d373 100644 --- a/dlls/coremessaging/main.c +++ b/dlls/coremessaging/main.c @@ -116,6 +116,110 @@ static const struct IActivationFactoryVtbl factory_vtbl = factory_ActivateInstance, };
+struct dispatcher_queue_controller +{ + IDispatcherQueueController IDispatcherQueueController_iface; + LONG ref; +}; + +static inline struct dispatcher_queue_controller *impl_from_IDispatcherQueueController( IDispatcherQueueController *iface ) +{ + return CONTAINING_RECORD( iface, struct dispatcher_queue_controller, IDispatcherQueueController_iface ); +} + +static HRESULT WINAPI dispatcher_queue_controller_QueryInterface( IDispatcherQueueController *iface, REFIID iid, void **out ) +{ + struct dispatcher_queue_controller *impl = impl_from_IDispatcherQueueController( 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_IDispatcherQueueController )) + { + *out = &impl->IDispatcherQueueController_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 dispatcher_queue_controller_AddRef( IDispatcherQueueController *iface ) +{ + struct dispatcher_queue_controller *impl = impl_from_IDispatcherQueueController( iface ); + ULONG ref = InterlockedIncrement( &impl->ref ); + TRACE( "iface %p increasing ref to %lu.\n", iface, ref ); + return ref; +} + +static ULONG WINAPI dispatcher_queue_controller_Release( IDispatcherQueueController *iface ) +{ + struct dispatcher_queue_controller *impl = impl_from_IDispatcherQueueController( iface ); + ULONG ref = InterlockedDecrement( &impl->ref ); + + TRACE( "iface %p decreasing ref to %lu.\n", iface, ref ); + + if (!ref) free( impl ); + return ref; +} + +static HRESULT WINAPI dispatcher_queue_controller_GetIids( IDispatcherQueueController *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 dispatcher_queue_controller_GetRuntimeClassName( IDispatcherQueueController *iface, HSTRING *class_name ) +{ + FIXME( "iface %p, class_name %p stub!\n", iface, class_name ); + return E_NOTIMPL; +} + +static HRESULT WINAPI dispatcher_queue_controller_GetTrustLevel( IDispatcherQueueController *iface, TrustLevel *trust_level ) +{ + FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level ); + return E_NOTIMPL; +} + +static HRESULT WINAPI dispatcher_queue_controller_get_DispatcherQueue( IDispatcherQueueController *iface, IDispatcherQueue **value ) +{ + FIXME( "iface %p, value %p stub!\n", iface, value ); + return E_NOTIMPL; +} + +static HRESULT shutdown_queue_async( IUnknown *invoker, IUnknown *param, PROPVARIANT *result ) +{ + return S_OK; +} + +static HRESULT WINAPI dispatcher_queue_controller_ShutdownQueueAsync( IDispatcherQueueController *iface, IAsyncAction **operation ) +{ + FIXME( "iface %p, operation %p stub!\n", iface, operation ); + + if (!operation) return E_POINTER; + *operation = NULL; + + return async_action_create( NULL, shutdown_queue_async, operation ); +} + +static const struct IDispatcherQueueControllerVtbl dispatcher_queue_controller_vtbl = +{ + dispatcher_queue_controller_QueryInterface, + dispatcher_queue_controller_AddRef, + dispatcher_queue_controller_Release, + /* IInspectable methods */ + dispatcher_queue_controller_GetIids, + dispatcher_queue_controller_GetRuntimeClassName, + dispatcher_queue_controller_GetTrustLevel, + /* IDispatcherQueueController methods */ + dispatcher_queue_controller_get_DispatcherQueue, + dispatcher_queue_controller_ShutdownQueueAsync, +}; + DEFINE_IINSPECTABLE( dispatcher_queue_controller_statics, IDispatcherQueueControllerStatics, struct dispatcher_queue_controller_statics, IActivationFactory_iface )
static HRESULT WINAPI dispatcher_queue_controller_statics_CreateOnDedicatedThread( IDispatcherQueueControllerStatics *iface, IDispatcherQueueController **result ) @@ -163,7 +267,20 @@ HRESULT WINAPI DllGetActivationFactory( HSTRING classid, IActivationFactory **fa
HRESULT WINAPI CreateDispatcherQueueController( DispatcherQueueOptions options, PDISPATCHERQUEUECONTROLLER *queue_controller ) { - FIXME( "options.dwSize = %lu, options.threadType = %d, options.apartmentType = %d, queue_controller %p stub!\n", + struct dispatcher_queue_controller *impl; + + FIXME( "options.dwSize = %lu, options.threadType = %d, options.apartmentType = %d, queue_controller %p semi-stub!\n", options.dwSize, options.threadType, options.apartmentType, queue_controller ); - return E_NOTIMPL; + + if (!queue_controller) return E_POINTER; + if (options.dwSize != sizeof( DispatcherQueueOptions )) return E_INVALIDARG; + if (options.threadType != DQTYPE_THREAD_DEDICATED && options.threadType != DQTYPE_THREAD_CURRENT) return E_INVALIDARG; + if (!(impl = calloc( 1, sizeof( *impl ) ))) return E_OUTOFMEMORY; + + impl->IDispatcherQueueController_iface.lpVtbl = &dispatcher_queue_controller_vtbl; + impl->ref = 1; + + *queue_controller = &impl->IDispatcherQueueController_iface; + TRACE( "created IDispatcherQueueController %p.\n", *queue_controller ); + return S_OK; } diff --git a/dlls/coremessaging/private.h b/dlls/coremessaging/private.h index 09567dedc1c..bf1a2fca7a1 100644 --- a/dlls/coremessaging/private.h +++ b/dlls/coremessaging/private.h @@ -38,6 +38,10 @@ #define WIDL_using_Windows_System #include "windows.system.h"
+#include "async_private.h" + +HRESULT async_action_create( IUnknown *invoker, async_operation_callback callback, IAsyncAction **out ); + #define DEFINE_IINSPECTABLE_( pfx, iface_type, impl_type, impl_from, iface_mem, expr ) \ static inline impl_type *impl_from( iface_type *iface ) \ { \ @@ -75,5 +79,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/coremessaging/tests/coremessaging.c b/dlls/coremessaging/tests/coremessaging.c index ab3daecb2ca..de511b4dcc2 100644 --- a/dlls/coremessaging/tests/coremessaging.c +++ b/dlls/coremessaging/tests/coremessaging.c @@ -235,9 +235,8 @@ static void check_create_dispatcher_queue_controller_( unsigned int line, DWORD options.apartmentType = apartment_type;
hr = CreateDispatcherQueueController( options, &dispatcher_queue_controller ); - todo_wine ok_(__FILE__, line)( hr == expected_hr, "got CreateDispatcherQueueController hr %#lx.\n", hr ); - if (FAILED(hr)) return; + if (hr == E_INVALIDARG) return;
hr = IDispatcherQueueController_get_DispatcherQueue( dispatcher_queue_controller, &dispatcher_queue ); todo_wine @@ -316,10 +315,8 @@ static void test_CreateDispatcherQueueController(void) HRESULT hr;
hr = CreateDispatcherQueueController( options, NULL ); - todo_wine ok( hr == E_POINTER || hr == 0x80000005 /* E_POINTER #if !defined(_WIN32) || defined(_MAC) */, "got hr %#lx.\n", hr ); hr = CreateDispatcherQueueController( options, &dispatcher_queue_controller ); - todo_wine ok( hr == E_INVALIDARG, "got hr %#lx.\n", hr ); ok( dispatcher_queue_controller == (void *)0xdeadbeef, "got dispatcher_queue_controller %p.\n", dispatcher_queue_controller );
On Wed May 14 15:30:07 2025 +0000, Mohamad Al-Jaf wrote:
changed this line in [version 8 of the diff](/wine/wine/-/merge_requests/7928/diffs?diff_id=177691&start_sha=e88854371e8224aefbe8711a93f99daaccfab0d8#bec8ad19ab6a926701bf8daf5d7f34698d5d7be5_402_402)
Thanks, updated the comment. It was originally `!defined(_WIN32)`. That's the HRESULT I got in my Windows 10 22H2 VM. It looks like an issue with the native implementation and seems to be fixed in Windows 11.
https://testbot.winehq.org/JobDetails.pl?Key=158268&f133=exe64.report#k1...
**v7** - Update HRESULT `0x80000005` comment - Update commit message
On Wed May 14 15:30:15 2025 +0000, Mohamad Al-Jaf wrote:
Thanks, updated the comment. It was originally `!defined(_WIN32)`. That's the HRESULT I got in my Windows 10 22H2 VM. It looks like an issue with the native implementation and seems to be fixed in Windows 11. https://testbot.winehq.org/JobDetails.pl?Key=158268&f133=exe64.report#k1...
In that case, use `hr == E_POINTER || broken(hr == 0x80000005) /* win10 22h2 */`.
Nikolay Sivov (@nsivov) commented about dlls/coremessaging/tests/coremessaging.c:
- if (hr == REGDB_E_CLASSNOTREG)
- {
win_skip( "%s runtimeclass not registered, skipping tests.\n", wine_dbgstr_w( dispatcher_queue_controller_statics_name ) );
return;
- }
- check_interface( factory, &IID_IUnknown );
- check_interface( factory, &IID_IInspectable );
- check_interface( factory, &IID_IAgileObject );
- hr = IActivationFactory_QueryInterface( factory, &IID_IDispatcherQueueControllerStatics, (void **)&dispatcher_queue_controller_statics );
- ok( hr == S_OK, "got hr %#lx.\n", hr );
- hr = IDispatcherQueueControllerStatics_CreateOnDedicatedThread( dispatcher_queue_controller_statics, NULL );
- todo_wine
- ok( hr == E_POINTER || hr == 0x80000005 /* E_POINTER #if !defined(_WIN32) || defined(_MAC) */, "got hr %#lx.\n", hr );
Just use FAILED(hr), or don't test for NULL argument case at all.
On Wed May 14 15:35:09 2025 +0000, Jinoh Kang wrote:
In that case, use `hr == E_POINTER || broken(hr == 0x80000005) /* win10 22h2 */`.
I think it's more likely to be a STATUS_BUFFER_OVERFLOW than the weirdo mac name, though both feel fairly farfetched to me. Maybe the best solution is what Jinoh is saying, simply skip the guesswork and keep it as a plain old integer.
And yes, we usually use broken() for strange behaviors that changed in newer Windows.
On Wed May 14 15:37:11 2025 +0000, Nikolay Sivov wrote:
Just use FAILED(hr), or don't test for NULL argument case at all.
If you're going to use FAILED(hr) we might as well scrap the test, there's little to no way the function would succeed with an invalid pointer if not outright crash.
@maljaf, you probably added the test because the app depends on it, right? Otherwise we could just remove the NULL check entirely in implementation side and let it crash. Maybe not if that makes the API inconsistent with other ones.
On Wed May 14 15:39:02 2025 +0000, Alfred Agrell wrote:
I think it's more likely to be a STATUS_BUFFER_OVERFLOW than the weirdo mac name, though both feel fairly farfetched to me. Maybe the best solution is what Jinoh is saying, simply skip the guesswork and keep it as a plain old integer. And yes, we usually use broken() for strange behaviors that changed in newer Windows.
Regardless, IMO it's clear their intention was to return an `E_POINTER`, as evidenced in the Windows 11 test.
I don't think `broken()` is appropriate here. It's usually used when a small percentage of VMs differ in their behavior and the test fails as a result. In this case, all the Windows 10 VMs that support `CreateDispatcherQueueController()` return this specific `HRESULT`.
Changing the comment to `win10 22h2` is fine by me if @rbernon approves.
On Wed May 14 15:45:05 2025 +0000, Jinoh Kang wrote:
If you're going to use FAILED(hr) we might as well scrap the test, there's little to no way the function would succeed with an invalid pointer if not outright crash. @maljaf, you probably added the test because the app depends on it, right? Otherwise we could just remove the NULL check entirely in implementation side and let it crash. Maybe not if that makes the API inconsistent with other ones.
I don't see any reason to remove it, though I don't have a strong opinion about it. In any case, I'm not changing anything unless @rbernon says otherwise.
On Wed May 14 17:44:31 2025 +0000, Mohamad Al-Jaf wrote:
Regardless, IMO it's clear their intention was to return an `E_POINTER`, as evidenced in the Windows 11 test. I don't think `broken()` is appropriate here. It's usually used when a small percentage of VMs differ in their behavior and the test fails as a result. In this case, all the Windows 10 VMs that support `CreateDispatcherQueueController()` return this specific `HRESULT`. Changing the comment to `win10 22h2` is fine by me if @rbernon approves.
`win10 22h2` would be better as a comment yes, so we know which Windows versions this is supposed to address. I don't mind either way wrt `broken` usage.
On Wed May 14 17:44:41 2025 +0000, Mohamad Al-Jaf wrote:
I don't see any reason to remove it, though I don't have a strong opinion about it. In any case, I'm not changing anything unless @rbernon says otherwise.
I'm fine keeping the tests with the comment indicating that specific Windows version. It's not unusual to have weird values with WinRT.