Discussion:
comポート番号取得方法
(too old to reply)
toshio Otsuka
2005-05-22 07:27:22 UTC
Permalink
VB6の環境の下でMscommコントロールを使用し、RS232C通信プログラムを作成しています。
VB6で使用可能なポート番号を自動取得する方法を教えてください。色々な書籍を見
てもポートの設定は予めプロパティー(CommPort)で手動設定する説明しか見当たり
ません。
これを自動的に検出して設定しようと考えています。出来ればOS(98、XP等)に依存
しない一番簡単な方法で教えていただければ幸いです。


::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::
Toshio Otsuka : Kodaira Tokyo Japan
E-Mail:***@d1.dion.jp
URL:http://www.h5.dion.ne.jp/~t_otsuka
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::
K.J.K.
2005-05-22 11:36:20 UTC
Permalink
K.J.K.です。

# 改行位置は、引用されることを前提にして決めてください。

"toshio Otsuka" wrote in message
Post by toshio Otsuka
VB6で使用可能なポート番号を自動取得する方法を教えてください。
レジストリを直接みるとか、WMIを使うとか、が基本ですが、
API関数のGetDefaultCommConfigで"COM1"から適当な範囲までを
チェックしてみるのが楽かもしれません。本当の意味での列挙
ではありませんけど。
--
K.J.K. mailto:***@koalanet.ne.jp
kitapon
2005-05-23 03:39:18 UTC
Permalink
kitapon です。

"toshio Otsuka" wrote in message
Post by toshio Otsuka
VB6で使用可能なポート番号を自動取得する方法を教えてください。
Dim PortNm As Integer

(For-Next 等でPortNmに番号を与え)

With MSComm1
.CommPort=PortNm
.PortOpen = True
End With

としてエラートラップすれば、[使用可能なポート番号]は解かります。
いい加減な方法ですが、システム次第では実用になると思います。

--
HIKARU SAYAMA
2005-05-23 03:47:18 UTC
Permalink
こんにちは佐山です。

MSCommコントロールを使った、
いんちきくさいポートスキャンサンプルです。

開いてみてNGならリストに加えないという強引な手口です。
強引ですがすでに開かれているポート含め、
使えないものはすべて弾きますので、
リストアップされた物に間違いはありません。

MSComm --> MSCommコントロール
cmbPort --> ポート表示用のドロップダウンリスト

Dim i As Integer
Dim Used As Boolean
Dim NowPort As Integer
Dim ResetPort As Integer

On Error GoTo ErrHandler

cmbPort.Clear
NowPort = MSComm.CommPort
iOldPort = NowPort
MSComm.PortOpen = False
For i = 1 To 10
Used = True
MSComm.CommPort = i
MSComm.PortOpen = True
If Used = True Then
cmbPort.AddItem "COM" & Str(i)
If i = NowPort Then _
ResetPort = cmbPort.ListCount - 1
End If
MSComm.PortOpen = False
Next i
cmbPort.ListIndex = ResetPort
Exit Sub

ErrHandler:
Used = False
Resume Next

このプロシージャを呼び出した直後に、
他のアプリでポートを開くと・・・
・・・そのときはご臨終です。
リストの内容が一転うそになります。
実際に使用する箇所でエラートラップしてください。

# Usedという変数名なのに
# Trueで使える論理に組まれているあたり
# とってもお茶目なコードです・・・

以上ご参考になれば幸いです。
--
HIKARU SAYAMA
<***@operamail.com>
toshio Otsuka
2005-05-24 11:13:05 UTC
Permalink
大塚です。
助かります。この件で半年ぐらい考えていました。雲が晴れた気分です。
早速試してみます。ところで以下一行は必要でしょうか?
iOldPort = NowPort
HIKARU SAYAMA
2005-05-25 00:36:41 UTC
Permalink
こんにちは、佐山です。
Post by toshio Otsuka
早速試してみます。ところで以下一行は必要でしょうか?
iOldPort = NowPort
あ”、いらないですね。

# 何に使ってたんだろう?・・・
# 自分で書いたコードなのに・・・
--
HIKARU SAYAMA
<***@operamail.com>
Shigehiro Mori
2005-05-25 14:31:48 UTC
Permalink
森と申します。
# 本題からはずれますが、
Post by toshio Otsuka
助かります。この件で半年ぐらい考えていました。雲が晴れた気分です。
 Commポートのような非プラグアンドプレイ?なもので
