none
USB COMで通信中にCOMを抜いた時の情報収集方法 RRS feed

  • 質問

  • C++ VS2010で開発してます。
    USB COMで通信中に、USBのCOMを人為的に抜いた時に、
    クローズのタイミングが悪く正しくクローズできません。

    再度USB COMを刺した時に再オープンを失敗してします。
    プログラムのみでCOMの再オープンをできるようにしたいと思ってます。

    作成中のソフトでは、COMポートは複数接続してオープン
    しているため、そのひとつが抜かれた場合、抜かれたCOM
    ポートのみをクローズしたいと思っています。

    ですので、抜かれたCOMのポート番号が知りたいと思ってます。

    そこで、
    USB COMが抜いた時に、ウインドウズのイベント(WN_DEVICECHANGE)
    を取得し、どのCOMが抜けたかという情報を収集しようと試みました。

    ですが、
    通信中にUSB COMが抜かれた時、イベントWN_DEVICECHANGEで、
    DBT_DEVICEARRIVAL/DBT_DEVICEREMOVECOMPLETEのメッセージを
    期待していたのですが、「DBT_DEVNODES_CHANGED」が返ってきました。

    メッセージのパラメータでDEV_BROADCAST_HDR*型の情報からCOMポートが何
    番かを知る手がかりにしたかったのですが、DBT_DEVNODES_CHANGEDではこの
    値が使えません。

    WN_DEVICECHANGEのイベントから、何番のCOMポートが抜けたかがわかる方法

    はないでしょうか? もしくは、他のイベントやAPIがあればご教授ください。
    (APIは標準ユーザで動作するもののみソフトで使えるものみ)

    よろしくお願いいたします。

    2015年5月25日 4:31

回答

  • お馬鹿さま

    >CancelIo function
    > CancelIoEx function

    試して見ましたが、どうもIOはキャンセルできずに、現象の改善はできませんでした。

    OSとドライバ間で初期化できていないんじゃないかと疑ってます。

    少し現象は違うのですが、下記のようなものがありました。
    "Device does not exist" error after you reinsert a USB COM port device
    <https://support.microsoft.com/en-us/kb/2990372>

    何の解決にもならないのですが、うまくいく動作としては、
    ①USBを抜く。
    ②USBを抜いたので送受信ができずに通信処理はタイムアウトになり、クローズ処理を行う。
    ③その後5秒待って、USBを刺すとオープンはでき再接続に成功します。
    (クローズした後、一定の時間を置いて、USBを刺すと再接続できます。)
    がありました。
    これを突破口にしたかったのですが、結局の所、抜本対策を見出せなかったので、応急対策として、
    デバイスマネージャでデバイスの「無効->有効」を行う管理者権限アプリを作成し、
    標準ユーザ権限のアプリから管理者権限アプリを呼び出すようにしようと思います。

    ありがとうございました。

    • 回答としてマーク Exp8001-02 2015年5月28日 11:10
    2015年5月28日 11:09

