web-dev-qa-db-ja.com

C#でUSB経由で接続されているすべてのシリアルデバイスに関する情報を検索する

プロジェクトがUSBに接続されている場合、特定のデバイスを検出する必要があります。このデバイスを特定できる唯一の方法は、COMポートではなく、その説明/デバイス名です。正しい機能を実行するために見つけたのは、WMIクエリを使用し、nameプロパティを確認することです。

ManagementObjectSearcher searcher = new ManagementObjectSearcher("Select * from WIN32_SerialPort");
            foreach (ManagementObject port in searcher.Get())
            {
                deviceName = (string)foundPort.GetPropertyValue("Name"); 
                ...

私は最初に電話を接続してこれをテストしましたが、クエリはCOM3で見つかった電話を期待どおりに返しました。次に、別のデバイス(このプロジェクトが必要なデバイスにより近いUSBシリアルコンバーター)を接続しましたが、クエリはそれを見つけませんでした。それは電話を見つけるだけです。ただし、このデバイスはデバイスマネージャーのポートCOM4に表示されます。さらに私をいじめるために、SerialPortクラスは両方のデバイスを検出しますが、デバイスを識別するために必要な情報を提供しません。

    string[] tempPorts = SerialPort.GetPortNames();

SOと他の場所で多数のスレッドを読みましたが、満足のいく解決策を見つけることができません。WIN32_SerialPortクエリが他のデバイスを見つけられない理由を誰かが明確にできますか?一部のWin32シリアルポートとは見なされませんか?そして、誰かがこの問題の解決策を教えてくれませんか?

26
sebo

すべてのシリアルポートを一覧表示する方法:

ハードウェアベンダーが利用できる システム定義のデバイスセットアップクラス がいくつかあります。 _COM-Ports_用に適切に記述されたドライバーは、Ports (COM & LPT ports)- class(guid:_4d36e978-e325-11ce-bfc1-08002be10318_)を使用する必要があります。おそらく、このクラスはデバイスマネージャでも使用されます。

したがって、次のクエリを使用して、devicemanagerにも表示されるすべてのシリアルポートを一覧表示できます。

_ManagementObjectSearcher searcher = new ManagementObjectSearcher(
    "root\\CIMV2",
    "SELECT * FROM Win32_PnPEntity WHERE ClassGuid=\"{4d36e978-e325-11ce-bfc1-08002be10318}\""
);
foreach (ManagementObject queryObj in searcher.Get())
{
    // do what you like with the Win32_PnpEntity
}
_

Win32_PnPEntity -classのこの詳細な説明を参照してください。デバイスの識別に必要なものがすべて揃っているはずです。

ポート番号を決定するために、nameプロパティを調べて抽出します。これまではこれで問題ありませんが、ポート番号が名前に含まれることが保証されているかどうかはわかりません。名前にポート番号が含まれていないシリアルポートデバイスは今まで見つかりませんでした。

上記のクエリは、Bluetooth SPP、FTDIチップ、メインボード上のポート、拡張カード、またはモデムドライバー(Globetrotter GTM66xxWなど)によって生成された仮想シリアルポートであるかどうかに関係なく、すべてのシリアルポートデバイスを検出します。

接続のタイプ(Bluetooth、USBなど)を判別するには、デバイスIDを調べます(デバイスIDの最初の部分を見てください)。そこでbt-macアドレスを抽出することもできます(注意してください:deviceidは少なくともWindows 7とWindows XPでは異なって見えます)。

一部のデバイスがWin32_SerialPortに表示されない理由について:

ポートを一覧表示するUSB​​デバイスとそうでないUSBデバイスがあるので、ドライバーの実装に依存すると思います。

22
AlexS

私はあなたが何をしようとしているのかわかっていると思います、WMICodeCreatorを使用して作成されたこのコードを見てください(WMICodeCreatorへのリンク http://www.Microsoft.com/en-us/download/details.aspx?id=8572 )この記事から http://www.codeproject.com/Articles/32330/A-Useful-WMI-Tool-How-To-Find-USB-to-Serial-Adapto

//Below is code pasted from WMICodeCreator
try
{
    ManagementObjectSearcher searcher =
        new ManagementObjectSearcher("root\\WMI",
        "SELECT * FROM MSSerial_PortName");

    foreach (ManagementObject queryObj in searcher.Get())
    {
        Console.WriteLine("-----------------------------------");
        Console.WriteLine("MSSerial_PortName instance");
        Console.WriteLine("-----------------------------------");
        Console.WriteLine("InstanceName: {0}", queryObj["InstanceName"]);

        Console.WriteLine("-----------------------------------");
        Console.WriteLine("MSSerial_PortName instance");
        Console.WriteLine("-----------------------------------");
        Console.WriteLine("PortName: {0}", queryObj["PortName"]);

        //If the serial port's instance name contains USB 
        //it must be a USB to serial device
        if (queryObj["InstanceName"].ToString().Contains("USB"))
        {
            Console.WriteLine(queryObj["PortName"] + " 
            is a USB to SERIAL adapter/converter");
        }
    }
}
catch (ManagementException e)
{
    MessageBox.Show("An error occurred while querying for WMI data: " + e.Message);
} 
5
Jay

「名前」で検索したいので、接続されているすべてのデバイスを繰り返し処理して、製品名を取得する必要があると思います。

WinUSBデバイスを反復処理するコードは次のとおりです。

https://github.com/MelbourneDeveloper/Device.Net/blob/master/src/Device.Net/Windows/WindowsDeviceFactoryBase.cs

  public async Task<IEnumerable<DeviceDefinition>> GetConnectedDeviceDefinitions(uint? vendorId, uint? productId)
    {
        return await Task.Run<IEnumerable<DeviceDefinition>>(() =>
        {
            var deviceDefinitions = new Collection<DeviceDefinition>();
            var spDeviceInterfaceData = new SpDeviceInterfaceData();
            var spDeviceInfoData = new SpDeviceInfoData();
            var spDeviceInterfaceDetailData = new SpDeviceInterfaceDetailData();
            spDeviceInterfaceData.CbSize = (uint)Marshal.SizeOf(spDeviceInterfaceData);
            spDeviceInfoData.CbSize = (uint)Marshal.SizeOf(spDeviceInfoData);

            var guidString = ClassGuid.ToString();
            var copyOfClassGuid = new Guid(guidString);

            var i = APICalls.SetupDiGetClassDevs(ref copyOfClassGuid, IntPtr.Zero, IntPtr.Zero, APICalls.DigcfDeviceinterface | APICalls.DigcfPresent);

            if (IntPtr.Size == 8)
            {
                spDeviceInterfaceDetailData.CbSize = 8;
            }
            else
            {
                spDeviceInterfaceDetailData.CbSize = 4 + Marshal.SystemDefaultCharSize;
            }

            var x = -1;

            while (true)
            {
                x++;

                var isSuccess = APICalls.SetupDiEnumDeviceInterfaces(i, IntPtr.Zero, ref copyOfClassGuid, (uint)x, ref spDeviceInterfaceData);
                if (!isSuccess)
                {
                    var errorCode = Marshal.GetLastWin32Error();
                    if (errorCode == APICalls.ERROR_NO_MORE_ITEMS)
                    {
                        break;
                    }

                    throw new Exception($"Could not enumerate devices. Error code: {errorCode}");
                }

                isSuccess = APICalls.SetupDiGetDeviceInterfaceDetail(i, ref spDeviceInterfaceData, ref spDeviceInterfaceDetailData, 256, out _, ref spDeviceInfoData);
                WindowsDeviceBase.HandleError(isSuccess, "Could not get device interface detail");

                //Note this is a bit nasty but we can filter Vid and Pid this way I think...
                var vendorHex = vendorId?.ToString("X").ToLower().PadLeft(4, '0');
                var productIdHex = productId?.ToString("X").ToLower().PadLeft(4, '0');
                if (vendorId.HasValue && !spDeviceInterfaceDetailData.DevicePath.ToLower().Contains(vendorHex)) continue;
                if (productId.HasValue && !spDeviceInterfaceDetailData.DevicePath.ToLower().Contains(productIdHex)) continue;

                var deviceDefinition = GetDeviceDefinition(spDeviceInterfaceDetailData.DevicePath);

                deviceDefinitions.Add(deviceDefinition);
            }

            APICalls.SetupDiDestroyDeviceInfoList(i);

            return deviceDefinitions;
        });
    }

これらの各デバイスについて、次のようにデバイスをクエリできます。

https://github.com/MelbourneDeveloper/Device.Net/blob/master/src/Usb.Net/Windows/WindowsUsbDevice.cs

        var isSuccess = WinUsbApiCalls.WinUsb_Initialize(_DeviceHandle, out var defaultInterfaceHandle);
        HandleError(isSuccess, "Couldn't initialize device");

        var bufferLength = (uint)Marshal.SizeOf(typeof(USB_DEVICE_DESCRIPTOR));
        isSuccess = WinUsbApiCalls.WinUsb_GetDescriptor(defaultInterfaceHandle, WinUsbApiCalls.DEFAULT_DESCRIPTOR_TYPE, 0, EnglishLanguageID, out _UsbDeviceDescriptor, bufferLength, out var lengthTransferred);
        HandleError(isSuccess, "Couldn't get device descriptor");

        if (_UsbDeviceDescriptor.iProduct > 0)
        {
            //Get the product name
            var buffer = new byte[256];
            isSuccess = WinUsbApiCalls.WinUsb_GetDescriptor(defaultInterfaceHandle, WinUsbApiCalls.USB_STRING_DESCRIPTOR_TYPE, _UsbDeviceDescriptor.iProduct, 1033, buffer, (uint)buffer.Length, out var transfered);
            HandleError(isSuccess, "Couldn't get product name");

            Product = new string(Encoding.Unicode.GetChars(buffer, 2, (int)transfered));
            Product = Product.Substring(0, Product.Length - 1);
        }


public static partial class WinUsbApiCalls
{
    public const uint DEVICE_SPEED = 1;
    public const byte USB_ENDPOINT_DIRECTION_MASK = 0X80;
    public const int WritePipeId = 0x80;

    /// <summary>
    /// Not sure where this constant is defined...
    /// </summary>
    public const int DEFAULT_DESCRIPTOR_TYPE = 0x01;
    public const int USB_STRING_DESCRIPTOR_TYPE = 0x03;

    [DllImport("winusb.dll", SetLastError = true)]
    public static extern bool WinUsb_ControlTransfer(IntPtr InterfaceHandle, WINUSB_SETUP_PACKET SetupPacket, byte[] Buffer, uint BufferLength, ref uint LengthTransferred, IntPtr Overlapped);

    [DllImport("winusb.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern bool WinUsb_GetAssociatedInterface(SafeFileHandle InterfaceHandle, byte AssociatedInterfaceIndex, out SafeFileHandle AssociatedInterfaceHandle);

    [DllImport("winusb.dll", SetLastError = true)]
    public static extern bool WinUsb_GetDescriptor(SafeFileHandle InterfaceHandle, byte DescriptorType, byte Index, ushort LanguageID, out USB_DEVICE_DESCRIPTOR deviceDesc, uint BufferLength, out uint LengthTransfered);

    [DllImport("winusb.dll", SetLastError = true)]
    public static extern bool WinUsb_GetDescriptor(SafeFileHandle InterfaceHandle, byte DescriptorType, byte Index, UInt16 LanguageID, byte[] Buffer, UInt32 BufferLength, out UInt32 LengthTransfered);

    [DllImport("winusb.dll", SetLastError = true)]
    public static extern bool WinUsb_Free(SafeFileHandle InterfaceHandle);

    [DllImport("winusb.dll", SetLastError = true)]
    public static extern bool WinUsb_Initialize(SafeFileHandle DeviceHandle, out SafeFileHandle InterfaceHandle);

    [DllImport("winusb.dll", SetLastError = true)]
    public static extern bool WinUsb_QueryDeviceInformation(IntPtr InterfaceHandle, uint InformationType, ref uint BufferLength, ref byte Buffer);

    [DllImport("winusb.dll", SetLastError = true)]
    public static extern bool WinUsb_QueryInterfaceSettings(SafeFileHandle InterfaceHandle, byte AlternateInterfaceNumber, out USB_INTERFACE_DESCRIPTOR UsbAltInterfaceDescriptor);

    [DllImport("winusb.dll", SetLastError = true)]
    public static extern bool WinUsb_QueryPipe(SafeFileHandle InterfaceHandle, byte AlternateInterfaceNumber, byte PipeIndex, out WINUSB_PIPE_INFORMATION PipeInformation);

    [DllImport("winusb.dll", SetLastError = true)]
    public static extern bool WinUsb_ReadPipe(SafeFileHandle InterfaceHandle, byte PipeID, byte[] Buffer, uint BufferLength, out uint LengthTransferred, IntPtr Overlapped);

    [DllImport("winusb.dll", SetLastError = true)]
    public static extern bool WinUsb_SetPipePolicy(IntPtr InterfaceHandle, byte PipeID, uint PolicyType, uint ValueLength, ref uint Value);

    [DllImport("winusb.dll", SetLastError = true)]
    public static extern bool WinUsb_WritePipe(SafeFileHandle InterfaceHandle, byte PipeID, byte[] Buffer, uint BufferLength, out uint LengthTransferred, IntPtr Overlapped);
}
0

アレックスの答えは本当に私を助けました。しかし、自分のマシンに基づいてクエリを調整する必要がありました...

「root\CIMV2」をクエリする必要がありましたが、別のテーブルからすべての行を選択する必要がありました:「SELECT * FROM Win32_SerialPort」。

WMIまたは管理オブジェクトサーチャーの使用方法を理解するのに苦労している人にとって、MicrosoftのWMIコード作成者は本当に私を救ってくれました。アプリを使用すると、WMIにさまざまな情報を照会して、Management Object Searcherクエリに何を入力するかを把握できます。

リンクは将来的に期限切れになる可能性がありますが、ここでは2020年です。

https://www.Microsoft.com/en-us/download/details.aspx?id=8572

0
chapter12