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

k10s72fa  于 2024-01-06  发布在  Windows
关注(0)|答案(8)|浏览(128)

有几种方法可以在Windows下列出串行端口,但我不确定哪种方法是正确的:检测所有可用串行端口的方法。
一个很好的代码示例是http://www.naughter.com/enumser.html-其中有9种(九种!)枚举串行设备的方法。
问题是:什么是最佳的做法。
要求:

  • 不打开端口以检查它们是否可用。
  • 以便能够检测具有不同于COMx的名称的端口。
  • 在Windows XP SP2或更高版本上工作
pjngdqdw

pjngdqdw1#

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

}

字符串

ccrfmcuu

ccrfmcuu2#

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

#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;
}

字符串

fcipmucu

fcipmucu3#

如果您可以访问注册表,则HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM项包含Windows当前支持的COM端口列表(在某些情况下,此信息可能过时/不正确;例如,我怀疑提供串行端口的即插即用设备尚未完成检测/安装或最近已被删除)。
这是.NET Framework的SerialPort.GetPortNames()方法报告可用COM端口的方式,并且上述信息是从链接页派生的。

eoxn13cs

eoxn13cs4#

这是@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

mxg2im7a

mxg2im7a5#

串行端口是非常简单的设备,可以追溯到计算硬件的石器时代。它们不支持即插即用,没有办法知道有人插入了设备。您唯一能做的就是发现哪些端口可用,SerialPort.GetPortNames()返回列表。一些USB模拟器可以生成一个描述性名称来搭配端口名,您可以使用WMI发现那些端口。Win32_SerialPort类别。
这些都不能帮助你发现哪个COM端口连接到了一个特定的设备上。只有一个人知道,她实际上把电缆插入了连接器。你需要提供一个配置UI,让用户选择端口号。一个组合框完成了这项工作。在你的配置数据中保存选择,很可能在你的程序下次启动时,该设备仍然连接到同一个端口。

hc8w905p

hc8w905p6#

枚举COM端口的正确方法是使用SetupDi函数。尝试使用SetupFile()打开COM端口对于多达256个端口名称来说太慢,并且会跳过已经打开的端口。
对于Windows,所有串行端口都有一个与“COM%u”匹配的别名。当然!从Windows 98开始,就提供了必要的SetupDi函数集,因此您的软件可以保持相当高的向后兼容性级别。只有在需要DOS或Windows < 98时,打开枚举才是可以的,而且速度很快,因为这些系统只支持COM 4。
这里有一个填充ComboBoxEx的例子:

#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

字符串
x1c 0d1x的数据

ohtdti5x

ohtdti5x7#

您可以检查Windows注册表库以列出所有COM端口。

ybzsozfc

ybzsozfc8#

CUIntArray ports;
EnumerateSerialPorts(ports);

for (int i = 0; i<ports.GetSize(); i++)
{
    CString str;
    str.Format(_T("COM%d"), ports.ElementAt(i));
    m_ctlPort.AddString(str);
}

字符串

相关问题