あまりこういう対応をしないほうがよいと思います。
 個人的に自分だけが使うものならOKかも知れませ
んが、他のアプリが決まったポートを利用しようとした
ときと同時にポートスキャンされたら迷惑かも?


--
「いくら議論しても、結論を出すのは結局自分!」 森 茂廣
UETA, Shin-ichi
2005-05-25 18:35:34 UTC
Permalink
こんにちは、植田です。
Post by Shigehiro Mori
 Commポートのような非プラグアンドプレイ?なもので
あまりこういう対応をしないほうがよいと思います。
 個人的に自分だけが使うものならOKかも知れませ
んが、他のアプリが決まったポートを利用しようとした
ときと同時にポートスキャンされたら迷惑かも?
―― というご指摘もあることなので、去年の投稿を引っ張り出しました ^^)

2つの例を示しますが、いずれもシリアルポート以外の通信ポートも列挙
しますので、適当に絞り込んでください。
まぁ、実際にオープン可能かどうかは結局総当りするしかないでしょうが、
実行時に動的にポートを決定するようなアプリケーションも稀かと。

【EnumPortsを使う方法】

Private Type PORT_INFO_1
pName As Long
End Type

Private Declare Function EnumPorts Lib "winspool.drv" _
Alias "EnumPortsA" (ByVal pName As String, ByVal Level As Long, pPorts As Any, ByVal cbBuf As Long, pcbNeeded As Long, pcReturned As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32" _
Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
Private Declare Function lstrcpy Lib "kernel32" _
Alias "lstrcpyA" (lpString1 As Any, lpString2 As Any) As Long

Public Sub MyEnumPorts()
Dim ports() As PORT_INFO_1
Dim buffer() As Byte
Dim cbNeeded As Long
Dim cReturned As Long
EnumPorts vbNullString, 1, ByVal vbNullString, 0, cbNeeded, cReturned
ReDim buffer(cbNeeded - 1)
EnumPorts vbNullString, 1, buffer(0), cbNeeded, cbNeeded, cReturned
ReDim ports(cReturned - 1)
CopyMemory ports(0), buffer(0), LenB(ports(0)) * cReturned
Dim i As Long
For i = 0 To cReturned - 1
Dim name As String
name = String(256, Chr(0)) ' 安全を期すなら文字列長をチェックすべし
lstrcpy ByVal name, ByVal ports(i).pName
name = Left(name, InStr(name, Chr(0)) - 1)
Debug.Print name ' とりあえずイミディエイトウィンドウに出力
Next
End Sub

【SetupDi〜系APIを使う方法】

Private Type GUID: Data(0 To 3) As Long: End Type 'A GUID is 16 bytes long

Private Type SP_DEVINFO_DATA
cbSize As Long
ClassGuid(0 To 3) As Long
DevInst As Long
Reserved As Long
End Type

Private Declare Function SetupDiClassGuidsFromName Lib "setupapi.dll" _
Alias "SetupDiClassGuidsFromNameA" (ByVal ClassName As String, ClassGuidList As Long, ByVal ClassGuidListSize As Long, RequiredSize As Long) As Boolean
Private Declare Function SetupDiGetClassDevs Lib "setupapi.dll" _
Alias "SetupDiGetClassDevsA" (ClassGuid As Long, ByVal Enumerator As String, ByVal HwndParent As Long, ByVal Flags As Long) As Long
Private Declare Function SetupDiDestroyDeviceInfoList Lib "setupapi.dll" (ByVal DeviceInfoSet As Long) As Boolean
Private Declare Function SetupDiEnumDeviceInfo Lib "setupapi.dll" (ByVal DeviceInfoSet As Long, ByVal MemberIndex As Long, DeviceInfoData As Long) As Boolean
Private Declare Function SetupDiGetDeviceRegistryProperty Lib "setupapi.dll" _
Alias "SetupDiGetDeviceRegistryPropertyA" (ByVal DeviceInfoSet As Long, DeviceInfoData As Long, ByVal Property As Long, PropertyRegDataType As Long, ByVal PropertyBuffer As String, ByVal PropertyBufferSize As Long, RequiredSize As Long) As Boolean

Private Const DIGCF_DEFAULT = &H1& ' only valid with DIGCF_DEVICEINTERFACE
Private Const DIGCF_PRESENT = &H2&
Private Const DIGCF_ALLCLASSES = &H4&
Private Const DIGCF_PROFILE = &H8&
Private Const DIGCF_DEVICEINTERFACE = &H10&

Private Const SPDRP_DEVICEDESC = &H0&
Private Const SPDRP_FRIENDLYNAME = &HC&

' SPDRP_xxxはほかにもいろいろ

Public Sub EnumPorts()

Dim guidPorts As GUID
Dim nRequiredSize As Long
SetupDiClassGuidsFromName "Ports", guidPorts.Data(0), 1, nRequiredSize
Dim hDiSet As Long
hDiSet = SetupDiGetClassDevs(guidPorts.Data(0), vbNullString, 0&, DIGCF_PRESENT)
Dim iDev As Long
Dim did As SP_DEVINFO_DATA
did.cbSize = LenB(did)
Do While SetupDiEnumDeviceInfo(hDiSet, iDev, did.cbSize)
Dim iRegDataType As Long
Dim strFriendlyName As String
strFriendlyName = String(256, Chr(0))
SetupDiGetDeviceRegistryProperty hDiSet, did.cbSize, SPDRP_FRIENDLYNAME, iRegDataType, strFriendlyName, 256, nRequiredSize
strFriendlyName = Left(strFriendlyName, InStr(strFriendlyName, Chr(0)) - 1)
Debug.Print strFriendlyName ' とりあえずイミディエイトウィンドウに出力
iDev = iDev + 1
Loop
SetupDiDestroyDeviceInfoList hDiSet

End Sub

【WMIを使う方法】

ん〜、知らん...。とりあえずWin32_SerialPortクラスかな?
--
植田システム設計事務所
Ueta System Design Studio
http://www.usdesign.jp/
植田真一
mailto:***@usdesign.jp
Microsoft MVP for Visual C++
UETA, Shin-ichi
2005-05-26 12:26:07 UTC
Permalink
どうも、植田です。

# DMで問い合わせがありましたが、こちらで回答します。
Post by UETA, Shin-ichi
【EnumPortsを使う方法】
何かを参照設定する必要はありません。実際に動くものを
そのまま転記したものですので、コピペして適当なイベント
ハンドラから呼び出せばすぐにお試しできます。

【SetupDi〜系APIを使う方法】も同様で、いずれもAPIを
直接呼び出しているだけなのですが、APIはC言語を標準
としているので、VBから呼び出す場合はトリッキーな技が
必要なることが多々あります。
--
植田システム設計事務所
Ueta System Design Studio
http://www.usdesign.jp/
植田真一
mailto:***@usdesign.jp
Microsoft MVP for Visual C++
toshio Otsuka
2005-05-26 12:42:56 UTC
Permalink
大塚です。

実際に以下のポートの付いたPCで試してみました。
COM1、LP1、USB-シリアル変換機接続

PCのデバイスマネージャーで確認すると
COM1、LP1、COM5(USB-シリアル) となっています。

しかし、【EnumPortsを使う方法】で検出したポートは
COM1、LP1、COM2、COM5 となりました。

この実体の無いCOM2がわかりません。??

----- Original Message -----
From: "UETA, Shin-ichi" <***@usdesign.jp>
Newsgroups: microsoft.public.jp.vb
Sent: Thursday, May 26, 2005 3:35 AM
Subject: Re: comポート番号取得方法
Post by UETA, Shin-ichi
こんにちは、植田です。
Post by Shigehiro Mori
 Commポートのような非プラグアンドプレイ?なもので
あまりこういう対応をしないほうがよいと思います。
 個人的に自分だけが使うものならOKかも知れませ
んが、他のアプリが決まったポートを利用しようとした
ときと同時にポートスキャンされたら迷惑かも?
―― というご指摘もあることなので、去年の投稿を引っ張り出しました ^^)
2つの例を示しますが、いずれもシリアルポート以外の通信ポートも列挙
しますので、適当に絞り込んでください。
まぁ、実際にオープン可能かどうかは結局総当りするしかないでしょうが、
実行時に動的にポートを決定するようなアプリケーションも稀かと。
UETA, Shin-ichi
2005-05-26 12:57:16 UTC
Permalink
どうも、植田です。
Post by toshio Otsuka
この実体の無いCOM2がわかりません。??
# ポートの列挙は簡単なようで奥の深いテーマだったりします...。

