質問: SerialPort.GetPortNames().ToList(); this.dispConnectedDevice.Invoke((Action)(() => { this.dispConnectedDevice.Clear(); foreach (var deviceName in this.usbDeviceList) { this.dispConnectedDevice.AppendText(deviceName + "\r\n"); } })); } } catch { } } } ``` 実際にテストしてみると、接続を解除した時に、SerialPort.GetPortNames().Count()の所でIOExceptionが発生してしまいます。(ので、try-catchしてます) MSDNには『IOException は、ファイルの読み取りまたは書き込みに失敗した場合など、I/O エラーが発生するとスローされます』と書かれていましたが、発生の原因が分かりません。 偶然かもしれませんが、決まってデバイスを抜いた時にこの例外が発生してしまいます。 原因が分かる方いらっしゃいましたら、御教授頂けますと幸いです。
回答: あなたの問題の場合、プラグアンドプレイ処理が働いてレジストリが書き換えられるタイミングと、プログラムでSerialPort.GetPortNames()を呼び出すタイミングが衝突した際に、例外が発生している可能性が考えられます。 以下にご質問とは別の現象ですが、MSDNフォーラムのQ&A記事があり、SerialPort.GetPortNames()を呼び出す処理を書いてデバッグした際に、だいたいは上手くいくけれど、ポートの変更によりレジストリが変更された場合、またはPC(質問ではラップトップ)を再起動した場合は、返される文字列が壊れて通知される、とあります。 Problem when using SerialPort.GetPortNames MicrosoftからMCC,MVPに認定された人がSerialPort.GetPortNames()はHKLM\Hardware\DeviceMap\SerialCommのレジストリデータを列挙しているだけであると言っていて、それが回答になっています。 おそらく今でも同様にSerialPort.GetPortNames()はレジストリデータを列挙しているだけなのでしょう。 あなたのプログラムでは、待ち時間も無い無限ループで常にSerialPort.GetPortNames()を呼び出しているために、問題の発生する確率が上がっているのだと思われます。 考えられる対処の選択肢は以下です。3番目が色々応用が効いてお勧めです。 1.原因を追究する sayuriさんのコメントのように、発生・通知された例外情報の各プロパティを調べるなりして、原因を追究する。 2.簡単に確率を下げる対処 a.無限ループではなく、1回でも取得に成功したら処理を終了する b.無限ループは残すが、途中に1~5秒程度の合理的と考えられる間隔でWaitかSleepを行う (必要になったら外部から直ぐに中断出来るように考えておく) 3.ポーリングではなく、プラグアンドプレイのイベント通知をトリガに処理を行う 最初の1回はSerialPort.GetPortNames()でリストを作成する 以下のキーワードに関する情報を元に、プラグアンドプレイのイベント通知を受け取れるようにして、そのタイミングで通知された情報を元にCOMポートのリストの情報を更新する。(その時点ではSerialPort.GetPortNames()は使わない) RegisterDeviceNotification()/UnregisterDeviceNotification() DEV_BROADCAST_HDR DEV_BROADCAST_PORT WM_DEVICECHANGE DBT_DEVICEARRIVAL DBT_DEVICEREMOVECOMPLETE そのものずばりでは無いし、古いですが、以下のような記事やプログラムの情報があります。 Check for device change (add/remove) events Detecting USB Drive Removal in a C# Program Hardware Helper Library for C#