Modem not responsive from win32 api - winapi

Hi
I have a problem with a modem and win32 api.
When I open the comm port from my source code the port is ready to receive commands but the modem does not write output. All at commands I write are processed by the modem but I don´t receive the output from the modem.
If I connect the modem using hyperterminal, disconect and then use my software, then the modem works succesfully.
My code is as follows:
bool open() {
unsigned long confSize = sizeof(COMMCONFIG);
winCommConfig.dwSize = confSize;
DWORD dwFlagsAndAttributes = 0;
if (!isOpen()) {
winHandle = CreateFileA(port.toAscii(), GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
dwFlagsAndAttributes, NULL);
if (winHandle != INVALID_HANDLE_VALUE) {
/*
* Configure the port settings.
*/
GetCommConfig(winHandle, &winCommConfig, &confSize);
GetCommState(winHandle, &(winCommConfig.dcb));
/*
* Configure port parameters.
*/
winCommConfig.dcb.fBinary = TRUE;
winCommConfig.dcb.fInX = FALSE;
winCommConfig.dcb.fOutX = FALSE;
winCommConfig.dcb.fAbortOnError = FALSE;
winCommConfig.dcb.fNull = FALSE;
setBaudRate(settings.BaudRate);
setDataBits(settings.DataBits);
setStopBits(settings.StopBits);
setParity(settings.Parity);
setFlowControl(settings.FlowControl);
setTimeout(settings.TimeoutMillisec);
/*
* Set the final parameters.
*/
SetCommConfig(winHandle, &winCommConfig, sizeof(COMMCONFIG));
SetCommState(winHandle, &(winCommConfig.dcb));
}
}
return isOpen();
}
Where:
port represents the comm port
baudrate: 115200
databits: 8
stopbits: 1
parity: none
flowcontrol: off
What am I doing wrong?
This happens if I use jablocom gdp-04. When I use other modems (huawei, novatel, ...) it works perfectly.
Thanks in advance.
Regards.

flowcontrol: off
The device won't send anything until it sees the RTS and DTR signals active, indicating that you are "online" and ready to receive. You will need to set fRtsControl = RTS_CONTROL_ENABLE and fDtrEnable = DTR_CONTROL_ENABLE. Enabling hardware handshaking never hurts btw, assuming the device implements it properly, especially handy while debugging. Also do a basic check with HyperTerminal or Putty to ensure that the wiring is okay.

Related

Three com ports communication programs in Visual Studio 2019

If there are three com ports ex, COM10, COM11, COM12.
I want to connect all NEW three com ports and sequentially transfer data to COM10, COM11, COM12.
Should I use CreateFile three times?
// my source
HANDLE m_hComm;
m_hComm = CreateFile(szPort, // communication port string (COMX)
GENERIC_READ | GENERIC_WRITE, // read/write types
0, // comm devices must be opened with exclusive access
NULL, // no security attributes
OPEN_EXISTING, // comm devices must use OPEN_EXISTING
FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, // Async I/O
0); // template must be 0 for comm devices
How to set the handle value for each com port when you want to transmit data sequentially to COM10, COM11, COM12?
// my source
n = WriteFile(m_hComm, pBuff, nToWrite, &dwWritten,&m_ovrWrite);
Calling CreateFile three times before using any if the ports is the best way to achieve what you want. Otherwise, you would have to repeatedly 'create' each port, send ('write') the data then close each port's handle (with a call to CloseHandle) - and this will be far less efficient.
(Note: For COM ports with an index greater than 9, you should use the "\\.\COMxx" syntax.)
So, to open the three ports together, you can use an array of handles (and an array of port names); like this:
char szPort[3][10] = { "\\\\.\\COM10", "\\\\.\\COM11", "\\\\.\\COM12" };
HANDLE m_hComm[3];
for (int i = 0; i < 3; ++i) {
m_hComm[i] = CreateFile(szPort[i], // communication port string (COMX)
GENERIC_READ | GENERIC_WRITE, // read/write types
0, // comm devices must be opened with exclusive access
NULL, // no security attributes
OPEN_EXISTING, // comm devices must use OPEN_EXISTING
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, // Async I/O
0); // template must be 0 for comm devices
}
You can then send data to any one of the open ports using the appropriate value from the m_hComm array. Thus, to send the same data to each of the three ports, you could use a loop like the following:
for (int p = 0; p < 3; ++p) { // Loop to send the same data to all three ports...
//...
BOOL n = WriteFile(m_hComm[p], pBuff, nToWrite, &dwWritten, &m_ovrWrite);
//...
}
Please feel free to ask for further clarification and/or explanation.

