読者です 読者をやめる 読者になる 読者になる

SIN@SAPPOROWORKSの覚書

C#を中心に、夜な夜な試行錯誤したコードの記録です。

NICのデバイス名とインデックスの列挙 (C#)(F#)


NICのデバイス名とインデックス番号は、通常、表面上はプログラムに現れて来ませんが、ネットワーク関連のプログラムを作成していると、要求されることがよくあります。
この情報は、iphlpapi.dllのGetInterfaceInfo()を使用することで取得できます。

GetInterfaceInfo()の返すデータサイズがインターフェースの数により変化し、不定なため、通常、バッファにnullを設定して、強制的にエラーを発生させ、必要なバッファサイズを取得してから使用します。

C#サンプル

using System;
using System.Runtime.InteropServices;

namespace Example {
    class Program {
        [DllImport("iphlpapi", CharSet = CharSet.Auto)]
        extern static int GetInterfaceInfo(Byte[] pIfTable, ref int pOutBufLen);

        const int MAX_ADAPTER_NAME = 128;
        const int ERROR_INSUFFICIENT_BUFFER = 122;
        const int ERROR_SUCCESS = 0;

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        struct IP_ADAPTER_INDEX_MAP {
            public int Index;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_ADAPTER_NAME)]
            public String Name;
        }
        //IP_ADAPTER_INDEX_MAPのサイズ取得
        static int SizeOfMap() {
            IP_ADAPTER_INDEX_MAP map = new IP_ADAPTER_INDEX_MAP();
            return Marshal.SizeOf(map);
        }

        //byte[]から IP_ADAPTER_INDEX_MAP構造体へのデータコピー
        static IP_ADAPTER_INDEX_MAP CreateIpAdapterIndexMax(byte[] buffer, int offset) {
            IntPtr ptr = Marshal.AllocHGlobal(SizeOfMap());
            Marshal.Copy(buffer, offset, ptr, SizeOfMap());
            IP_ADAPTER_INDEX_MAP map = (IP_ADAPTER_INDEX_MAP)Marshal.PtrToStructure(ptr, typeof(IP_ADAPTER_INDEX_MAP));
            Marshal.FreeHGlobal(ptr);
            return map;
        }

        static void Main(string[] args) {
            int size = 0;
            if (GetInterfaceInfo(null, ref size) == ERROR_INSUFFICIENT_BUFFER) {//nullを設定して必要サイズを得る
                Byte[] buffer = new Byte[size];//必要サイズを確保
                if (GetInterfaceInfo(buffer, ref size) == ERROR_SUCCESS) {//バッファをセットして2回目の呼び出し
                    int offSet = 0;
                    int num = BitConverter.ToInt32(buffer, offSet);//IP_ADAPTER_INDEX_MAP配列の数を取得
                    offSet += Marshal.SizeOf(num);
                    for (int i = 0; i < num; i++) {//インターフェース数分だけ処理する
                        IP_ADAPTER_INDEX_MAP map = CreateIpAdapterIndexMax(buffer, offSet);
                        Console.WriteLine(string.Format("nindex:{0}", map.Index));
                        Console.WriteLine(string.Format("Name:{0}", map.Name));
                        offSet += SizeOfMap();
                    }
                }
            }
            Console.WriteLine("何かのキーを押してください。");
            Console.ReadKey();
        }
    }
}


F#サンプル

open System
open System.Runtime.InteropServices

[<DllImport("iphlpapi")>]
extern int GetInterfaceInfo(Byte[] pIfTable,int *pOutBufLen)

let MAX_ADAPTER_NAME = 128
let ERROR_INSUFFICIENT_BUFFER = 122
let ERROR_SUCCESS = 0

[<StructLayout(LayoutKind.Sequential,CharSet=CharSet.Auto)>]
type IP_ADAPTER_INDEX_MAP = 
    struct
        val Index : int
         [<MarshalAs(UnmanagedType.ByValTStr, SizeConst=128)>]
        val Name : string
    end

//IP_ADAPTER_INDEX_MAPのサイズ取得    
let SizeOfMap() =
    let map = new IP_ADAPTER_INDEX_MAP()
    Marshal.SizeOf(map)

//byte[]から IP_ADAPTER_INDEX_MAP構造体へのデータコピー
let CreateIpAdapterIndexMax(buffer:byte[],offset:int) = 
    let ptr = Marshal.AllocHGlobal(SizeOfMap())
    Marshal.Copy(buffer, offset, ptr,SizeOfMap())
    let map = Marshal.PtrToStructure(ptr, typeof<IP_ADAPTER_INDEX_MAP>):?>IP_ADAPTER_INDEX_MAP
    Marshal.FreeHGlobal(ptr)
    map

let mutable size = 0;
if GetInterfaceInfo(null, &&size) = ERROR_INSUFFICIENT_BUFFER then //nullを設定して必要サイズを得る
    let buffer : byte array = Array.create size 0uy //必要サイズを確保
    if GetInterfaceInfo(buffer, &&size) = ERROR_SUCCESS then //バッファをセットして2回目の呼び出し
        let num = BitConverter.ToInt32(buffer,0);//IP_ADAPTER_INDEX_MAP配列の数を取得
        let mutable offSet= Marshal.SizeOf(num)
        for i in 1 .. num do //インターフェース数分だけ処理する
            let map = CreateIpAdapterIndexMax(buffer, offSet)
            printfn "nindex:%d" map.Index
            printfn "Name:%s" map.Name
            offSet <- offSet + SizeOfMap()
            

printfn "何かのキーを押してください。"
Console.ReadKey() |> ignore