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, исправил пару параметров различий в инициализации, и все проблемы исчезли с этой проблемой.

Другие вопросы по тегам