From 7c2cb387f2d212a354fb430b09617941d2953ac4 Mon Sep 17 00:00:00 2001 From: "j.foucher" Date: Wed, 18 Feb 2026 19:28:05 +0100 Subject: [PATCH] =?UTF-8?q?PS=5FWin=5FBLE=20:=20fix=20crash=20shutdown=20+?= =?UTF-8?q?=20cache=20caract=C3=A9ristiques=20GATT?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Crash shutdown (Index >= 0) : - OnPreExit se déclenche encore trop tard (GC déjà en cours). - Remplacé par OnEnginePreExit qui fire avant le shutdown des core modules, donc avant toute destruction d'UObject. RemoveFromRoot() est maintenant appelé au bon moment. Cache caractéristiques GATT : - Bug : Read/Write/Subscribe rappelaient GetCharacteristicsAsync(Cached) à chaque opération, ce qui peut retourner un ordre différent du discovery initial (Uncached) → mauvaise caractéristique ciblée. - Fix : les GattCharacteristic sont maintenant stockées dans FPS_GattServiceHandle::Characteristics (std::vector) lors du ConnectDevice, et réutilisées directement via [CI] dans toutes les opérations ultérieures. Co-Authored-By: Claude Opus 4.6 --- .../PS_Win_BLE/Private/PS_BLEModule.cpp | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/Unreal/Plugins/PS_Win_BLE/Source/PS_Win_BLE/Private/PS_BLEModule.cpp b/Unreal/Plugins/PS_Win_BLE/Source/PS_Win_BLE/Private/PS_BLEModule.cpp index 8ee2953..5513ae6 100644 --- a/Unreal/Plugins/PS_Win_BLE/Source/PS_Win_BLE/Private/PS_BLEModule.cpp +++ b/Unreal/Plugins/PS_Win_BLE/Source/PS_Win_BLE/Private/PS_BLEModule.cpp @@ -65,6 +65,8 @@ struct FPS_BLEDeviceHandle struct FPS_GattServiceHandle { winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattDeviceService Service{ nullptr }; + // Characteristics cached at discovery time (same order as ActiveServices[SI].Characteristics) + std::vector Characteristics; }; #endif // PLATFORM_WINDOWS @@ -95,9 +97,9 @@ void UPS_BLE_Module::StartupModule() LocalBLEManager->AttachModule(this); // Cleanup UObjects before Unreal's UObject array is destroyed. - // ShutdownModule() is called too late (UObjectArray already partially torn down) - // → we do RemoveFromRoot here, during pre-exit, which is still safe. - FCoreDelegates::OnPreExit.AddLambda([this]() + // Both ShutdownModule() and OnPreExit fire too late (UObjectArray already + // partially torn down). OnEnginePreExit fires earlier, before GC teardown. + FCoreDelegates::OnEnginePreExit.AddLambda([this]() { if (LocalBLEManager) { @@ -390,6 +392,12 @@ bool UPS_BLE_Module::ConnectDevice(UPS_BLE_Device* Device) for (uint32_t ci = 0; ci < Chars.Size(); ci++) { auto Ch = Chars.GetAt(ci); + + // Cache the characteristic object so all subsequent operations + // (Read / Write / Subscribe) use the exact same instance and index + // as what was discovered here — no re-fetch needed. + SvcHandle->Characteristics.push_back(Ch); + FPS_CharacteristicItem ChItem; GUID cg = Ch.Uuid(); ChItem.CharacteristicUUID = UPS_BLE_Device::GUIDToString(&cg); @@ -481,11 +489,9 @@ bool UPS_BLE_Module::ReadCharacteristic(UPS_BLE_Device* Device, uint8 SI, uint8 PS_BLE_WINRT_NS try { - auto* SvcH = static_cast(Device->NativeGattServices[SI]); - auto Chars = SvcH->Service.GetCharacteristicsAsync(WinBT::BluetoothCacheMode::Cached).get(); - if (Chars.Status() != WinGAP::GattCommunicationStatus::Success) return; - - auto Ch = Chars.Characteristics().GetAt(CI); + auto* SvcH = static_cast(Device->NativeGattServices[SI]); + if (CI >= SvcH->Characteristics.size()) return; + auto Ch = SvcH->Characteristics[CI]; auto Result = Ch.ReadValueAsync(WinBT::BluetoothCacheMode::Uncached).get(); EPS_GATTStatus Status = (Result.Status() == WinGAP::GattCommunicationStatus::Success) @@ -520,11 +526,9 @@ bool UPS_BLE_Module::WriteCharacteristic(UPS_BLE_Device* Device, uint8 SI, uint8 PS_BLE_WINRT_NS try { - auto* SvcH = static_cast(Device->NativeGattServices[SI]); - auto Chars = SvcH->Service.GetCharacteristicsAsync(WinBT::BluetoothCacheMode::Cached).get(); - if (Chars.Status() != WinGAP::GattCommunicationStatus::Success) return; - - auto Ch = Chars.Characteristics().GetAt(CI); + auto* SvcH = static_cast(Device->NativeGattServices[SI]); + if (CI >= SvcH->Characteristics.size()) return; + auto Ch = SvcH->Characteristics[CI]; auto Writer = WinStr::DataWriter(); for (uint8 B : DataCopy) Writer.WriteByte(B); @@ -554,11 +558,9 @@ bool UPS_BLE_Module::SubscribeCharacteristic(UPS_BLE_Device* Device, uint8 SI, u PS_BLE_WINRT_NS try { - auto* SvcH = static_cast(Device->NativeGattServices[SI]); - auto Chars = SvcH->Service.GetCharacteristicsAsync(WinBT::BluetoothCacheMode::Cached).get(); - if (Chars.Status() != WinGAP::GattCommunicationStatus::Success) return; - - auto Ch = Chars.Characteristics().GetAt(CI); + auto* SvcH = static_cast(Device->NativeGattServices[SI]); + if (CI >= SvcH->Characteristics.size()) return; + auto Ch = SvcH->Characteristics[CI]; auto WriteStatus = Ch.WriteClientCharacteristicConfigurationDescriptorAsync( WinGAP::GattClientCharacteristicConfigurationDescriptorValue::Notify).get(); @@ -604,11 +606,9 @@ bool UPS_BLE_Module::UnsubscribeCharacteristic(UPS_BLE_Device* Device, uint8 SI, PS_BLE_WINRT_NS try { - auto* SvcH = static_cast(Device->NativeGattServices[SI]); - auto Chars = SvcH->Service.GetCharacteristicsAsync(WinBT::BluetoothCacheMode::Cached).get(); - if (Chars.Status() != WinGAP::GattCommunicationStatus::Success) return; - - auto Ch = Chars.Characteristics().GetAt(CI); + auto* SvcH = static_cast(Device->NativeGattServices[SI]); + if (CI >= SvcH->Characteristics.size()) return; + auto Ch = SvcH->Characteristics[CI]; uint64 Key = ((uint64)SI << 8) | CI; if (winrt::event_token** TokenPtr = reinterpret_cast(Device->NotifyTokens.Find(Key)))