检测 Windows 上所有可用串行端口的正确方法是什么?

问题描述 投票:0回答:8

在 Windows 下有多种方法可以列出串行端口,但我不确定哪种方法是正确的:检测所有可用串行端口的方法。

一个很好的代码示例是 http://www.naughter.com/enumser.html - 其中有 9 种(九种!)枚举串行设备的方法。

问题是:最佳方法是什么。

要求:

  • 不打开端口以检查它们是否可用。
  • 能够检测与
    COMx
    不同名称的端口。
  • 适用于 Windows XP SP2 或更高版本
c++ c windows serial-port
8个回答
9
投票
void SelectComPort() //added function to find the present serial 
{

    TCHAR lpTargetPath[5000]; // buffer to store the path of the COMPORTS
    DWORD test;
    bool gotPort=0; // in case the port is not found

    for(int i=0; i<255; i++) // checking ports from COM0 to COM255
    {
        CString str;
        str.Format(_T("%d"),i);
        CString ComName=CString("COM") + CString(str); // converting to COM0, COM1, COM2

        test = QueryDosDevice(ComName, (LPSTR)lpTargetPath, 5000);

            // Test the return value and error if any
        if(test!=0) //QueryDosDevice returns zero if it didn't find an object
        {
            m_MyPort.AddString((CString)ComName); // add to the ComboBox
            gotPort=1; // found port
        }

        if(::GetLastError()==ERROR_INSUFFICIENT_BUFFER)
        {
            lpTargetPath[10000]; // in case the buffer got filled, increase size of the buffer.
            continue;
        }

    }

    if(!gotPort) // if not port
    m_MyPort.AddString((CString)"No Active Ports Found"); // to display error message incase no ports found

}

6
投票

修改了 @Dženan 答案以使用宽字符并返回整数列表

#include <string>
#include <list>

list<int> getAvailablePorts()
{
    wchar_t lpTargetPath[5000]; // buffer to store the path of the COM PORTS
    list<int> portList;

    for (int i = 0; i < 255; i++) // checking ports from COM0 to COM255
    {
        wstring str = L"COM" + to_wstring(i); // converting to COM0, COM1, COM2
        DWORD res = QueryDosDevice(str.c_str(), lpTargetPath, 5000);

        // Test the return value and error if any
        if (res != 0) //QueryDosDevice returns zero if it didn't find an object
        {
            portList.push_back(i);
            //std::cout << str << ": " << lpTargetPath << std::endl;
        }
        if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER)
        {
        }
    }
    return portList;
}

5
投票

如果您可以访问注册表,则

HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM
键包含 Windows 当前支持的 COM 端口列表(在某些情况下,此信息可能已过时/不正确;例如,我怀疑,当提供串行端口的即插即用设备具有未完成检测/安装或最近已被删除)。

这是 .NET Framework 的

SerialPort.GetPortNames()
方法报告可用 COM 端口的方式,上述信息来自链接页面。


4
投票

这是 @michael-jacob-mathew 答案的现代化版本:

#include <iostream>
#include <string>
#include <Windows.h>

bool SelectComPort() //added function to find the present serial 
{
    char lpTargetPath[5000]; // buffer to store the path of the COMPORTS
    bool gotPort = false; // in case the port is not found

    for (int i = 0; i < 255; i++) // checking ports from COM0 to COM255
    {
        std::string str = "COM" + std::to_string(i); // converting to COM0, COM1, COM2
        DWORD test = QueryDosDevice(str.c_str(), lpTargetPath, 5000);

        // Test the return value and error if any
        if (test != 0) //QueryDosDevice returns zero if it didn't find an object
        {
            std::cout << str << ": " << lpTargetPath << std::endl;
            gotPort = true;
        }

        if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER)
        {
        }
    }

    return gotPort;
}

它在我的计算机上产生以下输出:

COM1: \Device\Serial0
COM3: \Device\VCP0

3
投票

串行端口是非常简单的设备,可以追溯到计算硬件的石器时代。它们不支持即插即用,无法判断有人插入了设备。您唯一能做的就是发现哪些端口可用,SerialPort.GetPortNames() 返回列表。一些 USB 模拟器可以生成一个描述性名称来配合端口名称,您可以使用 WMI、Win32_SerialPort 类来发现这些名称。

这些都无法帮助您发现哪个 COM 端口连接到特定设备。只有人类知道,她将电缆实际插入了连接器。您需要提供一个配置 UI,让用户选择端口号。组合框即可完成工作。将选择保存在配置数据中,下次程序启动时设备很可能仍连接到同一端口。


0
投票
github 文件


0
投票

对于 Windows,所有串行端口都有一个与“COM%u”匹配的别名。当然! 从Windows 98开始就提供了必要的SetupDi函数集,因此您的软件可以保持相当高的向后兼容性级别。 仅当 DOS 或 Windows 时

此处填写 ComboBoxEx 的示例:< 98 is required, enumerate-by-opening is OK and fast as such systems only support up to COM4.

