真実の楽譜(フルスコア)

プログラム関係の忘備録になるはず

C#でCOMポート番号とシリアル接続機器名を同時に取得する方法

マイコン機器とデータ通信を行う方法としては、
シリアル通信(RS-232)を行うことがメジャーだと思います。

.NET Framework 2.0以降ではSerialPortクラスが実装され、
シリアル通信を行うプログラムを書くのがお手軽になりました。
http://msdn.microsoft.com/ja-jp/library/system.io.ports.serialport(v=vs.110).aspx

有効なCOM番号の取得

シリアル機器とPCを接続しているときに、
SerialPortクラスのGetPortNameクラスを呼べば、
現在有効なCOM番号が出てきます。

ShowSerialPortNum.cs
using System;
using System.IO.Ports;

namespace ShowSerialPortNum
{
  class ShowSerialPortNum
  {
    public static void Main()
    {
    string[] ports = SerialPort.GetPortNames();
    foreach(string port in ports)
    {
      Console.WriteLine(port);
    }
    Console.ReadLine();
    }
  }
}

出力例(COM8が有効なとき)

COM8

接続している機器が1台だけなら気にせずそこに繋げればOK。
でも複数台接続をしていたらどのCOM番号が何と接続しているのかわからない。

そんなときは、
マイコンピュータ右クリック」→「プロパティ」
→「デバイスマネージャ」→「ポート(COMとLPT)」

とデバイスマネージャを参照して確認する必要があり、
ユーザーに対して不親切だし非常にメンドクサイ。

頼みのSerialPortクラスもそこまでは教えてくれないので、
頑張って別の方法を使って探しに行きます。

COM番号と接続機器名の取得

方法としてはWMIのWin32_PnPEntityクラスを使って、
上記したデバイスマネージャに記述されている内容を拾いに行きます。

ShowSerialPortName.cs
using System;
using System.Management;

namespace ShowSerialPortName
{
  class ShowSerialPortName
  {
    public static void Main()
    {
      string[] ports = GetDeviceNames();
      if(ports != null)
      {
        foreach (string port in ports)
        {
          Console.WriteLine(port);
        }
      }
      Console.ReadLine();
    }

    public static string[] GetDeviceNames()
    {
      var deviceNameList = new System.Collections.ArrayList();
      var check = new System.Text.RegularExpressions.Regex("(COM[1-9][0-9]?[0-9]?)");

      ManagementClass mcPnPEntity = new ManagementClass("Win32_PnPEntity");
      ManagementObjectCollection manageObjCol = mcPnPEntity.GetInstances();

      //全てのPnPデバイスを探索しシリアル通信が行われるデバイスを随時追加する
      foreach (ManagementObject manageObj in manageObjCol)
      {
        //Nameプロパティを取得
        var namePropertyValue = manageObj.GetPropertyValue("Name");
        if (namePropertyValue == null)
        {
          continue;
        }

        //Nameプロパティ文字列の一部が"(COM1)~(COM999)"と一致するときリストに追加"
        string name = namePropertyValue.ToString();
        if (check.IsMatch(name))
        {
          deviceNameList.Add(name);
        }
      }

      //戻り値作成
      if (deviceNameList.Count > 0)
      {
        string[] deviceNames = new string[deviceNameList.Count];
        int index = 0;
        foreach (var name in deviceNameList)
        {
          deviceNames[index++] = name.ToString();
        }
        return deviceNames; 
      }
      else
      {
        return null;
      }
    }
  }
}

出力例(COM8にArduino Unoを接続しているとき)

Arduino Uno (COM8)

やっていることとしては、
ManagementClassのコンストラクタ
Win32_PnPEntityクラスを指定して、
GetInstances()メソッドを呼び出すことで、
プラグアンドプレイデバイスのコレクション配列を取得

ManagementClass mcPnPEntity = new ManagementClass("Win32_PnPEntity");
ManagementObjectCollection manageObjCol = mcPnPEntity.GetInstances();

コレクションの要素を順次アクセスし、
GetPropertyValue関数でNameプロパティを取得。
Nameプロパティで得られたオブジェクトが、
デバイスマネージャ上で表示される値みたいです。
その後正規表現で"(COM1-999の番号)"が含まれるデータのみを抽出。

foreach (ManagementObject manageObj in manageObjCol)
{
  //Nameプロパティを取得
  var namePropertyValue = manageObj.GetPropertyValue("Name");
  if (namePropertyValue == null)
  {
    continue;
  }

  //Nameプロパティ文字列の一部が"(COM1)~(COM999)"と一致するときリストに追加"
  string name = namePropertyValue.ToString();
  if (check.IsMatch(name))
  {
    deviceNameList.Add(name);
  }
}

これでデバイス名とCOM番号を同時に取得します。
COM番号は文字列から数値を抜き出せばOKです。

参考
http://www-ice.yamagata-cit.ac.jp/ken../senshu/sitedev/index.php?AVR%2Favrdude36#xceb3d84
http://www.wmifun.net/library/win32_pnpentity.html