Как захватить последовательный порт, который исчезает из-за отсоединения USB-кабеля

StackOverflow https://stackoverflow.com/questions/286184

  •  08-07-2019
  •  | 
  •  

Вопрос

У меня есть программа c # winforms, и она открывает последовательный порт.Проблема возникает, когда конечный пользователь отсоединяет usb-кабель, а затем устройство исчезает.После этого программа завершит работу и захочет сообщить об ошибке в корпорацию Майкрософт.

Есть ли способ зафиксировать это событие и корректно завершить работу?

Это было полезно?

Решение

Вы можете использовать WMI (инструментарий управления Windows) для получения уведомлений о событиях USB.Я делал именно это два года назад, отслеживая подключение и отсоединение определенного usb-устройства.
К сожалению, код остается у моего бывшего работодателя, но я нашел один пример по адресу bytes.com:

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Management;
class UsbWatcher 
{
    public static void Main() 
    {
        WMIEvent wEvent = new WMIEvent();
        ManagementEventWatcher watcher = null;
        WqlEventQuery query;
        ManagementOperationObserver observer = new ManagementOperationObserver();

        ManagementScope scope = new ManagementScope("root\\CIMV2");
        scope.Options.EnablePrivileges = true; 
        try 
        {
            query = new WqlEventQuery();
            query.EventClassName = "__InstanceCreationEvent";
            query.WithinInterval = new TimeSpan(0,0,10);

            query.Condition = @"TargetInstance ISA 'Win32_USBControllerDevice' ";
            watcher = new ManagementEventWatcher(scope, query);

            watcher.EventArrived 
                += new EventArrivedEventHandler(wEvent.UsbEventArrived);
            watcher.Start();
        }
        catch (Exception e)
        {
            //handle exception
        }
}

Я не помню, изменил ли я запрос, чтобы получать события только для определенного устройства, или я отфильтровал события с других устройств в своем обработчике событий.Для получения дополнительной информации вы, возможно, захотите ознакомиться с MSDN WMI .СЕТЕВОЙ каталог кодов.

Редактировать Я нашел еще немного информации об обработчике событий, это выглядит примерно так:

protected virtual void OnUsbConnected(object Sender, EventArrivedEventArgs Arguments)
{
    PropertyData TargetInstanceData = Arguments.NewEvent.Properties["TargetInstance"];

    if (TargetInstanceData != null)
    {
        ManagementBaseObject TargetInstanceObject = (ManagementBaseObject)TargetInstanceData.Value;
        if (TargetInstanceObject != null)
        {
            string dependent = TargetInstanceObject.Properties["Dependent"].Value.ToString();
            string deviceId = dependent.Substring(dependent.IndexOf("DeviceID=") + 10);

            // device id string taken from windows device manager
            if (deviceId = "USB\\\\VID_0403&PID_6001\\\\12345678\"")
            {
                // Device is connected
            }
        }
    }
}

Однако, возможно, вы захотите добавить некоторую обработку исключений.

Другие советы

Да, есть способ запечатлеть событие. К сожалению, может быть длительная задержка между моментом удаления устройства и моментом получения программой какого-либо уведомления.

Подход состоит в том, чтобы перехватывать события com-порта, такие как ErrorReceived, и перехватывать сообщение WM_DEVICECHANGE.

Не уверен, почему ваша программа падает; Вы должны взглянуть на стек, чтобы увидеть, где это происходит.

В реестре по адресу:
HKEY_LOCAL_MACHINE\АППАРАТНОЕ ОБЕСПЕЧЕНИЕ\КАРТА УСТРОЙСТВ\SERIALCOMM
это фактический список портов.Если ваш порт исчез, это означает, что он был отключен от сети.

Реальный пример: (Попробуйте удалить ваш USB-накопитель и нажмите клавишу F5 в редакторе реестра)

Windows Registry Editor Version 5.00
HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM]
"Winachsf0"="COM10"
"\\Device\\mxuport0"="COM1"
"\\Device\\Serial2"="COM13"

COM10 - Мой факс - модем
COM1 - Последовательный преобразователь USB - moxa usb
COM13 - Последовательный преобразователь USB -профиля

С уважением

Хотя уже приведенные ответы обеспечивают хорошую отправную точку, я хотел бы добавить несколько рабочих примеров для .net 4.5, а также пример захвата типа usb-устройства.

