COM ポート番号の変更方法
皆さん、こんにちは。A寿です。
突然ですが、皆さんは、モーターボートから手漕ぎボートに飛び移ったことはありますか?・・・このお話にご興味のある方は本文の最後の【閑話】までどうぞ。
さて、今回は、COM ポート番号の変更方法をご紹介したいと思います。COM ポート番号の変更方法は、Windows Driver Kit 7.1.0 の pnpports サンプル (\WinDDK\7600.16385.1\src\setup\pnpports) に含まれる advandlg.c の EnactComNameChanges() の実装に記述されています。
この関数で使用されている API と COM ポートのシーケンスの概要は、以下の通りです。
① ComDBGetCurrentPortUsage() で、現在使用されている COM ポート一覧を取得し、新しく設定する COM ポート番号が利用できるか確認
② QueryDosDevice() で、変更前の COM ポートが "\Device\Serial0"="COM1" のようなマッピングが行われているか確認
③ CreateFile() で変更前の COM ポートが他のアプリケーションによってオープンされていないか確認
④ DefineDosDevice() + DDD_REMOVE_DEFINITION で、変更前の COM ポートのマッピングの登録を解除
⑤ DefineDosDevice() + DDD_RAW_TARGET_PATH で、変更後の COM ポートのマッピングを登録
例:"\Device\Serial0"="COM1" → "\Device\Serial0"="COM2"
⑥ RegSetValueEx() でレジストリ HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM に ⑤ の内容を保存
⑦ ComDBReleasePort() / ComDBClaimPort() で COM ポート データベースを更新
⑧ SetupDiGetDeviceRegistryProperty() / SetupDiSetDeviceRegistryProperty() でフレンドリー名の情報を更新
例: "通信ポート (COM1)" → "通信ポート (COM2)"
⑨ SetupDiOpenDevRegKey() / RegSetValueEx() で、レジストリ HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\ACPI\PNP0501\1\Device Parameters の "PortName" の値を更新
例:"PortName"="COM1" → "PortName"="COM2"
⑩ SetupDiGetDeviceInstallParams() / SetupDiSetDeviceInstallParams() でデバイスマネージャーに変更を通知
以下、pnpports サンプルに関して補足します。
このサンプルそのものは、デバイスマネージャーにおける通信ポートのプロパティ設定画面と同様の処理を行うサンプルコードとなっています。その画面を出す手順を示しますと、例えば、[デバイスマネージャー] で [ポート (COM と LPT)] - [通信ポート (COM1)] を右クリックして [プロパティ] をクリックすると、[通信ポート (COM1)のプロパティ] が開きます。その [ポートの設定] タブの [詳細設定] ボタンをクリックすると、以下のように [COM1 の詳細設定]ダイアログが開きます。このダイアログの下部に COM ポート番号を変更できるドロップダウンリストがあります。
このサンプルはまた、"Ports" クラスにおけるクラス インストーラーおよびデバイス マネージャーに追加できるプロパティシート ページのサンプル コードでもあります。
そのため、このプログラム単体を起動して動作確認することはできませんので、上述のシーケンス部分を抜粋・変更してご利用いただく必要があります。
また、この pnpports サンプルは、https://code.msdn.microsoft.com のサイトにはありませんのでご注意ください。
[参考技術情報]
COM Port Database Support Routines
<https://msdn.microsoft.com/en-us/library/ff546483.aspx>
COM Port Database (Windows Drivers)
<https://msdn.microsoft.com/en-us/library/windows/hardware/ff546481(v=vs.85).aspx>
SERIALCOMM
<https://technet.microsoft.com/en-us/library/cc940908.aspx>
QueryDosDevice
<https://msdn.microsoft.com/ja-jp/library/cc429649.aspx>
DefineDosDevice
<https://msdn.microsoft.com/ja-jp/library/cc429208.aspx>
SetupDiSetDeviceRegistryProperty
<https://msdn.microsoft.com/en-us/library/ff552169.aspx>
SetupDiGetDeviceInstallParams
https://msdn.microsoft.com/en-us/library/ff551104.aspx
SetupDiSetDeviceInstallParams
<https://msdn.microsoft.com/en-us/library/ff552141.aspx>
なお、DefineDosDevice() で定義する DOS Device Name は、以下の技術情報にありますように、呼び出し元の context が、LocalSystem の場合のみ、Global となり、LocalSystem 以外は、Local となります。そのため、ユーザ セッションで、変更を行った場合は、再起動をしない限り、変更を行ったユーザ セッション以外では、使用できないことにご注意ください。
Defining an MS-DOS Device Name
<https://msdn.microsoft.com/en-us/library/aa363908(VS.85).aspx>
また、Global となっているか Local となっているかは、WinObj をご利用いただければ確認することができます。
WinObj
<https://technet.microsoft.com/en-us/sysinternals/bb896657.aspx>
※ Global の Object は、"GLOBAL??" に表示され、Local の Object は、"Sessions" に表示されます。
Local と Global の DOS Device Name に関してご興味のある方は、以下の技術情報も参考になれば幸いです。
Local and Global MS-DOS Device Names
https://msdn.microsoft.com/EN-US/library/windows/hardware/ff554302(v=vs.85).aspx
上記がご参考になれば幸いです。
ではまた。
――――――――――――――――
【閑話】突然ですが、皆さんは、モーターボートから手漕ぎボートに飛び移ったことはありますか?
私は昨年、特に意図したわけでもなく、そうすることになってしまいました。もちろん動いているボート同士ではありませんし、飛び移ると言っても、実際にはモーターボートの縁に腰を掛けて移動するのですが、そうは言っても、海の真ん中で、割と高さのある二十人乗りのモーターボートから、高さの低い四人乗りの手漕ぎボートに移動するので、結果として飛び移る形になってしまいました。さらに、はしけも、救命胴衣もない状態だったので、なかなかスリリングに感じました。そもそも、なぜそんなことをしたかと言いますと、とある海外のパックツアーの一部に組み込まれていたからです。そのツアーのパンフレットには、ボートで入る洞窟内の景色の美しさがアピールされてはいましたが、申込時の注意として、旅行会社の方から、特に明示的には、ボート間の移動の説明はなかったと記憶しています。(とても有名な場所だから説明が必要なかったのかもしれませんが。。。) その状態で、現地に行ったものですから、大変でした。海に出るまでの道中のバスで初めてボート間の移動の説明を聞かされ、しかも、「海に落ちても、足は着かない深さですが、すぐに現地スタッフが引っ張り上げてくれるから、ショックを受けるだけ」とお気軽な案内であったため、バスにいた乗客のうち泳げない方々は恐怖におののいて、涙している人もいました。それでも、なかなかない機会だからと、その方々も一緒に海の上まで出ましたが、やはり涙で顔をくしゃくしゃにしながら、なんとかボート間を移動していました。幸い、誰も、海に落ちることも、事故もなく、結果として良い思い出にはなりましたが、皆様も、ツアーの一部に海の上に出るプランが含まれている場合は、安全面にご注意ください。