QEMU Cannot Open Tap Device in Windows 10 Host

I am trying to bridge my QEMU VM to a local adapter in my Windows 10 host, but QEMU complains that the interface name provided by ifname option to the -netdev argument cannot be opened. I have followed the answer in https://superuser.com/questions/1317652/how-to-set-up-nat-for-qemu-with-tap-backend-windows-10, but with no luck. I have cross-compiled QEMU from source to debug this behavior, for whether or not the interface name can be detected, and apparently QEMU can find the adapter's name, but not the TAP device file. To further explain my point, here's a code snippet from net/tap-win32.c:595 from the QEMU 4.2.0 source code (latest release version as of this writing), specifically tap_win32_open function, and I will highlight where it was failing in this function (look for // THIS IS WHERE IT WILL FAIL. comments):
Testing with a correct existing network interface:
static int tap_win32_open(tap_win32_overlapped_t **phandle,
const char *preferred_name)
{
...
rc = get_device_guid(device_guid, sizeof(device_guid), name_buffer, sizeof(name_buffer));
if (rc)
return -1;
snprintf (device_path, sizeof(device_path), "%s%s%s",
USERMODEDEVICEDIR,
device_guid,
TAPSUFFIX);
handle = CreateFile (
device_path,
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
0 );
if (handle == INVALID_HANDLE_VALUE) {
return -1; // THIS IS WHERE IT WILL FAIL.
}
...
Testing with an incorrect (non-existent) network interface:
static int tap_win32_open(tap_win32_overlapped_t **phandle,
const char *preferred_name)
{
...
rc = get_device_guid(device_guid, sizeof(device_guid), name_buffer, sizeof(name_buffer));
if (rc)
return -1; // THIS IS WHERE IT WILL FAIL.
snprintf (device_path, sizeof(device_path), "%s%s%s",
USERMODEDEVICEDIR,
device_guid,
TAPSUFFIX);
handle = CreateFile (
device_path,
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
0 );
if (handle == INVALID_HANDLE_VALUE) {
return -1;
}
...
QEMU uses the prefix USERMODEDEVICEDIR, which is \\.\Global\ and a suffix .tap to the device's GUID to create the device's path in Windows. For example, the network adapter I am dealing with results in the following device path: \\.\Global\{990DA322-3986-4854-AE93-1D6FB0BFA137}.tap. Any idea why CreateFile always results in INVALID_HANDLE_VALUE on the device path? By the way, GetLastError() returns 2, which from learn.microsoft.com means the following:
...
ERROR_FILE_NOT_FOUND
2 (0x2)
The system cannot find the file specified.
...
I apologize. I was attaching to a non-TAP adapter, thinking it will acquire the address from it, much like VirtualBox. I did not entirely understand the bridging concept before in QEMU, but now I should attach to a TAP adapter, and bridge that adapter to another preferred adapter.

Need to write driver for USB peripheral device?

I'm working on designing a USB peripheral which will occasionally connect to a Windows PC, and transfer a few KB of data in each direction. There will be a custom PC application that controls the data transfers, using a proprietary protocol (i.e. for the USB payloads).
I see at the following link that Microsoft describes how to write a driver for a USB device. But do I need one?
Developing Windows client drivers for USB devices
The PC application is the only application that we intend to know how to communicate with the device, so there's no need for a driver from an application sharing standpoint.
Can I just bake the custom protocol directly into the application, have the application speak "raw USB" to the device, and do without a separate driver?
"raw USB", no, you can't do that from an application.
But because you control the device also, you can make it appear as one of the device classes for which Windows provides a device driver that's generic enough to do just about anything you want.
Those device classes are HID (Human Interface Device) and "WinUSB". Of these, HID is cross-platform but more limited in capability, WinUSB allows high performance data transfers as well as interrupt endpoints.
Instructions for setting up your device's string descriptors so that Windows automatically binds it to the WinUSB driver are on MSDN
A WinUSB device is a Universal Serial Bus (USB) device whose firmware defines certain Microsoft operating system (OS) feature descriptors that report the compatible ID as "WINUSB".
The purpose of a WinUSB device is to enable Windows to load Winusb.sys as the device's function driver without a custom INF file. For a WinUSB device, you are not required to distribute INF files for your device, making the driver installation process simple for end users.
There is another way with no need to write driver to write what You want to device using WriteFile function:WinUSB, how to do this:
Include WinUsb.h
Add WinUsb.lib to the list of linked libraries.
in Usb100.h some macros.
Use the device interface GUID to obtain the device path. The correct GUID is the one that you specified in the INF that was used to install WinUsb.sys.
Get a handle to the device information set by passing the device interface GUID that you defined in the INF to SetupDiGetClassDevs. The function returns an HDEVINFO handle.
Call SetupDiEnumDeviceInterfaces to enumerate the system’s device interfaces and obtain information on your device interface.
Call SetupDiGetDeviceInterfaceDetail to get detailed data for the device interface.
Call the GetDevicePath function to obtain the device path.
Pass the device path to CreateFile to obtain a file handle for the device. Use ReadFile and Write File to communicate with device!
Pass the file handle to WinUsb_Initialize to initialize WinUSB and obtain a WinUSB handle. You use the device’s WinUSB handle to identify the device when you call WinUSB API functions, not the device’s file handle.
For more advanced solutions - use functions:
WinUsb_QueryDeviceInformation to obtain the device’s speed.
WinUsb_QueryInterfaceSettings to obtain the corresponding interface descriptors. The WinUSB handle corresponds to the first interface.
WinUsb_QueryPipe gets information about each endpoint.
WinUsb_WritePipe writes the buffer to the device - default behavior: zero-length writes are forwarded down the stack. If the transfer length is greater than a maximum transfer length, WinUSB divides the request into smaller requests of maximum transfer length and submits them serially.
more functions and info: http://download.microsoft.com/download/9/c/5/9c5b2167-8017-4bae-9fde-d599bac8184a/winusb_howto.docx
For debugging purposes You probably need:
winusbtrace_tool https://blogs.msdn.microsoft.com/usbcoreblog/2010/02/05/how-to-generate-and-view-a-winusb-debug-trace-log/;
Wireshark https://www.wireshark.org with USBPcap plugin.
Other Example:
http://searchingforbit.blogspot.com/2012/04/winusb-communication-with-stm32-part-1.html.
Sample template comes with Visual Studio.
You need also have knowledge of writing .inf files.
Another easy way to communicate with USB - libusb-win32 https://sourceforge.net/projects/libusb-win32/
My simple console app sends chunks to device (raw data write immediately to device bypassing the stack):
#include "stdafx.h"
#include <SetupAPI.h>
#include <Hidsdi.h>
#include <devguid.h>
#include <winusb.h>
#include <usb.h>
#pragma comment(lib, "hid.lib")
#pragma comment(lib, "setupapi.lib")
#pragma comment(lib, "winusb.lib")
#include <iUString.h>
iString<char> DevicePath;
bool WinusbHandle_Open=false;
bool DeviceHandle_Open = false;
WINUSB_INTERFACE_HANDLE WinusbHandle;
HANDLE DeviceHandle;
UCHAR usb_out_buffer[64];
DEFINE_GUID(GUID_DEVCLASS_WINUSB, 0x88bae032L, 0x5a81, 0x49f0, 0xbc, 0x3d, 0xa4, 0xff, 0x13, 0x82, 0x16, 0xd6);
DEFINE_GUID(GUID_DEVCLASS_STL, 0xf177724dL, 0x74d3, 0x430e, 0x86, 0xb5, 0xf0, 0x36, 0x89, 0x10, 0xeb, 0x23);
GUID winusb_guid;
GUID stl_guid;
bool connectusb();
void disconnectusb();
int main()
{
DWORD n;
DWORD numEvents;
HANDLE rHnd;
WinusbHandle_Open = false;
DeviceHandle_Open = false;
winusb_guid = GUID_DEVCLASS_WINUSB;
stl_guid = GUID_DEVCLASS_STL;
usb_out_buffer[0] = 0;
usb_out_buffer[1] = 1;
usb_out_buffer[2] = 2;
usb_out_buffer[3] = 3;
ULONG bytesWritten;
ULONG timeout;
timeout = 100;
rHnd = GetStdHandle(STD_INPUT_HANDLE);
WinUsb_SetPipePolicy(WinusbHandle, 0x01, PIPE_TRANSFER_TIMEOUT, sizeof(ULONG), &timeout);
timeout = TRUE;
WinUsb_SetPipePolicy(WinusbHandle, 0x01, AUTO_CLEAR_STALL, sizeof(ULONG), &timeout);
timeout = TRUE;
WinUsb_SetPipePolicy(WinusbHandle, 0x01, RAW_IO, sizeof(ULONG), &timeout);//Bypasses queuing and error handling to boost performance for multiple read requests.
while (true)
{
if ((!WinusbHandle_Open) || (!WinusbHandle_Open)) { if (!connectusb())Sleep(2000); }
if ((!WinusbHandle_Open) || (!WinusbHandle_Open))continue;
bytesWritten = 0;
if (!WinUsb_WritePipe(WinusbHandle, 0x01, &usb_out_buffer[0], 2, &bytesWritten, NULL))
{
n = GetLastError();
disconnectusb();
}
Sleep(2000);
}
disconnectusb();
return 0;
}
bool connectusb()
{
BOOL bResult = FALSE;
HDEVINFO deviceInfo;
SP_DEVICE_INTERFACE_DATA interfaceData;
PSP_DEVICE_INTERFACE_DETAIL_DATA detailData = NULL;
DWORD n;
SP_DEVINFO_DATA devinfo;
BYTE devdetailbuffer[4096];
bool found;
deviceInfo = SetupDiGetClassDevs(&stl_guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (deviceInfo == INVALID_HANDLE_VALUE) { return false; }
found = false;
for (n = 0;; n++)
{
interfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
if (!SetupDiEnumDeviceInterfaces(deviceInfo, NULL, &stl_guid, n, &interfaceData))
{
n = GetLastError();
break;
}
detailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)devdetailbuffer;
detailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
devinfo.cbSize = sizeof(devinfo);
if (!SetupDiGetDeviceInterfaceDetail(deviceInfo, &interfaceData, detailData, sizeof(devdetailbuffer), NULL, &devinfo)) { printf("SetupDiGetDeviceInterfaceDetail: %u\n", GetLastError()); break; }
if (IsEqualGUID(devinfo.ClassGuid, winusb_guid))
{
if ((-1 != iStrPos(detailData->DevicePath, "VID_0483")) || (-1 != iStrPos(detailData->DevicePath, "vid_0483")))
{
if ((-1 != iStrPos(detailData->DevicePath, "PID_576B")) || (-1 != iStrPos(detailData->DevicePath, "pid_576b")))
{
DevicePath = detailData->DevicePath;
found = true;
break;
}
}
}
}
SetupDiDestroyDeviceInfoList(deviceInfo);
if (!found)return false;
DeviceHandle = CreateFile(DevicePath.Buffer() ,
GENERIC_WRITE | GENERIC_READ,
FILE_SHARE_WRITE | FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
NULL);
if (INVALID_HANDLE_VALUE == DeviceHandle) {
n = GetLastError();
}
if (INVALID_HANDLE_VALUE == DeviceHandle) return false;
DeviceHandle_Open = true;
if (!WinUsb_Initialize(DeviceHandle, &WinusbHandle))
{
n = GetLastError();
CloseHandle(DeviceHandle); DeviceHandle_Open = false;
return false;
}
WinusbHandle_Open = true;
return true;
}
void disconnectusb()
{
if (WinusbHandle_Open) { WinUsb_Free(WinusbHandle); WinusbHandle_Open = false; }
if (DeviceHandle_Open) { CloseHandle(DeviceHandle); DeviceHandle_Open = false; }
}
Making your firmware to be enumerated as a WINUSB (winusb generic driver) device makes life easier.
I believe it'd be clear if you have a demo and code so I made one for you :)
My KEIL project using the STM32F4 Discovery board working with WINUSB as an USB CDC device. You can see more information and have the source code from my GitHub.

Install winusb driver from user application

Please can anyone give me direct for realization next functional for Windows.
I have USB device which connects to the PC (it is JTAG programmer.) I know VID and PID of this hardware. I need:
1 Check what type of driver this hardware use (detecting winusb driver or not will be enough. Maybe do I need to read registry?)
2 If driver is not winusb I need to install winusb driver for this USB device from my application.
The current driver assigned to the device is stored in the registry, so you could read it directly from there. However, it is probably better to use SetupAPI, an API provided by Microsoft. The function to call is SetupDiGetDeviceRegistryProperty, and the third argument should be SPDRP_SERVICE. This will return the name of the driver as a string. Note that you will need to call several other SetupAPI functions before you have all the pieces of info you need to call SetupDiGetDeviceRegistryProperty.
I have not tried it, but libwdi has features for installing WinUSB onto a device node. It might also have functions for getting the current driver, so you should try using it before you spend too much time learning SetupAPI. The devcon utility from Microsoft (which is open source now) might be another option.
Without knowing the details of what you are doing, I question whether you really need to do this. It might be simpler to provide a signed driver package to users and instruct them to use the "Update Driver Software..." option from the Device Manager to apply it to particular device.
I made first part of task.
#ifdef Q_OS_WIN
DEFINE_GUID(GUID_DEVCLASS_WINUSB,0x88BAE032,0x5A81,0x49f0,
0xBC,0x3D,0xA4,0xFF,0x13,0x82,0x16,0xD6);
#endif
bool WinUSB::isWinUsbDriver(quint16 vid, quint16 pid)
{
#ifndef Q_OS_WIN
Q_UNUSED(vid);
Q_UNUSED(pid);
return true;
#else
HDEVINFO deviceInfoSet;
GUID *guidDev = (GUID*) &GUID_DEVCLASS_WINUSB;
deviceInfoSet = SetupDiGetClassDevs(guidDev, NULL, NULL, DIGCF_PRESENT | DIGCF_PROFILE);
DWORD buffersize =4000;
TCHAR buffer [buffersize];
int memberIndex = 0;
bool retval = false;
QString vidPid;
vidPid = "VID_" + QString("%1").arg(vid,4,16,QChar('0')) + "&";
vidPid += "PID_" + QString("%1").arg(pid,4,16,QChar('0'));
while (true)
{
SP_DEVINFO_DATA deviceInfoData;
ZeroMemory(&deviceInfoData, sizeof(SP_DEVINFO_DATA));
deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
if (SetupDiEnumDeviceInfo(deviceInfoSet, memberIndex, &deviceInfoData) == FALSE) {
if (GetLastError() == ERROR_NO_MORE_ITEMS) {
break;
}
}
DWORD nSize=0 ;
SetupDiGetDeviceInstanceId (deviceInfoSet, &deviceInfoData, buffer, sizeof(buffer), &nSize);
buffer [nSize] ='\0';
QString str = QString::fromWCharArray(buffer);
if (str.indexOf(vidPid) >= 0) {
retval = true;
break;
}
memberIndex++;
}
if (deviceInfoSet) {
SetupDiDestroyDeviceInfoList(deviceInfoSet);
}
return retval;
#endif
}

How do I get a list of available serial ports in Win32?

I have some legacy code that provides a list of the available COM ports on the PC by calling the EnumPorts() function and then filtering for the port names that start with "COM".
For testing purposes it would be very useful if I could use this code with something like com0com, which provides pairs of virtual COM ports looped together as a null-modem.
However the com0com ports are not found by the EnumPorts() function (even without filtering for "COM"). HyperTerminal and SysInternals PortMon can both see them, so I'm sure it is installed correctly.
So is there some other Win32 function that provides a definitive list of available serial ports?
The EnumSerialPorts v1.20 suggested by Nick D uses nine different methods to list the serial ports! We're certainly not short on choice, though the results seem to vary.
To save others the trouble, I'll list them here and indicate their success in finding the com0com ports on my PC (XP Pro SP2):
CreateFile("COM" + 1->255) as suggested by Wael Dalloul
✔ Found com0com ports, took 234ms.
QueryDosDevice()
✔ Found com0com ports, took 0ms.
GetDefaultCommConfig("COM" + 1->255)
✔ Found com0com ports, took 235ms.
"SetupAPI1" using calls to SETUPAPI.DLL
✔ Found com0com ports, also reported "friendly names", took 15ms.
"SetupAPI2" using calls to SETUPAPI.DLL
✘ Did not find com0com ports, reported "friendly names", took 32ms.
EnumPorts()
✘ Reported some non-COM ports, did not find com0com ports, took 15ms.
Using WMI calls
✔ Found com0com ports, also reported "friendly names", took 47ms.
COM Database using calls to MSPORTS.DLL
✔/✘ Reported some non-COM ports, found com0com ports, took 16ms.
Iterate over registry key HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM
✔ Found com0com ports, took 0ms. This is apparently what SysInternals PortMon uses.
Based on those results I think the WMI method probably suits my requirements best as it is relatively fast and as a bonus it also gives the friendly names (e.g. "Communications Port (COM1)", "com0com - serial port emulator").
It appears that it's not a simple task.
Check out this: EnumSerialPorts v1.20
you can make loop for example from 1 to 50 and try to open each port. If the port is available, the open will work. If the port is in use, you'll get a sharing error. If the port is not installed, you'll get a file not found error.
to open the port use CreateFile API:
HANDLE Port = CreateFile(
"\\\\.\\COM1",
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
then check the result.
In my case, I need both the full names and COM port addresses. I have physical serial ports, USB serial ports, and com0com virtual serial ports.
Like the accepted answer suggests, I use WMI calls. SELECT * FROM Win32_PnPEntity find all devices. It returns physical devices like this, and address can be parsed from Caption:
Serial Port for Barcode Scanner (COM13)
However, for com0com ports Caption is like this (no address):
com0com - serial port emulator
SELECT * FROM Win32_SerialPort returns addresses (DeviceID), as well as full names (Name). However, it only finds physical serial ports and com0com ports, not USB serial ports.
So in the end, I need two WMI calls: SELECT * FROM Win32_SerialPort (address is DeviceID) and SELECT * FROM Win32_PnPEntity WHERE Name LIKE '%(COM%' (address can be parsed from Caption). I have narrowed down the Win32_PnPEntity call, because it only needs to find devices that were not found in the first call.
This C++ code can be used to find all serial ports:
// Return list of serial ports as (number, name)
std::map<int, std::wstring> enumerateSerialPorts()
{
std::map<int, std::wstring> result;
HRESULT hres;
hres = CoInitializeEx(0, COINIT_APARTMENTTHREADED);
if (SUCCEEDED(hres) || hres == RPC_E_CHANGED_MODE) {
hres = CoInitializeSecurity(
NULL,
-1, // COM authentication
NULL, // Authentication services
NULL, // Reserved
RPC_C_AUTHN_LEVEL_DEFAULT, // Default authentication
RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation
NULL, // Authentication info
EOAC_NONE, // Additional capabilities
NULL // Reserved
);
if (SUCCEEDED(hres) || hres == RPC_E_TOO_LATE) {
IWbemLocator *pLoc = NULL;
hres = CoCreateInstance(
CLSID_WbemLocator,
0,
CLSCTX_INPROC_SERVER,
IID_IWbemLocator, (LPVOID *) &pLoc);
if (SUCCEEDED(hres)) {
IWbemServices *pSvc = NULL;
// Connect to the root\cimv2 namespace with
// the current user and obtain pointer pSvc
// to make IWbemServices calls.
hres = pLoc->ConnectServer(
bstr_t(L"ROOT\\CIMV2"), // Object path of WMI namespace
NULL, // User name. NULL = current user
NULL, // User password. NULL = current
0, // Locale. NULL indicates current
NULL, // Security flags.
0, // Authority (for example, Kerberos)
0, // Context object
&pSvc // pointer to IWbemServices proxy
);
if (SUCCEEDED(hres)) {
hres = CoSetProxyBlanket(
pSvc, // Indicates the proxy to set
RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx
RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx
NULL, // Server principal name
RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx
RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
NULL, // client identity
EOAC_NONE // proxy capabilities
);
if (SUCCEEDED(hres)) {
// Use Win32_PnPEntity to find actual serial ports and USB-SerialPort devices
// This is done first, because it also finds some com0com devices, but names are worse
IEnumWbemClassObject* pEnumerator = NULL;
hres = pSvc->ExecQuery(
bstr_t(L"WQL"),
bstr_t(L"SELECT Name FROM Win32_PnPEntity WHERE Name LIKE '%(COM%'"),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&pEnumerator);
if (SUCCEEDED(hres)) {
constexpr size_t max_ports = 30;
IWbemClassObject *pclsObj[max_ports] = {};
ULONG uReturn = 0;
do {
hres = pEnumerator->Next(WBEM_INFINITE, max_ports, pclsObj, &uReturn);
if (SUCCEEDED(hres)) {
for (ULONG jj = 0; jj < uReturn; jj++) {
VARIANT vtProp;
pclsObj[jj]->Get(L"Name", 0, &vtProp, 0, 0);
// Name should be for example "Serial Port for Barcode Scanner (COM13)"
const std::wstring deviceName = vtProp.bstrVal;
const std::wstring prefix = L"(COM";
size_t ind = deviceName.find(prefix);
if (ind != std::wstring::npos) {
std::wstring nbr;
for (size_t i = ind + prefix.length();
i < deviceName.length() && isdigit(deviceName[i]); i++)
{
nbr += deviceName[i];
}
try {
const int portNumber = boost::lexical_cast<int>(nbr);
result[portNumber] = deviceName;
}
catch (...) {}
}
VariantClear(&vtProp);
pclsObj[jj]->Release();
}
}
} while (hres == WBEM_S_NO_ERROR);
pEnumerator->Release();
}
// Use Win32_SerialPort to find physical ports and com0com virtual ports
// This is more reliable, because address doesn't have to be parsed from the name
pEnumerator = NULL;
hres = pSvc->ExecQuery(
bstr_t(L"WQL"),
bstr_t(L"SELECT DeviceID, Name FROM Win32_SerialPort"),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&pEnumerator);
if (SUCCEEDED(hres)) {
constexpr size_t max_ports = 30;
IWbemClassObject *pclsObj[max_ports] = {};
ULONG uReturn = 0;
do {
hres = pEnumerator->Next(WBEM_INFINITE, max_ports, pclsObj, &uReturn);
if (SUCCEEDED(hres)) {
for (ULONG jj = 0; jj < uReturn; jj++) {
VARIANT vtProp1, vtProp2;
pclsObj[jj]->Get(L"DeviceID", 0, &vtProp1, 0, 0);
pclsObj[jj]->Get(L"Name", 0, &vtProp2, 0, 0);
const std::wstring deviceID = vtProp1.bstrVal;
if (deviceID.substr(0, 3) == L"COM") {
const int portNumber = boost::lexical_cast<int>(deviceID.substr(3));
const std::wstring deviceName = vtProp2.bstrVal;
result[portNumber] = deviceName;
}
VariantClear(&vtProp1);
VariantClear(&vtProp2);
pclsObj[jj]->Release();
}
}
} while (hres == WBEM_S_NO_ERROR);
pEnumerator->Release();
}
}
pSvc->Release();
}
pLoc->Release();
}
}
CoUninitialize();
}
if (FAILED(hres)) {
std::stringstream ss;
ss << "Enumerating serial ports failed. Error code: " << int(hres);
throw std::runtime_error(ss.str());
}
return result;
}
It's available now in Windows, GetCommPorts can directly return a list of comm ports
Gets an array that contains the well-formed COM ports.
This function obtains the COM port numbers from the
HKLM\Hardware\DeviceMap\SERIALCOMM registry key and then writes them
to a caller-supplied array. If the array is too small, the function
gets the necessary size.
you will need to add this code in order to link the function correctly
#pragma comment (lib, "OneCore.lib")
I have reorganized PJ Naughter 's EnumSerialPorts as more portable and individual forms, that is more useful.
For better in compatibility, I use C, instead of C++.
If you need or be interested in it, please visit the post in my blogger.

Resources