Unhandled Exception: EXCEPTION_ACCESS_VIOLATION reading address 0x000001dd6280030f

I just ran the side by side tests and the console version (using the same DLL) ran without any error while the “UE” version resulted in EXCEPTION_ACCESS_VIOLATION. Here’s a snippet of the code from both versions:

UE version:

	try {
		if (!getAdapter())
			return false;

		adapter->set_callback_on_scan_found([this](SimpleBLE::Peripheral peripheral) {

			if (!peripheral.is_connectable())
				return;

			FString peripheralIdentifier(peripheral.identifier().c_str());
			FString peripheralMAC(peripheral.address().c_str());

			if (peripheralIdentifier.StartsWith("CAD-BLE") || peripheralIdentifier.StartsWith("SPD-BLE")) {
				CSCPeripheralsMap.Emplace(peripheralMAC, &peripheral);
			}

			});

		UE_LOG(LogTemp, Display, TEXT("Scanning for BLE peripherals."));
		
		adapter->scan_for(10000); //Scan for 10 seconds.
		//adapter->scan_start();
		//FPlatformProcess::Sleep(10);
		//adapter->scan_stop();

		UE_LOG(LogTemp, Display, TEXT("Found %d CSC BLE peripherals: "), CSCPeripheralsMap.Num()); //throws EXCEPTION_ACCESS_VIOLATION 5 seconds after adapter->scan_for(10000) statement is executed; 5 seconds short of the scan duration

		//if (CSCPeripherals.size() == 0)
		if (CSCPeripheralsMap.Num() == 0)
			return false;

		for (auto& Elm : CSCPeripheralsMap) {
...

Console version:

try {
	if (!getAdapter())
		return false;

	adapter->set_callback_on_scan_found([this](SimpleBLE::Peripheral peripheral) {

		if (!peripheral.is_connectable())
			return;

		std::string peripheralIdentifier = peripheral.identifier();
		std::string peripheralMAC = peripheral.address();

		if (peripheralIdentifier._Starts_with("CAD-BLE") || peripheralIdentifier._Starts_with("SPD-BLE")) {
			CSCPeripheralsMap.emplace(peripheral.address(), &peripheral);
		}

		});

	printf("Scanning for BLE peripherals...\n");

	adapter->scan_for(10000);
	//adapter->scan_start();
	//FPlatformProcess::Sleep(10);
	//adapter->scan_stop();

	printf("Found %d CSC BLE peripherals: \n", CSCPeripheralsMap.size());

	if (CSCPeripheralsMap.size() == 0)
		return false;

	for (auto& [key, value] : CSCPeripheralsMap) {
...

The adapter->scan_for(10000) function in the 3rd party library calls an async “start scan” function, then proceeds to std::this_thread::sleep_for() for the specified duration then finally calls the “stop scan” function. For this to work, windows runtime needs to be running in multi-threaded mode which the DLL handles internally by calling RoInitialize(RO_INIT_MULTITHREADED). I have posted in another thread where UE5 seems to be blocking any attempts at switching Windows runtime to multi-threaded mode via RoInitialize, hence I am not surprised this didn’t work.

I could probably resolve the error by

  1. making sure the TMap is thread-safe (this is a good practice which I will implement) and
  2. use delegates or mutex to ensure the scan has indeed stopped before checking the TMap.Num(),
  3. use UE’s FPlatformProcess::Sleep() and calling the start scan and stop scan functions in AsyncTasks
  4. other “hand holding”?

However, such exercise, IMO, completely negates the advantage of using a 3rd party library because it requires me to have in-depth knowledge of the 3rd party library’s inner workings and this is just the tip of the iceberg (e.g. I have not even shared tests I ran where I suspect data retrieved from the 3rd party library is getting garbage collected by UE prematurely due to the use of std::shared_ptr internally)… TBF, this library was not created for use in UE so the author(s) could not have anticipated these problems.