As TCPView calls
GetExtendedUdpTable and GetExtendedTcpTable
but also
OpenTrace, StartTrace, ... with "NT Kernel Logger", it can be complicated to do the same thing in VB.NET, then to simplify, I did this test with the help of ChatGPT and Microsoft.Diagnostics.Tracing.TraceEvent Nuget Package
You can ask ChatGPT (or Copilot) to change aggregation (PID instead of PID + Protocol) and do the same thing for disk
(KernelTraceEventParser.Keywords.DiskIO or KernelTraceEventParser.Keywords.FileIO)
Imports Microsoft.Diagnostics.Tracing.Parsers
Imports Microsoft.Diagnostics.Tracing.Session
Imports System.Collections.Concurrent
Imports System.Diagnostics
Public Class Form1
Public Class TrafficStat
Public Property Sent As Long
Public Property Received As Long
End Class
Private Shared ProcessTraffic As New ConcurrentDictionary(Of Tuple(Of Integer, String), TrafficStat)()
Private Shared traceSession As TraceEventSession
Private grid As DataGridView
Private refreshTimer As System.Windows.Forms.Timer
Public Sub New()
InitializeComponent()
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
' Add Manifest file with requireAdministrator
If Not IsAdministrator() Then
MessageBox.Show("Please run this app as Administrator!", "Admin Rights Needed", MessageBoxButtons.OK, MessageBoxIcon.Warning)
Return
End If
Text = "TCP/UDP Process Monitor"
Width = 800
Height = 600
grid = New DataGridView() With {
.Dock = DockStyle.Fill,
.ReadOnly = True,
.AutoGenerateColumns = False,
.AllowUserToAddRows = False
}
grid.Columns.Add(New DataGridViewTextBoxColumn() With {.HeaderText = "PID", .DataPropertyName = "PID"})
grid.Columns.Add(New DataGridViewTextBoxColumn() With {.HeaderText = "Process", .DataPropertyName = "ProcessName", .Width = 200})
grid.Columns.Add(New DataGridViewTextBoxColumn() With {.HeaderText = "Protocol", .DataPropertyName = "Protocol"})
grid.Columns.Add(New DataGridViewTextBoxColumn() With {.HeaderText = "Sent Bytes", .DataPropertyName = "SentBytes"})
grid.Columns.Add(New DataGridViewTextBoxColumn() With {.HeaderText = "Received Bytes", .DataPropertyName = "ReceivedBytes"})
grid.Columns.Add(New DataGridViewTextBoxColumn() With {.HeaderText = "Total Bytes", .DataPropertyName = "TotalBytes"})
Controls.Add(grid)
StartETWListener()
refreshTimer = New System.Windows.Forms.Timer() With {.Interval = 2000}
AddHandler refreshTimer.Tick, AddressOf RefreshProcessList
refreshTimer.Start()
End Sub
Private Shared Sub StartETWListener()
traceSession = New TraceEventSession("MyKernelNetworkSession")
' Enable Network TCPIP events (covers both v4 and v6 when you subscribe correctly)
traceSession.EnableKernelProvider(KernelTraceEventParser.Keywords.NetworkTCPIP)
Task.Run(Sub()
Dim kernel = traceSession.Source.Kernel
' TCP IPv4
AddHandler kernel.TcpIpRecv, Sub(data)
Dim pid = data.ProcessID
Dim size = data.size
Dim protocol = "TCP"
Dim key = Tuple.Create(pid, protocol)
ProcessTraffic.AddOrUpdate(key, New TrafficStat With {.Sent = 0, .Received = size},
Function(k, oldStat)
oldStat.Received += size
Return oldStat
End Function)
End Sub
AddHandler kernel.TcpIpSend, Sub(data)
Dim pid = data.ProcessID
Dim size = data.size
Dim protocol = "TCP"
Dim key = Tuple.Create(pid, protocol)
ProcessTraffic.AddOrUpdate(key, New TrafficStat With {.Sent = size, .Received = 0},
Function(k, oldStat)
oldStat.Sent += size
Return oldStat
End Function)
End Sub
' TCP IPv6
AddHandler kernel.TcpIpRecvIPV6, Sub(data)
Dim pid = data.ProcessID
Dim size = data.size
Dim protocol = "TCPv6"
Dim key = Tuple.Create(pid, protocol)
ProcessTraffic.AddOrUpdate(key, New TrafficStat With {.Sent = 0, .Received = size},
Function(k, oldStat)
oldStat.Received += size
Return oldStat
End Function)
End Sub
AddHandler kernel.TcpIpSendIPV6, Sub(data)
Dim pid = data.ProcessID
Dim size = data.size
Dim protocol = "TCPv6"
Dim key = Tuple.Create(pid, protocol)
ProcessTraffic.AddOrUpdate(key, New TrafficStat With {.Sent = size, .Received = 0},
Function(k, oldStat)
oldStat.Sent += size
Return oldStat
End Function)
End Sub
' UDP IPv4
AddHandler kernel.UdpIpRecv, Sub(data)
Dim pid = data.ProcessID
Dim size = data.size
Dim protocol = "UDP"
Dim key = Tuple.Create(pid, protocol)
ProcessTraffic.AddOrUpdate(key, New TrafficStat With {.Sent = 0, .Received = size},
Function(k, oldStat)
oldStat.Received += size
Return oldStat
End Function)
End Sub
AddHandler kernel.UdpIpSend, Sub(data)
Dim pid = data.ProcessID
Dim size = data.size
Dim protocol = "UDP"
Dim key = Tuple.Create(pid, protocol)
ProcessTraffic.AddOrUpdate(key, New TrafficStat With {.Sent = size, .Received = 0},
Function(k, oldStat)
oldStat.Sent += size
Return oldStat
End Function)
End Sub
' UDP IPv6
AddHandler kernel.UdpIpRecvIPV6, Sub(data)
Dim pid = data.ProcessID
Dim size = data.size
Dim protocol = "UDPv6"
Dim key = Tuple.Create(pid, protocol)
ProcessTraffic.AddOrUpdate(key, New TrafficStat With {.Sent = 0, .Received = size},
Function(k, oldStat)
oldStat.Received += size
Return oldStat
End Function)
End Sub
AddHandler kernel.UdpIpSendIPV6, Sub(data)
Dim pid = data.ProcessID
Dim size = data.size
Dim protocol = "UDPv6"
Dim key = Tuple.Create(pid, protocol)
ProcessTraffic.AddOrUpdate(key, New TrafficStat With {.Sent = size, .Received = 0},
Function(k, oldStat)
oldStat.Sent += size
Return oldStat
End Function)
End Sub
traceSession.Source.Process()
End Sub)
End Sub
Private Shared Function IsAdministrator() As Boolean
Dim identity = System.Security.Principal.WindowsIdentity.GetCurrent()
Dim principal = New System.Security.Principal.WindowsPrincipal(identity)
Return principal.IsInRole(System.Security.Principal.WindowsBuiltInRole.Administrator)
End Function
Private Sub RefreshProcessList()
Dim processes = Process.GetProcesses().ToDictionary(Function(p) p.Id, Function(p) p.ProcessName)
Dim stats = ProcessTraffic.
Select(Function(kvp)
Dim key = kvp.Key ' Tuple(Of Integer, String)
Dim pid = key.Item1
Dim protocol = key.Item2
Dim stat = kvp.Value
Dim processName As String = Nothing
processes.TryGetValue(pid, processName)
Return New ProcessStat With {
.PID = pid,
.ProcessName = If(processName, "Unknown"),
.Protocol = protocol,
.SentBytes = stat.Sent,
.ReceivedBytes = stat.Received,
.TotalBytes = stat.Sent + stat.Received
}
End Function).
OrderByDescending(Function(p) p.TotalBytes).
ToList()
grid.DataSource = stats
End Sub
Private Class ProcessStat
Public Property PID As Integer
Public Property ProcessName As String
Public Property Protocol As String
Public Property SentBytes As Long
Public Property ReceivedBytes As Long
Public Property TotalBytes As Long
End Class
End Class