web-dev-qa-db-fra.com

Recherche d'informations sur tous les périphériques série connectés via USB en C #

Mon projet nécessite la détection d'un appareil spécifique lorsqu'il est connecté à USB. La seule façon dont je peux identifier cet appareil est par sa description/nom d'appareil, pas le port com. Ce que j'ai trouvé pour exécuter la fonction correcte utilise une requête WMI et vérifie la propriété name:

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

J'ai d'abord testé cela en connectant mon téléphone, et la requête a renvoyé le téléphone trouvé sur COM3 comme prévu. Ensuite, j'ai connecté un autre appareil (un convertisseur USB vers série, qui ressemble plus à l'appareil pour lequel j'ai besoin de ce projet) et la requête ne l'a tout simplement pas trouvé. Il ne trouve que le téléphone. Ce périphérique apparaît cependant sur le port COM4 dans le Gestionnaire de périphériques. Pour me contrarier encore plus, la classe SerialPort trouve les deux appareils, mais elle ne fournit pas les informations dont j'ai besoin pour identifier l'appareil:

    string[] tempPorts = SerialPort.GetPortNames();

J'ai lu de nombreux threads sur SO et ailleurs et je ne trouve pas de solution satisfaisante. Quelqu'un pourrait-il expliquer pourquoi la requête WIN32_SerialPort ne trouve pas mon autre appareil? N'est-il pas considéré comme un port série win32 pour certains Et est-ce que quelqu'un pourrait me montrer la solution à ce problème?

26
sebo

Comment répertorier tous les ports série:

Il existe plusieurs classes de configuration de périphérique définies par le système disponibles pour les fournisseurs de matériel. Les pilotes correctement écrits pour COM-Ports Doivent utiliser la classe Ports (COM & LPT ports)guid: 4d36e978-e325-11ce-bfc1-08002be10318). Cette classe est probablement également utilisée par le gestionnaire de périphériques.

Vous pouvez donc utiliser la requête suivante pour répertorier tous les ports série que vous voyez également dans le 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
}

Voir cette description détaillée de la classe --- (Win32_PnPEntity -. Vous devriez avoir tout ce dont vous avez besoin pour identifier votre appareil.

Pour déterminer le numéro de port, j'examine la propriété du nom et l'extrait. Jusqu'à présent, cela fonctionne bien, mais je ne sais pas si le numéro de port est garanti pour être inclus dans le nom. Je n'ai trouvé aucun périphérique de port série jusqu'à présent, qui n'a pas le numéro de port inclus dans le nom.

La requête ci-dessus trouve chaque périphérique de port série, qu'il s'agisse d'un SPP Bluetooth, d'une puce FTDI, d'un port sur la carte mère, d'une carte d'extension ou d'un port série virtuel généré par un pilote de modem (par exemple Globetrotter GTM66xxW).

Pour déterminer le type de connexion (Bluetooth, USB, etc.), vous pouvez examiner l'ID de l'appareil (jetez un œil à la première partie de l'ID de l'appareil). Là, vous pouvez également extraire l'adresse bt-mac (attention à cela: l'ID d'appareil est différent au moins sous Windows 7 et Windows XP).

Concernant pourquoi certains périphériques ne sont pas répertoriés avec Win32_SerialPort:

Je soupçonne que cela dépend de la mise en œuvre du pilote, car j'ai certains périphériques USB qui répertorient leurs ports et d'autres non.

22
AlexS

Je pense que je vois ce que vous essayez de faire, regardez ce code créé à l'aide de WMICodeCreator (lien vers WMICodeCreator http://www.Microsoft.com/en-us/download/details.aspx?id=8572 ) de cet article 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

Étant donné que vous souhaitez effectuer une recherche par "Nom", je pense que vous devrez parcourir tous les appareils connectés et les interroger pour obtenir le nom du produit.

Voici le code pour parcourir les appareils 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;
        });
    }

Pour chacun de ces appareils, vous pouvez interroger l'appareil comme ceci:

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
Melbourne Developer

La réponse d'Alex m'a vraiment aidé. Mais j'ai dû ajuster ma requête en fonction de ma machine ...

J'ai dû interroger "root\CIMV2", mais j'ai dû sélectionner toutes les lignes d'une table différente: "SELECT * FROM Win32_SerialPort".

Pour ceux qui ont du mal à comprendre comment utiliser WMI ou le gestionnaire d'objets de recherche, le créateur de code WMI par Microsoft m'a vraiment sauvé. En utilisant l'application, vous pouvez interroger WMI pour différentes informations afin de comprendre ce qu'il faut mettre dans votre requête de recherche d'objet de gestion.

Le lien pourrait expirer à l'avenir, mais il est ici en 2020:

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

0
chapter12