Click here to Skip to main content
Click here to Skip to main content

Enumerating serial ports - W2K style.

By , 20 Jul 2002
 

Introduction

You might think that determining which serial ports are present on a Windows PC would be an easy task. It seems like a reasonable enough thing for the OS to support. Unfortunately, there was no support for it at all (short of reading the registry yourself) before Win2K, and even then, the API is a bit cumbersome.

The attached serial port enumeration code first determines which operating system it is running under, and then runs the appropriate routine to enumerate the serial ports. In Win 9x (and Me) it uses the registry. In W2K and later it uses the SetupAPI that was included in that version of the WinSDK. It also has support for "brute force" enumeration of serial ports under NT4.

Unfortunately, I statically linked with setupapi.lib, so the provided executable won't actually run under 95 and nt4 (I didn't really need to support those OS's for my application.) This could be finagled by replacing the SetupDi* function calls with dynamic binding via LoadLibrary if needed.

To use the EnumSerial code, simply include EnumSerial.cpp and .h in your project, and link with setupapi.lib in the win32 sdk (this is under "additional dependencies" in the project link settings in Visual Studio).

All you have to do now is #include "EnumSerial.h" in your source code, allocate an empty CArray of SSerInfo structs, and make a call to EnumSerialPorts. It will populate your array with filled-out SSerInfo structs which contain the following information:

CString strDevPath;      // Device path for use with CreateFile()
CString strPortName;     // Simple name (i.e. COM1)
CString strFriendlyName; // Full name to be displayed to a user
BOOL bUsbDevice;         // Provided through a USB connection?
CString strPortDesc;     // friendly name without the COMx

Example

CArray<SSerInfo,SSerInfo&> asi;
EnumSerialPorts(asi,FALSE/*include all*/);

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Zach Gorman
United States United States
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionEnumSerial classmemberalkam11 Aug '11 - 9:52 
does anyone know how to modify the enumseral code so that only the usb port can be populated on the output screen? i've been trying to modify it but haven't find any luck.
GeneralERROR_INVALID_USER_BUFFERmemberMaciej Polanczyk11 Dec '08 - 20:55 
Hi
 
In VS2008 C++ this code works fine but in Borland C++ 6.0 SetupDiGetDeviceInterfaceDetail fails with ERROR_INVALID_USER_BUFFER.
I also tried this:
SetupDiGetDeviceInterfaceDetail(hDevInfo, &ifcData, NULL, 0, &dwDetDataSize, &devdata);
pDetData = (SP_DEVICE_INTERFACE_DETAIL_DATA*) new char[dwDetDataSize];
pDetData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
SetupDiGetDeviceInterfaceDetail(hDevInfo, &ifcData, pDetData, dwDetDataSize, &dwDetDataSize, &devdata);
 
and it failed too with same error.
Do you have any idea why?
GeneralQuestion on bidirectional USBmembergbeez22 May '08 - 10:49 
I have a situation in which I need to communicate with USB printer bidirectionally. This I can do, but there may be 2 identical printers on one system. I need to be able to recognize which is which based on friendly (spooler) name for printer.
 
Anyone?
QuestionHow can I get Serial port properties?membermazhar_cse4 Feb '08 - 19:08 
Hello,
I don't find here strPortName the simple port name. also i need the baudrate,data bits, parity, stop bits, flow control etc properties.
 
Can u help me in this regard?
 

Thanks in advance
Mazhar
QuestionLNK2019 errormemberbob go4 Nov '07 - 19:46 
The link error occurred while the source was done with vc++2005 in build.
QuestionTest for connection to USB busmembersay.hello5 Oct '06 - 8:51 
What is the benefit of testing if the COM port has a connection to the USB bus?
Is this useful to distinguish virtual COM ports that actually are USB devices from "real" COM ports?

GeneralObtaining the PortNamememberPatrickA26 Nov '04 - 1:01 
Does anyone know how to obtain the PortName (i.e. COM1, COM2...) using this SetupDi.. API. I can obviously parse it from the friendly name but is this the right way to go?
GeneralRemoval relationsmemberyonalevine9 Nov '04 - 22:29 
1. I'm using CM_Get_Device_ID_List_Size to get the Removal Relations of devices. The function works fine on XP, but in win2k, I wasn't able to get the information (it returns no removal relations)
2. I want to disable the use of USB 2.0, So I want to disable the Enhaced Host Controller, Does anyone know of a way to check which of the Host Controllers is an Enhanced Host Controller?
Thanx,
Yona
GeneralUsing SetupAPI Functionssussmajid taghiloo7 Jun '04 - 23:03 
D'Oh! | :doh: hi
i try to write program for installing RamDrive Driver by using SetupAPI Function , but i can't Because i have not Good Sample code or Document about SetupAPI Functions ,like SetupInstallFile and ...
please help me
mtaghiloo@yahoo.com
 