以前はCOM1とCOM2を標準装備したPCが多く見られたのですが、
最近はシリアルポート自体がレガシーインターフェイスとして消えて
いきつつあります。ついていたとしてもCOM2の端子が表に出てい
ないことがあり、その場合、ダミーの通信ポートとして見えてしまう
ことがあります。

デバイスマネージャでCOM2が出てこないのであれば、SetupDi〜
系APIを試してみてください。こちらの方がよりデバイスマネージャ
に近い結果が得られると思います。

# EnumPortsは「印刷に利用可能なポート」の列挙が建前のようですし...。

サンプルコードでは、SPDRP_FRIENDLYNAMEを指定して、デバイス
マネージャで表示される「ユーザーフレンドリ」な文字列を得ています。
ほかにもデバイスの種類を特定するための情報が得られますので、
詳しくはAPIのリファレンスをご覧ください。
--
植田システム設計事務所
Ueta System Design Studio
http://www.usdesign.jp/
植田真一
mailto:***@usdesign.jp
Microsoft MVP for Visual C++
UETA, Shin-ichi
2005-05-26 13:14:05 UTC
Permalink
どうも、植田です。

もうひとつ別のアプローチとして、レジストリを参照する方法も
挙げておきます。Windowsのバージョンに依存してしまうのが
難点ですが、実行環境を限定できるのであれば手っ取り早い
方法かもしれません。

