Проблема с SerialPort
Я работаю с SerialPort для связи (только для чтения) с читателем штрих-кода.
Я установил драйвер для работы со считывателем, как если бы он был подключен через Com-порт, хотя это USB-устройство. Когда устройство подключено, в списке появляется еще один Com-порт.
Проблема в следующем. Я инициализирую объект SerialPort для чтения из считывателя штрих-кода, но если считыватель отключен, у меня нет возможности правильно завершить или утилизировать объект SerialPort, поскольку порт, к которому он "подключен", больше не существует.
Результатом является WinIOException, когда программа закрыта. Я не могу поймать это не только в коде, работающем с SerialPort, но и на уровне program.cs. В соответствии со стеком WinIOException генерируется после попыток завершить и утилизировать объект SerialPort.
Есть какие-нибудь идеи, как мне правильно справиться с этой ситуацией? Или хотя бы поймать исключение?
Я точно знаю, что проблема не в этом конкретном драйвере; У меня был еще один считыватель штрих-кода от другого производителя (с тем же целевым драйвером) - ситуация та же.
6 ответов
Вздох, это давняя проблема с эмуляторами последовательного порта USB. Последовательные порты - это устройства, которые датируются каменным веком. Раньше их вкручивали в шину, и их нельзя было убрать, пока программа использовала их, не создавая искр и вздымающегося дыма. Каменный век также включает в себя отсутствие какой-либо поддержки plug-and-play, чтобы программа могла обнаружить, что устройство внезапно превратилось в гонзо.
К сожалению, большинство отвратительных драйверов устройств, которые имитируют их, просто заставляют их исчезнуть, даже если программа открыла порт. Это работает примерно так же, как и извлечение флэш-накопителя из гнезда, когда Windows записывает на него файлы. Существует фоновый рабочий поток, который ожидает уведомлений от драйвера устройства, чтобы он мог генерировать события DataReceived, ErrorReceived и PinChanged. Этот поток страдает от сердечного приступа, когда устройство внезапно исчезает. Вы не можете поймать это, это поток, который был запущен классом SerialPort, вы не можете обернуть его с помощью try / catch.
По многочисленным просьбам Microsoft сделала что-то об этом в.NET 4.0. Не совсем уверен, что происходит в этом выпуске. Если вы застряли в более раннем выпуске, единственное разумное, что вы можете сделать, это прикрепить знак рядом со слотом USB: "Не удаляйте во время использования!" Что неизбежно заставляет кого-то отключать устройство как минимум дважды, чтобы увидеть, что происходит. После чего им это становится скучно и они оставляют вас в покое.
Весьма необоснованный обходной путь - файл app.exe.config с таким содержанием:
<?xml version ="1.0"?>
<configuration>
<runtime>
<legacyUnhandledExceptionPolicy enabled="1"/>
</runtime>
</configuration>
Не используйте это.
В моем коде это происходит как часть Finalize()
метод в BaseStream, который вызывается сборщиком мусора.
Таким образом, если кто-то наследует класс.NET SerialPort и переопределяет Open / Close, вы можете сделать следующее:
Во время Open просто позвоните GC.SuppressFinalize(Me.BaseStream)
Во время закрытия попробуйте позвонить GC.ReRegisterForFinalize(Me.BaseStream)
Если USB был извлечен, это вызовет исключение, стонет о доступе к BaseStream. Либо проверь .IsOpen
собственность до вызова GC
или завернуть в Try
Catch
если ты не веришь .IsOpen
возвращать Ложь каждый раз...
С этим все будет в порядке, ваше приложение будет обрабатывать тот факт, что оно было извлечено, и оно не будет аварийно завершать работу при закрытии.
В настоящее время я не могу заставить его снова открыть порт, если он снова подключен, но, по крайней мере, есть некоторый прогресс, выходящий за рамки надписи "Не трогай..."
Вы можете наследовать от SerialPort и переопределить метод Dispose() для обработки таких исключений. Вы можете просто проглотить исключение (утилизировать не следует в любом случае).
Если вы хотите зарегистрировать исключение или обработать его каким-либо другим способом, сначала вам нужно будет проверить флаг удаления. Если значение равно false, это означает, что Dispose был вызван деструктором SerialPort, и объект уже потерян.
Например
public class MySerialPort:SerialPort
{
protected override void Dispose(bool disposing)
{
try
{
base.Dispose(disposing);
}
catch (Exception exc )
{
if (disposing)
{
//Log the error
}
}
}
}
Я решил эту проблему, создав отдельный процесс, который обрабатывает последовательный порт. Когда последовательный порт отключен, я перезапускаю процесс. Теперь я могу подключиться к отключенному устройству последовательного порта без перезапуска основного приложения.
TLDR; Создайте отдельный процесс.
Пример кода
Код процесса последовательного порта
static void Main(string[] args)
{
using (var output = Console.OpenStandardOutput())
using (var serialPort = new SerialPort(args[0], int.Parse(args[1])))
{
serialPort.Open();
while (serialPort.IsOpen)
{
serialPort.BaseStream.CopyTo(output);
}
}
}
Основное применение
var options = new ProcessStartInfo()
{
FileName = "Serial Port Process name here",
UseShellExecute = false,
RedirectStandardOutput = true,
};
using(var process = Process.Start(options))
{
using (StreamReader reader = process.StandardOutput)
{
while (!process.HasExited)
{
string result = reader.ReadLine();
Console.WriteLine(result);
}
}
}
У меня был такой же опыт. Хотя это не рекомендуется, вы можете отключить и подключить последовательное устройство к фактическому последовательному порту, и связь начнется снова. Последовательный порт USB не работает таким образом. У меня также была проблема с тем, что последовательный порт USB не отображается как доступный порт методом SerialPort.GetPortNames() до тех пор, пока программное обеспечение не создаст виртуальный последовательный порт.
Ну, плохие новости.
Решение от Panagiotis Kanavos не помогает. Проблема все еще здесь.
.Net 4.0 тоже не помогает. Я установил VS2010 - ничего не изменилось. Неискаженное исключение все еще выброшено. К сожалению, "наклеить знак рядом со слотом USB: " Не удаляйте во время использования!"Кажется единственным решением...