majid taghiloo
GeneralClaim/Release SerialPortmemberorfeas2913 Apr '04 - 0:08 
This is a general question regarding serial ports..
How do you actually release and claim a serial port e.g. COM5.
I have implemented a VirtualSerialPort and everytime I use a different port e.g. COM5, COM6
the ports stay registered and I cannot use them next time I reboot the system as they are registered in the registry.
Is there a way of releasing those ports without having to delete them from the registry?
Anyone have a clue?
 
Hint: I want to change the status of the COM port in the "Port Settings->Advanced->Com Port Number" from "(in use)"
into a clear field for each COM port
 

P.S. CreateFile does not give you a handle if e.g. COM5 port was used by a device which is not present. So
this port must be released in some way first..
GeneralUSB/Serial cables in Win98 &amp; WinMEmemberHoward C. Anderson10 Nov '03 - 5:26 
Hi. There are at least 15 types of USB/Serial cables available at the moment. Very few of them seem to conform to ANYthing "standard".
 
For example, the KEYSPAN USA19QW has its own non-conforming registry key: HKLM\Enum\KEYSPAN\*USA19WMAP\00_00. This key cannot be found in Win98 and WinME by EnumSerial currently because of this line:
 
BOOL bMatch = (strcmp(acSubSubEnum,"*PNP0500")==0 ||
strcmp(acSubSubEnum,"*PNP0501")==0 ||
bUsbDevice);
 
I was able to find the KEYSPAN cable and presumably all other USB/Serial cables (assuming they do not depart even further from the "standard") by setting:
 
bMatch = TRUE;
 
(This will slow down the performance slightly of course since now more subkeys must be searched. However this is apparently what Device Manager is doing or KEYSPAN would not have been able to place their key where they have placed it...)
 
I also then had to filter out LPT ports and anything non-"COM" in SearchPnpKeyW9x:
 
if (!bDup) {
// Add an entry to the array
if(strPortName.Left(3) == "COM"){//Add COM ports only
asi.Add(si);
}
}
 
Of course if there is some serial port that has a name that does NOT start with COM, it would then not be listed... (Something I read indicated that COM ports do not HAVE to be named "COM" ports... Why are "standards" so non-standard?)
 
This is not the appropriate thread but I have discovered MANY problems with USB/Serial cables. Very few behave like a UART. For example, if you wire CTS to RTS on the Keyspan cable then assert RTS, the cable cannot see its own RTS signal on the CTS pin until 24 milliseconds have elapsed... (We use this to see if the user has our board plugged in...) Lots of little odds and ends that make these cables a nightmare if your software has to support their use...
 
Thanks,
 
Howard

 
Howard in Arizona
http://www.astroshow.com
QuestionHow to identify application which has access to port?memberJoerg Hoffmann10 Apr '03 - 23:35 
For diagnosis it could be helpfull to get the application which
blocks an comprt.
 
Any idea?
AnswerRe: How to identify application which has access to port?memberZach Gorman11 Apr '03 - 5:00 
I wish I knew how - I've often wanted this for files. Windows tells me a file is in use, but not which process has it open.
QuestionAny idea how to get friendly names on NT?memberJoerg Hoffmann10 Apr '03 - 4:54 
Many thanks to Zach Gorman.
You did really a good job.
 
