[PATCH v3 0/4] MR10006: windows.devices.bluetooth: Add initial implementation for BluetoothAdapter.
-- v3: windows.devices.bluetooth: Implement BluetoothAdapter::get_BluetoothAddress. windows.devices.bluetooth: Implement BluetoothAdapter::FromIdAsync. https://gitlab.winehq.org/wine/wine/-/merge_requests/10006
From: Vibhav Pant <vibhavp@gmail.com> --- .../tests/Makefile.in | 2 +- .../tests/bluetooth.c | 158 ++++++++++++++++++ 2 files changed, 159 insertions(+), 1 deletion(-) diff --git a/dlls/windows.devices.bluetooth/tests/Makefile.in b/dlls/windows.devices.bluetooth/tests/Makefile.in index 588b1c61989..c04dafe56d1 100644 --- a/dlls/windows.devices.bluetooth/tests/Makefile.in +++ b/dlls/windows.devices.bluetooth/tests/Makefile.in @@ -1,5 +1,5 @@ TESTDLL = windows.devices.bluetooth.dll -IMPORTS = combase +IMPORTS = combase bluetoothapis SOURCES = \ bluetooth.c diff --git a/dlls/windows.devices.bluetooth/tests/bluetooth.c b/dlls/windows.devices.bluetooth/tests/bluetooth.c index dcb40c46d99..9e10ca87db8 100644 --- a/dlls/windows.devices.bluetooth/tests/bluetooth.c +++ b/dlls/windows.devices.bluetooth/tests/bluetooth.c @@ -24,6 +24,8 @@ #include "windef.h" #include "winbase.h" #include "winstring.h" +#include "bthsdpdef.h" +#include "bluetoothapis.h" #include "roapi.h" @@ -281,14 +283,88 @@ static void check_bluetoothledevice_async( int line, IAsyncOperation_BluetoothLE } } +static void await_bluetoothadapter( int line, IAsyncOperation_BluetoothAdapter *async ) +{ + IAsyncOperationCompletedHandler_IInspectable *handler; + HANDLE event; + HRESULT hr; + DWORD ret; + + event = CreateEventW( NULL, FALSE, FALSE, NULL ); + ok_(__FILE__, line)( !!event, "CreateEventW failed, error %lu\n", GetLastError() ); + + handler = inspectable_async_handler_create( event, &IID_IAsyncOperationCompletedHandler_BluetoothAdapter ); + ok_( __FILE__, line )( !!handler, "inspectable_async_handler_create failed\n" ); + hr = IAsyncOperation_BluetoothAdapter_put_Completed( async, (IAsyncOperationCompletedHandler_BluetoothAdapter *)handler ); + ok_(__FILE__, line)( hr == S_OK, "put_Completed returned %#lx\n", hr ); + IAsyncOperationCompletedHandler_IInspectable_Release( handler ); + + ret = WaitForSingleObject( event, 5000 ); + ok_(__FILE__, line)( !ret, "WaitForSingleObject returned %#lx\n", ret ); + ret = CloseHandle( event ); + ok_(__FILE__, line)( ret, "CloseHandle failed, error %lu\n", GetLastError() ); +} + +static void check_bluetoothadapter_async( int line, IAsyncOperation_BluetoothAdapter *async, AsyncStatus expect_status, + HRESULT expect_hr, IBluetoothAdapter **result ) +{ + AsyncStatus async_status; + IAsyncInfo *async_info; + HRESULT hr, async_hr; + + hr = IAsyncOperation_BluetoothAdapter_QueryInterface( async, &IID_IAsyncInfo, (void **)&async_info ); + ok_(__FILE__, line)( hr == S_OK, "QueryInterface returned %#lx\n", hr ); + + 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 ); + + 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, "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, "got error %#lx\n", async_hr ); + else ok_(__FILE__, line)( async_hr == E_ILLEGAL_METHOD_CALL, "got error %#lx\n", async_hr ); + + IAsyncInfo_Release( async_info ); + + hr = IAsyncOperation_BluetoothAdapter_GetResults( async, result ); + switch (expect_status) + { + case Completed: + case Error: + todo_wine_if( FAILED(expect_hr)) + ok_(__FILE__, line)( hr == expect_hr, "GetResults returned %#lx\n", hr ); + break; + case Canceled: + case Started: + default: + ok_(__FILE__, line)( hr == E_ILLEGAL_METHOD_CALL, "GetResults returned %#lx\n", hr ); + break; + } +} + static void test_BluetoothAdapterStatics(void) { static const WCHAR *default_res = L"System.Devices.InterfaceClassGuid:=\"{92383B0E-F90E-4AC9-8D44-8C2D0D0EBDA2}\" " L"AND System.Devices.InterfaceEnabled:=System.StructuredQueryType.Boolean#True"; static const WCHAR *bluetoothadapter_statics_name = L"Windows.Devices.Bluetooth.BluetoothAdapter"; + BLUETOOTH_FIND_RADIO_PARAMS params = { sizeof( params ) }; + IAsyncOperation_BluetoothAdapter *adapter_async = NULL; + IBluetoothAdapter *adapter1 = NULL, *adapter2 = NULL; IBluetoothAdapterStatics *bluetoothadapter_statics; + boolean bool_val = FALSE, found = FALSE; + HBLUETOOTH_RADIO_FIND radio_find; IActivationFactory *factory; HSTRING str, default_str; + UINT64 address = 0; + HANDLE radio; HRESULT hr; INT32 res; LONG ref; @@ -324,6 +400,88 @@ static void test_BluetoothAdapterStatics(void) WindowsDeleteString( str ); WindowsDeleteString( default_str ); + + hr = IBluetoothAdapterStatics_GetDefaultAsync( bluetoothadapter_statics, &adapter_async ); + todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); + if (FAILED( hr )) + { + skip( "GetDefaultAsync failed.\n" ); + goto done; + } + await_bluetoothadapter( __LINE__, adapter_async ); + check_bluetoothadapter_async( __LINE__, adapter_async, Completed, S_OK, &adapter1 ); + IAsyncOperation_BluetoothAdapter_Release( adapter_async ); + if (!adapter1) + { + skip( "No Bluetooth adapter found.\n" ); + goto done; + } + + check_interface( adapter1, &IID_IUnknown ); + check_interface( adapter1, &IID_IInspectable ); + check_interface( adapter1, &IID_IAgileObject ); + + str = NULL; + hr = IBluetoothAdapter_get_DeviceId( adapter1, &str ); + todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); + todo_wine ok( !WindowsIsStringEmpty( str ), "got str %s.\n", debugstr_hstring( str ) ); + + hr = IBluetoothAdapterStatics_FromIdAsync( bluetoothadapter_statics, str, &adapter_async ); + todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); + if (SUCCEEDED( hr )) + { + await_bluetoothadapter( __LINE__, adapter_async ); + check_bluetoothadapter_async( __LINE__, adapter_async, Completed, S_OK, &adapter2 ); + IAsyncOperation_BluetoothAdapter_Release( adapter_async ); + ok( adapter2 != NULL, "got adapter2 %p.\n", adapter2 ); + ref = IBluetoothAdapter_Release( adapter2 ); + ok( !ref, "got ref %ld.\n", ref ); + } + WindowsDeleteString( str ); + + address = 0; + hr = IBluetoothAdapter_get_BluetoothAddress( adapter1, &address ); + todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); + todo_wine ok( address, "got address %#I64x.\n", address ); + + radio_find = BluetoothFindFirstRadio( ¶ms, &radio ); + ok( radio_find != NULL, "Got radio_find %p\n", radio_find ); + for (;;) + { + BLUETOOTH_RADIO_INFO info = { sizeof( info ) }; + DWORD ret; + + ret = BluetoothGetRadioInfo( radio, &info ); + ok( !ret, "Got ret %lu\n", ret ); + CloseHandle( radio ); + if (info.address.ullLong == address) + { + found = TRUE; + break; + } + if (!BluetoothFindNextRadio( radio_find, &radio )) break; + } + BluetoothFindRadioClose( radio_find ); + todo_wine ok( found, "got found %d\n", found ); + + hr = IBluetoothAdapter_get_IsLowEnergySupported( adapter1, &bool_val ); + todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); + + hr = IBluetoothAdapter_get_IsClassicSupported( adapter1, &bool_val ); + todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); + + hr = IBluetoothAdapter_get_IsPeripheralRoleSupported( adapter1, &bool_val ); + todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); + + hr = IBluetoothAdapter_get_IsCentralRoleSupported( adapter1, &bool_val ); + todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); + + hr = IBluetoothAdapter_get_IsAdvertisementOffloadSupported( adapter1, &bool_val ); + todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); + + ref = IBluetoothAdapter_Release( adapter1 ); + ok( !ref, "got ref %ld.\n", ref ); +done: ref = IBluetoothAdapterStatics_Release( bluetoothadapter_statics ); ok( ref == 2, "got ref %ld.\n", ref ); ref = IActivationFactory_Release( factory ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10006
From: Vibhav Pant <vibhavp@gmail.com> --- dlls/windows.devices.bluetooth/Makefile.in | 4 +- dlls/windows.devices.bluetooth/async.c | 667 ++++++++++++++++++ .../async_private.idl | 46 ++ .../bluetoothadapter.c | 223 +++++- dlls/windows.devices.bluetooth/private.h | 11 +- .../tests/bluetooth.c | 11 +- 6 files changed, 943 insertions(+), 19 deletions(-) create mode 100644 dlls/windows.devices.bluetooth/async.c create mode 100644 dlls/windows.devices.bluetooth/async_private.idl diff --git a/dlls/windows.devices.bluetooth/Makefile.in b/dlls/windows.devices.bluetooth/Makefile.in index 237a54b04fc..c931ba78706 100644 --- a/dlls/windows.devices.bluetooth/Makefile.in +++ b/dlls/windows.devices.bluetooth/Makefile.in @@ -1,8 +1,10 @@ MODULE = windows.devices.bluetooth.dll -IMPORTS = combase +IMPORTS = combase setupapi SOURCES = \ advertisement.c \ + async.c \ + async_private.idl \ bluetoothadapter.c \ bluetoothdevice.c \ classes.idl \ diff --git a/dlls/windows.devices.bluetooth/async.c b/dlls/windows.devices.bluetooth/async.c new file mode 100644 index 00000000000..2d9246c237c --- /dev/null +++ b/dlls/windows.devices.bluetooth/async.c @@ -0,0 +1,667 @@ +/* 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 BOOL async_info_complete( struct async_info *impl, BOOL called_async ) +{ + IInspectable *operation = impl->IInspectable_outer; + PROPVARIANT result = {0}; + HRESULT hr; + + hr = impl->callback( impl->invoker, impl->param, &result, called_async ); + if (!called_async && hr == STATUS_PENDING) return FALSE; + + 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 ); + return TRUE; +} + +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 ); + if (!async_info_complete( impl, FALSE )) 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 ); + + async_info_complete( impl, TRUE ); +} + +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_inspectable +{ + IAsyncOperation_IInspectable IAsyncOperation_IInspectable_iface; + IAsyncInfoImpl *IAsyncInfoImpl_inner; + LONG ref; + const GUID *iid; +}; + +static inline struct async_inspectable *impl_from_IAsyncOperation_IInspectable( IAsyncOperation_IInspectable *iface ) +{ + return CONTAINING_RECORD( iface, struct async_inspectable, IAsyncOperation_IInspectable_iface ); +} + +static HRESULT WINAPI async_inspectable_QueryInterface( IAsyncOperation_IInspectable *iface, REFIID iid, void **out ) +{ + struct async_inspectable *impl = impl_from_IAsyncOperation_IInspectable( 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, impl->iid )) + { + IInspectable_AddRef( (*out = &impl->IAsyncOperation_IInspectable_iface) ); + return S_OK; + } + + return IAsyncInfoImpl_QueryInterface( impl->IAsyncInfoImpl_inner, iid, out ); +} + +static ULONG WINAPI async_inspectable_AddRef( IAsyncOperation_IInspectable *iface ) +{ + struct async_inspectable *impl = impl_from_IAsyncOperation_IInspectable( iface ); + ULONG ref = InterlockedIncrement( &impl->ref ); + TRACE( "iface %p, ref %lu.\n", iface, ref ); + return ref; +} + +static ULONG WINAPI async_inspectable_Release( IAsyncOperation_IInspectable *iface ) +{ + struct async_inspectable *impl = impl_from_IAsyncOperation_IInspectable( 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_inspectable_GetIids( IAsyncOperation_IInspectable *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_inspectable_GetRuntimeClassName( IAsyncOperation_IInspectable *iface, HSTRING *class_name ) +{ + return WindowsCreateString( L"Windows.Foundation.IAsyncOperation`1<IInspectable>", + ARRAY_SIZE(L"Windows.Foundation.IAsyncOperation`1<IInspectable>"), + class_name ); +} + +static HRESULT WINAPI async_inspectable_GetTrustLevel( IAsyncOperation_IInspectable *iface, TrustLevel *trust_level ) +{ + FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level ); + return E_NOTIMPL; +} + +static HRESULT WINAPI async_inspectable_put_Completed( IAsyncOperation_IInspectable *iface, IAsyncOperationCompletedHandler_IInspectable *bool_handler ) +{ + IAsyncOperationCompletedHandlerImpl *handler = (IAsyncOperationCompletedHandlerImpl *)bool_handler; + struct async_inspectable *impl = impl_from_IAsyncOperation_IInspectable( iface ); + TRACE( "iface %p, handler %p.\n", iface, handler ); + return IAsyncInfoImpl_put_Completed( impl->IAsyncInfoImpl_inner, handler ); +} + +static HRESULT WINAPI async_inspectable_get_Completed( IAsyncOperation_IInspectable *iface, IAsyncOperationCompletedHandler_IInspectable **bool_handler ) +{ + IAsyncOperationCompletedHandlerImpl **handler = (IAsyncOperationCompletedHandlerImpl **)bool_handler; + struct async_inspectable *impl = impl_from_IAsyncOperation_IInspectable( iface ); + TRACE( "iface %p, handler %p.\n", iface, handler ); + return IAsyncInfoImpl_get_Completed( impl->IAsyncInfoImpl_inner, handler ); +} + +static HRESULT WINAPI async_inspectable_GetResults( IAsyncOperation_IInspectable *iface, IInspectable **results ) +{ + struct async_inspectable *impl = impl_from_IAsyncOperation_IInspectable( iface ); + PROPVARIANT result = {.vt = VT_UNKNOWN}; + HRESULT hr; + + TRACE( "iface %p, results %p.\n", iface, results ); + + if (SUCCEEDED(hr = IAsyncInfoImpl_get_Result( impl->IAsyncInfoImpl_inner, &result ))) + { + if ((*results = (IInspectable *)result.punkVal)) IInspectable_AddRef( *results ); + PropVariantClear( &result ); + } + + return hr; +} + +static const struct IAsyncOperation_IInspectableVtbl async_inspectable_vtbl = +{ + /* IUnknown methods */ + async_inspectable_QueryInterface, + async_inspectable_AddRef, + async_inspectable_Release, + /* IInspectable methods */ + async_inspectable_GetIids, + async_inspectable_GetRuntimeClassName, + async_inspectable_GetTrustLevel, + /* IAsyncOperation<IInspectable> */ + async_inspectable_put_Completed, + async_inspectable_get_Completed, + async_inspectable_GetResults, +}; + +HRESULT async_operation_inspectable_create( const GUID *iid, IUnknown *invoker, IUnknown *param, async_operation_callback callback, + IAsyncOperation_IInspectable **out ) +{ + struct async_inspectable *impl; + HRESULT hr; + + *out = NULL; + if (!(impl = calloc( 1, sizeof(*impl) ))) return E_OUTOFMEMORY; + impl->IAsyncOperation_IInspectable_iface.lpVtbl = &async_inspectable_vtbl; + impl->ref = 1; + impl->iid = iid; + + if (FAILED(hr = async_info_create( invoker, param, callback, (IInspectable *)&impl->IAsyncOperation_IInspectable_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_IInspectable_iface; + TRACE( "created IAsyncOperation_IInspectable %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/windows.devices.bluetooth/async_private.idl b/dlls/windows.devices.bluetooth/async_private.idl new file mode 100644 index 00000000000..40ea63ca4c1 --- /dev/null +++ b/dlls/windows.devices.bluetooth/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, BOOL called_async ); +} diff --git a/dlls/windows.devices.bluetooth/bluetoothadapter.c b/dlls/windows.devices.bluetooth/bluetoothadapter.c index bc8e04a9d33..780bd1adeab 100644 --- a/dlls/windows.devices.bluetooth/bluetoothadapter.c +++ b/dlls/windows.devices.bluetooth/bluetoothadapter.c @@ -1,6 +1,7 @@ /* WinRT Windows.Devices.Bluetooth BluetoothAdapter Implementation * * Copyright (C) 2023 Mohamad Al-Jaf + * Copyright (C) 2026 Vibhav Pant * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -18,25 +19,26 @@ */ #include "private.h" +#include "setupapi.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(bluetooth); -struct bluetoothadapter +struct bluetoothadapter_statics { IActivationFactory IActivationFactory_iface; IBluetoothAdapterStatics IBluetoothAdapterStatics_iface; LONG ref; }; -static inline struct bluetoothadapter *impl_from_IActivationFactory( IActivationFactory *iface ) +static inline struct bluetoothadapter_statics *impl_from_IActivationFactory( IActivationFactory *iface ) { - return CONTAINING_RECORD( iface, struct bluetoothadapter, IActivationFactory_iface ); + return CONTAINING_RECORD( iface, struct bluetoothadapter_statics, IActivationFactory_iface ); } static HRESULT WINAPI factory_QueryInterface( IActivationFactory *iface, REFIID iid, void **out ) { - struct bluetoothadapter *impl = impl_from_IActivationFactory( iface ); + struct bluetoothadapter_statics *impl = impl_from_IActivationFactory( iface ); TRACE( "iface %p, iid %s, out %p.\n", iface, debugstr_guid( iid ), out ); @@ -64,7 +66,7 @@ static HRESULT WINAPI factory_QueryInterface( IActivationFactory *iface, REFIID static ULONG WINAPI factory_AddRef( IActivationFactory *iface ) { - struct bluetoothadapter *impl = impl_from_IActivationFactory( iface ); + struct bluetoothadapter_statics *impl = impl_from_IActivationFactory( iface ); ULONG ref = InterlockedIncrement( &impl->ref ); TRACE( "iface %p increasing refcount to %lu.\n", iface, ref ); return ref; @@ -72,7 +74,7 @@ static ULONG WINAPI factory_AddRef( IActivationFactory *iface ) static ULONG WINAPI factory_Release( IActivationFactory *iface ) { - struct bluetoothadapter *impl = impl_from_IActivationFactory( iface ); + struct bluetoothadapter_statics *impl = impl_from_IActivationFactory( iface ); ULONG ref = InterlockedDecrement( &impl->ref ); TRACE( "iface %p decreasing refcount to %lu.\n", iface, ref ); return ref; @@ -115,7 +117,7 @@ static const struct IActivationFactoryVtbl factory_vtbl = factory_ActivateInstance, }; -DEFINE_IINSPECTABLE( bluetoothadapter_statics, IBluetoothAdapterStatics, struct bluetoothadapter, IActivationFactory_iface ) +DEFINE_IINSPECTABLE( bluetoothadapter_statics, IBluetoothAdapterStatics, struct bluetoothadapter_statics, IActivationFactory_iface ) static HRESULT WINAPI bluetoothadapter_statics_GetDeviceSelector( IBluetoothAdapterStatics *iface, HSTRING *result ) { @@ -134,9 +136,16 @@ static HRESULT WINAPI bluetoothadapter_statics_FromIdAsync( IBluetoothAdapterSta return E_NOTIMPL; } +static HRESULT bluetoothadapter_get_default_async( IUnknown *invoker, IUnknown *params, PROPVARIANT *result, BOOL called_async ); + static HRESULT WINAPI bluetoothadapter_statics_GetDefaultAsync( IBluetoothAdapterStatics *iface, IAsyncOperation_BluetoothAdapter **operation ) { - FIXME( "iface %p, operation %p stub!\n", iface, operation ); + TRACE( "iface %p, operation %p\n", iface, operation ); + return async_operation_inspectable_create( &IID_IAsyncOperation_BluetoothAdapter, + (IUnknown *)iface, + NULL, + bluetoothadapter_get_default_async, + (IAsyncOperation_IInspectable **)operation ); return E_NOTIMPL; } @@ -155,7 +164,7 @@ static const struct IBluetoothAdapterStaticsVtbl bluetoothadapter_statics_vtbl = bluetoothadapter_statics_GetDefaultAsync, }; -static struct bluetoothadapter bluetoothadapter_statics = +static struct bluetoothadapter_statics bluetoothadapter_statics = { {&factory_vtbl}, {&bluetoothadapter_statics_vtbl}, @@ -163,3 +172,199 @@ static struct bluetoothadapter bluetoothadapter_statics = }; IActivationFactory *bluetoothadapter_factory = &bluetoothadapter_statics.IActivationFactory_iface; + +struct bluetoothadapter +{ + IBluetoothAdapter IBluetoothAdapter_iface; + HSTRING id; + LONG ref; +}; + +static inline struct bluetoothadapter *impl_from_IBluetoothAdapter( IBluetoothAdapter *iface ) +{ + return CONTAINING_RECORD( iface, struct bluetoothadapter, IBluetoothAdapter_iface ); +} + +static WINAPI HRESULT bluetoothadapter_QueryInterface( IBluetoothAdapter *iface, REFIID iid, void **out ) +{ + 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_IBluetoothAdapter )) + { + IBluetoothAdapter_AddRef(( *out = iface )); + return S_OK; + } + + *out = NULL; + FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); + return E_NOINTERFACE; +} + +static WINAPI ULONG bluetoothadapter_AddRef( IBluetoothAdapter *iface ) +{ + struct bluetoothadapter *impl = impl_from_IBluetoothAdapter( iface ); + ULONG ref = InterlockedIncrement( &impl->ref ); + TRACE( "iface %p increasing refcount to %lu.\n", iface, ref ); + return ref; +} + +static WINAPI ULONG bluetoothadapter_Release( IBluetoothAdapter *iface ) +{ + struct bluetoothadapter *impl = impl_from_IBluetoothAdapter( iface ); + ULONG ref = InterlockedDecrement( &impl->ref ); + + TRACE("iface %p decreasing refcount to %lu.\n", iface, ref ); + + if (!ref) + { + WindowsDeleteString( impl->id ); + free( impl ); + } + return ref; +} + +static HRESULT WINAPI bluetoothadapter_GetIids( IBluetoothAdapter *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 bluetoothadapter_GetRuntimeClassName( IBluetoothAdapter *iface, HSTRING *class_name ) +{ + FIXME( "iface %p, class_name %p stub!\n", iface, class_name ); + return E_NOTIMPL; +} + +static HRESULT WINAPI bluetoothadapter_GetTrustLevel( IBluetoothAdapter *iface, TrustLevel *trust_level ) +{ + FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level ); + return E_NOTIMPL; +} + +static HRESULT WINAPI bluetoothadapter_get_DeviceId( IBluetoothAdapter *iface, HSTRING *id ) +{ + struct bluetoothadapter *impl = impl_from_IBluetoothAdapter( iface ); + TRACE( "iface %p, id %p\n", iface, id ); + return WindowsDuplicateString( impl->id, id ); +} + +static HRESULT WINAPI bluetoothadpter_get_BluetoothAddress( IBluetoothAdapter *iface, UINT64 *addr ) +{ + FIXME( "iface %p, addr %p stub!\n", iface, addr ); + return E_NOTIMPL; +} + +static HRESULT WINAPI bluetoothadapter_get_IsClassicSupported( IBluetoothAdapter *iface, boolean *value ) +{ + FIXME( "iface %p, value %p stub!\n", iface, value ); + return E_NOTIMPL; +} + +static HRESULT WINAPI bluetoothadapter_get_IsLowEnergySupported( IBluetoothAdapter *iface, boolean *value ) +{ + FIXME("iface %p, value %p stub!\n", iface, value ); + return E_NOTIMPL; +} + +static HRESULT WINAPI bluetoothadapter_get_IsPeripheralRoleSupported( IBluetoothAdapter *iface, boolean *value ) +{ + FIXME( "iface %p, value %p stub!\n", iface, value ); + return E_NOTIMPL; +} + +static HRESULT WINAPI bluetoothadapter_get_IsCentralRoleSupported( IBluetoothAdapter *iface, boolean *value ) +{ + FIXME( "iface %p, value %p stub!\n", iface, value ); + return E_NOTIMPL; +} + +static HRESULT WINAPI bluetoothadapter_get_IsAdvertisementOffloadSupported( IBluetoothAdapter *iface, boolean *value ) +{ + FIXME( "iface %p, value %p stub!\n", iface, value ); + return E_NOTIMPL; +} + +static HRESULT WINAPI bluetoothadapter_GetRadioAsync( IBluetoothAdapter *iface, IAsyncOperation_Radio **async_op ) +{ + FIXME("iface %p, async_op %p stub!\n", iface, async_op ); + return E_NOTIMPL; +} + +static const IBluetoothAdapterVtbl bluetoothadapter_vtbl = +{ + /* IUnknown */ + bluetoothadapter_QueryInterface, + bluetoothadapter_AddRef, + bluetoothadapter_Release, + /* IInspectable */ + bluetoothadapter_GetIids, + bluetoothadapter_GetRuntimeClassName, + bluetoothadapter_GetTrustLevel, + /* IBluetoothAdapter */ + bluetoothadapter_get_DeviceId, + bluetoothadpter_get_BluetoothAddress, + bluetoothadapter_get_IsClassicSupported, + bluetoothadapter_get_IsLowEnergySupported, + bluetoothadapter_get_IsPeripheralRoleSupported, + bluetoothadapter_get_IsCentralRoleSupported, + bluetoothadapter_get_IsAdvertisementOffloadSupported, + bluetoothadapter_GetRadioAsync, +}; + +static HRESULT create_bluetoothadapter( const WCHAR *path, IBluetoothAdapter **out ) +{ + struct bluetoothadapter *impl; + HRESULT hr; + + if (!(impl = calloc( 1, sizeof( *impl ) ))) return E_OUTOFMEMORY; + impl->IBluetoothAdapter_iface.lpVtbl = &bluetoothadapter_vtbl; + if (FAILED((hr = WindowsCreateString( path, wcslen( path ), &impl->id )))) + { + free( impl ); + return hr; + } + impl->ref = 1; + *out = &impl->IBluetoothAdapter_iface; + return S_OK; +} + +static HRESULT bluetoothadapter_get_default_async( IUnknown *invoker, IUnknown *params, PROPVARIANT *result, BOOL called_async ) +{ + char buffer[sizeof( SP_DEVICE_INTERFACE_DETAIL_DATA_W ) + MAX_PATH * sizeof( WCHAR )]; + SP_DEVICE_INTERFACE_DETAIL_DATA_W *iface_detail = (SP_DEVICE_INTERFACE_DETAIL_DATA_W *)buffer; + SP_DEVICE_INTERFACE_DATA iface_data; + HRESULT hr = S_OK; + HDEVINFO devinfo; + DWORD idx = 0; + + if (!called_async) return STATUS_PENDING; + + iface_detail->cbSize = sizeof( *iface_detail ); + iface_data.cbSize = sizeof( iface_data ); + + /* Windows.Devices.Bluetooth uses the GUID_BLUETOOTH_RADIO_INTERFACE interface class guid for radio devices. */ + devinfo = SetupDiGetClassDevsW( &GUID_BLUETOOTH_RADIO_INTERFACE, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE ); + if (devinfo == INVALID_HANDLE_VALUE) + return HRESULT_FROM_WIN32( GetLastError() ); + + while (SetupDiEnumDeviceInterfaces( devinfo, NULL, &GUID_BLUETOOTH_RADIO_INTERFACE, idx++, &iface_data )) + { + IBluetoothAdapter *adapter = NULL; + + if (!SetupDiGetDeviceInterfaceDetailW( devinfo, &iface_data, iface_detail, sizeof( buffer ), NULL, NULL )) + continue; + + if (SUCCEEDED((hr = create_bluetoothadapter( iface_detail->DevicePath, &adapter )))) + { + result->vt = VT_UNKNOWN; + result->punkVal = (IUnknown *)adapter; + } + break; + } + + SetupDiDestroyDeviceInfoList( devinfo ); + return hr; +} diff --git a/dlls/windows.devices.bluetooth/private.h b/dlls/windows.devices.bluetooth/private.h index 66dd1d34131..1e7f9a2440c 100644 --- a/dlls/windows.devices.bluetooth/private.h +++ b/dlls/windows.devices.bluetooth/private.h @@ -28,6 +28,7 @@ #include "winstring.h" #include "activation.h" +#include "bthdef.h" #define WIDL_using_Windows_Foundation #define WIDL_using_Windows_Foundation_Collections @@ -35,17 +36,24 @@ #define WIDL_using_Windows_Networking #include "windows.networking.connectivity.h" #include "windows.networking.h" +#define WIDL_using_Windows_Devices_Radios +#include "windows.devices.radios.h" #define WIDL_using_Windows_Devices_Bluetooth #include "windows.devices.bluetooth.rfcomm.h" #include "windows.devices.bluetooth.h" #define WIDL_using_Windows_Devices_Bluetooth_Advertisement #include "windows.devices.bluetooth.advertisement.h" +#include "async_private.h" + extern IActivationFactory *bluetoothadapter_factory; extern IActivationFactory *bluetoothdevice_statics_factory; extern IActivationFactory *bluetoothledevice_statics_factory; extern IActivationFactory *advertisement_watcher_factory; +HRESULT async_operation_inspectable_create( const GUID *iid, IUnknown *invoker, IUnknown *param, + async_operation_callback callback, IAsyncOperation_IInspectable **out ); + #define DEFINE_IINSPECTABLE_( pfx, iface_type, impl_type, impl_from, iface_mem, expr ) \ static inline impl_type *impl_from( iface_type *iface ) \ { \ @@ -83,5 +91,6 @@ extern IActivationFactory *advertisement_watcher_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/windows.devices.bluetooth/tests/bluetooth.c b/dlls/windows.devices.bluetooth/tests/bluetooth.c index 9e10ca87db8..608f05533a0 100644 --- a/dlls/windows.devices.bluetooth/tests/bluetooth.c +++ b/dlls/windows.devices.bluetooth/tests/bluetooth.c @@ -402,12 +402,7 @@ static void test_BluetoothAdapterStatics(void) WindowsDeleteString( default_str ); hr = IBluetoothAdapterStatics_GetDefaultAsync( bluetoothadapter_statics, &adapter_async ); - todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); - if (FAILED( hr )) - { - skip( "GetDefaultAsync failed.\n" ); - goto done; - } + ok( hr == S_OK, "got hr %#lx.\n", hr ); await_bluetoothadapter( __LINE__, adapter_async ); check_bluetoothadapter_async( __LINE__, adapter_async, Completed, S_OK, &adapter1 ); IAsyncOperation_BluetoothAdapter_Release( adapter_async ); @@ -423,8 +418,8 @@ static void test_BluetoothAdapterStatics(void) str = NULL; hr = IBluetoothAdapter_get_DeviceId( adapter1, &str ); - todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); - todo_wine ok( !WindowsIsStringEmpty( str ), "got str %s.\n", debugstr_hstring( str ) ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + ok( !WindowsIsStringEmpty( str ), "got str %s.\n", debugstr_hstring( str ) ); hr = IBluetoothAdapterStatics_FromIdAsync( bluetoothadapter_statics, str, &adapter_async ); todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10006
From: Vibhav Pant <vibhavp@gmail.com> --- .../bluetoothadapter.c | 57 ++++++++++++++++--- .../tests/bluetooth.c | 17 +++--- 2 files changed, 57 insertions(+), 17 deletions(-) diff --git a/dlls/windows.devices.bluetooth/bluetoothadapter.c b/dlls/windows.devices.bluetooth/bluetoothadapter.c index 780bd1adeab..917b60d00be 100644 --- a/dlls/windows.devices.bluetooth/bluetoothadapter.c +++ b/dlls/windows.devices.bluetooth/bluetoothadapter.c @@ -19,6 +19,7 @@ */ #include "private.h" +#include "roapi.h" #include "setupapi.h" #include "wine/debug.h" @@ -130,13 +131,34 @@ static HRESULT WINAPI bluetoothadapter_statics_GetDeviceSelector( IBluetoothAdap return WindowsCreateString( default_res, wcslen(default_res), result ); } +static HRESULT bluetoothadapter_get_adapter_async( IUnknown *invoker, IUnknown *params, PROPVARIANT *result, BOOL called_async ); + static HRESULT WINAPI bluetoothadapter_statics_FromIdAsync( IBluetoothAdapterStatics *iface, HSTRING id, IAsyncOperation_BluetoothAdapter **operation ) { - FIXME( "iface %p, id %s, operation %p stub!\n", iface, debugstr_hstring(id), operation ); - return E_NOTIMPL; -} + static const WCHAR *class_name = RuntimeClass_Windows_Foundation_PropertyValue; + IPropertyValueStatics *statics; + IInspectable *id_propval; + HSTRING_HEADER hdr; + HSTRING str; + HRESULT hr; -static HRESULT bluetoothadapter_get_default_async( IUnknown *invoker, IUnknown *params, PROPVARIANT *result, BOOL called_async ); + TRACE( "iface %p, id %s, operation %p\n", iface, debugstr_hstring(id), operation ); + + if (FAILED((hr = WindowsCreateStringReference( class_name, wcslen( class_name ), &hdr, &str )))) return hr; + if (FAILED((hr = RoGetActivationFactory( str, &IID_IPropertyValueStatics, (void **)&statics )))) return hr; + + hr = IPropertyValueStatics_CreateString( statics, id, &id_propval ); + IPropertyValueStatics_Release( statics ); + if (FAILED(hr)) return hr; + + hr = async_operation_inspectable_create( &IID_IAsyncOperation_BluetoothAdapter, + (IUnknown *)iface, + (IUnknown *)id_propval, + bluetoothadapter_get_adapter_async, + (IAsyncOperation_IInspectable **)operation ); + IInspectable_Release( id_propval ); + return hr; +} static HRESULT WINAPI bluetoothadapter_statics_GetDefaultAsync( IBluetoothAdapterStatics *iface, IAsyncOperation_BluetoothAdapter **operation ) { @@ -144,7 +166,7 @@ static HRESULT WINAPI bluetoothadapter_statics_GetDefaultAsync( IBluetoothAdapte return async_operation_inspectable_create( &IID_IAsyncOperation_BluetoothAdapter, (IUnknown *)iface, NULL, - bluetoothadapter_get_default_async, + bluetoothadapter_get_adapter_async, (IAsyncOperation_IInspectable **)operation ); return E_NOTIMPL; } @@ -331,16 +353,30 @@ static HRESULT create_bluetoothadapter( const WCHAR *path, IBluetoothAdapter **o return S_OK; } -static HRESULT bluetoothadapter_get_default_async( IUnknown *invoker, IUnknown *params, PROPVARIANT *result, BOOL called_async ) +static HRESULT bluetoothadapter_get_adapter_async( IUnknown *invoker, IUnknown *params, PROPVARIANT *result, BOOL called_async ) { char buffer[sizeof( SP_DEVICE_INTERFACE_DETAIL_DATA_W ) + MAX_PATH * sizeof( WCHAR )]; SP_DEVICE_INTERFACE_DETAIL_DATA_W *iface_detail = (SP_DEVICE_INTERFACE_DETAIL_DATA_W *)buffer; SP_DEVICE_INTERFACE_DATA iface_data; + const WCHAR *adapter_id = NULL; + HSTRING adapter_id_hstr = NULL; + UINT32 adapter_id_size = 0; HRESULT hr = S_OK; HDEVINFO devinfo; DWORD idx = 0; if (!called_async) return STATUS_PENDING; + if (params) + { + IPropertyValue *id_propval; + + if (FAILED((hr = IUnknown_QueryInterface( params, &IID_IPropertyValue, (void **)&id_propval )))) return hr; + hr = IPropertyValue_GetString( id_propval, &adapter_id_hstr ); + IPropertyValue_Release( id_propval ); + if (FAILED(hr)) return hr; + adapter_id = WindowsGetStringRawBuffer( adapter_id_hstr, &adapter_id_size ); + adapter_id_size *= sizeof( WCHAR ); + } iface_detail->cbSize = sizeof( *iface_detail ); iface_data.cbSize = sizeof( iface_data ); @@ -353,10 +389,16 @@ static HRESULT bluetoothadapter_get_default_async( IUnknown *invoker, IUnknown * while (SetupDiEnumDeviceInterfaces( devinfo, NULL, &GUID_BLUETOOTH_RADIO_INTERFACE, idx++, &iface_data )) { IBluetoothAdapter *adapter = NULL; + DWORD path_size; - if (!SetupDiGetDeviceInterfaceDetailW( devinfo, &iface_data, iface_detail, sizeof( buffer ), NULL, NULL )) + if (!SetupDiGetDeviceInterfaceDetailW( devinfo, &iface_data, iface_detail, sizeof( buffer ), &path_size, NULL )) continue; + if (adapter_id) + { + path_size -= (offsetof( SP_DEVICE_INTERFACE_DETAIL_DATA_W, DevicePath ) + sizeof( WCHAR )); + if (adapter_id_size != path_size || wcsicmp( iface_detail->DevicePath, adapter_id )) continue; + } if (SUCCEEDED((hr = create_bluetoothadapter( iface_detail->DevicePath, &adapter )))) { result->vt = VT_UNKNOWN; @@ -365,6 +407,7 @@ static HRESULT bluetoothadapter_get_default_async( IUnknown *invoker, IUnknown * break; } + WindowsDeleteString( adapter_id_hstr ); SetupDiDestroyDeviceInfoList( devinfo ); return hr; } diff --git a/dlls/windows.devices.bluetooth/tests/bluetooth.c b/dlls/windows.devices.bluetooth/tests/bluetooth.c index 608f05533a0..3ccc4117e5e 100644 --- a/dlls/windows.devices.bluetooth/tests/bluetooth.c +++ b/dlls/windows.devices.bluetooth/tests/bluetooth.c @@ -422,16 +422,13 @@ static void test_BluetoothAdapterStatics(void) ok( !WindowsIsStringEmpty( str ), "got str %s.\n", debugstr_hstring( str ) ); hr = IBluetoothAdapterStatics_FromIdAsync( bluetoothadapter_statics, str, &adapter_async ); - todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); - if (SUCCEEDED( hr )) - { - await_bluetoothadapter( __LINE__, adapter_async ); - check_bluetoothadapter_async( __LINE__, adapter_async, Completed, S_OK, &adapter2 ); - IAsyncOperation_BluetoothAdapter_Release( adapter_async ); - ok( adapter2 != NULL, "got adapter2 %p.\n", adapter2 ); - ref = IBluetoothAdapter_Release( adapter2 ); - ok( !ref, "got ref %ld.\n", ref ); - } + ok( hr == S_OK, "got hr %#lx.\n", hr ); + await_bluetoothadapter( __LINE__, adapter_async ); + check_bluetoothadapter_async( __LINE__, adapter_async, Completed, S_OK, &adapter2 ); + IAsyncOperation_BluetoothAdapter_Release( adapter_async ); + ok( adapter2 != NULL, "got adapter2 %p.\n", adapter2 ); + ref = IBluetoothAdapter_Release( adapter2 ); + ok( !ref, "got ref %ld.\n", ref ); WindowsDeleteString( str ); address = 0; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10006
From: Vibhav Pant <vibhavp@gmail.com> --- dlls/windows.devices.bluetooth/Makefile.in | 2 +- .../bluetoothadapter.c | 22 +++++++++++++++++-- dlls/windows.devices.bluetooth/private.h | 2 ++ .../tests/bluetooth.c | 6 ++--- 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/dlls/windows.devices.bluetooth/Makefile.in b/dlls/windows.devices.bluetooth/Makefile.in index c931ba78706..0e0364f2a3f 100644 --- a/dlls/windows.devices.bluetooth/Makefile.in +++ b/dlls/windows.devices.bluetooth/Makefile.in @@ -1,5 +1,5 @@ MODULE = windows.devices.bluetooth.dll -IMPORTS = combase setupapi +IMPORTS = bluetoothapis combase setupapi SOURCES = \ advertisement.c \ diff --git a/dlls/windows.devices.bluetooth/bluetoothadapter.c b/dlls/windows.devices.bluetooth/bluetoothadapter.c index 917b60d00be..2c2450d39df 100644 --- a/dlls/windows.devices.bluetooth/bluetoothadapter.c +++ b/dlls/windows.devices.bluetooth/bluetoothadapter.c @@ -199,6 +199,7 @@ struct bluetoothadapter { IBluetoothAdapter IBluetoothAdapter_iface; HSTRING id; + HANDLE radio; LONG ref; }; @@ -243,6 +244,7 @@ static WINAPI ULONG bluetoothadapter_Release( IBluetoothAdapter *iface ) if (!ref) { WindowsDeleteString( impl->id ); + CloseHandle( impl->radio ); free( impl ); } return ref; @@ -275,8 +277,17 @@ static HRESULT WINAPI bluetoothadapter_get_DeviceId( IBluetoothAdapter *iface, H static HRESULT WINAPI bluetoothadpter_get_BluetoothAddress( IBluetoothAdapter *iface, UINT64 *addr ) { - FIXME( "iface %p, addr %p stub!\n", iface, addr ); - return E_NOTIMPL; + struct bluetoothadapter *impl = impl_from_IBluetoothAdapter( iface ); + BLUETOOTH_RADIO_INFO info = {0}; + DWORD ret; + + TRACE( "iface %p, addr %p\n", iface, addr ); + + info.dwSize = sizeof( info ); + ret = BluetoothGetRadioInfo( impl->radio, &info ); + if (!ret) + *addr = info.address.ullLong; + return HRESULT_FROM_WIN32( ret ); } static HRESULT WINAPI bluetoothadapter_get_IsClassicSupported( IBluetoothAdapter *iface, boolean *value ) @@ -343,8 +354,15 @@ static HRESULT create_bluetoothadapter( const WCHAR *path, IBluetoothAdapter **o if (!(impl = calloc( 1, sizeof( *impl ) ))) return E_OUTOFMEMORY; impl->IBluetoothAdapter_iface.lpVtbl = &bluetoothadapter_vtbl; + impl->radio = CreateFileW( path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL ); + if (impl->radio == INVALID_HANDLE_VALUE) + { + free( impl ); + return HRESULT_FROM_WIN32( GetLastError() ); + } if (FAILED((hr = WindowsCreateString( path, wcslen( path ), &impl->id )))) { + CloseHandle( impl->radio ); free( impl ); return hr; } diff --git a/dlls/windows.devices.bluetooth/private.h b/dlls/windows.devices.bluetooth/private.h index 1e7f9a2440c..48cc5c04447 100644 --- a/dlls/windows.devices.bluetooth/private.h +++ b/dlls/windows.devices.bluetooth/private.h @@ -29,6 +29,8 @@ #include "activation.h" #include "bthdef.h" +#include "bthsdpdef.h" +#include "bluetoothapis.h" #define WIDL_using_Windows_Foundation #define WIDL_using_Windows_Foundation_Collections diff --git a/dlls/windows.devices.bluetooth/tests/bluetooth.c b/dlls/windows.devices.bluetooth/tests/bluetooth.c index 3ccc4117e5e..b53021a2cb2 100644 --- a/dlls/windows.devices.bluetooth/tests/bluetooth.c +++ b/dlls/windows.devices.bluetooth/tests/bluetooth.c @@ -433,8 +433,8 @@ static void test_BluetoothAdapterStatics(void) address = 0; hr = IBluetoothAdapter_get_BluetoothAddress( adapter1, &address ); - todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); - todo_wine ok( address, "got address %#I64x.\n", address ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + ok( address, "got address %#I64x.\n", address ); radio_find = BluetoothFindFirstRadio( ¶ms, &radio ); ok( radio_find != NULL, "Got radio_find %p\n", radio_find ); @@ -454,7 +454,7 @@ static void test_BluetoothAdapterStatics(void) if (!BluetoothFindNextRadio( radio_find, &radio )) break; } BluetoothFindRadioClose( radio_find ); - todo_wine ok( found, "got found %d\n", found ); + ok( found, "got found %d\n", found ); hr = IBluetoothAdapter_get_IsLowEnergySupported( adapter1, &bool_val ); todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10006
Is this compatible with native device ids?
For bluetooth adapters, the device id on native is the interface symlink path, but that is not guaranteed to be stable between updates/releases AFAIK
Shouldn't we compare it case-insensitively?
Yes, thanks. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10006#note_129190
On Mon Feb 9 14:02:37 2026 +0000, Vibhav Pant wrote:
changed this line in [version 2 of the diff](/wine/wine/-/merge_requests/10006/diffs?diff_id=243356&start_sha=6ab9ecfc7c3277a4538bb9c901c9435034306ae3#62aca71664d2bb57ca2863ac634b16b0f7c49a92_290_288) Never mind, I got a little confused. WinRT does not swap the address. Nonetheless, I've added tests to ensure that we can find a radio from `bluetoothapis` with the same address.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10006#note_129191
On Mon Feb 9 14:26:19 2026 +0000, Rémi Bernon wrote:
Also, any reason not using bluetoothapis for the enumeration as well instead of setupapi? `bluetoothapis` uses the `GUID_BTHPORT_DEVICE_INTERFACE` interface symlink for handles, while WinRT uses `GUID_BLUETOOTH_RADIO_INTERFACE`, so I thought it'd be better to do the same here as well.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10006#note_129192
This merge request was approved by Rémi Bernon. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10006
participants (3)
-
Rémi Bernon (@rbernon) -
Vibhav Pant -
Vibhav Pant (@vibhavp)