SIN@SAPPOROWORKSの覚書

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

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

2011.11.28 追記
本記事の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#サンプル
まったくひどいサンプルになってます。

本当は、

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

としたかったのですが、「ジェネリックコンストラクターの型’IP_ADAPTER_INDEX_MAP’は、アンマネージ型にする必要があります」っと言って受け付けてくれません。
IP_ADAPTER_INDEX_MAPにStringが無ければ問題無いようなのですが・・・・

結局、パラメータを(IntPtr AdapterInfo)として宣言したので、非常にひどい状態になってしまってます。


#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 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 num = Marshal.ReadInt32(p)//IP_INTERFACE_INFO.NumAdapters(データ数)
            let ptr = IntPtr.Add(p, 4)
            let ar =
                let step = Marshal.SizeOf(typeof<IP_ADAPTER_INDEX_MAP>)
                [0..num-1]
                |>List.map(fun i ->IntPtr.Add(ptr, step * i)) //データへのポインタ
                |>List.map(fun p -> Marshal.PtrToStructure(p,typeof<IP_ADAPTER_INDEX_MAP>):?>IP_ADAPTER_INDEX_MAP)
            Marshal.Release(p)|>ignore
            ar
        else
            []
    else
        []

ar|>Seq.iter(fun a -> printfn "%d %s" a.Index a.Name) 

let ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof<IP_ADAPTER_INDEX_MAP>))

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

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

Marshal.FreeHGlobal(ptr)

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