

お世話になります。

C#にて、PCに接続されているデバイスの数を調べる為に

開いているCOMポートを表示するアプリケーションを作っています。

新たな接続や、解除を検知して表示を更新する為に、以下の様な方法でポーリングを行っています。

[code]

        public void startPoring()
        {
            while(true){
                try
                {
                    if(this.usbDeviceList.Count!=SerialPort.GetPortNames().Count();)
                    {
                        this.usbDeviceList = SerialPort.GetPortNames().ToList();
                        this.dispConnectedDevice.Invoke((Action)(() =>
                        {
                            this.dispConnectedDevice.Clear();
                            foreach (var deviceName in this.usbDeviceList)
                            {
                                this.dispConnectedDevice.AppendText(deviceName + "\r\n");
                            }
                        }));
                    }
                }
                catch
                {
    
                }
            }
    
        }
    
[/code]

実際にテストしてみると、接続を解除した時に、SerialPort.GetPortNames().Count()の所でIOExceptionが発生してしまいます。（ので、try-
catchしてます）

MSDNには『IOException は、ファイルの読み取りまたは書き込みに失敗した場合など、I/O
エラーが発生するとスローされます』と書かれていましたが、発生の原因が分かりません。

偶然かもしれませんが、決まってデバイスを抜いた時にこの例外が発生してしまいます。

原因が分かる方いらっしゃいましたら、御教授頂けますと幸いです。


CC BY-SA 4.0
2018-06-08T02:46:23.663
44608
2018-06-08T14:42:05.990
28828
question
c# windows .net usb
C# IOExceptionの発生理由について


あなたの問題の場合、プラグアンドプレイ処理が働いてレジストリが書き換えられるタイミングと、プログラムでSerialPort.GetPortNames()を呼び出すタイミングが衝突した際に、例外が発生している可能性が考えられます。  

以下にご質問とは別の現象ですが、MSDNフォーラムのQ&A記事があり、SerialPort.GetPortNames()を呼び出す処理を書いてデバッグした際に、だいたいは上手くいくけれど、ポートの変更によりレジストリが変更された場合、またはPC(質問ではラップトップ)を再起動した場合は、返される文字列が壊れて通知される、とあります。  
[Problem when using
SerialPort.GetPortNames](https://social.msdn.microsoft.com/Forums/en-
US/440c79b8-2f55-4d1b-a4e8-df931f3d7fa9/problem-when-using-
serialportgetportnames?forum=Vsexpressinstall)  

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](https://stackoverflow.com/questions/16245706/check-for-device-change-
add-remove-events)  
[Detecting USB Drive Removal in a C#
Program](https://www.codeproject.com/Articles/18062/Detecting-USB-Drive-
Removal-in-a-C-Program)  
[Hardware Helper Library for
C#](https://www.codeproject.com/Articles/21503/Hardware-Helper-Library-for-C)


CC BY-SA 4.0
2018-06-08T14:42:05.990
44633
2018-06-08T14:42:05.990
26370
44608
answer