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.
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; } } }
To test if the computer COM port is functioning correctly, you can do a simple loopback test. (In a loopback test, a signal is sent from a device and returned, or looped back, to the device.) For this test, connect a serial cable to the COM port that you want to test. Then short pin 2 and pin 3 of the cable together.
You can check what device is using what COM port from the Device Manager. It will be listed under the hidden devices. From the Device Manager, select View - Show Hidden Devices. Now when you expand the (PORTS) COM ports section you will see all of the COM ports listed there.
Find Port Number on LinuxOpen terminal and type: ls /dev/tty* . Note the port number listed for /dev/ttyUSB* or /dev/ttyACM* . The port number is represented with * here.
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; } } }
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.
To use the code, simply subscribe to the PortsChanged
event, e.g. SerialPortService.PortsChanged += (sender1, changedArgs) => DoSomethingSerial(changedArgs.SerialPorts);
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); } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With