Como capturar uma porta serial que desaparece porque o cabo USB é desconectado
-
08-07-2019 - |
Pergunta
Eu tenho um programa C # winforms e abre uma porta serial. O problema acontece quando o usuário final desconecta o cabo USB e, em seguida, o dispositivo desaparece. Depois disso, o programa irá falhar e quer denunciar o erro à Microsoft.
Existe uma maneira de capturar este evento e desligar normalmente?
Solução
Você pode usar o WMI (Windows Management Instrumentation) para receber a notificação sobre eventos USB.
Eu fiz exatamente isso há dois anos, o monitoramento para ligar e desligar de um dispositivo USB específico.
Infelizmente, as estadias de código com o meu antigo empregador, mas eu encontrei um exemplo em 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
}
}
Eu não me lembro se eu modifiquei a consulta para receber apenas os eventos para o dispositivo e específico, ou se eu filtrados eventos de outros dispositivos no meu manipulador de eventos. Para mais informações você pode querer ter um olhar para o href="http://msdn.microsoft.com/en-us/library/ms257338.aspx" rel="nofollow"> MSDN Repertório .NET WMI .
Editar Eu encontrei mais algumas informações sobre o manipulador de eventos, parece mais ou menos assim:
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
}
}
}
}
Você pode querer adicionar um pouco de tratamento de exceções, no entanto.
Outras dicas
Sim, há uma maneira de capturar o evento. Infelizmente, pode haver um longo atraso entre o tempo que o dispositivo é removido eo tempo que o programa recebe qualquer notificação.
A abordagem é para eventos de porta de COM como ErrorReceived e para pegar a mensagem WM_DEVICECHANGE.
Não sei por que seu programa está falhando; você deve dar uma olhada na pilha para ver onde isso está acontecendo.
No registro em:
HKEY_LOCAL_MACHINE \ HARDWARE \ DEVICEMAP \ SERIALCOMM
é verdadeira lista de portas. Se a sua porta desapareceu isso significa que ele foi desconectado.
Exemplo real: (Tente remover o seu USB e pressione F5 no editor de registro)
Windows Registry Editor Version 5.00
HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM]
"Winachsf0"="COM10"
"\\Device\\mxuport0"="COM1"
"\\Device\\Serial2"="COM13"
COM10 - Meu fax modem
COM1 - USB - moxa conversor USB série
COM13 - USB - Profilic série conversor
Saudações
Embora as respostas já dadas proporcionar um bom ponto de partida, gostaria de acrescentar alguns exemplos de trabalho para .NET 4.5 e também um exemplo de captura de um tipo do dispositivo USB.
A resposta de De Treb, ele usou a 'Win32_USBControllerDevice'
. Isto pode ou não pode ser a melhor condição para a sua consulta, dependendo do que você quer realizar. O ID do dispositivo a partir do Win32_USBControllerDevice é exclusivo para cada dispositivo. Então, se você está procurando uma identificação única que identifica um único dispositivo, então isso é exatamente o que você quer. Mas se você estiver procurando por um determinado tipo do dispositivo, você poderia usar 'Win32_PnPEntity'
e acesso a propriedade Description
. Aqui está um exemplo de obter um certo tipo do dispositivo por sua descrição:
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;
}
}
Alguns links que podem ser úteis para pesquisar quais as classes para usar em suas instruções SQL:
Win32 Classes - Em o exemplo acima, foi usado o classe 'Win32_PnPEntity'
.
sistema WMI - no exemplo acima, foram usadas as classes __InstanceCreationEvent
e __InstanceDeletionEvent
.
Você poderia tentar ErrorReceived
alça.
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
}
Além disso, você pode verificar se a porta existe antes de prosseguir. Você pode querer verificá-lo de vez em quando, talvez um pouco antes de leitura / escrita.
string[] ports = System.IO.Ports.SerialPort.GetPortNames();
if (ports.Contains("COM7:"))
{
// TODO: Can continue
}
else
{
// TODO: Cannot, terminate properly
}
Você também deve colocar blocos try-catch
para todas as suas operações de porta serial. Deve ajudar a prevenir desligamentos inesperados.
Você pode querer tentar executar o aplicativo no modo de depuração sob o seu IDE e simular o erro. Se uma exceção é jogar, você seria capaz de identificar onde o problema se torna mais evidente. De lá, você provavelmente poderia tentar encontrar soluções mais específicas.
Se o seu try não está pegando a exceção, então vamos esperar que a Microsoft vai inspecionar as lixeiras.
Existem algumas APIs SetupDi (acho ... Tem sido um tempo) que permitem que você seja avisado da chegada de dispositivo e remoções, mas não vai ajudar se você já caiu porque o dispositivo removido estava no meio de sua operação de leitura ou escrita.