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

SIN@SAPPOROWORKSの覚書

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

DHCPで取得したアドレスのreleaseとrenew (C#)(F#) その2


先日の同タイトルの記事のうち、F#サンプルを書き直してみたので再投です。

DHCPで取得した動的アドレスは、iphelapi.dllのIpReleaseAddressとIpRenewAddressで開放と再取得が可能です。同APIのパラメータ(IP_ADAPTER_INDEX_MAP )は、GetInterfaceInfoで取得したインターフェース情報をそのまま渡します。

C#サンプル

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

namespace Examlpe {
    class Program {

        const int MAX_ADAPTER_NAME = 128;
        const int ERROR_INSUFFICIENT_BUFFER = 122;
        const int ERROR_SUCCESS = 0;
        
        [DllImport("Iphlpapi.dll", CharSet = CharSet.Auto)]
        static extern int IpReleaseAddress(ref IP_ADAPTER_INDEX_MAP AdapterInfo);

        [DllImport("Iphlpapi.dll", CharSet = CharSet.Auto)]
        static extern int IpRenewAddress(ref IP_ADAPTER_INDEX_MAP AdapterInfo);

        [DllImport("Iphlpapi.dll", CharSet = CharSet.Auto)]
        static extern int GetInterfaceInfo(IntPtr PIfTableBuffer, ref int size);

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        struct IP_ADAPTER_INDEX_MAP {
            public int Index;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_ADAPTER_NAME)]
            public String Name;
        }

        static void Main(string[] args) {
            var ar = new List<IP_ADAPTER_INDEX_MAP>();
            int size = 0;
            if (GetInterfaceInfo(IntPtr.Zero, ref size) == ERROR_INSUFFICIENT_BUFFER) {//nullを設定して必要サイズを得る
                var p = Marshal.AllocHGlobal(size);//メモリ割当て
                if (GetInterfaceInfo(p, ref size) == ERROR_SUCCESS) {//バッファをセットして2回目の呼び出し
                    var num = Marshal.ReadInt32(p);//IP_ADAPTER_INDEX_MAP配列の数を取得
                    var ptr = IntPtr.Add(p, 4);
                    for (int i = 0; i < num; i++) {//インターフェース数分だけ処理する
                        var adapter = (IP_ADAPTER_INDEX_MAP)Marshal.PtrToStructure(ptr, typeof(IP_ADAPTER_INDEX_MAP));
                        Console.WriteLine(string.Format("[{0}]{1}", adapter.Index,adapter.Name));
                        ar.Add(adapter);
                        ptr = IntPtr.Add(ptr, Marshal.SizeOf(typeof(IP_ADAPTER_INDEX_MAP)));//次のデータ
                    }
                }
                Marshal.Release(p);
            }
            //release
            Console.WriteLine("n何かのキーを押すと、IPアドレスをreleaseします。");
            Console.ReadKey();
            ar.ForEach(n => IpReleaseAddress(ref n));
            Console.WriteLine("releaseしました。");

            //renew
            Console.WriteLine("n何かの)押すと、IPアドレスをrenewします。");
            Console.ReadKey();
            ar.ForEach(n => IpRenewAddress(ref n));
            Console.WriteLine("renewしました。");

            Console.WriteLine("n何かのキーを押してください。");
            Console.ReadKey();
        }
    }
}

F#サンプル
GCを全面的に信用して?インターフェースの一覧をポインタで保持してみました。


#nowarn "9" "51"

open System
open System.Runtime.InteropServices

[<Literal>]
let MAX_ADAPTER_NAME=128
let ERROR_INSUFFICIENT_BUFFER = 122
let ERROR_SUCCESS = 0

[<Struct; StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)>]
type IP_ADAPTER_INDEX_MAP = 
    val Index:int
    [<MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_ADAPTER_NAME)>]
    val Name:String

[<DllImport("Iphlpapi.dll", CharSet = CharSet.Auto)>]
extern int IpReleaseAddress(IntPtr AdapterInfo)

[<DllImport("Iphlpapi.dll", CharSet = CharSet.Auto)>]
extern int IpRenewAddress(IntPtr AdapterInfo)

[<DllImport("Iphlpapi.dll", CharSet = CharSet.Auto)>]
extern int GetInterfaceInfo(IntPtr PIfTableBuffer,int *size)

let TYPE = typeof<IP_ADAPTER_INDEX_MAP>

//インターフェースの一覧取得
let ar =
    [
    let mutable size = 0
    if GetInterfaceInfo(IntPtr.Zero,&&size) = ERROR_INSUFFICIENT_BUFFER then
        let p = Marshal.AllocHGlobal(size)//メモリ割当て
        if GetInterfaceInfo(p,&&size) = ERROR_SUCCESS then
            let step = Marshal.SizeOf(TYPE)
            let num = Marshal.ReadInt32(p)//IP_INTERFACE_INFO.NumAdapters(データ数)
            let ptr = IntPtr.Add(p, 4)
            for i in [0..num-1] do
                let p = IntPtr.Add(ptr, step*i) //データへのポインタ
                yield p
         Marshal.Release(p)|>ignore
    ]

//インターフェースの一覧表示
ar
|>Seq.map(fun p -> Marshal.PtrToStructure(p,TYPE):?>IP_ADAPTER_INDEX_MAP)
|>Seq.iter(fun n -> printfn "%d %s" n.Index n.Name) 

//release
printfn "n何かのキーを押すと、IPアドレスをreleaseします。"
Console.ReadKey() |> ignore
ar|>Seq.iter(fun p -> IpReleaseAddress(p)|>ignore)
printfn "releaseしました。"

//renew
printfn "n何かのキーを押すと、IPアドレスをrenewします。"
Console.ReadKey() |> ignore
ar|>Seq.iter(fun p -> IpRenewAddress(p)|>ignore)
printfn "renewしました。"


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