我的设备管理器中列出了以下串行端口:
- COM3
- COM4 (BT)
- COM5 (BT)
- COM6 (环球猪蹄 MO67xx - 控制接口)
- COM7 (环球猪蹄 MO67xx - 全球定位系统控制接口)
- COM8 (环球猪蹄 MO67xx - 全球定位系统数据接口)
- COM9 (环球猪蹄 MO67xx - 诊断接口)
- COM11 (USB 串行端口)
- COM12 (USB 串行端口)
- COM45 (SUNIX COM Port)
- COM46 (SUNIX COM Port)
SUNIX COM端口通过内部PCI卡连接。USB 串行端口通过 USB(FDTI 芯片)连接GlobeTrotter端口来自通过USB连接的GlobeTrotter设备。还列出了此调制解调器的调制解调器,USB设备和网络设备。
所以我有几种不同的串行端口来源。
我想做的只是使用 WMI 获取包含所有这些端口的列表。
对于我的测试,我正在使用WMI代码创建器
测试 1:
rootCIMV2
;查询: SELECT * FROM Win32_SerialPort
仅返回以下串口:
- COM3
- COM4
- COM5
测试 2:
rootWMI
;查询: SELECT * FROM MSSerial_PortName
仅返回以下串口:
- COM3
- COM11
- COM12
- COM45
- COM45
如何获取串行端口的完整列表?
我找到了解决方案。
以下查询 ( rootCIMV2
) 获取请求的结果:
SELECT * FROM Win32_PnPEntity WHERE ClassGuid="{4d36e978-e325-11ce-bfc1-08002be10318}"
更新
这个答案现在已经很老了。我问它我仍然必须考虑WinXP并且正在使用Windows7。由于我不再处理串行端口,因此我无法提供有关该问题的任何新信息。当时,此解决方案报告了设备管理器显示的所有端口。但我知道列出串行端口并不容易,所以这个答案可能并非在所有情况下都是正确的。
就我而言,我有物理串行端口、USB 串行端口和 com0com 虚拟串行端口。我需要全名和COM端口地址。
此答案中建议的查询找不到 com0com 端口。此答案中建议的查询需要管理员权限。
SELECT * FROM Win32_PnPEntity
找到所有设备。它像这样返回物理设备,地址可以从Caption
解析:
Serial Port for Barcode Scanner (COM13)
但是,对于com0com端口Caption
如下所示(无地址):
com0com - serial port emulator
SELECT * FROM Win32_SerialPort
返回地址(DeviceID
)以及全名(Name
)。但是,它只查找物理串行端口和com0com端口,而不是USB串行端口。
所以最后,我需要两个 WMI 调用:SELECT * FROM Win32_SerialPort
(地址为 DeviceID
)和SELECT * FROM Win32_PnPEntity WHERE Name LIKE '%(COM%'
(地址可以从Caption
解析)。我已经缩小了Win32_PnPEntity
呼叫的范围,因为它只需要查找在第一次呼叫中未找到的设备。
此C++代码可用于查找所有串行端口:
// 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 rootcimv2 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;
}
中使用的Win32_SerialPort
类报告物理 com 端口,如果要枚举包括USB-Serial/COM
端口在内的所有串行端口,则必须使用位于 rootwmi
命名空间中的 MSSerial_PortName
类。
还可以尝试位于同一命名空间中的这些类
-
MSSerial_CommInfo
-
MSSerial_CommProperties
-
MSSerial_HardwareConfiguration
-
MSSerial_PerformanceInformation
注意:如果你想知道这个类的属性和方法,你可以使用WMI Delphi Code Creator。
我在尝试让应用程序找到 USB 串行设备的 COM 端口时遇到了类似的问题。
通过使用查询SELECT * FROM Win32_PnPEntity WHERE ConfigManagerErrorCode = 0
的作用域\localhostrootCIMV2
,应用程序能够通过每个返回对象的caption
找到COM端口,或者通过检查设备名称的caption
来定位确切的端口。
ManagementObjectSearcher comPortSearcher = new ManagementObjectSearcher(@"\localhostrootCIMV2", "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);
}
}
}
}
}
解析代码位于 [C#] 如何按友好名称以编程方式查找 COM 端口