Because of my need for using it on Windows NT machines I've tried to replace the fixed linking to setupapi.lib to the LoadLibrary stuff.
(Don't know if it really works on W2K / not testet at the moment)
 
Know I' searching for a possibility to get the friendly names on NT, too?
 
How can I do this?
AnswerRe: Any idea how to get friendly names on NT?memberZach Gorman10 Apr '03 - 6:19 
On the LoadLibrary note, it turns out that there is an easier way in Visual Studio 6 (I assume it's there in .net too but haven't tried it). In the Link tab of 'Project Settings' for all builds, include 'delayimp.lib' under "Object/library modules." Then, at the bottom of "Project Options" add '/delayload:setupapi.dll'
 
This invokes the compiler's built-in support for delay loading; your program will not attempt to load setupapi.dll until you call a function that lives in it. Since no code in the NT4 execution path calls SetupDi functions, this enables NT4 support.
 
Regarding friendly names on NT4, I wasn't able to find any during my preliminary search. If they exist at all, you may be able to find them by digging around in the registry; my suggestion is to crack open a copy of regedit and search for COM1 on an NT4 box.
 
If they exist in the registry, you can add a function to dig them out based on EnumSerial's W98 code. If not, you can always just make up a friendly name - most ports end up being called "Communications Port" anyway.
GeneralRe: Any idea how to get friendly names on NT?memberJoerg Hoffmann10 Apr '03 - 20:22 
Zach Gorman wrote:
On the LoadLibrary note, it turns out that there is an easier way in Visual Studio 6 (I assume it's there in .net too but haven't tried it). In the Link tab of 'Project Settings' for all builds, include 'delayimp.lib' under "Object/library modules." Then, at the bottom of "Project Options" add '/delayload:setupapi.dll'
 
Thank you for this tip. It works good.Laugh | :laugh:
But for my use it will be better to use it with LoadLibrary because it will be easier to share the code between different projects or with other people here on this site.
 
It seems that on NT no friendly names are used. But there is another way to enumerate serial ports using "QueryDosDevice()".
 
I'll try to add it to your code on weekend and send it to you by email.
 
Bye
GeneralFTDI(Win98) and PCI modem(WinXP)sussPabloII22 Dec '02 - 20:59 
It doesn't found USB <-> Serial converter in Win 98. The key of FTDI converter is placed in the HKLM\Enum\FTDIBUS section.
 
Other thing: I'm looking for a solution to exclude modems from obtained list. I have PCI modem wich was also detected as a port in Win XP. There is a problem with it, because it takes a lot of time to operate on it.
 
P_awe_L
GeneralRe: FTDI(Win98) and PCI modem(WinXP)memberZach Gorman7 Jan '03 - 9:40 
Sorry for the delayed reply. I tested the prolific & aten usb2serial converters; I haven't tried the ftdi. If you have a code patch to add ftdi compatibility, post it here.
 
Regarding filtering out modems, the only approach I can think of off the top of my head is to hack it and look for the word "modem" in the device description.
GeneralRe: FTDI(Win98) and PCI modem(WinXP)memberChristian B21 Apr '03 - 9:05 
Hi,
 
Regarding modems, why not try to enumerate the MODEMS class instead of the PORTS class.
 
regards
// Christian
GeneralRe: FTDI(Win98) and PCI modem(WinXP)memberSamuliA4 Sep '06 - 23:26 
I have an Edgeport USB Converter with 4 serial ports, which are not found by the code.
 
I can sometimes find the ports reading straight from registry HKLM\HARDWARE\DEVICEMAP\SERIALCOMM,
but this seems not to be foolproof, as sometimes the ports are not found.
Anyone know why?
GeneralRe: FTDI(Win98) and PCI modem(WinXP)membergrandmasta14 Nov '07 - 21:32 
ftdichip.com
GeneralData Structure Item Missing in EnumPortsWdmmemberavidgator7 Aug '02 - 5:25 
The "strPortName" part of the SSerInfo structure does not seem to get populated in the EnumPortsWdm method. It does in SearchPnpKeyW9x and EnumPortsWNt4 (although that one is a bit of a no-brainer.) Is this an oversight? What is the "reccomended" method for pulling the port name using the Wdm method? I imagine it could be stripped off of the Firendly Name, but only when the port is not taken up by a device (a practical example is a friendly name of "Xircom Cardbus Ethernet 100 + Modem 56 (Modem Interface)" when the same item is inserted into the PCMCIA slot of a laptop. It "ties up" COM2, but I cannot determine COM2 from the Friendly Name or the Port Desc...)
 
Ideas? Suggestions? Thanks much
 
--JMG
GeneralRe: Data Structure Item Missing in EnumPortsWdmmemberZach Gorman10 Aug '02 - 7:36 
The SetupDi API didn't provide any simple port name, so I left it out of the WDM method. I didn't need it for my application, so I gave up without much of a fight.
 
Perhaps the information you need is available through the SetupDiGetDeviceRegistryProperty( ..., SPDRP_LOCATION_INFORMATION, ...) call.
GeneralRe: Data Structure Item Missing in EnumPortsWdmmemberChristian B21 Apr '03 - 9:02 
Hi,
 
Attached is my version of portlister for w2k that I used a couple of years ago. There is a SetupApi for W9x aswell that can be used with the same result, not suplied here.
 
Pros:
It uses the ClassName "PORTS" instead of the hard-coded guid.
It also extracts the PortName from the registry in a proper way.
 
Enjoy!
// Christian
 

// portlister.cpp : Defines the entry point for the console application.
//
 
#include "stdafx.h"
#include "tchar.h"
#include "setupapi.h"
 
int main(int argc, char* argv[])
{
GUID ClassGuid[1];
DWORD dwRequiredSize;
BOOL bRet;
HDEVINFO DeviceInfoSet = NULL;

SP_DEVINFO_DATA DeviceInfoData;
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
 
DWORD dwMemberIndex = 0;
 
// Get ClassGuid from ClassName for PORTS class
bRet = SetupDiClassGuidsFromName(_T("PORTS"), (LPGUID)&ClassGuid, 1, &dwRequiredSize);
if (!bRet) goto cleanup;
 
// Get class devices
DeviceInfoSet = SetupDiGetClassDevs(ClassGuid, NULL, NULL, DIGCF_PROFILE);
 
if (DeviceInfoSet)
{
// Enumerate devices
dwMemberIndex = 0;
while (SetupDiEnumDeviceInfo(DeviceInfoSet, dwMemberIndex++, &DeviceInfoData))
{
TCHAR szFriendlyName[MAX_PATH];
TCHAR szPortName[MAX_PATH];
TCHAR szMessage[MAX_PATH];
DWORD dwReqSize = 0;
DWORD dwPropType;
DWORD dwType = REG_SZ;
HKEY hKey = NULL;
 
// Get friendlyname
bRet = SetupDiGetDeviceRegistryProperty(DeviceInfoSet,
&DeviceInfoData,
SPDRP_FRIENDLYNAME,
&dwPropType,
(LPBYTE)szFriendlyName,
sizeof(szFriendlyName),
&dwReqSize);

// Open device parameters reg key
hKey = SetupDiOpenDevRegKey(DeviceInfoSet,
&DeviceInfoData,
DICS_FLAG_GLOBAL,
0,
DIREG_DEV,
KEY_READ);
if (hKey)
{
// Qurey for portname
dwReqSize = sizeof(szPortName);
long lRet = RegQueryValueEx(hKey,
_T("PortName"),
0,
&dwType,
(LPBYTE)&szPortName,
&dwReqSize);

// Close reg key
RegCloseKey(hKey);
}

wsprintf(szMessage, _T("Name: %s\nPort: %s\n"), szFriendlyName, szPortName);
MessageBox(NULL, szMessage, _T("Found Port"), MB_OK);
}
}
 
cleanup:
// Destroy device info list
SetupDiDestroyDeviceInfoList(DeviceInfoSet);
 
return 0;
}

GeneralRe: Data Structure Item Missing in EnumPortsWdmmemberPeter Wurmsdobler10 Jun '04 - 3:40 
Hi,
 
I have added parts of the proposed code to my EnumSerial, in
 
void EnumPortsWdm(CArray &asi)
 
right before
 
if
(bSuccess) { // ...
 
// Open device parameters reg key
HKEY hKey = SetupDiOpenDevRegKey
(hDevInfo, &devdata, DICS_FLAG_GLOBAL,0,DIREG_DEV,KEY_READ);
 
TCHAR szPortName[MAX_PATH];
if (hKey)
{
DWORD dwType = REG_SZ;
DWORD dwReqSize = sizeof(szPortName);
 
// Query for portname
long lRet = RegQueryValueEx
(hKey,_T("PortName"), 0, &dwType, (LPBYTE)&szPortName, &dwReqSize);
if (lRet == ERROR_SUCCESS)
bSuccess &= TRUE;
else
bSuccess &= FALSE;
}
else
bSuccess &= FALSE;
 

and add the folllowing line in
 

if (bSuccess) {
si.strPortName = szPortName;
 


General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130516.1 | Last Updated 21 Jul 2002
Article Copyright 2002 by Zach Gorman
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid