WMIでIPアドレスを取得する方法

2008.05.30 金 18:11:00

※この記事はココログからの移転に伴ってコピーしたものです。

前回UDPでブロードキャストを送信する方法を紹介しましたが、制限ブロードキャストであれば常に一定だからいいですがローカルブロードキャストについてはその場その場で異なるIPアドレスとなります。これをやはり動的に計算してしまいたい。ということで、IPアドレスを取得してブロードキャストアドレスを求めるプログラムを作成しました。

使用したのは、WMI。正確にはWMIをラップしたWMI.NETです。
Windows Management Instrumentationの略で、システム情報の取得を共通のインタフェースで行えるような仕組みです。Win32バージョンはずいぶん難解だったとMicrosoftもドキュメント内で言っていますが、C#から普通に利用するにはサンプルのコピペだけで十分です。
ただ、取得したい情報がどのカテゴリ(WMIでは"クラス")でどの属性名なのかとか、主に名前を探すのがずいぶん大変です。英語のドキュメントしかありません。

プログラムの機能は、今アクティブとなっているネットワークカードの名前とそのIPアドレスと、サブネットマスクと、そこから送るときのローカルブロードキャストアドレスを表示することです。

手順としては、
・WMIでアダプタの番号と名前の一覧を取得
・WMIでIPアドレスを取得し、値があればサブネットマスクを取得する
・IPアドレスがコンピュータ上のアダプタのもので、またサブネットマスク値が存在するなら、IPアドレス一覧にそれぞれの値を追加
・IPアドレス一覧から、ローカルブロードキャストアドレスを求めつつデータを表示

ローカルブロードキャストアドレスを求める式は以下のとおりです。確認してください。
   (IPアドレス AND サブネットマスク) OR (NOT サブネットマスク)

では早速プログラム。

//getipad.cs
using System;
using System.Collections;
using System.Management;
using System.Net;

namespace getipad
{
    /// <summary>
    /// システムのネットワークアダプタ名と対応するIPアドレスとそのサブネットマスクを格納します。
    /// </summary>
    struct IPinfo
    {
        public string Adapter;        //アダプタ名
        public byte[] IP;            //IPアドレスのバイト値
        public byte[] Subnet;        //サブネットマスクのバイト値
        public string IPString        //IPアドレスを文字列としてアクセス
        {
            get
            {
                if(IP != null)
                {
                    //バイト配列からIPアドレスを取得して標準表記に変換
                    return (new IPAddress(IP).ToString());
                }else{
                    throw new ArgumentNullException();
                }
            }
            set
            {
                if(value != null)
                {
                    //文字配列からIPアドレスを取得してバイト配列に変換
                    this.IP = IPAddress.Parse(value).GetAddressBytes();
                }else{
                    throw new ArgumentNullException();
                }
            }
        }
        public string SubnetString        //サブネットマスクを文字列としてアクセス
        {
            get
            {
                if(Subnet != null)
                {
                    return (new IPAddress(Subnet).ToString());
                }else{
                    throw new ArgumentNullException();
                }
            }
            set
            {
                if(value != null)
                {
                    this.Subnet = IPAddress.Parse(value).GetAddressBytes();
                }else{
                    throw new ArgumentNullException();
                }
            }
        }
    }

