I'm working on porting functionality from an example Windows Forms App to a Xamarin.Forms UWP app where it should write to & read from a bluetooth device on a COM port. I have it working fine most of the time, but intermittently the UWP app will get itself into a state where any call to dataReader.LoadAsync will trigger the exception:
Exception thrown at 0x74AF1A62 (KernelBase.dll) in MyApp.UWP.exe: WinRT originate error - 0x800710DD : 'The operation identifier is not valid.'.
Exception thrown: 'System.Runtime.InteropServices.COMException' in MyApp.UWP.exe
WinRT information: The operation identifier is not valid.
Restarting the app or Visual Studio does not help, the issue persists.
The last time it happened it did not appear to impact my dataWriter writing to the device, only the subsequent read.
All of the code is in the UWP project.
private DataReader _dataReader;
private DataWriter _dataWriter;
private SerialDevice _currentSerialDevice;
private async Task ReadAsync(SerialDevice serialDevice)
{
const uint ReadBufferLength = 1024;
if (_dataReader == null)
{
_dataReader = new DataReader(_currentSerialDevice.InputStream) { InputStreamOptions = InputStreamOptions.Partial };
}
uint bytesRead = await _dataReader.LoadAsync(ReadBufferLength); // <- exception here
if (bytesRead > 0)
{
var vals = new byte[bytesRead];
_dataReader.ReadBytes(vals);
DoStuffWithBytes(vals);
}
}
The serial device is chosen from a list in the application.
// Get serial devices
DeviceInformationCollection serialDeviceCollection = await DeviceInformation.FindAllAsync(SerialDevice.GetDeviceSelector());
// Load serial device from user choosing a device from serialDeviceCollection
public async void ConnectToSerialDevice(DeviceInformation device)
{
_currentSerialDevice = await SerialDevice.FromIdAsync(device.Id);
_currentSerialDevice.BaudRate = 115200;
_currentSerialDevice.Parity = SerialParity.None;
_currentSerialDevice.DataBits = 8;
_currentSerialDevice.StopBits = SerialStopBitCount.One;
_currentSerialDevice.Handshake = SerialHandshake.RequestToSend;
}
Code for writing to the device, which works even when it gets in the odd state:
private async Task WriteToDevice(byte[] outBuffer)
{
if (_currentSerialDevice != null)
{
if (_dataWriter == null)
{
_dataWriter = new DataWriter(_currentSerialDevice.OutputStream);
}
_dataWriter.WriteBytes(outBuffer);
await _dataWriter.StoreAsync();
}
}
I've tried things like flushing the data writer, recreating the datawriter & datareaders each time, but I get the same error nonetheless and cannot read anything from the device. In normal operation I am able successfully read the bytes I'm expecting (even when there are no bytes to be read, it "reads" 0 bytes) and can output this result with no exception.
The curious thing about it all is that not only does the original Windows Forms app work fine (with the same bluetooth device) even after it gets in this state, but just opening the port and reading from the device (in the old app) actually fixes the issue in the UWP app for a time, allowing me to read from the device again.
This may be related to asynchronous methods. You can try this:
var task = await _dataReader.LoadAsync(ReadBufferLength);
task.AsTask().Wait();
uint bytesRead = task.GetResults();
For asynchronous methods (such as DataReader.LoadAsync
), events occur on the UI thread and can only be triggered once, and can only continue to be triggered after the previous asynchronous method is completed. Your question may be related to this.
In the end it turns out that the cause of the problem was the LoadAsync method hanging while waiting to fill the entire buffer (1024 bytes) despite the InputStreamOptions being set to Partial. The exception I was getting was somewhat unrelated and was to do with the asynchronous method not working properly (the method was being called again when the first task had not completed).
The fix was a combination of adding a ReadTimeout to the SerialDevice:
_currentSerialDevice.ReadTimeout = TimeSpan.FromMilliseconds(500);
and also wrapping the LoadAsync task itself in a timed cancellation token:
using (var cts = new CancellationTokenSource(500))
{
var task = _dataReader.LoadAsync(ReadBufferLength);
var readTask = task.AsTask(cts.Token);
uint bytesRead = await readTask;
}
This allowed the LoadAsync method to complete both when the device had less than 1024 bytes to consume (handled by the SerialDevice.ReadTimeout) and also when the device had 0 bytes to consume (handled by the CancellationToken).
I'm still not sure why running the win forms app fixed the issue for a time, possibly it was setting the ReadTimeout (while my UWP app was not) and this was persisting on the serial port in some way.
User contributions licensed under CC BY-SA 3.0