4

I had been using the code below for a console application in Visual Studio on Windows 8 to return the description and device ID of connected serial devices. I was using a modified version of this in an application I'm creating to automatically detect the COM port of an Arduino. It no longer returns anything since I've done a fresh install with Windows 10. I have a USB to serial AVR programmer that still shows up using this code however. I checked the registry and the Arduino is listed in SERIALCOMM, the Arduino is showing as 'USB Serial Port (COM6)' under 'Ports (COM & LPT)' in Device Manager and I can program the Arduino using the Arduino software. I have no idea why it isn't working any more.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Management;
using System.IO.Ports;
using System.Diagnostics;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main() 
        {


            ManagementScope connectionScope = new ManagementScope();
            SelectQuery serialQuery = new SelectQuery("SELECT * FROM Win32_SerialPort");
            ManagementObjectSearcher searcher = new ManagementObjectSearcher(connectionScope, serialQuery);

            try
            {
                foreach (ManagementObject item in searcher.Get())
                {
                    string desc = item["Description"].ToString();
                    string deviceId = item["DeviceID"].ToString();

                    Console.WriteLine(desc);
                    Console.WriteLine(deviceId);
                }
            }
            catch (ManagementException e)
            {
                Console.WriteLine(e.Message);
            }

            Console.ReadKey();
        }
    }
}

It might also be relevant that while trying to find a solution I found the following implementation to find port names using MSSerial_PortName and I got an Access Denied error.

using System;
using System.Management;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main() 
        {
            try
            {
                ManagementObjectSearcher MOSearcher = new ManagementObjectSearcher("root\\WMI", "SELECT * FROM MSSerial_PortName");

                foreach (ManagementObject MOject in MOSearcher.Get())
                {
                    Console.WriteLine(MOject["PortName"]); 
                }
            }
            catch (ManagementException me)
            {
                Console.WriteLine("An error occurred while querying for WMI data: " + me.Message);
            }
            Console.ReadKey();
        }
    }
}
thoward
  • 349
  • 3
  • 12
  • Sounds like an authentication issue. Pass a ConnectionOptions instance to your ManagementScope constructor, defining your Authentication and Impersonation properties. – Juderb Dec 02 '15 at 07:25
  • Thanks @Juderb I'm just using this locally on my own machine and I tried your suggestion and I set the ImpersonationLevel to Impersonate (which is the only level it would run on) and I tried all the different options for AuthenticationLevel and the code would still only find my USB to Serial AVR Programmer but not my Arduinos. Adding AuthenticationLevel and ImpersonationLevel to the second block of code I posted which uses MSSerial_PortName made no difference, I still got Access Denied (with all different combinations of impersonation and authentication levels). – thoward Dec 02 '15 at 09:55
  • Default user can't access to system devices ! Need run as administrator for shared library or devices... – dsgdfg Dec 02 '15 at 11:28
  • @dsgdfg I am using an administrator account (or do I specifically have to run the console application as admin?) and was also using an admin account on Windows 8 when this code worked for my Arduino. As it is working for one serial device but not another it makes me think it might not be permissions or authentication related. – thoward Dec 03 '15 at 02:26

3 Answers3

4

