私はオープンで、イベントSerialPort
を通じてデータを受け取ります。DataReceived
SerialPort
が切断されたかどうかを検出する方法はありますか?
ErrorReceived
とイベントを試しましたPinChanged
が、運がありませんでした。
それに加えて、SerialPort.IsOpen
物理的に切断されたときにtrueを返します。
USBシリアルポートは大きな苦痛です。たとえば、この質問を参照してください。それが本当に.NET4.0で修正されたかどうかはわかりませんが、当時、私は次のようなものでプログラム全体をクラッシュさせる切断の問題に対処しようとしました。
public class SafeSerialPort : SerialPort
{
private Stream theBaseStream;
public SafeSerialPort(string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits)
: base(portName, baudRate, parity, dataBits, stopBits)
{
}
public new void Open()
{
try
{
base.Open();
theBaseStream = BaseStream;
GC.SuppressFinalize(BaseStream);
}
catch
{
}
}
public new void Dispose()
{
Dispose(true);
}
protected override void Dispose(bool disposing)
{
if (disposing && (base.Container != null))
{
base.Container.Dispose();
}
try
{
if (theBaseStream.CanRead)
{
theBaseStream.Close();
GC.ReRegisterForFinalize(theBaseStream);
}
}
catch
{
// ignore exception - bug with USB - serial adapters.
}
base.Dispose(disposing);
}
}
私がこれを採用した人にはお詫びしますが、コードにそれを書き留めなかったようです。この問題は、シリアルポートが消えた場合に.NETが基になるストリームをどのように処理したかに起因しているようです。シリアルポートが切断された後、ストリームを閉じることができなかったようです。
私が使用したもう1つの戦略は、シリアル通信部分だけを実行し、メインプログラムが接続するWCFサービスを公開する小さなプログラムを作成することでした。そうすれば、USBシリアルアダプタがフレークアウトして通信プログラムがクラッシュしたときに、メインプログラムから自動的に再起動できます。
最後に、特にUSBシリアルアダプタの場合、偶発的な切断の問題全体を回避するために、ロックUSBポートを販売した人がいない理由がわかりません。
問題は、メソッドが実行されたときにIsOpen
のみ値が戻されることです。false
Close
WM_DEVICECHANGE
メッセージをキャプチャして使用してみてください。
http://msdn.microsoft.com/en-us/library/windows/desktop/aa363480(v=vs.85).aspx
また、USB-Serialアダプターが切断されるとすぐに、処理できない例外(およびコードで問題を検出/回避できない)の問題に直面しました。MatttBurlandのソリューションが機能していることを確認できます。
簡略化されたバージョン:
class SafeSerialPort : SerialPort {
public new void Open() {
if (!base.IsOpen) {
base.Open();
GC.SuppressFinalize(this.BaseStream);
}
}
public new void Close() {
if (base.IsOpen) {
GC.ReRegisterForFinalize(this.BaseStream);
base.Close();
}
}
protected override void Dispose(bool disposing) {
try {
base.Dispose(disposing);
} catch (Exception ex) {
Debug.WriteLine(ex.Message);
}
}
}
接続しているデバイスがCDピンを使用している場合は、その変化を監視できます(フロー制御を使用しているデバイスによっては、他のピンが適用される場合があります)。そうでない場合、これを行うための決定的な方法は実際にはありません。
接続されたデバイスの予想される動作に応じて、タイムアウトまたはある種の維持を実装することをお勧めします。
まだこの問題に直面している人のために更新してください。いくつかのポイント...
SerialPort.GetPortNames()
yankですぐに更新しましたが、4.7は開いているポートのリストを更新しません。このリストの変更をチェックするバックグラウンドスレッドがあります(上記のように)。これは以前はうまく機能していましたが、現在は機能していません...SerialPort.IsOpen()
。これは、4.7で、ユーザーがデバイスをヤンクアウトするとすぐにデバイスが閉じたことを報告します。WM_DEVICECHANGE
yankですぐにメッセージが表示されますが、このアプリでは、バックグラウンドスレッドでテストする方が簡単ですSerialPort.IsOpen()
。私はこの問題を単純なバックグラウンドワーカーで解決しました。現在のすべてのポートを継続的にチェック/アレイに設定し、新しいポートが自分のports-arrayと異なる場合は、これら2つのアレイから変更されたポートを取得します。
例:
// global variable
List<string> oldPorts = new List<string>(); // contains all connected serial ports
private void bgw_checkSerialPorts_DoWork(object seder, DoWorkEventArgs e)
{
while (true)
{
string[] currentPorts = SerialPort.GetPortNames(); // get all connected serial ports
string[] diffPorts = currentPorts.Except(oldPorts.ToArray()); // get the differences between currentPorts[] and oldPorts-list
if (diffPorts.Length > 0)
{
// iterate all changed ports
for (int i = 0; i < diff.Length; i++)
{
// check if changed port was in old list
if (oldPorts.Contains(diff[i]))
{
// port in diff[] was removed
}
else
{
// port in diff[] is a new device
}
}
}
oldPorts = currentPorts.ToList(); // update oldPortlist
// check every 100ms
Thread.Sleep(100);
}
}
C#SerialPort.CDHoldingフラグを使用します。
したがって、開ループと読み取りループの前に、ブール値をlastCDHoldingフラグに保存します。
test SerialPort.CDHolding!= lastCDHoldingを読んだ後、変更をトラップするか、SerialPort.CDHoldin==falseをテストしてポートが閉じられたことを検出します。
私はRFIDデバイスでも同じ問題を抱えていました。
ダミーデータを送信することで、USBの取り外しを検出することができます。
通常、RFIDからデータを受信するだけで済みますが、(私の場合は)毎秒データを送信しても問題はありません。
USBケーブルが切断されると、serialPort.Writeは例外をスローします。
次に、ポートをすぐに閉じる必要があります。そうしないと、プログラムがハングします。
bool verify_connection()
{
try
{
if (serialport.isOpen)
{
serialport.WriteLine("Dummy");
}
}
catch (Exception ex)
{
serialport.Close();
}
return serialport.isOpen;
}
.NETCore3.1でテスト済み
私は同じ問題に直面していました。何度か試した後、を使って欲しいものを手に入れましたSerialPort.DsrHolding
。
USBケーブルが引っ張られるとすぐにfalseに戻ります。これが失敗する可能性のある他の条件を私は知りません。しかし、これは私にとってはうまくいきました。
Jebのコードを編集してこのソリューションを入手しました。私は常にそのような場合のバックグラウンドタイマーを持っています(ただし、Comportを接続するときに開始することもできます)Comportを接続するとき、bool "canDisconnect"をtrueに設定しました。これにより、作業を常に完璧にできました。
bool canDisconnect = false;
private void tmrBackgroundTick_Tick(object sender, EventArgs e)
{
if (canDisconnect == true ){
//Automatic disconnect program if Comport is removed
try
{
ComPort.WriteLine("test");
}
catch (Exception ex)
{
disconnect();
canDisconnect = false;
}
}
}