WinNT系の場合は、以下のレジストリキーを参照することで
Windowsが認識している通信ポートを確認できます。

HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM

詳しくはこちら↓をご覧ください。

Windows 2000 Registry Reference
DeviceSerial#
http://www.microsoft.com/resources/documentation/Windows/2000/server/reskit/en-us/regentry/45.asp
--
植田システム設計事務所
Ueta System Design Studio
http://www.usdesign.jp/
植田真一
mailto:***@usdesign.jp
Microsoft MVP for Visual C++
toshio Otsuka
2005-05-28 12:29:32 UTC
Permalink
植田さん
毎回ご丁寧に説明頂き有難うございます。
レジストリ参照方法を勉強してみます。
何か参考書があれば教えてください。
市販の本でこの辺の技を詳しく説明しているものを探しているのですが、なかなかみ
あたりません。
Post by UETA, Shin-ichi
どうも、植田です。
もうひとつ別のアプローチとして、レジストリを参照する方法も
挙げておきます。Windowsのバージョンに依存してしまうのが
難点ですが、実行環境を限定できるのであれば手っ取り早い
方法かもしれません。
WinNT系の場合は、以下のレジストリキーを参照することで
Windowsが認識している通信ポートを確認できます。
HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM
詳しくはこちら↓をご覧ください。
Windows 2000 Registry Reference
DeviceSerial#
http://www.microsoft.com/resources/documentation/Windows/2000/server/reskit/
en-us/regentry/45.asp
Post by UETA, Shin-ichi
--
植田システム設計事務所
Ueta System Design Studio
http://www.usdesign.jp/
植田真一
Microsoft MVP for Visual C++
UETA, Shin-ichi
2005-05-29 09:18:40 UTC
Permalink
どうも、植田です。
Post by toshio Otsuka
レジストリ参照方法を勉強してみます。
何か参考書があれば教えてください。
市販の本でこの辺の技を詳しく説明しているものを探しているのですが、なかなかみ
あたりません。
VB6で任意のレジストリキーにアクセスするにはAPIを直接使わざる
を得ないようです。となると、APIのレファレンスを見るしかないので、
VBの一般的な入門書の範疇ではなくなるでしょうね。
DLLの呼び出し方の基本を学んで、あとはAPIのレファレンスを読み
解く力を養うしかないと思います。

# VB.NETであれば、.NET Frameworkに専用のクラスが用意されて
# いるようです。

とりあえず、google辺りを検索してみては?

Google 検索: VB レジストリ
http://www.google.co.jp/search?hl=ja&q=VB+%E3%83%AC%E3%82%B8%E3%82%B9%E3%83%88%E3%83%AA&btnG=Google+%E6%A4%9C%E7%B4%A2&lr=lang_ja

一応簡単なプログラムを書いてみましたので、参考にしてください。

