كيفية التقاط منفذ تسلسلي يختفي بسبب فصل كابل USB

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

  •  08-07-2019
  •  | 
  •  

سؤال

لدي برنامج c# winforms وهو يفتح منفذ تسلسلي.تحدث المشكلة عندما يقوم المستخدم النهائي بفصل كابل USB ثم يختفي الجهاز.بعد ذلك سوف يتعطل البرنامج ويريد إبلاغ Microsoft بالخطأ.

هل هناك طريقة لالتقاط هذا الحدث وإغلاقه بأمان؟

هل كانت مفيدة؟

المحلول

يمكنك استخدام WMI (Windows Management Instrumentation) لتلقي إشعارات حول أحداث 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.

يحررلقد وجدت المزيد من المعلومات حول معالج الأحداث، ويبدو تقريبًا كما يلي:

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

قد ترغب في إضافة بعض معالجة الاستثناءات.

نصائح أخرى

نعم، هناك وسيلة لالتقاط الحدث. لسوء الحظ، يمكن أن يكون هناك تأخير لمدة طويلة بين الوقت يتم إزالة الجهاز والوقت يتلقى البرنامج أي إشعار.

وهذا النهج هو أن الأحداث ميناء فخ كوم مثل ErrorReceived وللقبض على رسالة WM_DEVICECHANGE.

ولا يدري لماذا البرنامج وتحطمها. يجب أن نلقي نظرة على كومة لنرى أين يحدث هذا.

في التسجيل في:
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 - محول تسلسلي Moxa USB
كوم13 - USB - محول تسلسلي احترافي

يعتبر

وعلى الرغم من أن الإجابات بالفعل توفر نقطة انطلاق جيدة، وأنا أود أن أضيف بعض الأمثلة عمل لصافي 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 ومحاكاة الخطأ. إذا كان الاستثناء هو رمي، هل سيكون قادرا على تحديد حيث تصبح المشكلة أكثر وضوحا. من هناك، هل يمكن ربما في محاولة لإيجاد حلول أكثر تحديدا.

إذا البيان محاولة الخاص بك لا اصطياد استثناء ثم دعونا نأمل مايكروسوفت سوف تفقد مقالب.

وهناك بعض واجهات برمجة التطبيقات SetupDi (على ما أظن ... انها كانت فترة من الوقت) التي تسمح لك لأخذ العلم الوافدين الجهاز وإزالتها، لكنها لن تساعد إذا تحطمت بالفعل لأن الجهاز إزالتها كان في منتصف لديك القراءة أو الكتابة العملية.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top