TCPコネクションの一覧取得(プロセスID含む) (C#)(F#)
netstatに-oオプションを付けると、それぞれのコネクションのプロセスIDが表示されますが、GetTcpTable()では、プロセスIDを取得することができません。
WindowsXPまでは、この情報が必要な場合、非公開のAPIであるAllocateAndGetTcpExTableFromStack()を使用していましたが、Vista以降、同APIは廃止され、GetExtendedTcpTable()の使用が推奨されています。
GetExtendedTcpTableの定義は、下記のようになっているのですが、DllImportで呼び出すとき4番目と6番目の引数をint(32ビット)で指定しないと、エラー(※1 PInvokeStacImbalance)となりました。ドキュメントに誤りがあるのか、私の使用方法が間違っているのか、ちょっと不明です。
DWORD GetExtendedTcpTable(
__out PVOID pTcpTable,
__inout PDWORD pdwSize,
__in BOOL bOrder,
__in ULONG ulAf,
__in TCP_TABLE_CLASS TableClass,
__in ULONG Reserved
);
※1 PInvokeStacImbalanceが検出されました。
シグネチャが案マネージターゲットシグネチャに一致していないことが原因と考えられます。
サンプルは、「netstat -ao -p TCP」とした時と同じ出力です。
C#サンプル
using System; using System.Runtime.InteropServices; namespace Example { class Program { enum TCP_TABLE_CLASS { TCP_TABLE_BASIC_LISTENER, TCP_TABLE_BASIC_CONNECTIONS, TCP_TABLE_BASIC_ALL, TCP_TABLE_OWNER_PID_LISTENER, TCP_TABLE_OWNER_PID_CONNECTIONS, TCP_TABLE_OWNER_PID_ALL, TCP_TABLE_OWNER_MODULE_LISTENER, TCP_TABLE_OWNER_MODULE_CONNECTIONS, TCP_TABLE_OWNER_MODULE_ALL }; [DllImport("iphlpapi.dll")] extern static int GetExtendedTcpTable(IntPtr pTcpTable, ref int pdwSize, bool bOrder, uint ulAf,TCP_TABLE_CLASS TableClass, int Reserved); [StructLayout(LayoutKind.Sequential)] public struct MIB_TCPROW_OWNER_PID { public int State; public int LocalAddr; public int LocalPort; public int RemoteAddr; public int RemotePort; public int OwningPid; } public static string[] StateStrings = { "", "CLOSED","LISTENING","SYN_SENT","SYN_RCVD","ESTABLISHED","FIN_WAIT1", "FIN_WAIT2","CLOSE_WAIT","CLOSING","LAST_ACK","TIME_WAIT","DELETE_TCB"}; static void Main(string[] args) { Console.WriteLine("プロトコルtローカルアドレスt外部アドレスt状態tPID"); int size = 0; uint AF_INET = 2; // IPv4 //必要サイズの取得 GetExtendedTcpTable(IntPtr.Zero,ref size,true,AF_INET,TCP_TABLE_CLASS.TCP_TABLE_OWNER_PID_ALL, 0); var p = Marshal.AllocHGlobal(size);//メモリ割当て //TCPテーブルの取得 if (GetExtendedTcpTable(p, ref size, true, AF_INET, TCP_TABLE_CLASS.TCP_TABLE_OWNER_PID_ALL, 0) == 0){ var num = Marshal.ReadInt32(p);//MIB_TCPTABLE_OWNER_PID.dwNumEntries(データ数) var ptr = IntPtr.Add(p, 4); for (int i = 0; i < num; i++) { var o = (MIB_TCPROW_OWNER_PID)Marshal.PtrToStructure(ptr, typeof(MIB_TCPROW_OWNER_PID)); if (o.RemoteAddr == 0) o.RemotePort = 0;//RemoteAddrが0の場合は、RemotePortも0にする Console.WriteLine(string.Format("TCPt{0}:{1}t{2}:{3}t{4}t{5}" , ipstr(o.LocalAddr), htons(o.LocalPort) , ipstr(o.RemoteAddr), htons(o.RemotePort) , StateStrings[o.State] , o.OwningPid )); ptr = IntPtr.Add(ptr, Marshal.SizeOf(typeof(MIB_TCPROW_OWNER_PID)));//次のデータ } Marshal.FreeHGlobal(p); //メモリ開放 } Console.WriteLine(); Console.WriteLine("何かのキーを押してください。"); Console.ReadKey(); } 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]); } public static int htons(int i) { var tmp = (((0x000000ff & i) << 8) + ((0x0000ff00 & i) >> 8)) + ((0x00ff0000 & i) << 8) + ((0xff000000 & i) >> 8); return (int)tmp; } } }
F#サンプル
open System open System.Runtime.InteropServices type TCP_TABLE_CLASS = | TCP_TABLE_BASIC_LISTENER=0 | TCP_TABLE_BASIC_CONNECTIONS=1 | TCP_TABLE_BASIC_ALL=2 | TCP_TABLE_OWNER_PID_LISTENE=3 | TCP_TABLE_OWNER_PID_CONNECTIONS=4 | TCP_TABLE_OWNER_PID_ALL=5 | TCP_TABLE_OWNER_MODULE_LISTENER=6 | TCP_TABLE_OWNER_MODULE_CONNECTIONS=7 | TCP_TABLE_OWNER_MODULE_ALL=8 [<DllImport("iphlpapi.dll")>] extern int GetExtendedTcpTable(IntPtr pTcpTable,int *pdwSize, bool bOrder, UInt32 ulAf, TCP_TABLE_CLASS TableClass, int Reserved) [<StructLayout(LayoutKind.Sequential)>] type MIB_TCPROW_OWNER_PID = struct val State:int val LocalAddr:int val LocalPort:int val RemoteAddr:int val mutable RemotePort:int val OwningPid:int end let StateStrings = [ "";"CLOSED";"LISTENING";"SYN_SENT";"SYN_RCVD";"ESTABLISHED";"FIN_WAIT1"; "FIN_WAIT2";"CLOSE_WAIT";"CLOSING";"LAST_ACK";"TIME_WAIT";"DELETE_TCB"] let ipstr(addr:int)= let b = BitConverter.GetBytes(addr) sprintf "%d.%d.%d.%d" b.[0] b.[1] b.[2] b.[3] let htons(i) = (((0x000000ff&&&i) <<< 8) + ((0x0000ff00&&&i) sma>>> 8)) + ((0x00ff0000&&&i)<<<8) + ((0xff000000&&&i)>>>8) printfn "プロトコルtローカルアドレスt外部アドレスt状態tPID" let AF_INET = 2u // IPv4 let mutable size = 0 //必要サイズの取得 GetExtendedTcpTable(IntPtr.Zero,&&size,true,AF_INET,TCP_TABLE_CLASS.TCP_TABLE_OWNER_PID_ALL, 0)|>ignore let p = Marshal.AllocHGlobal(size);//メモリ割当て //TCPテーブルの取得 if GetExtendedTcpTable(p, &&size, true, AF_INET, TCP_TABLE_CLASS.TCP_TABLE_OWNER_PID_ALL, 0) = 0 then let num = Marshal.ReadInt32(p);//MIB_TCPTABLE_OWNER_PID.dwNumEntries(データ数) let mutable ptr = IntPtr.Add(p, 4); for i in [0..num-1] do let mutable o = Marshal.PtrToStructure(ptr, typeof<MIB_TCPROW_OWNER_PID>):?>MIB_TCPROW_OWNER_PID if o.RemoteAddr = 0 then o.RemotePort <- 0//RemoteAddrが0の場合は、RemotePortも0にする printfn "TCPt%s:%dt%s:%dt%st%d" (ipstr(o.LocalAddr)) (htons(o.LocalPort)) (ipstr(o.RemoteAddr)) (htons(o.RemotePort)) StateStrings.[o.State] o.OwningPid ptr <- IntPtr.Add(ptr, Marshal.SizeOf(typeof<MIB_TCPROW_OWNER_PID>));//次のデータ Marshal.FreeHGlobal(p); //メモリ開放 printfn "何かのキーを押してください。" Console.ReadKey() |> ignore