#include <windowsx.h> #include <setupapi.h> #include <devguid.h> #include <cfgmgr32.h> //CM_Get_Parent // Fills or re-fills (after WM_DEVICECHANGE) a ComboBoxEx with all COM ports // and their descriptive names. // <nr> is the zero-based current (i.e. to be selected) COM port number. void FillComboComPorts(HWND hCombo,UINT nr) { ComboBox_ResetContent(hCombo); HANDLE devs=SetupDiGetClassDevs((LPGUID)&GUID_DEVCLASS_PORTS,0,0,DIGCF_PRESENT); if (devs==INVALID_HANDLE_VALUE) return; SP_DEVINFO_DATA devInfo; devInfo.cbSize=sizeof devInfo; TCHAR s[80]; COMBOBOXEXITEM cbei; cbei.mask=CBEIF_IMAGE|CBEIF_LPARAM|CBEIF_OVERLAY|CBEIF_SELECTEDIMAGE|CBEIF_TEXT; cbei.pszText=s; for (DWORD i=0; SetupDiEnumDeviceInfo(devs,i,&devInfo); i++) { HKEY hKey=SetupDiOpenDevRegKey(devs,&devInfo,DICS_FLAG_GLOBAL,0,DIREG_DEV,KEY_READ); if (hKey==INVALID_HANDLE_VALUE) continue; TCHAR t[16]; // The COM port name will be placed here *t=0; DWORD len=sizeof(t); RegQueryValueEx(hKey,T("PortName"),0,0,(LPBYTE)t,&len); RegCloseKey(hKey); if (*t!='C') continue; // bail out on errors and LPT%u cbei.lParam=StrToInt(t+3)-1; // I use zero-based numbering // Already open COM ports are marked with an overlay // If your <nr> is currently open by design, change code here. HANDLE h=myOpen((UINT)cbei.lParam); if (h) CloseHandle(h); cbei.iOverlay=h?0:2; // 2 is the "not available" overlay. DEVINST parent; // Graying text would require ownerdrawn combobox and much more code const GUID*pguid=&GUID_DEVCLASS_PORTS; // Show class icon for parent(!) device class, so get a clue where your COM port is connected (USB or onboard, or somewhere else) if (!CM_Get_Parent(&parent,devInfo.DevInst,0)) { ULONG l=sizeof s; if (!CM_Get_DevNode_Registry_Property(parent,CM_DRP_CLASSGUID,0,s,&l,0)) { GUID guid; if (!CLSIDFromString(s,&guid)) pguid=&guid; // change pointer on success } } SetupDiGetClassImageIndex(&ild,pguid,&cbei.iImage); cbei.iSelectedImage=cbei.iImage; // Show descriptive string, not only COM%u SetupDiGetDeviceRegistryProperty(devs,&devInfo,SPDRP_FRIENDLYNAME,0,(PBYTE)s,sizeof s,0); // ab Windows 2k? // The COM number should bei included by help of device driver // If not, append it if (!StrStr(s,t)) { // Caution! StrStr(Haystack,Needle) vs. strstr(needle,haystack) int l=lstrlen(s); wnsprintf(s+l,elemof(s)-l,T(" (%s)"),t); } cbei.iItem=myFindIndex(hCombo,cbei.lParam); // helper for sorting by COM number SendMessage(hCombo,CBEM_INSERTITEM,0,(LPARAM)&cbei); if (UINT(cbei.lParam)!=nr) continue; ComboBox_SetCurSel(hCombo,cbei.iItem); } SetupDiDestroyDeviceInfoList(devs); } // Locates upcoming <t> in sorted list by ItemData, convergates fast static int myFindIndex(HWND hCombo, LPARAM t) { int l=0, r=ComboBox_GetCount(hCombo); while(l!=r) { int m=(l+r)>>1; COMBOBOXEXITEM cbei; cbei.mask=CBEIF_LPARAM; cbei.iItem=m; SendMessage(hCombo,CBEM_GETITEM,0,(LPARAM)&cbei); if (cbei.lParam>t) r=m; else l=m+1; } return l; } // My personal zero-based COM port opener; returns 0 on failure static HANDLE myOpen(UINT ComNr,DWORD dwFlags=0) { TCHAR s[12]; wnsprintf(s,elemof(s),T("\\\\.\\COM%u"),ComNr+1); HANDLE h=CreateFile(s,GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,dwFlags,0); if (h==INVALID_HANDLE_VALUE) h=0; return h; } // The ComboBoxEx needs such an ImageList static SP_CLASSIMAGELIST_DATA ild; // initialization somewhere before ild.cbSize=sizeof ild; SetupDiGetClassImageList(&ild); SendMessage(hCombo,CBEM_SETIMAGELIST,0,(LPARAM)ild.ImageList); // and SetupDiDestroyClassImageList(&ild) when done



-3
投票
© www.soinside.com 2019 - 2024. All rights reserved.