Ok, I think I see the issue now (I will add a new answer, but won't replace the old in case someone finds the information useful).

Win32_SerialPort only detects hardware serial ports (e.g. RS232). Win32_PnPEntity identifies plug-and-play devices including hardware and virtual serial ports (e.g. COM port created by the FTDI driver).

To use Win32_PnPEntity to identify a driver requires a little extra work. The following code identifies all COM ports on the system and produces a list of said ports. From this list, you may identify the appropriate COM port number to create your SerialPort object.

// Class to contain the port info.
public class PortInfo
{
  public string Name;
  public string Description;
}

// Method to prepare the WMI query connection options.
public static ConnectionOptions PrepareOptions ( )
{
  ConnectionOptions options = new ConnectionOptions ( );
  options . Impersonation = ImpersonationLevel . Impersonate;
  options . Authentication = AuthenticationLevel . Default;
  options . EnablePrivileges = true;
  return options;
}

// Method to prepare WMI query management scope.
public static ManagementScope PrepareScope ( string machineName , ConnectionOptions options , string path  )
{
  ManagementScope scope = new ManagementScope ( );
  scope . Path = new ManagementPath ( @"\\" + machineName + path );
  scope . Options = options;
  scope . Connect ( );
  return scope;
}

// Method to retrieve the list of all COM ports.
public static List<PortInfo> FindComPorts ( )
{
  List<PortInfo> portList = new List<PortInfo> ( );
  ConnectionOptions options = PrepareOptions ( );
  ManagementScope scope = PrepareScope ( Environment . MachineName , options , @"\root\CIMV2" );

  // Prepare the query and searcher objects.
  ObjectQuery objectQuery = new ObjectQuery ( "SELECT * FROM Win32_PnPEntity WHERE ConfigManagerErrorCode = 0" );
  ManagementObjectSearcher portSearcher = new ManagementObjectSearcher ( scope , objectQuery );

  using ( portSearcher )
  {
    string caption = null;
    // Invoke the searcher and search through each management object for a COM port.
    foreach ( ManagementObject currentObject in portSearcher . Get ( ) )
    {
      if ( currentObject != null )
      {
        object currentObjectCaption = currentObject [ "Caption" ];
        if ( currentObjectCaption != null )
        {
          caption = currentObjectCaption . ToString ( );
          if ( caption . Contains ( "(COM" ) )
          {
            PortInfo portInfo = new PortInfo ( );
            portInfo . Name = caption . Substring ( caption . LastIndexOf ( "(COM" ) ) . Replace ( "(" , string . Empty ) . Replace ( ")" , string . Empty );
            portInfo . Description = caption;
            portList . Add ( portInfo );
          }
        }
      }
    }
  }
  return portList;
}
Visual Micro
  • 1,561
  • 13
  • 26
Juderb
  • 735
  • 3
  • 9
1

Your Windows 8 user profile probably included full administrator privileges while your Windows 10 user profile is a standard user. Because your Windows 10 user profile is a standard user, you must adjust some access permissions.

  1. Run the Computer Management utility.

  2. Go to Computer Management > Services and Applications > WMI Control.

  3. Go to Actions > WMI Control > More Actions > Properties to open the WMI Control Properties window.

  4. Under the Security tab, select Root, then press Security.

  5. In the Security for Root dialog, select your username or the group you belong to. Select Allow for Permissions > Execute Methods. Click OK to close the dialog.

  6. Repeat the previous step for CIMV2.

  7. Try running again with these new permissions.

Computer Management Utility WMI Control Properties for Root WMI Control Properties for CIMV2 Security Options for Root

Juderb
  • 735
  • 3
  • 9
  • I tried your suggestion and it's still not working. The fact that when using Win32_SerialPort (as opposed to MSSerial_PortName) it can find my USB AVR programmer but not my Arduino makes me think that it is not a permission related issue. Interestingly, as I mentioned, it works for the USB AVR programmer which is installed using the driver from the programmer manufacturer (Pololu) and doesn't work for my Arduino and a USB to serial adapter I have, both of which are installed as "USB Serial Port" with the manufacturer listed as FTDI (they make the USB to serial IC on these devices). – thoward Dec 03 '15 at 02:24
  • @thoward, give my second answer a try. That is the approach I use for several components in a fully automated microscopy system and Class III medical device (i.e. its robust when implemented properly). – Juderb Dec 03 '15 at 05:52
1

A bit smaller solution than Juderb

    public static List<string> FindComPorts()
    {
        using (var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_PnPEntity WHERE ConfigManagerErrorCode = 0"))
        {
            return searcher.Get().OfType<ManagementBaseObject>()
                .Select(port => Regex.Match(port["Caption"].ToString(), @"\((COM\d*)\)"))
                .Where(match => match.Groups.Count >= 2)
                .Select(match => match.Groups[1].Value).ToList();
        }
    }

Tested this on win7 + win10. Btw, you can add extra search criteria to the regex if you want (or just add a new Where clause)

Wouter Schut
  • 907
  • 2
  • 10
  • 22