Private Declare Function RegOpenKeyEx Lib "advapi32.dll" _
Alias "RegOpenKeyExA" (ByVal hKey As Long, ByVal lpSubKey As String, ByVal ulOptions As Long, ByVal samDesired As Long, phkResult As Long) As Long
Private Declare Function RegCloseKey Lib "advapi32.dll" (ByVal hKey As Long) As Long
Private Declare Function RegEnumValue Lib "advapi32.dll" _
Alias "RegEnumValueA" (ByVal hKey As Long, ByVal dwIndex As Long, ByVal lpValueName As String, lpcbValueName As Long, ByVal lpReserved As Long, lpType As Any, lpData As Any, lpcbData As Long) As Long

Private Const HKEY_LOCAL_MACHINE As Long = &H80000002

Private Const KEY_QUERY_VALUE As Long = &H1&

Sub EnumPorts()

Dim hKey As Long
If RegOpenKeyEx(HKEY_LOCAL_MACHINE, "HARDWARE\DEVICEMAP\SERIALCOMM", 0, KEY_QUERY_VALUE, hKey) = 0 Then
Dim i As Long
Do
Dim strName As String
strName = String(256, Chr(0))
Dim strValue As String
strValue = String(256, Chr(0))
Dim cbValue As Long
cbValue = 256
If RegEnumValue(hKey, i, strName, 256, 0, ByVal 0, ByVal strValue, cbValue) <> 0 Then Exit Do
strName = Left(strName, InStr(strName, Chr(0)) - 1)
strValue = Left(strValue, InStr(strValue, Chr(0)) - 1)
Debug.Print i & ":" & strName & "=" & strValue
i = i + 1
Loop
RegCloseKey hKey
End If

End Sub
--
植田システム設計事務所
Ueta System Design Studio
http://www.usdesign.jp/
植田真一
mailto:***@usdesign.jp
Microsoft MVP for Visual C++
UETA, Shin-ichi
2005-05-29 09:30:34 UTC
Permalink
どうも、植田です。一部訂正です...^^;)
Post by UETA, Shin-ichi
Dim cbValue As Long
cbValue = 256
If RegEnumValue(hKey, i, strName, 256, 0, ByVal 0, ByVal strValue, cbValue) <> 0 Then Exit Do
lpcbDataだけでなく、lpcbValueNameも入出力パラメータなので、
次のようにコードを訂正してください。

Dim cbName As Long
cbName = 256
Dim cbValue As Long
cbValue = 256
If RegEnumValue(hKey, i, strName, cbName, 0, ByVal 0, ByVal strValue, cbValue) <> 0 Then Exit Do

# こういうミスがあってもそれなりに動いてしまう辺りが怖い...。
--
植田システム設計事務所
Ueta System Design Studio
http://www.usdesign.jp/
植田真一
mailto:***@usdesign.jp
Microsoft MVP for Visual C++
toshio Otsuka
2005-05-30 12:23:51 UTC
Permalink
SetupDi〜系API
を試してみました。今度はCOM2検出されず、実情に近く動作しています。

 以下API関数使用のものも試して見ます。
VB初心者ですが、こんなに奥深いものとは思ってもいませんでした。
本音はCOMポート番号の取得くらい数行で書けるものと思っていました。

COMポート番号の取得結果はもとより、しばらくはAPIとレジストリの理解に悩みそう
です・・・・。
Post by UETA, Shin-ichi
どうも、植田です。一部訂正です...^^;)
Post by UETA, Shin-ichi
Dim cbValue As Long
cbValue = 256
If RegEnumValue(hKey, i, strName, 256, 0, ByVal 0, ByVal strValue,
cbValue) <> 0 Then Exit Do
Post by UETA, Shin-ichi
lpcbDataだけでなく、lpcbValueNameも入出力パラメータなので、
次のようにコードを訂正してください。
Dim cbName As Long
cbName = 256
Dim cbValue As Long
cbValue = 256
If RegEnumValue(hKey, i, strName, cbName, 0, ByVal 0, ByVal strValue,
cbValue) <> 0 Then Exit Do
Post by UETA, Shin-ichi
# こういうミスがあってもそれなりに動いてしまう辺りが怖い...。
--
植田システム設計事務所
Ueta System Design Studio
http://www.usdesign.jp/
植田真一
Microsoft MVP for Visual C++
Loading...