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.
Related
I am wanting to learn how to get asset tag and serial number from the bios of a local machine using c++. I have been searching online for almost an hour now and all I have come up with so far is a few script written in vb.
I am working on a Windows 7 computer, but would like it to work with Windows 8 as well.
You can use WMI
An example function would be something like the following source which I put together to obtain the serial number for a PC and it seems to work quite nicely. There is a fall back to pull a serial number from the Windows Registry however just ignore that as it is for a specialized environment.
# pragma comment(lib, "wbemuuid.lib")
static SHORT CDeviceConfigCheckSerialNumber (TCHAR *tcsSerialNo)
{
HRESULT hres;
IWbemLocator *pLoc = NULL;
*tcsSerialNo = 0; // initialze return string to empty string
hres = CoCreateInstance(
CLSID_WbemLocator,
0,
CLSCTX_INPROC_SERVER,
IID_IWbemLocator, (LPVOID *) &pLoc);
if (FAILED(hres))
{
return 1; // Program has failed.
}
// Step 4: -----------------------------------------------------
// Connect to WMI through the IWbemLocator::ConnectServer method
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 (FAILED(hres))
{
pLoc->Release();
return 2; // Program has failed.
}
// Step 5: --------------------------------------------------
// Set security levels on the proxy -------------------------
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 (FAILED(hres))
{
pSvc->Release();
pLoc->Release();
return 3; // Program has failed.
}
// Step 6: --------------------------------------------------
// Use the IWbemServices pointer to make requests of WMI ----
// For example, get the name of the operating system
IEnumWbemClassObject* pEnumerator = NULL;
hres = pSvc->ExecQuery(
bstr_t("WQL"),
// bstr_t("SELECT * FROM Win32_SystemEnclosure"),
// bstr_t("SELECT * FROM Win32_BaseBoard"),
bstr_t("SELECT * FROM Win32_BIOS"),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&pEnumerator);
if (FAILED(hres))
{
pSvc->Release();
pLoc->Release();
return 4; // Program has failed.
}
// Step 7: -------------------------------------------------
// Get the data from the query in step 6 -------------------
IWbemClassObject *pclsObj;
ULONG uReturn = 0;
SHORT sRetStatus = -100;
while (pEnumerator)
{
HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
if(0 == uReturn)
{
break;
}
VARIANT vtProp;
// Get the value of the Name property
hr = pclsObj->Get(L"SerialNumber", 0, &vtProp, 0, 0);
if (!FAILED(hres)) {
switch (vtProp.vt) {
case VT_BSTR:
_tcscpy (tcsSerialNo, vtProp.bstrVal);
sRetStatus = 0;
break;
}
}
VariantClear(&vtProp);
pclsObj->Release();
}
pEnumerator->Release();
// Cleanup
// ========
pSvc->Release();
pLoc->Release();
// if the search for a serial number in the BIOS did not provide
// something then lets see if the NCR Retail Systems Manager has
// put a serial number into the Windows Registry.
if (sRetStatus != 0) {
ULONG ulCount = 0;
TCHAR lpszValue[256] = {0};
ScfGetRsmValue (L"SerialNumber", &ulCount, lpszValue);
if (ulCount > 0) {
_tcscpy (tcsSerialNo, lpszValue);
sRetStatus = 0;
}
}
return sRetStatus;
}
I would like to change the performance and behaviour of my C++ application, according to whether the system drive is an SSD or not.
Example:
With SSD, I want my gameserver application to load each map fully, with all objects in order to maximize performance.
With HDD, I want my gameserver application to load only the essential objects and entities in each map, with no external objects loaded.
I've seen http://msdn.microsoft.com/en-gb/library/windows/desktop/aa364939(v=vs.85).aspx, which is a way of determining if a certain drive is a HDD, CD ROM, DVD ROM, Removable Media, etc, but it STILL can't detect whether the main system drive is an SSD.
I've also seen Is there any way of detecting if a drive is a SSD?, but the solution only applies to Linux.
I thought that I could somehow generate a large fine (500MB), and then time how long it takes to write the file, but however other system variables can easily influence the result.
In Windows, using C++, is there any way to get whether the main system drive is an SSD or not?
Having done some research and using the info from the answers on this page, here's my implementation using C WinAPIs for Windows 7 and later:
//Open drive as such: "\\?\PhysicalDriveX" where X is the drive number
//INFO: To get drive number from a logical drive letter, check this method:
// (But keep in mind that a single logical drive, or a volume,
// can span across several physical drives, as a "spanned volume.")
// http://stackoverflow.com/a/11683906/843732
#include <WinIoCtl.h>
#include <Ntddscsi.h>
DWORD bytesReturned;
//As an example, let's test 1st physical drive
HANDLE hDevice = ::CreateFile(L"\\\\?\\PhysicalDrive0",
GENERIC_READ | GENERIC_WRITE, //We need write access to send ATA command to read RPMs
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
OPEN_EXISTING, 0, NULL);
if(hDevice != INVALID_HANDLE_VALUE)
{
//Check TRIM -- should be Y for SSD
_tprintf(L"TRIM=");
STORAGE_PROPERTY_QUERY spqTrim;
spqTrim.PropertyId = (STORAGE_PROPERTY_ID)StorageDeviceTrimProperty;
spqTrim.QueryType = PropertyStandardQuery;
bytesReturned = 0;
DEVICE_TRIM_DESCRIPTOR dtd = {0};
if(::DeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY,
&spqTrim, sizeof(spqTrim), &dtd, sizeof(dtd), &bytesReturned, NULL) &&
bytesReturned == sizeof(dtd))
{
//Got it
_tprintf(L"%s", dtd.TrimEnabled ? L"Y" : L"N");
}
else
{
//Failed
int err = ::GetLastError();
_tprintf(L"?");
}
//Check the seek-penalty value -- should be N for SSD
_tprintf(L", seekPenalty=");
STORAGE_PROPERTY_QUERY spqSeekP;
spqSeekP.PropertyId = (STORAGE_PROPERTY_ID)StorageDeviceSeekPenaltyProperty;
spqSeekP.QueryType = PropertyStandardQuery;
bytesReturned = 0;
DEVICE_SEEK_PENALTY_DESCRIPTOR dspd = {0};
if(::DeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY,
&spqSeekP, sizeof(spqSeekP), &dspd, sizeof(dspd), &bytesReturned, NULL) &&
bytesReturned == sizeof(dspd))
{
//Got it
_tprintf(L"%s", dspd.IncursSeekPenalty ? L"Y" : L"N");
}
else
{
//Failed
int err = ::GetLastError();
_tprintf(L"?");
}
//Get drive's RPMs reading -- should be 1 for SSD
//CODE SOURCE: https://emoacht.wordpress.com/2012/11/06/csharp-ssd/
_tprintf(L", RPM=");
ATAIdentifyDeviceQuery id_query;
memset(&id_query, 0, sizeof(id_query));
id_query.header.Length = sizeof(id_query.header);
id_query.header.AtaFlags = ATA_FLAGS_DATA_IN;
id_query.header.DataTransferLength = sizeof(id_query.data);
id_query.header.TimeOutValue = 5; //Timeout in seconds
id_query.header.DataBufferOffset = offsetof(ATAIdentifyDeviceQuery, data[0]);
id_query.header.CurrentTaskFile[6] = 0xec; // ATA IDENTIFY DEVICE
bytesReturned = 0;
if(::DeviceIoControl(hDevice, IOCTL_ATA_PASS_THROUGH,
&id_query, sizeof(id_query), &id_query, sizeof(id_query), &bytesReturned, NULL) &&
bytesReturned == sizeof(id_query))
{
//Got it
//Index of nominal media rotation rate
//SOURCE: http://www.t13.org/documents/UploadedDocuments/docs2009/d2015r1a-ATAATAPI_Command_Set_-_2_ACS-2.pdf
// 7.18.7.81 Word 217
//QUOTE: Word 217 indicates the nominal media rotation rate of the device and is defined in table:
// Value Description
// --------------------------------
// 0000h Rate not reported
// 0001h Non-rotating media (e.g., solid state device)
// 0002h-0400h Reserved
// 0401h-FFFEh Nominal media rotation rate in rotations per minute (rpm)
// (e.g., 7 200 rpm = 1C20h)
// FFFFh Reserved
#define kNominalMediaRotRateWordIndex 217
_tprintf(L"%d", (UINT)id_query.data[kNominalMediaRotRateWordIndex]);
}
else
{
//Failed
int err = ::GetLastError();
_tprintf(L"?");
}
_tprintf(L"\n");
::CloseHandle(hDevice);
}
In case you don't have driver DDK includes, here're some definitions:
#ifndef StorageDeviceTrimProperty
#define StorageDeviceTrimProperty 8
#endif
#ifndef DEVICE_TRIM_DESCRIPTOR
typedef struct _DEVICE_TRIM_DESCRIPTOR {
DWORD Version;
DWORD Size;
BOOLEAN TrimEnabled;
} DEVICE_TRIM_DESCRIPTOR, *PDEVICE_TRIM_DESCRIPTOR;
#endif
#ifndef StorageDeviceSeekPenaltyProperty
#define StorageDeviceSeekPenaltyProperty 7
#endif
#ifndef DEVICE_SEEK_PENALTY_DESCRIPTOR
typedef struct _DEVICE_SEEK_PENALTY_DESCRIPTOR {
DWORD Version;
DWORD Size;
BOOLEAN IncursSeekPenalty;
} DEVICE_SEEK_PENALTY_DESCRIPTOR, *PDEVICE_SEEK_PENALTY_DESCRIPTOR;
#endif
struct ATAIdentifyDeviceQuery
{
ATA_PASS_THROUGH_EX header;
WORD data[256];
};
Lastly, conclusion of my tests.
I have several Samsung SSDs connected via a SATA cable, and one PCIe SSD drive that is connected directly to the logic board using PCIe slot. I also have one large internal Western Digital HDD (spinning drive) that is also connected via a SATA cable, and a couple of external spinning HDDs.
Here's what I get for them:
Samsung SSD 256GB: TRIM=Y, seekPenalty=N, RPM=1
Samsung SSD 500GB: TRIM=Y, seekPenalty=N, RPM=1
PCIs SSD: TRIM=Y, seekPenalty=?, RPM=0
Internal WD HDD: TRIM=N, seekPenalty=?, RPM=0
External WD HDD: TRIM=?, seekPenalty=?, RPM=?
External Cavalry HDD: TRIM=?, seekPenalty=Y, RPM=?
So as you see, in my case, the only parameter that is correct for all 6 drives is TRIM. I'm not saying that it will be in your case as well. It's just my finding with the drives that I own.
I believe you are using the wrong tool. Instead of making assumptions based on a drive being an SSD you should make your code work well with slow and fast drives, for example by loading the essential objects first and the rest later. In three years the invention of [...] may make regular hard drives faster than SSDs which would break your code.
Going purely based on speed will also work for RAM discs, NFS, USB3.0-sticks and other stuff you didn't or cannot thing about.
EDIT: A HDD is not actually the same as a slow SSD. While they are both fast at reading and writing a HDD needs significant time for seeking. It thus makes sense to use two different access strategies: picking the important data via random access for the SSD and sequentially reading for the HDD. You will probably get away with only implementing the sequential strategy as that should still work ok with SSDs. It makes more sense to check for a HDD instead of a SSD though, because you need to treat the HDD special while SSD, RAMdisc, NFS and so on should not suffer from seek times and can thus be treated the same.
You can use the Microsoft WMI Class MSFT_PhysicalDisk. The mediatype of 4 is SSD and SpindleSpeed will be 0.
Yes, there is a high chance of determining whether a drive is an SSD. SSD typically support the TRIM command, so I would check to see if the drive supports the TRIM command.
In Windows, you can use IOCTL_STORAGE_QUERY_PROPERTY to get the DEVICE_TRIM_DESCRIPTOR structure which will tell you if TRIM is enabled.
If you really know what you're doing, you can get the raw IDENTIFY DEVICE package, and interpret the data yourself. For SATA drives it would be word 169 bit 0.
do not bother of drive type. make a measurement by reading some of your game data that is loaded anyways and decide which strategy to use. (do not forget to make an configuration option :)
nether the less my gut instinct tells me that the approach is wrong. if someone has a slow disk then preloading should be more important since on the fly loading will cause stuttering. On the other side if the drive is fast enough i do not need to waste memory because i can load data on the fly fast enough.
The best way I found was using the MSFT_PhysicalDisk in the ROOT\microsoft\windows\storage namespace with WMI
This gives you two properties
SpindleSpeed
MediaType
The Media Type gives you values
0 Unspecified
3 HDD
4 SSD
5 SCM
And a spindle speed of 0 is pretty self-explanatory
Main.cpp
#include <iostream>
#include <windows.h>;
#include <Wbemidl.h>
#include <comdef.h>
#include "StorageDevice.h"
#include <vector>
#pragma comment(lib, "wbemuuid.lib")
using namespace::std;
void IntializeCOM()
{
HRESULT hres;
hres = CoInitializeEx(0, COINIT_MULTITHREADED);
if (FAILED(hres))
{
cout << "Failed to initialize COM library. Error code = 0x" << hex << hres << endl;
// Program has failed.
}
// Step 2: --------------------------------------------------
// Set general COM security levels --------------------------
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 (FAILED(hres))
{
cout << "Failed to initialize security. Error code = 0x" << hex << hres << endl;
CoUninitialize(); // Program has failed.
}
}
void SetupWBEM(IWbemLocator*& pLoc, IWbemServices*& pSvc)
{
// Step 3: ---------------------------------------------------
// Obtain the initial locator to WMI -------------------------
HRESULT hres;
//IWbemLocator *pLoc = NULL;
hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *)&pLoc);
if (FAILED(hres))
{
cout << "Failed to create IWbemLocator object." << " Err code = 0x" << hex << hres << endl;
CoUninitialize();
}
// Step 4: -----------------------------------------------------
// Connect to WMI through the IWbemLocator::ConnectServer method
//IWbemServices *pSvc = NULL;
// Connect to the ROOT\\\microsoft\\windows\\storage namespace with
// the current user and obtain pointer pSvc
// to make IWbemServices calls.
hres = pLoc->ConnectServer(
_bstr_t(L"ROOT\\microsoft\\windows\\storage"), // 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 (FAILED(hres))
{
cout << "Could not connect. Error code = 0x" << hex << hres << endl;
pLoc->Release();
CoUninitialize();
}
// Step 5: --------------------------------------------------
// Set security levels on the proxy -------------------------
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 (FAILED(hres))
{
cout << "Could not set proxy blanket. Error code = 0x" << hex << hres << endl;
pSvc->Release();
pLoc->Release();
CoUninitialize();
}
}
int main()
{
IWbemLocator *wbemLocator = NULL;
IWbemServices *wbemServices = NULL;
IntializeCOM();
SetupWBEM(wbemLocator, wbemServices);
IEnumWbemClassObject* storageEnumerator = NULL;
HRESULT hres = wbemServices->ExecQuery(
bstr_t("WQL"),
bstr_t("SELECT * FROM MSFT_PhysicalDisk"),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&storageEnumerator);
if (FAILED(hres))
{
cout << "Query for MSFT_PhysicalDisk. Error code = 0x" << hex << hres << endl;
wbemServices->Release();
wbemLocator->Release();
CoUninitialize();
}
IWbemClassObject *storageWbemObject = NULL;
ULONG uReturn = 0;
vector<StorageDevice> storageDevices;
while (storageEnumerator)
{
HRESULT hr = storageEnumerator->Next(WBEM_INFINITE, 1, &storageWbemObject, &uReturn);
if (0 == uReturn || hr != S_OK)
{
break;
}
StorageDevice storageDevice;
VARIANT deviceId;
VARIANT busType;
VARIANT healthStatus;
VARIANT spindleSpeed;
VARIANT mediaType;
storageWbemObject->Get(L"DeviceId", 0, &deviceId, 0, 0);
storageWbemObject->Get(L"BusType", 0, &busType, 0, 0);
storageWbemObject->Get(L"HealthStatus", 0, &healthStatus, 0, 0);
storageWbemObject->Get(L"SpindleSpeed", 0, &spindleSpeed, 0, 0);
storageWbemObject->Get(L"MediaType", 0, &mediaType, 0, 0);
storageDevice.DeviceId = deviceId.bstrVal == NULL ? "" : _bstr_t(deviceId.bstrVal);
storageDevice.BusType = busType.uintVal;
storageDevice.HealthStatus = healthStatus.uintVal;
storageDevice.SpindleSpeed = spindleSpeed.uintVal;
storageDevice.MediaType = mediaType.uintVal;
storageDevices.push_back(storageDevice);
storageWbemObject->Release();
}
}
The programs stores the disk properties in a strongly typed object "storageDevice" here, and pushed it onto a vector so we can use it later
StorageDevice.h
#pragma once
#include <iostream>
using namespace::std;
class StorageDevice
{
public:
StorageDevice();
~StorageDevice();
string DeviceId;
int BusType;
int HealthStatus;
int SpindleSpeed;
int MediaType;
};
StorageDevice.cpp
#include "StorageDevice.h"
StorageDevice::StorageDevice()
{
}
StorageDevice::~StorageDevice()
{
}
Video tutorial and source code c++ download here
I have the following serial ports listed in my devicemanager:
COM3
COM4 (BT)
COM5 (BT)
COM6 (GlobeTrotter MO67xx - Control Interface)
COM7 (GlobeTrotter MO67xx - GPS Control Interface)
COM8 (GlobeTrotter MO67xx - GPS Data Interface)
COM9 (GlobeTrotter MO67xx - Diagnostics Interface)
COM11 (USB Serial Port)
COM12 (USB Serial Port)
COM45 (SUNIX COM Port)
COM46 (SUNIX COM Port)
The SUNIX COM ports are connected via an internal PCI-Card.
The USB Serial Port is connected via USB (FDTI-chip)
The GlobeTrotter ports are from a GlobeTrotter device connected via USB. There are also a modem, a USB-device and a network device listed for this modem.
So I have several different sources of serial ports.
All I want to do is to get a list containing all those ports using WMI.
For my tests I am using WMI Code Creator
Test 1:
root\CIMV2; Query: SELECT * FROM Win32_SerialPort only returns the following serial ports:
COM3
COM4
COM5
Test 2:
root\WMI; Query: SELECT * FROM MSSerial_PortName only returns the following serial ports:
COM3
COM11
COM12
COM45
COM45
How can I get a complete list of serial ports?
I found the solution.
The following query (root\CIMV2) gets the requested results:
SELECT * FROM Win32_PnPEntity WHERE ClassGuid="{4d36e978-e325-11ce-bfc1-08002be10318}"
Update
This answer is pretty old now. Ehen I asked it I still had to consider WinXP and was using Windows7.
Since I don't deal with serial ports any more, I can't give any new information on that issue. At that time this solution reported all ports that the devicemanager was showing. But I know listing serial ports is not that easy so this answer might not be correct in all scenarios.
In my case, I have physical serial ports, USB serial ports, and com0com virtual serial ports. I need both the full names and COM port addresses.
The query suggested in this answer does not find com0com ports. The query suggested in this answer requires Administrator priviledges.
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;
}
The Win32_SerialPort class used in this article reports the physical com ports, if you wanna enumerate all the serial ports including the USB-Serial/COM ports, you must use the MSSerial_PortName class located in the root\wmi namespace.
Also try these classes located in the same namespace
MSSerial_CommInfo
MSSerial_CommProperties
MSSerial_HardwareConfiguration
MSSerial_PerformanceInformation
Note : If you want to know the properties and methods of this class you can use the WMI Delphi Code Creator.
I had a similar issues trying to have an application locate the COM port for a USB Serial device.
By using the scope \\localhost\root\CIMV2 for the query SELECT * FROM Win32_PnPEntity WHERE ConfigManagerErrorCode = 0, the application was able to find COM ports via each returned object's caption or locate the exact port by checking the caption for the device name.
ManagementObjectSearcher comPortSearcher = new ManagementObjectSearcher(#"\\localhost\root\CIMV2", "SELECT * FROM Win32_PnPEntity WHERE ConfigManagerErrorCode = 0");
using (comPortSearcher)
{
string caption = null;
foreach (ManagementObject obj in comPortSearcher.Get())
{
if (obj != null)
{
object captionObj = obj["Caption"];
if (captionObj != null)
{
caption = captionObj.ToString();
if (caption.Contains("CH340"))
{
_currentSerialSettings.PortName = caption.Substring(caption.LastIndexOf("(COM")).Replace("(", string.Empty).Replace(")", string.Empty);
}
}
}
}
}
The parsing code was found at [C#] How to programmatically find a COM port by friendly name
I am writing a VC++ program which needs to change Network Connection name (eg. "Local Connection").
The Windows's IPHELPER provides API to get network connection, but it doesn't provide write method.
And I know I can use "netsh" to change this network connection name, yes, in English Windows, this method works properly, but in Japanese or Chinese Windows this method can't work properly.
After read some documents in MSDN, I found WMI Win32_NetworkAdapter class has a property "NetConnectionID". This property contains the adapter's network connection name, and its read/write perperty.
http://msdn.microsoft.com/en-us/library/windows/desktop/aa394216(v=vs.85).aspx
NetConnectionID
Data type: string
Access type: Read/write
So, I wrote a WMI code to change this property value.
int RasWmiAdapterSetInfo(void *pWmiAdapterCfg)
{
if(pWmiAdapterCfg)
{
int retVal = -1;
HRESULT hres;
VARIANT varWrite;
RAS_WMI_W32_ADAPTER_PTR *pRasWmiAdapterCfg = (RAS_WMI_W32_ADAPTER_PTR*)pWmiAdapterCfg;
VariantInit(&varWrite);
V_VT(&varWrite) = VT_BSTR;
V_BSTR(&varWrite) = SysAllocString(L"My New Connection 1");
hres = pRasWmiAdapterCfg->pclsObj->Put(L"NetConnectionID", 0, &varWrite, 0);
if(FAILED(hres))
{
printf("RasWmiAdapterSetInfo Failed HR=%08x\n", hres);
goto CLEARUP;
}
retVal = 0;
CLEARUP:
VariantClear(&varWrite);
return retVal;
}
return -1;
}
The return value hres was OK, but this property value was not changed.
IWbemLocator *pLoc;
IWbemServices *pSvc;
IEnumWbemClassObject *pEnumerator;
...
... (ignore some code to init pLoc, pSvc, pEnumerator)
...
IWbemClassObject *pclsObj;
IWbemClassObject *pClass;
pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
pSvc->GetObject( bstr_t(adapterClsName), 0, NULL, &pClass, NULL );
Is anyone can tell me why the pclsObj->Put method did not take effect?
Thanks.
As far as I understand, IWbemClassObject::Put() only updates a class instance's property value in memory. To commit changes to WMI repository, you need to call IWbemServices::PutInstance() to update entire class instance.
So adding something like this to your code should get it to work:
IWbemCallResult* callResult = nullptr;
HRESULT hr = wbemSvc_->PutInstance(pClass, WBEM_FLAG_UPDATE_ONLY,
nullptr, &callResult);
if (SUCCEEDED(hr) && callResult)
callResult->Release();
Hope that helps!
i am using msdn WMI sample code to get the list of devices that show up in the device manager with yellow exclamation mark but its only returning a list of all properly
installed devices on the machine. Could anyone here help me reslove this problem only using c++.
Thanks
void GetUnKnownDeviceList()
{
HRESULT hres;
hres = CoInitializeEx(0, COINIT_MULTITHREADED);
hres = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT,
RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);
IWbemLocator *pLoc = 0;
hres = CoCreateInstance( CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *) &pLoc);
IWbemServices *pSvc = 0;
hres = pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"),NULL,NULL,0,NULL,0,0,&pSvc);
hres = CoSetProxyBlanket(pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL,
RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);
IEnumWbemClassObject* pEnumerator = NULL;
hres = pSvc->ExecQuery(
bstr_t("WQL"),
// bstr_t("SELECT * FROM Win32_PnPEntity"), DOES NOT LIST DEVICES SHOWING WITH YELLOW EXCLAMATION MARK
bstr_t("SELECT * FROM Win32_PnPEntity WHERE ConfigManagerErrorCode = 0"), // LIST ONLY PROPERLY INSTALLED DEVICES
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&pEnumerator);
IWbemClassObject *pclsObj;
ULONG uReturn = 0;
while(1)
{
HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
VARIANT vtProp;
hr = pclsObj->Get(L"Name", 0, &vtProp, 0, 0);
TRACE("Device Name : %s\r\n\", vtProp.bstrVal);
VariantClear(&vtProp);
pclsObj->Release();
}
}
To list he devices that aren’t working yoou need query for all devices with a ConfigManagerErrrorCode other than 0, you must modify your WQL sentence to.
SELECT * FROM Win32_PnPEntity WHERE ConfigManagerErrorCode <> 0
Why don't you try using setupapi for this? I think you should be able to enumerate the device node list of known and unknown devices with it.
it's possible to use wmi with this example in c#:
/ Query the device list trough the WMI. If you want to get
// all the properties listen in the MSDN article mentioned
// below, use "select * from Win32_PnPEntity" instead!
ManagementObjectSearcher deviceList =
new ManagementObjectSearcher("Select Name, Status from Win32_PnPEntity");
// Any results? There should be!
if ( deviceList != null )
// Enumerate the devices
foreach ( ManagementObject device in deviceList.Get() )
{
// To make the example more simple,
string name = device.GetPropertyValue("Name").ToString();
string status = device.GetPropertyValue("Status").ToString();
// Uncomment these lines and use the "select * query" if you
// want a VERY verbose list
// foreach (PropertyData prop in device.Properties)
// Console.WriteLine( "\t" + prop.Name + ": " + prop.Value);
// More details on the valid properties:
// http://msdn.microsoft.com/en-us/library/aa394353(VS.85).aspx
Console.WriteLine( "Device name: {0}", name );
Console.WriteLine( "\tStatus: {0}", status );
// Part II, Evaluate the device status.
bool working = (( status == "OK" ) || ( status == "Degraded" )
|| ( status == "Pred Fail" ));
Console.WriteLine( "\tWorking?: {0}", working );
}
http://www.codeproject.com/Articles/30031/Query-hardware-device-status-in-C
just change the status or ConfigManagerErrorCode to get the kind of error tou look for.