trama sirve serie serial rs232 que puerto para funciona como .net wmi serial-port

.net - sirve - Detectar inserción/extracción de puerto serie



puerto serial vb6 (5)

Me estoy conectando con un puerto USB a serie que se puede insertar o quitar en cualquier momento. Descubrí que puedo usar WMI (particularmente con el uso de WMI Code Creator ) para consultar los cambios de dispositivo en la PC.

En el fragmento de Win32_DeviceChangeEvent generado a continuación, se ha suscrito el Win32_DeviceChangeEvent . Sin embargo, este evento no revela qué dispositivo (por ejemplo, USB, puerto serie, etc.) causó el evento. ¿Hay alguna forma de recibir notificaciones solo cuando se insertan o eliminan puertos serie?

Para aclarar, el punto del código no es detectar la apertura / cierre de los puertos serie, es detectar si se ha agregado un nuevo puerto a la máquina o si se eliminó un puerto anterior.

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


Aquí hay una versión DeviceChangeEvents de una clase de notificación de DeviceChangeEvents que escribí hace algún tiempo, aunque nunca la completé completamente. Lo eliminé todo, excepto el evento PortArrived, ya que es bastante feo de lo contrario.

using System; using System.ComponentModel; using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows.Forms; public sealed class PortArrivalEventArgs : EventArgs { public string Name { get; private set; } public PortArrivalEventArgs(string name) { Name = name; } } public static class DeviceChangeEvents { #region Events #region PortArrived private static object PortArrivedEvent = new Object(); public static event EventHandler<PortArrivalEventArgs> PortArrived { add { AddEvent(PortArrivedEvent, value); } remove { RemoveEvent(PortArrivedEvent, value); } } private static void FirePortArrived(IntPtr lParam) { EventHandler<PortArrivalEventArgs> handler = (EventHandler<PortArrivalEventArgs>)events[PortArrivedEvent]; if (handler != null) { string portName = Marshal.PtrToStringAuto((IntPtr)((long)lParam + 12)); handler(null, new PortArrivalEventArgs(portName)); } } #endregion #endregion #region Internal private static EventHandlerList events = new EventHandlerList(); private static MessageWindow messageWindow = null; private static void AddEvent(object key, Delegate value) { events.AddHandler(key, value); if (messageWindow == null) messageWindow = new MessageWindow(); } private static void RemoveEvent(object key, Delegate value) { events.RemoveHandler(key, value); // In the more complete version of DeviceChangedEvents, System.ComponentModel.EventHandlerList // is replaced by an identical event storage object which exposes a count of the number of // handlers installed. It also removes empty handler stubs. Both of these are required // to safely destroy the message window when the last handler is removed. //if (messageWindow != null && events.Count == 0) // messageWindow.DestroyHandle(); } #endregion private sealed class MessageWindow : NativeWindow { public MessageWindow() { CreateParams cp = new CreateParams(); cp.Caption = GetType().FullName; // NOTE that you cannot use a "message window" for this broadcast message //if (Environment.OSVersion.Platform == PlatformID.Win32NT) // cp.Parent = (IntPtr)(-3); // HWND_MESSAGE //Debug.WriteLine("Creating MessageWindow " + cp.Caption); CreateHandle(cp); } const int WM_DESTROY = 0x02; const int WM_DEVICECHANGE = 0x219; enum DBT { DEVICEARRIVAL = 0x8000, } protected override void WndProc(ref Message m) { if (m.Msg == WM_DESTROY) { messageWindow = null; } else if (m.Msg == WM_DEVICECHANGE) { DBT changeType = (DBT)m.WParam; int deviceType = m.LParam == IntPtr.Zero ? 0 : Marshal.ReadInt32(m.LParam, 4); Debug.WriteLine(String.Format("WM_DEVICECHANGE changeType = {0}, deviceType = {1}", changeType, deviceType)); switch (changeType) { case DBT.DEVICEARRIVAL: switch (deviceType) { case 3: // DBT_DEVTYP_PORT FirePortArrived(m.LParam); break; } break; } } base.WndProc(ref m); } } }


El evento de cambio de dispositivo se puede utilizar con la entidad WMI-PNP. Lo siguiente devolverá los detalles del dispositivo; en el código que aparece a continuación se muestra el nombre del dispositivo.

Dim moReturn As Management.ManagementObjectCollection Dim moSearch As Management.ManagementObjectSearcher Dim mo As Management.ManagementObject moSearch = New Management.ManagementObjectSearcher("Select * from Win32_PnPEntity") moReturn = moSearch.Get For Each mo In moReturn If CStr(mo.Properties.Item("Name").Value).Contains("Prolific") Then returns something like: "Prolific USB-to-Serial Comm Port (COM17)" txtStatus.Text &= CStr(mo.Properties.Item("Name").Value) & vbCrLf End If Next

También vea el código para acceder a otras propiedades de PNP que podrían usarse para filtrar o monitorear el cambio:

On Error Resume Next strComputer = "." Set objWMIService = GetObject("winmgmts://" & strComputer & "/root/cimv2") Set colItems = objWMIService.ExecQuery("Select * from Win32_PnPEntity",,48) For Each objItem in colItems "Availability: " & objItem.Availability "Caption: " & objItem.Caption "ClassGuid: " & objItem.ClassGuid "ConfigManagerErrorCode: " & objItem.ConfigManagerErrorCode "ConfigManagerUserConfig: " & objItem.ConfigManagerUserConfig "CreationClassName: " & objItem.CreationClassName "Description: " & objItem.Description "DeviceID: " & objItem.DeviceID "ErrorCleared: " & objItem.ErrorCleared "ErrorDescription: " & objItem.ErrorDescription "InstallDate: " & objItem.InstallDate "LastErrorCode: " & objItem.LastErrorCode "Manufacturer: " & objItem.Manufacturer "Name: " & objItem.Name "PNPDeviceID: " & objItem.PNPDeviceID "PowerManagementCapabilities: " & objItem.PowerManagementCapabilities "PowerManagementSupported: " & objItem.PowerManagementSupported "Service: " & objItem.Service "Status: " & objItem.Status "StatusInfo: " & objItem.StatusInfo "SystemCreationClassName: " & objItem.SystemCreationClassName "SystemName: " & objItem.SystemName Next


NB: Intenté publicar esto como un comentario sobre la respuesta de @Pat, pero no tengo suficiente reputación para hacerlo.

Además del comentario de @ 2pietjuh2, RaisePortsChangedIfNecessary () se puede cambiar a lo siguiente:

private static void RaisePortsChangedIfNecessary(EventType eventType) { lock (_serialPorts) { var availableSerialPorts = GetAvailableSerialPorts(); if (eventType == EventType.Insertion) { var added = availableSerialPorts.Except(_serialPorts).ToArray(); _serialPorts = availableSerialPorts; PortsChanged.Raise(null, new PortsChangedArgs(eventType, added)); } else if (eventType == EventType.Removal) { var removed = _serialPorts.Except(availableSerialPorts).ToArray(); _serialPorts = availableSerialPorts; PortsChanged.Raise(null, new PortsChangedArgs(eventType, removed)); } } }

Los eventos generados incluyen el puerto serie insertado / eliminado, en lugar de la lista de puertos serie disponibles después de la inserción / eliminación.


No. Ve y descubre qué pasó con SerialPort.GetPortNames (). Escuchar el mensaje WM_DEVICECHANGE en una ventana puede darle una mejor información.


Terminé usando el consejo de WMI y @Hans para verificar qué puertos serie son nuevos / faltan.

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

El método MonitorDeviceChanges realidad ve todos los cambios del dispositivo (como el Administrador de dispositivos), pero la verificación de los puertos serie nos permite solo generar un evento cuando estos han cambiado.

Para usar el código, simplemente suscríbase al evento PortsChanged , por ejemplo, SerialPortService.PortsChanged += (sender1, changedArgs) => DoSomethingSerial(changedArgs.SerialPorts);

Ah, y el método .Raise es solo un método de extensión que recogí en alguna parte:

/// <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); } }