    class getipad
    {
        public static void Main()
        {
            //列挙用リスト
            ArrayList adptIdx    = new ArrayList();    //アダプタ番号(int)
            ArrayList adptName    = new ArrayList();    //アダプタ名(string)

            //値
            ArrayList ipinfo    = new ArrayList();    //IPinfo

            //ネットワークアダプタの列挙
            ManagementClass mc    = new ManagementClass("Win32_NetworkAdapter");
            ManagementObjectCollection mcol = mc.GetInstances();

            foreach(ManagementObject m in mcol)
            {
                adptIdx.Add(m["Index"]);    //アダプタ番号を取得し、リストに投入
                adptName.Add(m["Name"]);    //アダプタ名を取得し、リストに投入
            }

            //IPアドレスの列挙
            mc = new ManagementClass("Win32_NetworkAdapterConfiguration");
            mcol = mc.GetInstances();

            foreach(ManagementObject m in mcol)
            {
                string[] ad = (string[])m["IPAddress"];    //IPアドレスの入った文字配列を取得
                string[] sn = (string[])m["IPSubnet"];    //サブネットマスクの入った文字配列を取得

                if(
                    ad != null &&                    //IPアドレスがあるか
                    adptIdx.Contains(m["Index"]) &&    //IPアドレスのあるアダプタのIndexが先に列挙したやつのなかにあるか
                    sn[0] != string.Empty            //サブネットマスクが空でないか(※null checkは不要)
                    )
                {
                    IPinfo tmp = new IPinfo();

                    tmp.Adapter    = (string)adptName[ adptIdx.IndexOf(m["Index"]) ];    //アダプタ名取得
                    tmp.IP        = IPAddress.Parse(ad[0]).GetAddressBytes();            //IPアドレスをByte配列に変換
                    tmp.Subnet    = IPAddress.Parse(sn[0]).GetAddressBytes();            //サブネットマスクをByte配列に変換
                    ipinfo.Add(tmp);
                }
            }

            if(ipinfo.Count == 0)
            {
                Console.WriteLine("IPアドレスの割り当てられたネットワークアダプタが見つかりませんでした。");
                return;
            }

            //IPアドレスごとにローカルブロードキャストアドレス計算
            for(int cnt = 0; cnt < ipinfo.Count; cnt++)
            {
                IPinfo ip = (IPinfo)ipinfo[cnt];

                //※ビッグエンディアンで解釈されるがコンピュータがリトルエンディアンなので逆順に。
                uint _ip = (uint)0x00000000 | ((uint)ip.IP[3] << 24) | ((uint)ip.IP[2] << 16) | ((uint)ip.IP[1] << 8) | (uint)ip.IP[0];
                uint _sn = (uint)0x00000000 | ((uint)ip.Subnet[3] << 24) | ((uint)ip.Subnet[2] << 16) | ((uint)ip.Subnet[1] << 8) | (uint)ip.Subnet[0];

                _ip &= _sn;
                _ip |= ~_sn;

                Console.WriteLine(ip.Adapter);
                Console.WriteLine("tIPアドレス:" + ip.IPString);
                Console.WriteLine("tサブネットマスク:" + ip.SubnetString);   
                Console.WriteLine("tローカルブロードキャストアドレス:" + (new IPAddress((long)_ip)).ToString());    //long値のIPアドレスを標準表記に変換
            }
        }
    }
}

//WMIクラスについて(Microsoft Document Explorer 2005のURL欄に(2008でもokを確認))
//ms-help://MS.VSCC.v80/MS.MSDN.v80/MS.WIN32COM.v10.en/wmisdk/wmi/win32_networkadapterconfiguration.htm

※注意:
このプログラムは個人の環境で作成したものであり正式な運用を想定していません。このプログラムを利用する際は自己責任でお願いします。

実行・試験環境:
Windows XP Professional SP3
Visual Studio 2005 Standardで動作確認
Visual Studio 2008 Standardで動作確認
編集: CPad for C#

か、感想です。

最後の計算部分、_ipや_snをlongとして定義しているとかならず最後のnew IPAddress(_ip)でArgumentOutOfRangeExceptionが出て困りました。自分だって例外が好きなわけではありませんから。理由は、32ビットのIPアドレスの先頭ビットが1だったからか64ビットのlongにした際に符号拡張されてしまったからと思われます。簡単な解決策はulongにするか上のソースのように書くことくらいでしょうか。これで2時間以上格闘しまして今朝5時。

WMIは、上に書いた"難しい"点についてはドキュメントを見つけりゃおkなので、あとは非常に使いよい仕組みだと思われます。今回使用したWMIクラスはソースリストの最後に書いてあります。
これだけでしばらく遊べるのでやってみてはどうでしょうか?WMIを使ってOSの情報を取得する - DOBON.NETが参考になります。

 

Powered by ScribeFire.

  1. 2008年05月30日(金) 18時11分00秒|
  2. カテゴリ: ネットワーク|
  3. コメント:0

コメント


コメントの投稿

管理者にだけ表示を許可する


画像の文字を半角数字で下記ボックスに記入ください。
文字が読みにくい場合はブラウザの更新をすると新しい文字列が表示されます。

プロフィール

xptn
Author:xptn
例外人生まっしぐらの高専卒大学生。平成生まれの21歳。
twitter

最近のエントリー

カテゴリー

最近のコメント

月別アーカイブ

リンク

RSSリンク

ブラックジャックによろしく
DTIブログ
ブログでアフィリエイト
DTIブログポータルへ
このブログを通報
Report Abuse