ac# winforms プログラムがあり、シリアル ポートを開きます。この問題は、エンド ユーザーが USB ケーブルを抜いてデバイスが消えると発生します。この後、プログラムはクラッシュし、Microsoft にエラーを報告します。
このイベントをキャプチャして正常にシャットダウンする方法はありますか?
ac# winforms プログラムがあり、シリアル ポートを開きます。この問題は、エンド ユーザーが USB ケーブルを抜いてデバイスが消えると発生します。この後、プログラムはクラッシュし、Microsoft にエラーを報告します。
このイベントをキャプチャして正常にシャットダウンする方法はありますか?
はい、イベントをキャプチャする方法があります。残念ながら、デバイスが取り外されてからプログラムが通知を受信するまでに長い遅延が発生する可能性があります。
このアプローチは、ErrorReceivedなどのCOMポートイベントをトラップし、WM_DEVICECHANGEメッセージをキャッチすることです。
プログラムがクラッシュする理由がわかりません。スタックを調べて、これがどこで発生しているかを確認する必要があります。
WMI(Windows Management Instrumentation)を使用して、USBイベントに関する通知を受信できます。私はまさにそれを2年前に行い、特定のUSBデバイスのプラグの抜き差しを監視しました。
残念ながら、コードは以前の雇用主に残っていますが、bytes.comで1つの例を見つけました:
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
}
}
特定のデバイスのイベントのみを受信するようにクエリを変更したのか、イベントハンドラで他のデバイスからのイベントを除外したのかを覚えていません。詳細については、MSDNWMI.NETコードディレクトリを参照してください。
編集 私はイベントハンドラーに関するいくつかのより多くの情報を見つけました、それは大まかに次のように見えます:
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
}
}
}
}
ただし、例外処理を追加することもできます。
レジストリ:
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 シリアル コンバーター
よろしく
すでに与えられた回答は良い出発点を提供しますが、.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 クラス- 上記の例では、'Win32_PnPEntity'
クラスが使用されました。
WMI システム クラス- 上記の例では、 クラス__InstanceCreationEvent
と__InstanceDeletionEvent
クラスが使用されています。
を処理しようとすることができ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 でアプリをデバッグ モードで実行して、エラーをシミュレートすることをお勧めします。例外がスローされた場合、問題が最も顕著になる場所を特定できます。そこから、おそらくより具体的な解決策を見つけようとすることができます。
try ステートメントが例外をキャッチしていない場合は、Microsoft がダンプを検査することを期待しましょう。
デバイスの到着と削除の通知を受け取ることができる SetupDi API がいくつかありますが (かなり前のことだと思います)、削除されたデバイスが読み取りの途中だったために既にクラッシュした場合や書き込み操作。