如何使用 WMI 找到使用 CH340 芯片的 Arduino 的 COM 端口

问题描述 投票:0回答:1

我正在为 Windows 10 机器使用 C# 编写代码。我正在使用 Paul van Dinther 的“SerialPortManager.cs”类。它使用“System.Management”,并开始使用带有查询字符串的 WMI 扫描连接的 USB 串行端口设备:

SELECT DeviceID, PNPDeviceID FROM Win32_SerialPort

该类还提供了一种检测USB串口设备连接或断开的方法。它通过使用 ManagementEventWatcher 来实现此目的,其中“eventType”是“__InstanceCreationEvent”(用于检测连接)或“__InstanceDeletionEvent”(用于检测断开连接)。

private ManagementEventWatcher USBWatcherSetUp(string eventType)
{
    _watcherQuery = new WqlEventQuery();
    _watcherQuery.EventClassName = eventType;
    _watcherQuery.WithinInterval = new TimeSpan(0, 0, 2);
    _watcherQuery.Condition = @"TargetInstance ISA 'Win32_SerialPort'";
    return new ManagementEventWatcher(_scope, _watcherQuery);
}

到目前为止一切顺利。它非常适合我使用 PIC 微控制器(板上有 USB 串行端口)的设备,并且也适用于本机 Arduino。

除此之外,我有一个 Arduino 克隆,它使用 CH340 芯片连接到 USB 端口。我已经为其安装了特定的驱动程序。它与我的 Arduino IDE 完美连接,并显示为 COM6。

但是我的代码没有检测到它。

我将所有出现的“Win32_SerialPort”替换为“Win32_PnPEntity”,并将初始搜索查询更改为:

SELECT DeviceID, PNPDeviceID FROM Win32_PnPEntity WHERE Name LIKE ‘%COM([0-9]%’

但没有成功。然后我把它改为:

SELECT DeviceID, PNPDeviceID FROM Win32_PnPEntity

然后我看到我的基于 CH340 的设备被检测到,如果我断开/重新连接,我会看到事件被触发。但使用 Win32_PnPEntity 的问题是我不知道 COM 端口是什么。我需要该 COM 端口能够与代码另一部分中的“SerialPort”类一起使用。

这里有一些代码来解释我在做什么。基本上,当我连接/断开设备时,我会检索 ManagementBaseObject 并将其传递给方法“CreatePortArgs”。

private void HandlePortAdded(object sender, EventArrivedEventArgs e)
{
    var instance = e.NewEvent.GetPropertyValue("TargetInstance") as ManagementBaseObject;
    SerialPortEventArgs serialPortEventArgs = CreatePortArgs(instance);
    if (CheckIDMatch(serialPortEventArgs))
    {
        OnPortAddedEvent?.Invoke(this, serialPortEventArgs);
    }
}

在“CreatePortArgs”方法中,我提取 VID、PID、SerialNumber,最后使用“DeviceID”检索 COM 端口:

private SerialPortEventArgs CreatePortArgs(ManagementBaseObject queryObj)
{
    string PNPDeviceID = ((string)queryObj.GetPropertyValue("PNPDeviceID")).ToUpper();
    int vid = 0;
    int pid = 0;
    string serialNumber = "";

    int index = PNPDeviceID.IndexOf("VID_");
    if (index > -1 && PNPDeviceID.Length >= index + 8)
    {
        string id = PNPDeviceID.Substring(index + 4, 4);
        vid = Convert.ToInt32(id, 16);
    }

    index = PNPDeviceID.IndexOf("PID_");
    if (index > -1 && PNPDeviceID.Length >= index + 8)
    {
        string id = PNPDeviceID.Substring(index + 4, 4);
        pid = Convert.ToInt32(id, 16);
    }

    index += 9;
    if (PNPDeviceID.Length > index)
        serialNumber = PNPDeviceID.Substring(index);

    return new SerialPortEventArgs((string)queryObj["DeviceID"], vid, pid, serialNumber, PNPDeviceID);
}

当我使用“Win32_SerialPort”时,“queryObj["DeviceID"]”返回找到的设备的 COM 端口(这是我在本机 Arduino 或基于 PIC 的硬件中看到的)。

当我使用“Win32_PnPEntity”时,我没有获得 COM 端口。相反,我得到了一个像“USB\VID_2341&PID_0042\8554856875F132”这样的字符串。

我绝对不是 WMI 专家,但我认为其逻辑是 Win32_SerialPort 是 Win32_PnPEntity 的特殊子集,它将其属性转换为 COM 端口。使用 Win32_PnPEntity 不了解 COM 端口。对吗?

问题:有没有办法在 COM 端口中转换 PNPDeviceID? Arduino IDE 也在这样做,所以我想知道其中的技巧是什么。

几年前,我使用了一种完全不同的方法,效果很好。该方法正在处理注册表,但使用了一些“未记录的功能”,如果微软决定改变这一点,我担心它们可能会被破坏。这就是为什么我更愿意依赖 WMI,这是一种完整记录的方法。

c# arduino wmi
1个回答
0
投票

感谢这篇文章中人们的贡献,我可以提出自己的解决方案。

首先,@user246821 提供的提示(查看其中一篇文章中的 powershell 命令)对我理解和实验 WMI 有很大帮助。尤其是这个:

Get-CimInstance -Namespace Root\Cimv2 -Query "Select * From Win32_PnPEntity where PnPClass = 'Ports' and Name like '%COM%'"

这与使用 WMICodeCreator 一起确实帮助我使用 WMI 找到检测所有设备的正确方法,包括带有 CH340 芯片的 Arduino。

生成的代码可以在我的 GitHub 存储库中的文件 SerialPortManager.cs 中找到CockpitHardwareHUB_v2

基本上,我使用的查询字符串如下:

Select * From Win32_PnPEntity where PnPClass = 'Ports' and Name like '%COM%'

这是检测所有可以与“串行端口”一起使用的“设备”(至少,这是我的理解)。通过上面的查询字符串,我可以找到带有板载 USB 的 PIC 微控制器、我的原生 Arduino 和使用 CH340 芯片的中国克隆 Arduino。

但是 PNPDeviceID 不包含 COM 端口,因此我需要找到 COM 端口可用的正确属性。在那里,上面提到的 powershell 命令向我显示属性“Caption”似乎始终包含 COM 端口。我使用了一些正则表达式(感谢 ChatGPT)来提取它。我忽略 Caption 属性中没有 COM 端口的设备。

基本上就是这样。最终结果是我可以检测我的设备,并提取其 VID、PID、序列号和 COM 端口。然后我可以使用该 COM 端口打开串行端口,并开始与设备通信。

到目前为止,我还没有发现任何问题。

© www.soinside.com 2019 - 2024. All rights reserved.