diff --git a/bin/i18n/!example.txt b/bin/i18n/!example.txt index d4613880..dc0f90dc 100644 Binary files a/bin/i18n/!example.txt and b/bin/i18n/!example.txt differ diff --git a/bin/i18n/Spanish.ini b/bin/i18n/Spanish.ini index b69c0d8f..788f4bc1 100644 Binary files a/bin/i18n/Spanish.ini and b/bin/i18n/Spanish.ini differ diff --git a/src/global.h b/src/global.h index dab02d92..fe776b2e 100644 --- a/src/global.h +++ b/src/global.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/src/listview.c b/src/listview.c index 9f4cbb47..d26d5947 100644 --- a/src/listview.c +++ b/src/listview.c @@ -883,9 +883,11 @@ INT CALLBACK _app_listview_compare_callback ( { PR_STRING item_text_1, item_text_2; PITEM_LOG ptr_log1, ptr_log2; + PITEM_NETWORK ptr_network1, ptr_network2; WCHAR section_name[0x80]; HWND hwnd; LONG64 timestamp1 = 0, timestamp2 = 0; + LONG64 value1 = 0, value2 = 0; ULONG context1, context2; LONG column_id; INT item_id1, item_id2, listview_id, result = 0; @@ -971,6 +973,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 632cc64a..ed133db2 100644 --- a/src/main.c +++ b/src/main.c @@ -1685,6 +1685,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 3724c737..b6f28379 100644 --- a/src/main.h +++ b/src/main.h @@ -434,6 +434,17 @@ typedef struct _ITEM_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_STATUS diff --git a/src/messages.c b/src/messages.c index bc450dda..ffaae9b1 100644 --- a/src/messages.c +++ b/src/messages.c @@ -319,6 +319,13 @@ VOID _app_message_localize ( _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; } @@ -1272,7 +1279,9 @@ VOID _app_displayinfonetwork_callback ( PITEM_APP ptr_app; PR_STRING string; LPCWSTR name; + WCHAR bytes_string[64]; LONG icon_id = 0; + LONG64 bytes_value; // set text if ((lpnmlv->item.mask & LVIF_TEXT) == LVIF_TEXT) @@ -1391,6 +1400,32 @@ VOID _app_displayinfonetwork_callback ( 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 (SUCCEEDED (_r_format_bytesize64 (bytes_string, RTL_NUMBER_OF (bytes_string), (ULONG64)bytes_value))) + _r_str_printf (lpnmlv->item.pszText, lpnmlv->item.cchTextMax, L"%s%s", bytes_string, lpnmlv->item.iSubItem == 11 ? L"" : L"/s"); + + break; + } } } diff --git a/src/network.c b/src/network.c index 13e1eb5b..b25860fc 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 = _r_sys_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 () { @@ -116,11 +228,16 @@ 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; } @@ -145,6 +262,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)) @@ -193,11 +312,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; } @@ -222,6 +346,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)) @@ -654,15 +780,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 dd96ecd0..eed38292 100644 --- a/src/resource.h +++ b/src/resource.h @@ -587,6 +587,7 @@ #define IDS_DIRECTION_LOCAL 277 #define IDS_WARNING_UNEXPECTED 278 #define IDS_WARNING_ME 279 +#define IDS_TOTAL 280 // RC data #define IDR_PROFILE_INTERNAL 1 diff --git a/src/resource.rc b/src/resource.rc index 8596184e..73c217ea 100644 --- a/src/resource.rc +++ b/src/resource.rc @@ -838,6 +838,7 @@ BEGIN IDS_WARNING_UNEXPECTED L"Be careful, through service/driver host (svchost.exe/System) internet traffic can let out through unexpected ways. Continue?" IDS_WARNING_ME "If you disallow main process, you cannot use resolve network addresses option. Continue?" + IDS_TOTAL "Total" END //