В ответе Треба он использовал 'Win32_USBControllerDevice' . Это может или не может быть лучшим условием для вашего запроса, в зависимости от того, что вы хотите выполнить. Идентификатор устройства из Win32_USBControllerDevice уникален для каждого устройства. Так что, если вы ищете уникальный идентификатор, который идентифицирует одно устройство, то это именно то, что вы хотите. Но если вы ищете устройство типа , вы можете использовать 'Win32_PnPEntity' и получить доступ к свойству Description . Вот пример получения определенного типа устройства по его описанию:

using System;
using System.ComponentModel.Composition;
using System.Management;

public class UsbDeviceMonitor
{
    private ManagementEventWatcher plugInWatcher;
    private ManagementEventWatcher unPlugWatcher;
    private const string MyDeviceDescription = @"My Device Description";

    ~UsbDeviceMonitor()
    {
        Dispose();
    }

    public void Dispose()
    {
        if (plugInWatcher != null)
            try
            {
                plugInWatcher.Dispose();
                plugInWatcher = null;
            }
            catch (Exception) { }

        if (unPlugWatcher == null) return;
        try
        {
            unPlugWatcher.Dispose();
            unPlugWatcher = null;
        }
        catch (Exception) { }
    }

    public void Start()
    {
        const string plugInSql = "SELECT * FROM __InstanceCreationEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_PnPEntity'";
        const string unpluggedSql = "SELECT * FROM __InstanceDeletionEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_PnPEntity'";

        var scope = new ManagementScope("root\\CIMV2") {Options = {EnablePrivileges = true}};

        var pluggedInQuery = new WqlEventQuery(plugInSql);
        plugInWatcher = new ManagementEventWatcher(scope, pluggedInQuery);
        plugInWatcher.EventArrived += HandlePluggedInEvent;
        plugInWatcher.Start();

        var unPluggedQuery = new WqlEventQuery(unpluggedSql);
        unPlugWatcher = new ManagementEventWatcher(scope, unPluggedQuery);
        unPlugWatcher.EventArrived += HandleUnPluggedEvent;
        unPlugWatcher.Start();
    }

    private void HandleUnPluggedEvent(object sender, EventArrivedEventArgs e)
    {
        var description = GetDeviceDescription(e.NewEvent);
        if (description.Equals(MyDeviceDescription))
            // Take actions here when the device is unplugged
    }

    private void HandlePluggedInEvent(object sender, EventArrivedEventArgs e)
    {
        var description = GetDeviceDescription(e.NewEvent);
        if (description.Equals(MyDeviceDescription))
            // Take actions here when the device is plugged in
    }

    private static string GetDeviceDescription(ManagementBaseObject newEvent)
    {
        var targetInstanceData = newEvent.Properties["TargetInstance"];
        var targetInstanceObject = (ManagementBaseObject) targetInstanceData.Value;
        if (targetInstanceObject == null) return "";

        var description = targetInstanceObject.Properties["Description"].Value.ToString();
        return description;
    }
}

Некоторые ссылки, которые могут быть полезны для исследования того, какие классы использовать в ваших инструкциях sql:

классы Win32 - в В приведенном выше примере использовался класс 'Win32_PnPEntity' .

Системные классы WMI - В приведенном выше примере использовались классы __ InstanceCreationEvent и __ InstanceDeletionEvent .

Вы можете попытаться обработать ErrorReceived .

private void buttonStart_Click(object sender, EventArgs e)
{
    port.ErrorReceived += new System.IO.Ports.SerialErrorReceivedEventHandler(port_ErrorReceived);
}

void port_ErrorReceived(object sender, System.IO.Ports.SerialErrorReceivedEventArgs e)
{
    // TODO: handle the problem here
}

Кроме того, вы можете проверить, существует ли порт, прежде чем продолжить. Возможно, вы захотите проверить это время от времени, возможно, непосредственно перед чтением / письмом.

string[] ports = System.IO.Ports.SerialPort.GetPortNames();
if (ports.Contains("COM7:"))
{
    // TODO: Can continue
}
else
{
    // TODO: Cannot, terminate properly
}

Вы также должны разместить блоки try-catch для всех операций с последовательным портом. Это должно помочь предотвратить неожиданные завершения.

Вы можете попытаться запустить приложение в режиме отладки в вашей среде IDE и смоделировать ошибку. Если исключением является throw, вы сможете определить, где проблема становится наиболее очевидной. Оттуда вы, вероятно, могли бы попытаться найти более конкретные решения.

Если ваше предложение try не перехватывает исключение, давайте надеяться, что Microsoft проверит дампы.

Существуют некоторые API-интерфейсы SetupDi (я думаю ... это было давно), которые позволяют вам получать уведомления о прибытии и удалении устройств, но это не поможет, если вы уже упали, потому что удаленное устройство находилось в середине Ваша операция чтения или записи.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top