我有一个c#winforms程序,它打开了一个串口。当最终用户拔下USB电缆然后设备消失时,就会出现问题。在此之后,程序将崩溃,并希望将错误报告给microsoft。
有没有办法捕获此事件并优雅地关闭?
答案 0 :(得分:8)
是的,有一种方法可以捕获事件。不幸的是,在删除设备和程序收到任何通知的时间之间可能会有很长的延迟。
该方法是捕获诸如ErrorReceived之类的com端口事件并捕获WM_DEVICECHANGE消息。
不确定程序崩溃的原因;你应该看看堆栈,看看发生了什么。
答案 1 :(得分:5)
您可以使用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 Code Directory。
修改强> 我在事件处理程序上找到了更多信息,它看起来大致如下:
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
}
}
}
}
但是,您可能希望添加一些异常处理。
答案 2 :(得分:3)
在注册表中:
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串行转换器
COM13 - USB - Profilic串行转换器
此致
答案 3 :(得分:2)
虽然已经给出的答案提供了一个很好的起点,但我想为.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 Classes - 在上面的示例中,使用了'Win32_PnPEntity'
类。
WMI System Classes - 在上面的示例中,使用了__InstanceCreationEvent
和__InstanceDeletionEvent
类。
答案 4 :(得分:1)
您可以尝试处理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下以调试模式运行应用程序并模拟错误。如果抛出异常,您将能够确定问题变得最明显的位置。从那里,您可能会尝试找到更具体的解决方案。
答案 5 :(得分:0)
如果你的try语句没有捕获异常,那么我们希望微软将检查转储。
有一些SetupDi API(我认为......已经有一段时间了)允许您被告知设备到达和删除,但如果您已经崩溃,因为删除的设备位于中间,它将无济于事你的读写操作。