IcmpSendEcho()によるpingの送受信(#C)(#F)
IPHLPAPI.DLLのIcmpSendEcho()によりpingの送受信が行えます。
以前に紹介した、System.Net.NetworkInformation.Pingより、ややきめ細かい指定が可能です。
C#サンプル
using System; using System.Runtime.InteropServices; namespace Example { class Program { [DllImport("icmp.dll", SetLastError=true)] static extern IntPtr IcmpCreateFile(); [DllImport("icmp.dll", SetLastError=true)] static extern bool IcmpCloseHandle(IntPtr handle); [DllImport("icmp.dll", SetLastError=true)] static extern Int32 IcmpSendEcho(IntPtr icmpHandle, Int32 destinationAddress, String requestData, Int16 requestSize, ref ICMP_OPTIONS requestOptions, ref ICMP_ECHO_REPLY replyBuffer, Int32 replySize, Int32 timeout); [DllImport("ws2_32.dll", ExactSpelling = true)] static extern Int32 inet_addr(string cp); [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] private struct ICMP_OPTIONS { public Byte Ttl; public Byte Tos; public Byte Flags; public Byte OptionsSize; public IntPtr OptionsData; }; [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] private struct ICMP_ECHO_REPLY { public Int32 Address; public Int32 Status; public Int32 RoundTripTime; public Int16 DataSize; public Int16 Reserved; public IntPtr DataPtr; public ICMP_OPTIONS Options; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 250)] public String Data; } static void Main(string[] args){ ICMP_ECHO_REPLY reply = new ICMP_ECHO_REPLY(); ICMP_OPTIONS option = new ICMP_OPTIONS(); option.Ttl = 255; var data = "ABCDEF"; var timeout = 1000; var ipStr = "192.168.0.254"; var handle = IcmpCreateFile(); if (0 != IcmpSendEcho(handle, inet_addr(ipStr), data, (Int16)data.Length , ref option, ref reply, Marshal.SizeOf(reply), timeout)) { if (reply.Status == 0) { Console.WriteLine(string.Format("{0} からの応答: バイト数 ={1} 時間 {2}ms TTL={3}" , ipStr, reply.DataSize, reply.RoundTripTime, reply.Options.Ttl)); } else { Console.WriteLine("ERROR Status={0}",reply.Status); } } else { Console.WriteLine("ERROR Status={0}", reply.Status); } IcmpCloseHandle(handle); Console.WriteLine("n何かのキーを押してください。"); Console.ReadKey(); } } }
F#サンプル
やはり、下記のように定義することはできません。
[<DllImport("icmp.dll")>]
extern Int32 IcmpSendEcho( , , ICMP_ECHO_REPLY *replay , , , )
ジェネリックコンストラクターの型'ICMP_ECHO_REPLY'はアンマネージ型にする必要があります。
構造体の中が要素がすべてアンマネージ型の場合は、上記のような指定が可能なのですがICMP_ECHO_REPLYにはstringが含まれているためエラーとなるようです。
ここに構造体のポインタで使用できないため、いちいちMarshalでIntPtrへ変換する手順が非常にうるさくなってしまいます。解決策では無いのですが、今回は、ラッパ関数_IcmpSendEcho()を定義してみました。
#nowarn "9" "51" open System open System.Runtime.InteropServices [<Struct;StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)>] type ICMP_OPTIONS = val mutable Ttl:SByte val Tos:Byte val Flags:Byte val OptionsSize:Byte val OptionsData:IntPtr [<Struct;StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)>] type ICMP_ECHO_REPLY = val Address:IntPtr val Status:Int32 val RoundTripTime:Int32 val DataSize:Int16 val Reserved:Int16 val DataPtr:IntPtr val Options:ICMP_OPTIONS [<MarshalAs(UnmanagedType.ByValTStr, SizeConst = 250)>] val Data:String [<DllImport("icmp.dll", SetLastError=true)>] extern IntPtr IcmpCreateFile() [<DllImport("icmp.dll", SetLastError=true)>] extern bool IcmpCloseHandle(IntPtr handle) [<DllImport("icmp.dll", SetLastError=true)>] extern Int32 IcmpSendEcho(IntPtr icmpHandle, Int32 destinationAddress, String requestData, Int16 requestSize,ICMP_OPTIONS *requestOptions,IntPtr replay, Int32 replySize, Int32 timeout) [<DllImport("ws2_32.dll", ExactSpelling = true)>] extern Int32 inet_addr(string cp) //IcmpSendEcho()をラッピング let _IcmpSendEcho(handle,addr,data,ttl,timeout) = let TYPE = typeof<ICMP_ECHO_REPLY> let mutable option = new ICMP_OPTIONS() option.Ttl <- ttl let size = Marshal.SizeOf(TYPE) let p = Marshal.AllocCoTaskMem(size) let result = IcmpSendEcho(handle,addr,data,int16(data.Length),&&option,p,size,timeout); let reply = Marshal.PtrToStructure(p,TYPE):?>ICMP_ECHO_REPLY Marshal.FreeCoTaskMem(p) result,reply //main() let data = "ABCDEF" //データ let timeout = 1000 //タイムアウト let ttl = 125y //TTL let ipStr = "192.168.0.254" //宛先 let handle = IcmpCreateFile() let result,reply = _IcmpSendEcho(handle,inet_addr(ipStr),data,ttl,timeout) if result <> 0 then if reply.Status = 0 then printfn "%s からの応答: バイト数 =%d 時間 %dms TTL=%d" ipStr reply.DataSize reply.RoundTripTime reply.Options.Ttl else printfn "ERROR Status=%d" reply.Status else printfn "ERROR Status=%d" reply.Status IcmpCloseHandle(handle)|>ignore printfn "n何かのキーを押してください。" Console.ReadKey() |> ignore