From 16fffe9540f22fcd7485a6a15a336dc2b220b1c1 Mon Sep 17 00:00:00 2001 From: Tarkiin Date: Tue, 30 Jun 2026 22:38:35 +0200 Subject: [PATCH 1/2] feat: add download, upload and total traffic columns to connections list --- bin/i18n/!example.txt | Bin 18596 -> 18630 bytes bin/i18n/Spanish.ini | Bin 21386 -> 21420 bytes src/global.h | 9 +-- src/listview.c | 41 +++++++++++- src/main.c | 3 + src/main.h | 19 ++++-- src/messages.c | 93 ++++++++++++++++++++++++---- src/network.c | 141 ++++++++++++++++++++++++++++++++++++++++-- src/resource.h | 5 +- src/resource.rc | 7 ++- 10 files changed, 287 insertions(+), 31 deletions(-) diff --git a/bin/i18n/!example.txt b/bin/i18n/!example.txt index d4613880e419d1cbac73ed65961565f2d7772644..dc0f90dc4beb13415ff2d71f2e70f182e30e4bd5 100644 GIT binary patch delta 32 mcmZ27k@46>#tj?X1Vb478A2Ew8GIOQfpk7Y$>#ZPtV{r=iU{2R delta 18 acmX>$k#WgH#tj?XCjW64*!;-t2_pbh& #include #include -#include +#include +#include #include #include #include @@ -26,9 +27,9 @@ #include "rapp.h" #include "main.h" -#include "resource.h" - -DECLSPEC_SELECTANY STATIC_DATA config = {0}; +#include "resource.h" + +DECLSPEC_SELECTANY STATIC_DATA config = {0}; DECLSPEC_SELECTANY PROFILE_DATA profile_info = {0}; DECLSPEC_SELECTANY PR_HASHTABLE apps_table = NULL; diff --git a/src/listview.c b/src/listview.c index cfdd7754..043ebc96 100644 --- a/src/listview.c +++ b/src/listview.c @@ -172,7 +172,7 @@ VOID _app_listview_addnetworkitem ( _In_ ULONG network_hash ) { - _r_listview_additem (hwnd, IDC_NETWORK, INT_ERROR, LPSTR_TEXTCALLBACK, I_IMAGECALLBACK, I_GROUPIDCALLBACK, _app_listview_createcontext (network_hash)); + _r_listview_additem (hwnd, IDC_NETWORK, INT_MAX, LPSTR_TEXTCALLBACK, I_IMAGECALLBACK, I_GROUPIDCALLBACK, _app_listview_createcontext (network_hash)); } VOID _app_listview_addlogitem ( @@ -999,10 +999,14 @@ INT CALLBACK _app_listview_compare_callback ( PR_STRING item_text_2; PITEM_LOG ptr_log1; PITEM_LOG ptr_log2; + PITEM_NETWORK ptr_network1; + PITEM_NETWORK ptr_network2; WCHAR section_name[128]; HWND hwnd; LONG64 timestamp1 = 0; LONG64 timestamp2 = 0; + LONG64 value1 = 0; + LONG64 value2 = 0; ULONG context1; ULONG context2; LONG column_id; @@ -1098,6 +1102,41 @@ INT CALLBACK _app_listview_compare_callback ( if (ptr_log2) _r_obj_dereference (ptr_log2); } + else if (listview_id == IDC_NETWORK && column_id >= 9 && column_id <= 11) + { + ptr_network1 = _app_network_getitem (context1); + ptr_network2 = _app_network_getitem (context2); + + if (ptr_network1 && ptr_network2) + { + if (column_id == 9) + { + value1 = _InterlockedCompareExchange64 (&ptr_network1->download_speed, 0, 0); + value2 = _InterlockedCompareExchange64 (&ptr_network2->download_speed, 0, 0); + } + else if (column_id == 10) + { + value1 = _InterlockedCompareExchange64 (&ptr_network1->upload_speed, 0, 0); + value2 = _InterlockedCompareExchange64 (&ptr_network2->upload_speed, 0, 0); + } + else + { + value1 = _InterlockedCompareExchange64 (&ptr_network1->download_total, 0, 0) + _InterlockedCompareExchange64 (&ptr_network1->upload_total, 0, 0); + value2 = _InterlockedCompareExchange64 (&ptr_network2->download_total, 0, 0) + _InterlockedCompareExchange64 (&ptr_network2->upload_total, 0, 0); + } + + if (value1 < value2) + result = -1; + else if (value1 > value2) + result = 1; + } + + if (ptr_network1) + _r_obj_dereference (ptr_network1); + + if (ptr_network2) + _r_obj_dereference (ptr_network2); + } } if (!result) diff --git a/src/main.c b/src/main.c index 5a11b9a5..f90daf0c 100644 --- a/src/main.c +++ b/src/main.c @@ -2000,6 +2000,9 @@ VOID _app_tabs_init ( _r_listview_addcolumn (hwnd, tab_context->listview_id, 6, L"", -10, LVCFMT_RIGHT); _r_listview_addcolumn (hwnd, tab_context->listview_id, 7, L"", -10, LVCFMT_RIGHT); _r_listview_addcolumn (hwnd, tab_context->listview_id, 8, L"", -10, LVCFMT_RIGHT); + _r_listview_addcolumn (hwnd, tab_context->listview_id, 9, L"", -10, LVCFMT_RIGHT); + _r_listview_addcolumn (hwnd, tab_context->listview_id, 10, L"", -10, LVCFMT_RIGHT); + _r_listview_addcolumn (hwnd, tab_context->listview_id, 11, L"", -10, LVCFMT_RIGHT); _r_listview_addgroup (hwnd, tab_context->listview_id, 0, L"", 0, LVGS_COLLAPSIBLE, LVGS_COLLAPSIBLE); _r_listview_addgroup (hwnd, tab_context->listview_id, 1, L"", 0, LVGS_COLLAPSIBLE, LVGS_COLLAPSIBLE); diff --git a/src/main.h b/src/main.h index dd77d661..2b58a990 100644 --- a/src/main.h +++ b/src/main.h @@ -395,10 +395,21 @@ typedef struct _ITEM_NETWORK UINT16 remote_port; UINT16 local_port; - UINT8 protocol; - - BOOLEAN is_connection; -} ITEM_NETWORK, *PITEM_NETWORK; + UINT8 protocol; + + BOOLEAN is_connection; + BOOLEAN is_stats_enabled; + BOOLEAN is_stats_initialized; + + volatile LONG64 download_speed; + volatile LONG64 upload_speed; + volatile LONG64 download_total; + volatile LONG64 upload_total; + + ULONG64 last_bytes_in; + ULONG64 last_bytes_out; + ULONG64 last_stats_tick; +} ITEM_NETWORK, *PITEM_NETWORK; typedef struct _ITEM_LOG { diff --git a/src/messages.c b/src/messages.c index 2bac3099..d5b0fb87 100644 --- a/src/messages.c +++ b/src/messages.c @@ -427,8 +427,15 @@ VOID _app_message_localize ( _r_obj_movereference ((PVOID_PTR)&localized_string, _r_format_string (L"%s (%s)", _r_locale_getstring (IDS_PORT), _r_locale_getstring (IDS_DIRECTION_REMOTE))); _r_listview_setcolumn (hwnd, tab_context->listview_id, 6, localized_string->buffer, 0); - _r_listview_setcolumn (hwnd, tab_context->listview_id, 7, _r_locale_getstring (IDS_PROTOCOL), 0); - _r_listview_setcolumn (hwnd, tab_context->listview_id, 8, _r_locale_getstring (IDS_STATE), 0); + _r_listview_setcolumn (hwnd, tab_context->listview_id, 7, _r_locale_getstring (IDS_PROTOCOL), 0); + _r_listview_setcolumn (hwnd, tab_context->listview_id, 8, _r_locale_getstring (IDS_STATE), 0); + _r_obj_movereference ((PVOID_PTR)&localized_string, _r_format_string (L"\u2193 %s", _r_locale_getstring (IDS_UPDATE_SPEED))); + _r_listview_setcolumn (hwnd, tab_context->listview_id, 9, localized_string->buffer, 0); + + _r_obj_movereference ((PVOID_PTR)&localized_string, _r_format_string (L"\u2191 %s", _r_locale_getstring (IDS_UPDATE_SPEED))); + _r_listview_setcolumn (hwnd, tab_context->listview_id, 10, localized_string->buffer, 0); + + _r_listview_setcolumn (hwnd, tab_context->listview_id, 11, _r_locale_getstring (IDS_TOTAL), 0); break; } @@ -1110,13 +1117,13 @@ LONG_PTR _app_message_custdraw ( return CDRF_DODEFAULT; } -VOID _app_displayinfoapp_callback ( +VOID _app_displayinfoapp_callback ( _In_ PITEM_APP ptr_app, _Inout_ LPNMLVDISPINFOW lpnmlv ) { - PR_STRING string; - LONG icon_id = 0; + PR_STRING string; + LONG icon_id = 0; // set text if (lpnmlv->item.mask & LVIF_TEXT) @@ -1319,15 +1326,20 @@ VOID _app_displayinforule_callback ( } } -VOID _app_displayinfonetwork_callback ( +VOID _app_displayinfonetwork_callback ( _In_ PITEM_NETWORK ptr_network, _Inout_ LPNMLVDISPINFOW lpnmlv ) { PITEM_APP ptr_app; - PR_STRING string; - LPCWSTR name; - LONG icon_id = 0; + PR_STRING string; + LPCWSTR name; + LONG icon_id = 0; + LONG64 bytes_value; + ULONG64 divisor; + ULONG64 decimal; + ULONG64 whole; + LPCWSTR suffix; // set text if (lpnmlv->item.mask & LVIF_TEXT) @@ -1459,16 +1471,71 @@ VOID _app_displayinfonetwork_callback ( break; } - case 8: + case 8: { name = _app_db_getconnectionstatename (ptr_network->state); if (name) _r_str_copy (lpnmlv->item.pszText, lpnmlv->item.cchTextMax, name); - break; - } - } + break; + } + + case 9: + case 10: + case 11: + { + if (!ptr_network->is_stats_initialized) + break; + + if (lpnmlv->item.iSubItem == 9) + { + bytes_value = _InterlockedCompareExchange64 (&ptr_network->download_speed, 0, 0); + } + else if (lpnmlv->item.iSubItem == 10) + { + bytes_value = _InterlockedCompareExchange64 (&ptr_network->upload_speed, 0, 0); + } + else + { + bytes_value = _InterlockedCompareExchange64 (&ptr_network->download_total, 0, 0) + _InterlockedCompareExchange64 (&ptr_network->upload_total, 0, 0); + } + + if ((ULONG64)bytes_value < 1024) + { + _r_str_printf (lpnmlv->item.pszText, lpnmlv->item.cchTextMax, L"%" TEXT (PRIu64) L" B%s", (ULONG64)bytes_value, lpnmlv->item.iSubItem == 11 ? L"" : L"/s"); + break; + } + + if ((ULONG64)bytes_value < (1ULL << 20)) + { + divisor = 1ULL << 10; + suffix = L"KB"; + } + else if ((ULONG64)bytes_value < (1ULL << 30)) + { + divisor = 1ULL << 20; + suffix = L"MB"; + } + else if ((ULONG64)bytes_value < (1ULL << 40)) + { + divisor = 1ULL << 30; + suffix = L"GB"; + } + else + { + divisor = 1ULL << 40; + suffix = L"TB"; + } + + whole = (ULONG64)bytes_value / divisor; + decimal = (((ULONG64)bytes_value % divisor) * 10) / divisor; + + _r_str_printf (lpnmlv->item.pszText, lpnmlv->item.cchTextMax, L"%" TEXT (PRIu64) L".%" TEXT (PRIu64) L" %s%s", whole, decimal, suffix, lpnmlv->item.iSubItem == 11 ? L"" : L"/s"); + + break; + } + } } // set image diff --git a/src/network.c b/src/network.c index 0f137b77..2db0480b 100644 --- a/src/network.c +++ b/src/network.c @@ -3,6 +3,118 @@ #include "global.h" +static VOID _app_network_update_stats_values ( + _Inout_ PITEM_NETWORK ptr_network, + _In_ ULONG64 bytes_in, + _In_ ULONG64 bytes_out +) +{ + ULONG64 elapsed; + ULONG64 bytes_delta; + ULONG64 current_tick; + + current_tick = GetTickCount64 (); + + if (!ptr_network->is_stats_initialized) + { + ptr_network->last_bytes_in = bytes_in; + ptr_network->last_bytes_out = bytes_out; + ptr_network->last_stats_tick = current_tick; + ptr_network->is_stats_initialized = TRUE; + + return; + } + + elapsed = current_tick - ptr_network->last_stats_tick; + + if (!elapsed) + return; + + bytes_delta = bytes_in >= ptr_network->last_bytes_in ? bytes_in - ptr_network->last_bytes_in : 0; + + _InterlockedExchangeAdd64 (&ptr_network->download_total, bytes_delta); + _InterlockedExchange64 (&ptr_network->download_speed, (bytes_delta * 1000) / elapsed); + + bytes_delta = bytes_out >= ptr_network->last_bytes_out ? bytes_out - ptr_network->last_bytes_out : 0; + + _InterlockedExchangeAdd64 (&ptr_network->upload_total, bytes_delta); + _InterlockedExchange64 (&ptr_network->upload_speed, (bytes_delta * 1000) / elapsed); + + ptr_network->last_bytes_in = bytes_in; + ptr_network->last_bytes_out = bytes_out; + ptr_network->last_stats_tick = current_tick; +} + +static VOID _app_network_update_tcp4_stats ( + _Inout_ PITEM_NETWORK ptr_network, + _In_ PMIB_TCPROW_OWNER_MODULE row +) +{ + TCP_ESTATS_DATA_ROD_v0 data_rod = {0}; + TCP_ESTATS_DATA_RW_v0 data_rw = {0}; + MIB_TCPROW tcp_row = {0}; + ULONG status; + + tcp_row.dwState = row->dwState; + tcp_row.dwLocalAddr = row->dwLocalAddr; + tcp_row.dwLocalPort = row->dwLocalPort; + tcp_row.dwRemoteAddr = row->dwRemoteAddr; + tcp_row.dwRemotePort = row->dwRemotePort; + + if (!ptr_network->is_stats_enabled) + { + data_rw.EnableCollection = TRUE; + + status = SetPerTcpConnectionEStats (&tcp_row, TcpConnectionEstatsData, (PUCHAR)&data_rw, 0, sizeof (data_rw), 0); + + if (status != NO_ERROR) + return; + + ptr_network->is_stats_enabled = TRUE; + } + + status = GetPerTcpConnectionEStats (&tcp_row, TcpConnectionEstatsData, (PUCHAR)&data_rw, 0, sizeof (data_rw), NULL, 0, 0, (PUCHAR)&data_rod, 0, sizeof (data_rod)); + + if (status == NO_ERROR && data_rw.EnableCollection) + _app_network_update_stats_values (ptr_network, data_rod.DataBytesIn, data_rod.DataBytesOut); +} + +static VOID _app_network_update_tcp6_stats ( + _Inout_ PITEM_NETWORK ptr_network, + _In_ PMIB_TCP6ROW_OWNER_MODULE row +) +{ + TCP_ESTATS_DATA_ROD_v0 data_rod = {0}; + TCP_ESTATS_DATA_RW_v0 data_rw = {0}; + MIB_TCP6ROW tcp_row = {0}; + ULONG status; + + RtlCopyMemory (&tcp_row.LocalAddr, row->ucLocalAddr, sizeof (tcp_row.LocalAddr)); + tcp_row.dwLocalScopeId = row->dwLocalScopeId; + tcp_row.dwLocalPort = row->dwLocalPort; + RtlCopyMemory (&tcp_row.RemoteAddr, row->ucRemoteAddr, sizeof (tcp_row.RemoteAddr)); + tcp_row.dwRemoteScopeId = row->dwRemoteScopeId; + tcp_row.dwRemotePort = row->dwRemotePort; + tcp_row.State = row->dwState; + + if (!ptr_network->is_stats_enabled) + { + data_rw.EnableCollection = TRUE; + + status = SetPerTcp6ConnectionEStats (&tcp_row, TcpConnectionEstatsData, (PUCHAR)&data_rw, 0, sizeof (data_rw), 0); + + if (status != NO_ERROR) + return; + + ptr_network->is_stats_enabled = TRUE; + } + + status = GetPerTcp6ConnectionEStats (&tcp_row, TcpConnectionEstatsData, (PUCHAR)&data_rw, 0, sizeof (data_rw), NULL, 0, 0, (PUCHAR)&data_rod, 0, sizeof (data_rod)); + + if (status == NO_ERROR && data_rw.EnableCollection) + _app_network_update_stats_values (ptr_network, data_rod.DataBytesIn, data_rod.DataBytesOut); +} + _Ret_maybenull_ PITEM_NETWORK_CONTEXT _app_network_getcontext () { @@ -122,13 +234,18 @@ VOID _app_network_generatetable ( tcp4_table->table[i].dwState ); - if (_app_network_isitemfound (network_hash)) + ptr_network = _app_network_getitem (network_hash); + + if (ptr_network) { + _app_network_update_tcp4_stats (ptr_network, &tcp4_table->table[i]); + _r_queuedlock_acquireexclusive (&network_context->lock_checker); _r_obj_addhashtablepointer (network_context->checker_ptr, network_hash, NULL); _r_queuedlock_releaseexclusive (&network_context->lock_checker); + _r_obj_dereference (ptr_network); continue; } @@ -154,6 +271,8 @@ VOID _app_network_generatetable ( ptr_network->state = tcp4_table->table[i].dwState; + _app_network_update_tcp4_stats (ptr_network, &tcp4_table->table[i]); + if (tcp4_table->table[i].dwState == MIB_TCP_STATE_ESTAB) { if (_app_network_isvalidconnection (ptr_network->af, &ptr_network->remote_addr) || _app_network_isvalidconnection (ptr_network->af, &ptr_network->local_addr)) @@ -201,11 +320,16 @@ VOID _app_network_generatetable ( tcp6_table->table[i].dwState ); - if (_app_network_isitemfound (network_hash)) + ptr_network = _app_network_getitem (network_hash); + + if (ptr_network) { + _app_network_update_tcp6_stats (ptr_network, &tcp6_table->table[i]); + _r_queuedlock_acquireexclusive (&network_context->lock_checker); _r_obj_addhashtablepointer (network_context->checker_ptr, network_hash, NULL); _r_queuedlock_releaseexclusive (&network_context->lock_checker); + _r_obj_dereference (ptr_network); continue; } @@ -231,6 +355,8 @@ VOID _app_network_generatetable ( ptr_network->state = tcp6_table->table[i].dwState; + _app_network_update_tcp6_stats (ptr_network, &tcp6_table->table[i]); + if (tcp6_table->table[i].dwState == MIB_TCP_STATE_ESTAB) { if (_app_network_isvalidconnection (ptr_network->af, &ptr_network->remote_addr6) || _app_network_isvalidconnection (ptr_network->af, &ptr_network->local_addr6)) @@ -693,15 +819,22 @@ VOID _app_network_printlistviewtable ( _app_queue_resolver (network_context->hwnd, IDC_NETWORK, network_hash, ptr_network); _r_obj_dereference (string); - is_refresh = TRUE; + } _r_queuedlock_releaseshared (&network_context->lock_network); - // refresh network tab + // refresh network tab (traffic counters change on every pass) if (is_refresh) + { _app_listview_updateby_id (network_context->hwnd, IDC_NETWORK, PR_UPDATE_NORESIZE); + } + else + { + _r_listview_redraw (network_context->hwnd, IDC_NETWORK); + _app_listview_sort (network_context->hwnd, IDC_NETWORK, INT_ERROR, FALSE); + } // remove closed connections from list item_count = _r_listview_getitemcount (network_context->hwnd, IDC_NETWORK); diff --git a/src/resource.h b/src/resource.h index 626339ca..233c3b24 100644 --- a/src/resource.h +++ b/src/resource.h @@ -583,8 +583,9 @@ #define IDS_LIMIT_REACHED 273 #define IDS_INTERNAL_RULE 274 #define IDS_LOADING 275 -#define IDS_DIRECTION_REMOTE 276 -#define IDS_DIRECTION_LOCAL 277 +#define IDS_DIRECTION_REMOTE 276 +#define IDS_DIRECTION_LOCAL 277 +#define IDS_TOTAL 278 // RC data #define IDR_PROFILE_INTERNAL 1 diff --git a/src/resource.rc b/src/resource.rc index 8846153d..98b85f0d 100644 --- a/src/resource.rc +++ b/src/resource.rc @@ -833,9 +833,10 @@ BEGIN IDS_LOADING "Loading..." - IDS_DIRECTION_REMOTE "Remote" - IDS_DIRECTION_LOCAL "Local" -END + IDS_DIRECTION_REMOTE "Remote" + IDS_DIRECTION_LOCAL "Local" + IDS_TOTAL "Total" +END // // RC data From 92f50546bc7c0f14c0348911f3898fda8cadbd48 Mon Sep 17 00:00:00 2001 From: Tarkiin Date: Tue, 30 Jun 2026 22:58:44 +0200 Subject: [PATCH 2/2] fix: address feedback (use _r_sys_gettickcount64 and revert listview index) --- src/listview.c | 2 +- src/network.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/listview.c b/src/listview.c index 043ebc96..4a74cf7d 100644 --- a/src/listview.c +++ b/src/listview.c @@ -172,7 +172,7 @@ VOID _app_listview_addnetworkitem ( _In_ ULONG network_hash ) { - _r_listview_additem (hwnd, IDC_NETWORK, INT_MAX, LPSTR_TEXTCALLBACK, I_IMAGECALLBACK, I_GROUPIDCALLBACK, _app_listview_createcontext (network_hash)); + _r_listview_additem (hwnd, IDC_NETWORK, INT_ERROR, LPSTR_TEXTCALLBACK, I_IMAGECALLBACK, I_GROUPIDCALLBACK, _app_listview_createcontext (network_hash)); } VOID _app_listview_addlogitem ( diff --git a/src/network.c b/src/network.c index 2db0480b..aceb6120 100644 --- a/src/network.c +++ b/src/network.c @@ -13,7 +13,7 @@ static VOID _app_network_update_stats_values ( ULONG64 bytes_delta; ULONG64 current_tick; - current_tick = GetTickCount64 (); + current_tick = _r_sys_gettickcount64 (); if (!ptr_network->is_stats_initialized) {