SIN@SAPPOROWORKSの覚書

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

ARPテーブルの取得 (C#)(F#)


ARPテーブル一覧は、iphlpapi.dllのGetIpNetTable()で取得できます。
サンプルは、コマンドラインから「arp -a」としたものと同じです。

C#サンプル

using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Linq;

namespace Example {
    class Program {

        [DllImport("iphlpapi.dll")]
        extern static int GetIpNetTable(IntPtr pTcpTable, ref int pdwSize, bool bOrder);

        [StructLayout(LayoutKind.Sequential)]
        public struct MIB_IPNETROW {
            public int Index;
            public int PhysAddrLen;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
            public byte[] PhysAddr;
            public int Addr;
            public int Type;
        }

        static string[] typeStr = { "", "その他", "無効", "動的", "静的" };

        static void Main(string[] args) {

            List<MIB_IPNETROW> ar = new List<MIB_IPNETROW>();

            int size = 0;
            GetIpNetTable(IntPtr.Zero, ref size, true);//必要サイズの取得
            var p = Marshal.AllocHGlobal(size);//メモリ割当て
            if (GetIpNetTable(p, ref size, true) == 0) {//データの取得
                var num = Marshal.ReadInt32(p);//MIB_IPNETTABLE.dwNumEntries(データ数)
                var ptr = IntPtr.Add(p, 4);
                for (int i = 0; i < num; i++) {
                    ar.Add((MIB_IPNETROW)Marshal.PtrToStructure(ptr, typeof(MIB_IPNETROW)));
                    ptr = IntPtr.Add(ptr, Marshal.SizeOf(typeof(MIB_IPNETROW)));//次のデータ
                }
                Marshal.FreeHGlobal(p);  //メモリ開放
            }

            foreach (var i in ar.GroupBy(x => x.Index)) {
                Console.WriteLine(string.Format("nインターフェース:0x{0:x}", i.Key));
                Console.WriteLine("  インターネット アドレスt物理アドレスt種類");
                foreach (var n in ar.Where(x => x.Index == i.Key)) {
                    Console.WriteLine(string.Format("  {0,-15}t{1}t{2}"
                        ,ipstr(n.Addr)
                        ,macstr(n.PhysAddr)
                        ,typeStr[n.Type]));
                }
            }
            Console.WriteLine();
            Console.WriteLine("何かのキーを押してください。");
            Console.ReadKey();
        }

        private static string macstr(byte[] m) {
            return string.Format("{0:x2}-{1:x2}-{2:x2}-{3:x2}-{4:x2}-{5:x2}", m[0], m[1], m[2], m[3], m[4], m[5]);
        }

        private static string ipstr(int addr) {
            var b = BitConverter.GetBytes(addr);
            return string.Format("{0}.{1}.{2}.{3}", b[0], b[1], b[2], b[3]);
        }
    }
}

F#サンプル

open System
open System.Runtime.InteropServices
open System.Collections.Generic
open System.Linq

[<DllImport("iphlpapi.dll")>]
extern int GetIpNetTable(IntPtr pTcpTable, int *pdwSize, bool bOrder);

[<Struct; StructLayout(LayoutKind.Sequential)>]
type MIB_IPNETROW =
    val Index:int
    val PhysAddrLen:int
    [<MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)>]
    val PhysAddr:byte []
    val Addr:int
    val Type:int

let ipstr(addr:int)=
    let b = BitConverter.GetBytes(addr)
    sprintf "%d.%d.%d.%d" b.[0] b.[1] b.[2] b.[3] 

let macstr(m:byte []) = 
    sprintf "%02x-%02x-%02x-%02x-%02x-%02x"  m.[0] m.[1] m.[2] m.[3] m.[4] m.[5]

let typeStr = ["";"その他";"無効";"動的";"静的"]

let mutable ar  = [] 
printfn "インデックスtインターネット アドレスt物理アドレスt種類"
let mutable size = 0
GetIpNetTable(IntPtr.Zero,&&size, true)|>ignore//必要サイズの取得
let p = Marshal.AllocHGlobal(size)//メモリ割当て
if GetIpNetTable(p, &&size, true) = 0 then //データの取得
    let num = Marshal.ReadInt32(p)//MIB_IPNETTABLE.dwNumEntries(データ数)
    let mutable ptr = IntPtr.Add(p, 4)
    for i in [0..num-1] do
        ar <- ar @ [Marshal.PtrToStructure(ptr,typeof<MIB_IPNETROW>):?>MIB_IPNETROW]
        ptr <- IntPtr.Add(ptr, Marshal.SizeOf(typeof<MIB_IPNETROW>))//次のデータ
        
    Marshal.FreeHGlobal(p)  //メモリ開放

for i in ar.GroupBy( fun n -> n.Index) do
    printfn "nインターフェース:0x%x" i.Key
    printfn "  インターネット アドレスt物理アドレスt種類"
    for n in ar.Where(fun (x:MIB_IPNETROW) -> x.Index=i.Key) do
        printfn "  %-15st%st%s"
            (ipstr(n.Addr))
            (macstr(n.PhysAddr))
            (typeStr.[n.Type])
    
printfn "何かのキーを押してください。"
Console.ReadKey() |> ignore

もう少し、進歩できないかと思い始めています・・・・
zeclさん、mclh46さん もっとF#らしい書き方があったら、ぜひ教えてやってください。
 「っと、何気なくここに書いてみるテスト」