Nur kurz zur Info - es scheint, dass das Enumerieren der Characteristiken in Win11 IN einem Thread sein sollt - nicht im Haupthread...
(Message loop und BTLE stehen sowieso auf "Kriegsfuß". Manche Dinge müssen im Haupthread gemacht werden, manche eben nicht).
Der Fix ist im Endefekt in der Datei System.Win.BluetoothWinRT.pas in den Routinen "DoDiscoverServices" und
"DoGetCharacteristics" und "DoGetIncludedServices" einzubauen (Ich habe auch noch eine Art "Timeout" hinzugefügt...):
Hier mein Code:
Code:
function TWinRTBluetoothLEDevice.DoDiscoverServices: Boolean;
var
I: Integer;
LGattService: GenericAttributeProfile_IGattDeviceService;
dev3 : IBluetoothLEDevice3;
res3 : IAsyncOperation_1__GenericAttributeProfile_IGattDeviceServicesResult;
serviceRes : GenericAttributeProfile_IGattDeviceServicesResult;
LGattServices: IVectorView_1__GenericAttributeProfile_IGattDeviceService;
begin
Result := True;
FServices.Clear;
CheckInitialized;
if FID = 0 then
begin
if not Supports(FBluetoothLEDevice, IBluetoothLEDevice3, dev3) then
raise EBluetoothDeviceException.CreateRes(@SBluetoothLEDeviceNotPaired);
if TAsyncOperation<IAsyncOperation_1__GenericAttributeProfile_IGattDeviceServicesResult>.Wait(
dev3.GetGattServicesAsync(BluetoothCacheMode.Uncached), res3 ) = AsyncStatus.Completed then
begin
serviceRes := res3.GetResults;
LGattServices := serviceRes.Services;
if LGattServices.Size > 0 then
begin
for I := 0 to LGattServices.Size - 1 do
begin
LGattService := LGattServices.GetAt(I);
FServices.Add(TWinRTBluetoothGattService.Create(Self, LGattService, TBluetoothServiceType.Primary));
end;
end;
end;
end
else
begin
if FBluetoothLEDevice.GattServices.Size > 0 then
for I := 0 to FBluetoothLEDevice.GattServices.Size - 1 do
begin
LGattService := FBluetoothLEDevice.GattServices.GetAt(I);
FServices.Add(TWinRTBluetoothGattService.Create(Self, LGattService, TBluetoothServiceType.Primary));
end;
end;
// ###########################################
// #### enumerate the characteristics at once: note on windows 11 it seems
// that the enumeration of characteristics NEED to be in a thread and
// we trigger the characteristics fetch
for i := 0 to fServices.Count - 1 do
begin
TWinRTBluetoothGattService(fServices[i]).FTimeout := fOperationTimeout;
TWinRTBluetoothGattService(fServices[i]).DoGetCharacteristics;
end;
DoOnServicesDiscovered(Self, FServices);
end;
tion TWinRTBluetoothGattService.DoGetCharacteristics: TBluetoothGattCharacteristicList;
var I: Integer;
service3 : GenericAttributeProfile_IGattDeviceService3;
LGattCharacteristics: IVectorView_1__GenericAttributeProfile_IGattCharacteristic;
res3 : IAsyncOperation_1__GenericAttributeProfile_IGattCharacteristicsResult;
status1 : IAsyncOperation_1__GenericAttributeProfile_GattOpenStatus;
charactersRes : GenericAttributeProfile_IGattCharacteristicsResult;
characteristic : GenericAttributeProfile_IGattCharacteristic;
begin
Result := FCharacteristics;
if fCharacteristicsFetched then
exit;
CheckNotClosed;
FCharacteristics.Clear;
if Supports(FGattService, GenericAttributeProfile_IGattDeviceService3, service3) then
begin
if TAsyncOperation<IAsyncOperation_1__GenericAttributeProfile_GattOpenStatus>.Wait(
service3.OpenAsync(GenericAttributeProfile_GattSharingMode.SharedReadAndWrite), status1 ) <> AsyncStatus.Completed
then
exit(nil);
if (status1.GetResults = GenericAttributeProfile_GattOpenStatus.Success) or
(status1.GetResults = GenericAttributeProfile_GattOpenStatus.AlreadyOpened) then
begin
if TAsyncOperation<IAsyncOperation_1__GenericAttributeProfile_IGattCharacteristicsResult>.Wait(
service3.GetCharacteristicsAsync(BluetoothCacheMode.Uncached), res3, FTimeout ) = AsyncStatus.Completed
then
begin
charactersRes := res3.GetResults;
LGattCharacteristics := charactersRes.Characteristics;
if LGattCharacteristics.Size > 0 then
begin
for I := LGattCharacteristics.Size - 1 downto 0 do
begin
characteristic := LGattCharacteristics.GetAt(I);
FCharacteristics.Add(TWinRTBluetoothGattCharacteristic.Create(Self, characteristic));
end;
end;
end;
end
else
OutputDebugString(PChar('RequestAccessAysync failed with code ' + Integer(status1.GetResults).tostring));
end
else
begin
// does not work any more -> need to do sync
LGattCharacteristics := (FGattService as GenericAttributeProfile_IGattDeviceService2).GetAllCharacteristics;
if LGattCharacteristics.Size > 0 then
for I := 0 to LGattCharacteristics.Size - 1 do
FCharacteristics.Add(TWinRTBluetoothGattCharacteristic.Create(Self, LGattCharacteristics.GetAt(I)));
end;
for i := 0 to FCharacteristics.Count - 1 do
begin
TWinRTBluetoothGattCharacteristic(fCharacteristics[i]).FTimeout := FTimeout;
TWinRTBluetoothGattCharacteristic(fCharacteristics[i]).DoGetDescriptors;
end;
fCharacteristicsFetched := True;
Result := FCharacteristics;
end;
function TWinRTBluetoothGattService.DoGetIncludedServices: TBluetoothGattServiceList;
var
I: Integer;
LGattServices: IVectorView_1__GenericAttributeProfile_IGattDeviceService;
service3 : GenericAttributeProfile_IGattDeviceService3;
res3 : IAsyncOperation_1__GenericAttributeProfile_IGattDeviceServicesResult;
serviceRes : GenericAttributeProfile_IGattDeviceServicesResult;
status1 : IAsyncOperation_1__GenericAttributeProfile_GattOpenStatus;
begin
CheckNotClosed;
FIncludedServices.Clear;
if Supports(FGattService, GenericAttributeProfile_IGattDeviceService3, service3) then
begin
if TAsyncOperation<IAsyncOperation_1__GenericAttributeProfile_GattOpenStatus>.Wait(
service3.OpenAsync(GenericAttributeProfile_GattSharingMode.SharedReadAndWrite), status1 ) <> AsyncStatus.Completed
then
exit(nil);
if (status1.GetResults = GenericAttributeProfile_GattOpenStatus.Success) or
(status1.GetResults = GenericAttributeProfile_GattOpenStatus.AlreadyOpened) then
begin
if TAsyncOperation<IAsyncOperation_1__GenericAttributeProfile_IGattDeviceServicesResult>.Wait(
service3.GetIncludedServicesAsync, res3, FTimeout ) = AsyncStatus.Completed
then
begin
serviceRes := res3.GetResults;
LGattServices := serviceRes.Services;
if LGattServices.Size > 0 then
begin
for I := LGattServices.Size - 1 downto 0 do
FIncludedServices.Add(TWinRTBluetoothGattService.Create(FDevice, LGattServices.GetAt(I), TBluetoothServiceType.Primary));
end;
end;
end
else
OutputDebugString(PChar('RequestAccessAysync failed with code ' + Integer(status1.GetResults).tostring));
end
else
begin
LGattServices := (FGattService as GenericAttributeProfile_IGattDeviceService2).GetAllIncludedServices;
if LGattServices.Size > 0 then
for I := 0 to LGattServices.Size - 1 do
FIncludedServices.Add(TWinRTBluetoothGattService.Create(FDevice, LGattServices.GetAt(I), TBluetoothServiceType.Primary));
end;
Result := FIncludedServices;
end;
Bitte mal mit dem Snipplets probieren und für fTimeout = 10000 einsetzen. fCharacteristicsFetched ist auch von mir und sollte im TWinRTBluetoothGattService als private variable deklariert werden.
Falls wer noch interesse hat ich kann das File auch schicken - nur nicht ganz öffentlich machen...