Wie eine serielle Schnittstelle erfassen, das verschwindet, weil das USB-Kabel herausgezogen wird

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

  •  08-07-2019
  •  | 
  •  

Frage

Ich habe eine c # WinForms-Programm und es öffnet sich eine serielle Schnittstelle auf. Das Problem tritt auf, wenn der Benutzer die USB-Kabel unplugs und dann verschwindet das Gerät. Danach wird das Programm zum Absturz bringen und den Fehler an Microsoft melden.

Gibt es eine Möglichkeit, dieses Ereignis zu erfassen und ordnungsgemäß heruntergefahren?

War es hilfreich?

Lösung

Sie können mit WMI (Windows Management Instrumentation) Benachrichtigung über USB-Aktionen erhalten. Ich habe genau das vor zwei Jahren, die Überwachung für Ein- und Ausstecken eines bestimmten USB-Geräts.
Leider bleibt der Code mit meinem früheren Arbeitgeber, aber ich fand ein Beispiel unter 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
        }
}

Ich erinnere mich nicht, wenn ich die Abfrage geändert Ereignisse empfängt nur für e bestimmtes Gerät oder, wenn ich herausgefiltert Ereignisse von anderen Geräten in meinem Ereignishandler. Für weitere Informationen können Sie einen Blick auf die MSDN WMI .NET-Code-Verzeichnis haben wollen .

Bearbeiten Ich fand einige weitere Informationen über den Event-Handler, es ungefähr wie folgt aussieht:

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

Sie können einige Ausnahmebehandlung hinzuzufügen, though.

Andere Tipps

Ja, es ist ein Weg, um das Ereignis zu erfassen. Leider kann es eine lange Verzögerung zwischen der Zeit, das Gerät entfernt wird und die Zeit, das Programm jede Benachrichtigung erhält.

Der Ansatz ist zu stoppen COM-Port-Ereignisse wie ErrorReceived und die WM_DEVICECHANGE Nachricht zu fangen.

Nicht sicher, warum Ihr Programm abstürzt; Sie sollten einen Blick auf den Stapel nehmen, um zu sehen, wo dies geschieht.

In Registrierung auf:
HKEY_LOCAL_MACHINE \ HARDWARE \ DEVICEMAP \ SERIALCOMM
Ist ist eine Liste der Ports. Wenn Ihr Port verschwand es bedeutet, es war nicht angeschlossen.

Reales Beispiel: (Versuchen Sie Ihr USB und drücken Sie F5 in Registrierungs-Editor zu entfernen)

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

COM10 - My Faxmodem
COM1 - USB - Moxa usb Seriell-Konverter
COM13 - USB - Profilic Seriell-Konverter

Viele Grüße

Obwohl bereits die Antworten einen guten Ausgangspunkt bieten gegeben, würde Ich mag einige Arbeitsbeispiele für .net 4.5 hinzuzufügen und auch ein Beispiel für die Erfassung einen type von USB-Gerät.

In Treb Antwort nutzte er die 'Win32_USBControllerDevice'. Dies kann oder möglicherweise nicht die beste Voraussetzung für Ihre Anfrage, je nachdem, was Sie erreichen möchten. Die Geräte-ID aus dem Win32_USBControllerDevice ist an jedes Gerät einzigartig. Also, wenn Sie sich für eine eindeutige ID suchen, der ein einzelnes Gerät identifiziert, das ist dann genau das, was Sie wollen. Aber wenn Sie sich für eine bestimmte Suche type von Gerät, Sie 'Win32_PnPEntity' verwenden könnte und die Description Eigenschaft zugreifen. Hier ist ein Beispiel, ich eine bestimmte Art von Gerät durch seine Beschreibung:

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

Einige Links, die für die Erforschung von Nutzen sein könnten, welche Klassen in SQL-Anweisungen verwenden:

Win32-Klassen - In das obige Beispiel wurde die 'Win32_PnPEntity'-Klasse verwendet.

WMI System Klassen - in dem obigen Beispiel wurden die __InstanceCreationEvent und __InstanceDeletionEvent Klassen verwendet.

Sie könnten versuchen, ErrorReceived zu behandeln.

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
}

Darüber hinaus könnten Sie prüfen, ob der Port, bevor Sie fortfahren existiert. Vielleicht möchten Sie es hin und wieder überprüfen, vielleicht kurz vor dem Lesen / Schreiben.

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

Sie sollten auch try-catch Blöcke für alle seriellen Port-Operationen statt. Es soll dazu beitragen, unerwartete Kündigungen zu vermeiden.

Sie sollten versuchen, die App im Debug-Modus unter dem IDE auszuführen und den Fehler zu simulieren. Wenn eine Ausnahme werfen, wäre Sie in der Lage sein zu erkennen, wo das Problem am deutlichsten wird. Von dort könnten Sie wahrscheinlich versuchen, mehr spezifische Lösungen zu finden.

Wenn Sie Ihre try-Anweisung ist nicht die Ausnahme fangen dann hoffen wir, dass Microsoft die Deponien inspizieren wird.

Es gibt einige SetupDi APIs (glaube ich ... es ist schon eine Weile) durch die Sie beraten Geräte Ankünften und Umzüge werden, aber es wird nicht helfen, wenn Sie bereits abgestürzt, weil das entfernte Gerät in der Mitte war von Ihr Lese- oder Schreibvorgang.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top