Как захватить последовательный порт, который исчезает, потому что USB-кабель отключен - PullRequest
7 голосов
/ 13 ноября 2008

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

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

Ответы [ 6 ]

8 голосов
/ 13 ноября 2008

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

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

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

5 голосов
/ 12 марта 2011

Вы можете использовать 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 .NET Code Directory .

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

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
            }
        }
    }
}

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

3 голосов
/ 25 августа 2009

В реестре по адресу:
HKEY_LOCAL_MACHINE \ HARDWARE \ DEVICEMAP \ 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 - последовательный конвертер USB moxa
COM13 - USB - последовательный преобразователь Profilic

Привет

2 голосов
/ 05 февраля 2014

Хотя уже приведенные ответы обеспечивают хорошую отправную точку, я хотел бы добавить несколько рабочих примеров для .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.

1 голос
/ 11 марта 2011

Вы можете попытаться обработать 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, вы сможете определить, где проблема становится наиболее очевидной. Отсюда вы, вероятно, можете попытаться найти более конкретные решения.

0 голосов
/ 13 ноября 2008

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...