すべての返信

  • 回答ではありません。あしからず。
    一般論として、

    1.通信では、実際にデータのやり取りが確認できる場合を除き、
     通信できるかどうかを事前に調べる手立てはありません。

    つまり、対象ポートが通信可能であるかどうかは、やってみなくてはわからないわけですね。
    もちろん、それにはケーブルがつながっていない場合も含まれます。
    当たり前ですが通信の障害となる事項はそれだけではありません。
    つまり、ケーブル抜けが監視できても、たいした慰めにはならないわけですね。

    さて、以上を勘案すると、「エラーになったポートを記録して処理する方法」を模索するより、
    「正しく通信できないポートを初期化して使えるように復帰する」方法を探すのが正しい道だと考えられます。

    2015年5月25日 6:13
  • 返信ありがとうございます。

    そうですね。

    まずは、正しく通信できていないポートを初期化しないといけません。

    そのために、まずはクローズを正しく行うべきかと考えましたが、

    他の方法も探してみます。

    デバイスマネージャで無効にして、有効にするようなことをプログラムで

    作成してみましたが、管理者権限でないと動作しなかったので、苦戦しております。

    COMが中途半端な状態(オープンもクローズもできない状態)を

    なんとか解消したいと思っております。

    2015年5月25日 8:25
  • そんな状況を考慮しようと思った事ないので自信はありませんが・・・。

    デバイス変化イベントが発生してるなら OS は COM ポートを切断した事にしてそうですから、イベント後にシステムの COMポート一覧を WMI や レジストリで取得すればわかるのじゃないですか?

    しかし、物理接続が無くなった認識時点で不正ハンドルになりそうなので、アプリはハンドル操作でエラーが起こればクローズで良いような・・・。

    中途半端な状態になるらしいから、理由次第だけどプロセス終了の必要がありそうな気もする・・・。

    2015年5月25日 12:29
  • > DBT_DEVNODES_CHANGEDでは この値が使えません。

    調べてみると、 RegisterDeviceNotification を使えとありますが、どうでしょうか?
    <https://msdn.microsoft.com/ja-jp/aa363211>

    以前、COM USBを使っていましたが、単にタイムアウトしていたら、未接続とみなしていたので、実際はどうか分かりませんが、参考までに。

    2015年5月25日 16:07
  • > USB COMで通信中に、USBのCOMを人為的に抜いた時に、
    > クローズのタイミングが悪く正しくクローズできません。

    今回のご質問されている問題の発端って、↑ これですよね?
    これって、未処理の I/O が残っていることが原因なのでは?
    COM ポートに限ったことではありませんが、処理待ちの I/O が残っていると、その対象となるオブジェクトはクローズできないはずです。
    なのでこれが原因で、COM ポート ハンドルもクローズできないのでは?
    もしこれ原因だとすれば、COM ポート ハンドルのクローズ処理を行う前に、発行済みの I/O をキャンセルすることで解決できるかも。
    発行済み I/O のキャンセルは、下記 API でできます。

    ----------------------------------------
    CancelIo function
    https://msdn.microsoft.com/ja-jp/library/windows/desktop/aa363791(v=vs.85).aspx

    CancelIoEx function
    https://msdn.microsoft.com/ja-jp/library/windows/desktop/aa363792(v=vs.85).aspx
    ----------------------------------------

    個人的な見解ですけど。。。
    COM ポートへのアクセスは、非同期 I/O で実現されることをお勧めします。

    ----------------------------------------
    Windowsの非同期I/Oと、OVERLAPPED構造体の寿命
    http://golb.hatenadiary.jp/entry/20110921/1316622884
    ----------------------------------------

    参考になりましたら、幸いに存じます。

    • 回答の候補に設定 星 睦美 2015年5月26日 5:02
    2015年5月26日 0:30
  • kyano30さん、ありがとうございます。

    レジストリからデバイスの一覧は取得しているので、

    最悪は切断前、切断後で一覧を比較してどのCOMポート

    かを調べようと思っています。

    接続がなくなった時点でハンドルをクローズしたいのですが、

    作成中のアプリが複数のCOMと通信しているため、

    どのCOMポートをクローズすればいいのかわからないという状況です。

    プロセス終了をしても、COMがOSに掴まれた状態になっているようで、

    再起動したプロセスからもアクセスできない状態になってしまってます。

    なんとかしてI/Oをキャンセルできればいいなと思っています。

    2015年5月26日 4:49
  • > DBT_DEVNODES_CHANGEDでは この値が使えません。

    調べてみると、 RegisterDeviceNotification を使えとありますが、どうでしょうか?
    <https://msdn.microsoft.com/ja-jp/aa363211>

    以前、COM USBを使っていましたが、単にタイムアウトしていたら、未接続とみなしていたので、実際はどうか分かりませんが、参考までに。

    pepperleaf01さん、ありがとうございます!

    試してみます。

    2015年5月26日 4:51
  • お馬鹿さま、ご返信ありがとうございます。

    -----------------------------------------------------

    >> USB COMで通信中に、USBのCOMを人為的に抜いた時に、
    >> クローズのタイミングが悪く正しくクローズできません。
    >
    >今回のご質問されている問題の発端って、↑ これですよね?

    -----------------------------------------------------

    そうです。
    (わかりずらい書き方をしてすみません。)

    >なのでこれが原因で、COM ポート ハンドルもクローズできないのでは?
    その通りでございます。


    -----------------------------------------------------

    >CancelIo function
    >CancelIoEx function

    -----------------------------------------------------

    お~。こういうのがあるんですね。
    早速試してみます!!

    >COM ポートへのアクセスは、非同期 I/O で実現されることをお勧めします。
    参考にさせていただきます。
    既存ソフトをカスタマイズ中なので、最短の修正でパッチを当てたいと
    思っており、大幅改修ができない状態です。

    URLその他諸々は大変参考になりました。感謝です!

    確認しましたらご報告させていただきます。

    • 回答としてマーク Exp8001-02 2015年5月28日 11:09
    • 回答としてマークされていない Exp8001-02 2015年5月28日 11:10
    2015年5月26日 4:58
  • お馬鹿さま

    >CancelIo function
    > CancelIoEx function

    試して見ましたが、どうもIOはキャンセルできずに、現象の改善はできませんでした。

    OSとドライバ間で初期化できていないんじゃないかと疑ってます。

    少し現象は違うのですが、下記のようなものがありました。
    "Device does not exist" error after you reinsert a USB COM port device
    <https://support.microsoft.com/en-us/kb/2990372>

    何の解決にもならないのですが、うまくいく動作としては、
    ①USBを抜く。
    ②USBを抜いたので送受信ができずに通信処理はタイムアウトになり、クローズ処理を行う。
    ③その後5秒待って、USBを刺すとオープンはでき再接続に成功します。
    (クローズした後、一定の時間を置いて、USBを刺すと再接続できます。)
    がありました。
    これを突破口にしたかったのですが、結局の所、抜本対策を見出せなかったので、応急対策として、
    デバイスマネージャでデバイスの「無効->有効」を行う管理者権限アプリを作成し、
    標準ユーザ権限のアプリから管理者権限アプリを呼び出すようにしようと思います。

    ありがとうございました。

    • 回答としてマーク Exp8001-02 2015年5月28日 11:10
    2015年5月28日 11:09
  • どのような実装を行い、どのような結果を得られたのかなど、詳細が分からないので何とも言えませんが。。。

    提示されたサポート技術情報は、本件に関連している可能性があると思います。
    USB COM 用ドライバとして Usbser.sys ドライバを利用しているのであれば、このサイトで公開されている Hotfix をダウンロードし試されることをお勧めします。
    (USB COM 用ドライバとして Usbser.sys ドライバを利用していないのであれば、このサポート技術情報とは異なる要因。。。ということだと思います。)


    > デバイスマネージャでデバイスの「無効->有効」を行う管理者権限アプリを作成し、
    > 標準ユーザ権限のアプリから管理者権限アプリを呼び出すようにしようと思います。

    既にご存知かもしれませんが、一応。。。
    ご要望されている機能は、SetupDi API を使えばできますが。。。この API Set の使い方、ちょっと複雑です。
    SetupDi API を使ったサンプル コードとしては、"DevCon.exe" というツールがソース コード込みで公開されていて、そのツールを解説したブログもマイクロソフト社員の方が書かれていますので、そちらが参考になるかと思います。

    ------------------------------------------------------
    Device Console (DevCon) Tool
    https://code.msdn.microsoft.com/windowshardware/DevCon-Sample-4e95d71c

    DevCon と SetupDi API ~ DevCon の使い方編 ~
    http://blogs.msdn.com/b/jpwdkblog/archive/2009/06/16/devcon-setupdi-api-devcon.aspx

    SetupDi API と DevCon ~ SetupDi API の使い方編 ~
    http://blogs.msdn.com/b/jpwdkblog/archive/2009/07/15/setupdi-api-devcon-setupdi-api.aspx
    ------------------------------------------------------

    あくまでも個人的な感想ですが、デバイスの「無効->有効」を行う管理者権限アプリを作るよりも、COM ポートをクローズできない原因を調べる方が、問題解決への近道のように思います。

    2015年5月29日 4:18