USB 케이블이 플러그를 뽑아서 사라지는 직렬 포트를 캡처하는 방법

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

  •  08-07-2019
  •  | 
  •  

문제

AC# Winforms 프로그램이 있으며 직렬 포트를 열어줍니다. 최종 사용자가 USB 케이블을 풀고 장치가 사라지면 문제가 발생합니다. 그 후 프로그램이 충돌하여 오류를 Microsoft에보고하고 싶습니다.

이 이벤트를 포착하고 우아하게 종료하는 방법이 있습니까?

도움이 되었습니까?

해결책

WMI (Windows Management Instrumentation)를 사용하여 USB 이벤트에 대한 알림을받을 수 있습니다. 2 년 전 특정 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
        }
}

E 특정 장치에 대해서만 이벤트를 수신하도록 쿼리를 수정했는지 또는 이벤트 핸들러의 다른 장치에서 이벤트를 걸러 냈는지 기억이 나지 않습니다. 자세한 내용은 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와 같은 COM 포트 이벤트를 트랩하고 WM_DEVICECHANGE 메시지를 포착하는 것입니다.

프로그램이 왜 충돌하는지 잘 모르겠습니다. 스택을 살펴보고 이것이 어디에서 일어나고 있는지 확인해야합니다.

레지스트리에서 :
hkey_local_machine Hardware devicemap serialcomm
실제 포트 목록입니다. 포트가 사라지면 플러그를 뽑았다는 것을 의미합니다.

Real example: (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 장치의.

Treb의 대답에서 그는 사용했습니다 'Win32_USBControllerDevice'. 이는 달성하려는 내용에 따라 쿼리에 가장 적합한 조건이 될 수도 있고 아닐 수도 있습니다. Win32_usbControllerDevice의 장치 ID는 각 장치마다 고유합니다. 따라서 단일 장치를 식별하는 고유 한 ID를 찾고 있다면 바로 원하는 것입니다. 그러나 당신이 확실한 것을 찾고 있다면 유형 장치의 경우 사용할 수 있습니다 '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에서 디버그 모드에서 앱을 실행하고 오류를 시뮬레이션하려고 할 수 있습니다. 예외가 발생하면 문제가 가장 분명 해지는 위치를 식별 할 수 있습니다. 거기에서 아마도 더 구체적인 솔루션을 찾으려고 노력할 수 있습니다.

시도 명세서가 예외를 포착하지 않으면 Microsoft가 덤프를 검사하기를 바랍니다.

장치 도착 및 제거에 대한 조언을받을 수있는 Setupdi API가 있습니다 (시간이 오래 걸렸습니다). 그러나 제거 된 장치가 읽기 또는 읽기 중간에 있었기 때문에 이미 충돌 한 경우에는 도움이되지 않습니다. 작전을 작성하십시오.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top