ObjectDisposedException: безопасный дескриптор был закрыт
Так что это довольно маленький вопрос с большим объяснением. Как видно из названия, я получаю необработанное исключение, сообщающее, что моя безопасная ручка закрыта. Вероятно, мне придется несколько раз отредактировать этот пост, добавляя все больше и больше кода, чтобы помочь мне диагностировать проблему.
Я использую POS для.NET, чтобы создать объект службы для моего устройства RFID и MSR. Хотя мои устройства одинаковы, у меня есть 2 разных чипа Virtual COM Port, которые обмениваются данными с этими устройствами. Один от Silicon Labs, другой от FTDI. Я хотел использовать функции Plug and Play с POS для.NET, поэтому я дал ему оба идентификатора оборудования. Поскольку это подключи и играй, у меня есть полный доступный аппаратный путь, который я могу затем создать SafeFileHandle, используя вызов PInvoke и используя этот SafeFileHandle, я создаю FileStream. Чип FTDI не позволяет мне напрямую общаться с устройствами, поэтому я должен получить понятное имя устройства, затем использовать mutex, чтобы вытащить COM-порт, а затем создать экземпляр SerialPort. Этот шаг работает отлично и отлично. Как к сведению, я пытался использовать дружественное имя обоих чипов, чтобы получить COM-порт, а один из кремниевых лабораторий (по какой-то странной причине) не отображается в списке с помощью SetupAPI.GetDeviceDetails с использованием GUID порта. Я не уверен в этом, так как в диспетчере устройств Guid класса устройств в Silicon labs является GUID порта.
Так как SerialPort и FileStream имеют объект Stream, я решил использовать его для чтения и записи на этот порт. Проблема в том, что если я посылаю команду RFID на устройство MSR, устройство MSR ничего не отвечает. Так что, если я использую этот код int fromReader = ReaderStream.ReadByte();
моя тема заблокирована Это блокирующий вызов, и для его завершения требуется минимум 1 байт. Итак, я оглянулся и оказалось, что единственное решение - использовать отдельный поток и установить тайм-аут. Если время ожидания истекло, прервите поток.
Thread t = new Thread(new ThreadStart(ReadFromStream));
t.Start();
if (!t.Join(timeout))
{
t.Abort();
}
(t.Abort был окружен попыткой / уловом безрезультатно, так как это не решило проблему, я удалил это)
ReadFromStream - это абстрактный метод в RFID-устройстве. Вот одна из реализаций
protected override void ReadFromStream()
{
var commandLength = USN3170Constants.MIN_RESPONSE_LENGTH;
var response = new System.Collections.Generic.List<byte>(USN3170Constants.MIN_RESPONSE_LENGTH);
for (int i = 0; i <= commandLength; i++)
{
int fromReader = ReaderStream.ReadByte();
if (fromReader == -1) break; //at end of stream
response.Add((byte)fromReader);
if (response.Count > USN3170Constants.DATA_LENGTH_INDEX && response[USN3170Constants.DATA_LENGTH_INDEX] > 0)
{
commandLength = response[USN3170Constants.DATA_LENGTH_INDEX] + 3;
}
}
streamBuffer = response.ToArray();
}
(int fromReader = ReaderStream.ReadByte();
был окружен попыткой / ловлей. Единственное, что он поймал, это исключение прерванного потока, поэтому я его убрал)
Приведенный выше код, где я подозреваю, что проблема лежит. Странно то, что у меня есть модульное тестирование, которое, как мне кажется, довольно хорошо имитирует Microsoft Test App.
(FYI QUADPORT - это чипсет FTDI)
PosExplorer posExplorer;
DeviceCollection smartCardRWs;
[Test]
public void TestQuadPortOpen()
{
posExplorer = new PosExplorer();
smartCardRWs = posExplorer.GetDevices(DeviceType.SmartCardRW, DeviceCompatibilities.CompatibilityLevel1);
//if using quadport one item is the MSR and the other is the RFID
//because of that one of them will fail. Currently the first Device in the collection is the the RFID, and the second is MSR
Assert.GreaterOrEqual(smartCardRWs.Count, 2);
//Hardware Id: QUADPORT\QUAD_SERIAL_INTERFACE
foreach(DeviceInfo item in smartCardRWs)
{
Assert.AreEqual("QUADPORT\\QUAD_SERIAL_INTERFACE", item.HardwareId);
}
SmartCardRW rfidDevice = (SmartCardRW)posExplorer.CreateInstance(smartCardRWs[0]);
SmartCardRW msrDevice = (SmartCardRW)posExplorer.CreateInstance(smartCardRWs[1]);
rfidDevice.Open();
Assert.AreNotEqual(ControlState.Closed, rfidDevice.State);
rfidDevice.Close();
try
{
msrDevice.Open();
Assert.Fail("MSR Device is not a RFID Device");
}
catch
{
Assert.AreEqual(ControlState.Closed, msrDevice.State);
}
rfidDevice = null;
msrDevice = null;
}
Когда я запускаю этот тест, я не получаю исключение SafeFileHandle. На самом деле тест проходит.
Так что я не знаю, как отследить эту ошибку. Поскольку я буду использовать этот объект службы в другой программе, которую я также создаю, я, вероятно, в конечном итоге буду использовать этот код из этого теста в этой программе. Тем не менее, я чувствую, что приложение Microsoft Test более или менее является "золотым стандартом". Это действительно... вероятно, нет. Но это работает хорошо для моих целей, поэтому я чувствую, что это проблема с моим кодом, а не с их.
Любые уловки о том, как я могу сузить это? К вашему сведению, я пытался использовать отладчик, но при обходе открытого кода ошибка не возникает. Я также прошел таймер состояния обновления, и он также не выдает ошибку. Как только я нажму "продолжить", я получу исключение. Я перешел к Just My Code и Loaded Symbols, и он говорит мне: "Информация об источнике отсутствует в информации об отладке для этого модуля"
4 ответа
Эта проблема (и в частности ссылка на экземпляр SerialPort) подозрительно напоминает проблему, задокументированную по адресу http://connect.microsoft.com/VisualStudio/feedback/details/140018/serialport-crashes-after-disconnect-of-usb-com-port.
Насколько я понимаю, в случае непостоянного SerialPort (например, связанного с устройством USB), когда порт неожиданно "уходит", базовый поток, связанный с ним, удаляется. Если в данный момент на порту активна операция чтения или записи, то последующий вызов SerialPort.Close может привести к упомянутому вами исключению, однако это происходит в коде Microsoft, работающем в другом потоке, и не может быть перехвачено внутри вашего код. (Он по-прежнему будет виден любому обработчику исключений "последнего шанса", который вы связали с событием UnhandledException в AppDomain.)
Кажется, в связанном документе есть два основных стиля обхода. В обоих случаях после открытия порта вы сохраняете ссылку на экземпляр BaseStream для открытого порта. Один обходной путь затем подавляет сборку мусора в этом базовом потоке. Другой явно вызывает Close в базовом потоке, фиксируя любые исключения, возникшие во время этой операции, перед вызовом Close в SerialPort.
РЕДАКТИРОВАТЬ: Для чего стоит, в.NET Framework V4.5, кажется, что ни один из документированных обходных путей на сайте Microsoft Connect полностью не решает проблему, хотя они могут снизить частоту, с которой это происходит.:-(
Я хотел бы опубликовать мой случай, в котором у меня была похожая проблема при попытке чтения с последовательного порта (виртуальный com, управляемый Moxa RS232 в Ethernet). Поскольку у меня не было возможности перехватить исключение ObjectDisposedException, единственным решением было увеличение свойства ReadTimeout, которое изначально было установлено равным -1 (непрерывное чтение). Установка ReadTimeout в 100 миллис решил эту проблему в моем случае.
РЕДАКТИРОВАТЬ
Это не окончательное решение: может случиться так, что если вы закроете приложение во время попытки чтения, вы можете получить то же самое неуловимое исключение. Мое окончательное решение - убить процесс приложения непосредственно в событии FormClosing:
private void MyForm_FormClosing(object sender, FormClosingEventArgs e)
{
Process p = Process.GetCurrentProcess();
p.Kill();
}
У меня была та же ошибка, когда я использовал поток для чтения из SerialPort. Вызов прерывания в потоке иногда вызывал неуловимое исключение ObjectDisposedException. После часов отладки и внимательного прочтения этого:
https://blogs.msdn.microsoft.com/bclteam/2006/10/10/top-5-serialport-tips-kim-hamilton/
Я понял, что проблема заключается только в следующем:NET 2.0 (и выше) не позволяет вам сойтись с некоторыми вещами, такими как попытка отменить чтение SerialPort, прерывая поток, обращающийся к SerialPort.
Поэтому, прежде чем вызывать Thread.Interrupt(), вы должны закрыть COM... Это вызовет перехватываемое исключение в операции ReadByte.
Или вы можете использовать свойство ReadTimeout на SerialPort, чтобы избежать использования потока просто для того, чтобы иметь время ожидания.
Пожалуйста, посмотрите на это:
https://github.com/jcurl/SerialPortStream
Я заменил System.IO.Ports на RJPC.IO.Ports, исправил пару параметров различий в инициализации, и все проблемы исчезли с этой проблемой.