问题描述
我正在连接一个可以随时插入或移除的 USB 转串口.我发现我可以使用 WMI (特别是使用 WMI 代码创建器) 查询 PC 中的设备更改.
I am interfacing with a USB-to-serial port that can be inserted or removed at any time. I've found that I can use WMI (particularly with the use of WMI Code Creator) to query for device changes in the PC.
在下面生成的代码段中,订阅了 Win32_DeviceChangeEvent.但是,此事件不会显示 哪个设备(例如 USB、串行端口等)导致了该事件.有没有办法只在插入或移除串行端口时接收通知?
In the generated snippet below, the Win32_DeviceChangeEvent is subscribed to. However, this event doesn't reveal which device (e.g. USB, serial port, etc) caused the event. Is there a way to only receive notifications when serial ports are inserted or removed?
澄清一下,代码的重点不是检测串口的开启/关闭,而是检测是否有新端口已添加到计算机或之前的端口已删除.
To clarify, the point of the code is not to detect opening/closing of serial ports, it is to detect whether a new port has been added to the machine or a previous port was removed.
using System;
using System.Management;
using System.Windows.Forms;
namespace WMISample
{
public class WMIReceiveEvent
{
public WMIReceiveEvent()
{
try
{
WqlEventQuery query = new WqlEventQuery(
"SELECT * FROM Win32_DeviceChangeEvent");
ManagementEventWatcher watcher = new ManagementEventWatcher(query);
Console.WriteLine("Waiting for an event...");
watcher.EventArrived +=
new EventArrivedEventHandler(
HandleEvent);
// Start listening for events
watcher.Start();
// Do something while waiting for events
System.Threading.Thread.Sleep(10000);
// Stop listening for events
watcher.Stop();
return;
}
catch(ManagementException err)
{
MessageBox.Show("An error occurred while trying to receive an event: " + err.Message);
}
}
private void HandleEvent(object sender,
EventArrivedEventArgs e)
{
Console.WriteLine("Win32_DeviceChangeEvent event occurred.");
}
public static void Main()
{
WMIReceiveEvent receiveEvent = new WMIReceiveEvent();
return;
}
}
}
推荐答案
我最终使用 WMI 和 @Hans 的建议来检查哪些串行端口是新的/缺少的.
I ended up using WMI and @Hans' advice to check what serial ports are new/missing.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics.Contracts;
using System.IO.Ports;
using System.Management;
public static class SerialPortService
{
private static SerialPort _serialPort;
private static string[] _serialPorts;
private static ManagementEventWatcher arrival;
private static ManagementEventWatcher removal;
static SerialPortService()
{
_serialPorts = GetAvailableSerialPorts();
MonitorDeviceChanges();
}
/// <summary>
/// If this method isn't called, an InvalidComObjectException will be thrown (like below):
/// System.Runtime.InteropServices.InvalidComObjectException was unhandled
///Message=COM object that has been separated from its underlying RCW cannot be used.
///Source=mscorlib
///StackTrace:
/// at System.StubHelpers.StubHelpers.StubRegisterRCW(Object pThis, IntPtr pThread)
/// at System.Management.IWbemServices.CancelAsyncCall_(IWbemObjectSink pSink)
/// at System.Management.SinkForEventQuery.Cancel()
/// at System.Management.ManagementEventWatcher.Stop()
/// at System.Management.ManagementEventWatcher.Finalize()
///InnerException:
/// </summary>
public static void CleanUp()
{
arrival.Stop();
removal.Stop();
}
public static event EventHandler<PortsChangedArgs> PortsChanged;
private static void MonitorDeviceChanges()
{
try
{
var deviceArrivalQuery = new WqlEventQuery("SELECT * FROM Win32_DeviceChangeEvent WHERE EventType = 2");
var deviceRemovalQuery = new WqlEventQuery("SELECT * FROM Win32_DeviceChangeEvent WHERE EventType = 3");
arrival = new ManagementEventWatcher(deviceArrivalQuery);
removal = new ManagementEventWatcher(deviceRemovalQuery);
arrival.EventArrived += (o, args) => RaisePortsChangedIfNecessary(EventType.Insertion);
removal.EventArrived += (sender, eventArgs) => RaisePortsChangedIfNecessary(EventType.Removal);
// Start listening for events
arrival.Start();
removal.Start();
}
catch (ManagementException err)
{
}
}
private static void RaisePortsChangedIfNecessary(EventType eventType)
{
lock (_serialPorts)
{
var availableSerialPorts = GetAvailableSerialPorts();
if (!_serialPorts.SequenceEqual(availableSerialPorts))
{
_serialPorts = availableSerialPorts;
PortsChanged.Raise(null, new PortsChangedArgs(eventType, _serialPorts));
}
}
}
public static string[] GetAvailableSerialPorts()
{
return SerialPort.GetPortNames();
}
}
public enum EventType
{
Insertion,
Removal,
}
public class PortsChangedArgs : EventArgs
{
private readonly EventType _eventType;
private readonly string[] _serialPorts;
public PortsChangedArgs(EventType eventType, string[] serialPorts)
{
_eventType = eventType;
_serialPorts = serialPorts;
}
public string[] SerialPorts
{
get
{
return _serialPorts;
}
}
public EventType EventType
{
get
{
return _eventType;
}
}
}
MonitorDeviceChanges
方法实际上可以查看所有设备更改(如设备管理器),但检查串行端口允许我们仅在更改时引发事件.
The MonitorDeviceChanges
method actually sees all device changes (like Device Manager), but checking the serial ports allows us to only raise an event when those have changed.
要使用代码,只需订阅 PortsChanged
事件,例如SerialPortService.PortsChanged += (sender1, changedArgs) =>DoSomethingSerial(changedArgs.SerialPorts);
To use the code, simply subscribe to the PortsChanged
event, e.g. SerialPortService.PortsChanged += (sender1, changedArgs) => DoSomethingSerial(changedArgs.SerialPorts);
哦,.Raise
方法只是我在某处找到的扩展方法:
Oh, and the .Raise
method is just an extension method I picked up somewhere:
/// <summary>
/// Tell subscribers, if any, that this event has been raised.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="handler">The generic event handler</param>
/// <param name="sender">this or null, usually</param>
/// <param name="args">Whatever you want sent</param>
public static void Raise<T>(this EventHandler<T> handler, object sender, T args) where T : EventArgs
{
// Copy to temp var to be thread-safe (taken from C# 3.0 Cookbook - don't know if it's true)
EventHandler<T> copy = handler;
if (copy != null)
{
copy(sender, args);
}
}
这篇关于检测串口插入/移除的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!