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)))