如何列举计算机上的可用COM端口?

问题描述 投票:2回答:4

除了从1到32的循环,并尝试打开每一个端口,有没有一个可靠的方法来获得系统上的COM端口?

vb6 serial-port
4个回答
2
投票

是1到255。最快的方法是用 QueryDosDevice 这样

Option Explicit

'--- for CreateFile
Private Const GENERIC_READ                  As Long = &H80000000
Private Const GENERIC_WRITE                 As Long = &H40000000
Private Const OPEN_EXISTING                 As Long = 3
Private Const INVALID_HANDLE_VALUE          As Long = -1
'--- error codes
Private Const ERROR_ACCESS_DENIED           As Long = 5&
Private Const ERROR_GEN_FAILURE             As Long = 31&
Private Const ERROR_SHARING_VIOLATION       As Long = 32&
Private Const ERROR_SEM_TIMEOUT             As Long = 121&

Private Declare Function QueryDosDevice Lib "kernel32" Alias "QueryDosDeviceA" (ByVal lpDeviceName As Long, ByVal lpTargetPath As String, ByVal ucchMax As Long) As Long
Private Declare Function CreateFile Lib "kernel32" Alias "CreateFileA" (ByVal lpFileName As String, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, ByVal lpSecurityAttributes As Long, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long

Private Function PrintError(sFunc As String)
    Debug.Print sFunc; ": "; Error
End Function

Public Function IsNT() As Boolean
    IsNT = True
End Function

Public Function EnumSerialPorts() As Variant
    Const FUNC_NAME     As String = "EnumSerialPorts"
    Dim sBuffer         As String
    Dim lIdx            As Long
    Dim hFile           As Long
    Dim vRet            As Variant
    Dim lCount          As Long

    On Error GoTo EH
    ReDim vRet(0 To 255) As Variant
    If IsNT Then
        sBuffer = String$(100000, 1)
        Call QueryDosDevice(0, sBuffer, Len(sBuffer))
        sBuffer = Chr$(0) & sBuffer
        For lIdx = 1 To 255
            If InStr(1, sBuffer, Chr$(0) & "COM" & lIdx & Chr$(0), vbTextCompare) > 0 Then
                vRet(lCount) = "COM" & lIdx
                lCount = lCount + 1
            End If
        Next
    Else
        For lIdx = 1 To 255
            hFile = CreateFile("COM" & lIdx, GENERIC_READ Or GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0)
            If hFile = INVALID_HANDLE_VALUE Then
                Select Case Err.LastDllError
                Case ERROR_ACCESS_DENIED, ERROR_GEN_FAILURE, ERROR_SHARING_VIOLATION, ERROR_SEM_TIMEOUT
                    hFile = 0
                End Select
            Else
                Call CloseHandle(hFile)
                hFile = 0
            End If
            If hFile = 0 Then
                vRet(lCount) = "COM" & lIdx
                lCount = lCount + 1
            End If
        Next
    End If
    If lCount = 0 Then
        EnumSerialPorts = Split(vbNullString)
    Else
        ReDim Preserve vRet(0 To lCount - 1) As Variant
        EnumSerialPorts = vRet
    End If
    Exit Function
EH:
    PrintError FUNC_NAME
    Resume Next
End Function

该片段又回到了 CreateFile 在9x上。IsNT 为了简洁起见,功能被支开了。


3
投票

我相信在现代windows环境下,你可以在注册表中找到它们,在下面的键下。HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM. 我不确定指定注册表键的正确方法。然而,我只在Windows XP上测试过。


3
投票

看看Randy Birch网站上的这篇文章。创建文件:确定可用的COM端口

还有一种方法是使用MSCOMM控件。ConfigurePort: 用MSCOMM控件确定可用的COM端口

代码有点长,我不方便在这里发,但链接里有你需要的一切。


1
投票

使用VB6或VBScript来枚举可用的COM端口可以像使用VB.NET一样简单,这可以通过枚举注册表路径中的值来实现。HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM. 总比打电话给我好 QueryDosDevice() 并进行字符串比较,以筛选出名称前面有 COM 因为你会得到这样的东西 CompositeBattery 或其他以大写姓名开头的东西。COM),而不是COM端口。这样做的另一个好处是,注册表的值中也包含了 USB转COM设备编码,例如,使用 "我的名字",就无法检测到。WMIService.ExecQuery("Select * from Win32_SerialPort"). 如果你试着把USB转COM的设备插进或拔出电脑,你可以看到注册表的值也会马上出现或消失,因为它一直在更新。

Option Explicit

Sub ListComPorts()
List1.Clear

Dim Registry As Object, Names As Variant, Types As Variant
Set Registry = GetObject("winmgmts:\\.\root\default:StdRegProv")
If Registry.EnumValues(&H80000002, "HARDWARE\DEVICEMAP\SERIALCOMM", Names, Types) <> 0 Then Exit Sub

Dim I As Long
If IsArray(Names) Then
    For I = 0 To UBound(Names)
        Dim PortName As Variant
        Registry.GetStringValue &H80000002, "HARDWARE\DEVICEMAP\SERIALCOMM", Names(I), PortName
        List1.AddItem PortName & " - " & Names(I)
    Next
End If

End Sub

Private Sub Form_Load()
ListComPorts
End Sub

上面的代码使用的是 StdRegProv类 来枚举一个注册表键的值。我在XP、Windows 7、Windows 10中测试了这段代码,它可以正常工作,没有任何抱怨。添加到列表框中的项目如下。

COM1 - \Device\Serial0
COM3 - \Device\ProlificSerial0

列表框中添加的项目如下: 缺点 这段代码的问题是,它无法检测到哪个端口是 已开 因为每个端口只能被打开一次。检测一个COM端口是否被其他程序打开的方法可以通过调用 API CreateFile. 这里是一个例子。

© www.soinside.com 2019 - 2024. All rights reserved.