diff --git a/Common/Common.vcxproj b/Common/Common.vcxproj index ed7fc4e5a..652d116bc 100644 --- a/Common/Common.vcxproj +++ b/Common/Common.vcxproj @@ -69,6 +69,7 @@ + @@ -121,6 +122,7 @@ + diff --git a/Common/Common.vcxproj.filters b/Common/Common.vcxproj.filters index 9e08e5c9c..8db94748a 100644 --- a/Common/Common.vcxproj.filters +++ b/Common/Common.vcxproj.filters @@ -4,6 +4,7 @@ + @@ -129,6 +130,7 @@ + diff --git a/Common/NetworkUtil.cpp b/Common/NetworkUtil.cpp new file mode 100644 index 000000000..e63170984 --- /dev/null +++ b/Common/NetworkUtil.cpp @@ -0,0 +1,136 @@ +/* Copyright (C) 2021 Rainmeter Project Developers + * + * This Source Code Form is subject to the terms of the GNU General Public + * License; either version 2 of the License, or (at your option) any later + * version. If a copy of the GPL was not distributed with this file, You can + * obtain one at . */ + +#include "StdAfx.h" +#include "NetworkUtil.h" + +ULONG NetworkUtil::s_InterfaceCount = 0UL; +MIB_IF_TABLE2* NetworkUtil::s_InterfaceTable = nullptr; + +void NetworkUtil::Initialize() +{ +} + +void NetworkUtil::Finalize() +{ + DisposeInterfaceTable(); +} + +bool NetworkUtil::UpdateInterfaceTable() +{ + static ULONGLONG s_LastUpdateTickCount = 0ULL; + const ULONGLONG updateInterval = 250ULL; // ms + + ULONGLONG tickCount = GetTickCount64(); + if (tickCount >= (s_LastUpdateTickCount + updateInterval)) + { + s_LastUpdateTickCount = tickCount; + + DisposeInterfaceTable(); + if (GetIfTable2(&s_InterfaceTable) == NO_ERROR) + { + s_InterfaceCount = s_InterfaceTable->NumEntries; + return true; + } + } + return false; +} + +ULONG NetworkUtil::FindBestInterface(LPCWSTR interfaceName) +{ + if (s_InterfaceTable) + { + if (_wcsicmp(interfaceName, L"BEST") == 0) + { + DWORD dwBestIndex = 0; + if (NO_ERROR == GetBestInterface(INADDR_ANY, &dwBestIndex)) + { + return (ULONG)dwBestIndex; + } + } + else + { + for (size_t i = 0; i < s_InterfaceCount; ++i) + { + MIB_IF_ROW2* table = s_InterfaceTable->Table; + if (_wcsicmp(interfaceName, table[i].Description) == 0) + { + return table[i].InterfaceIndex; + } + } + } + } + return 0UL; +} + +ULONG NetworkUtil::GetIndexFromIfIndex(const ULONG ifIndex) +{ + if (s_InterfaceTable) + { + for (ULONG i = 0; i < s_InterfaceCount; ++i) + { + MIB_IF_ROW2* table = s_InterfaceTable->Table; + if (ifIndex == table[i].InterfaceIndex) + { + return i; + } + } + } + return 0UL; +} + +LPCWSTR NetworkUtil::GetInterfaceTypeString(const ULONG ifIndex) +{ + switch (ifIndex) + { + case IF_TYPE_ETHERNET_CSMACD: return L"Ethernet"; + case IF_TYPE_FDDI: return L"Fiber"; + case IF_TYPE_PPP: return L"PPP"; + case IF_TYPE_SOFTWARE_LOOPBACK: return L"Loopback"; + case IF_TYPE_IEEE80211: return L"Wireless"; + case IF_TYPE_TUNNEL: return L"Tunnel"; + case IF_TYPE_IEEE1394: return L"Firewire"; + case IF_TYPE_IEEE80216_WMAN: return L"Mobile WiMax"; + case IF_TYPE_WWANPP: return L"Mobile GSM"; + case IF_TYPE_WWANPP2: return L"Mobile CDMA"; + } + return L"Other"; +} + +LPCWSTR NetworkUtil::GetInterfaceMediaConnectionString(const NET_IF_MEDIA_CONNECT_STATE state) +{ + switch (state) + { + case MediaConnectStateConnected: return L"Connected"; + case MediaConnectStateDisconnected: return L"Disconnected"; + } + return L"Unknown"; +} + +LPCWSTR NetworkUtil::GetInterfaceOperStatusString(const IF_OPER_STATUS status) +{ + switch (status) + { + case IfOperStatusUp: return L"Up"; + case IfOperStatusDown: return L"Down"; + case IfOperStatusTesting: return L"Testing"; + case IfOperStatusDormant: return L"Dormant"; + case IfOperStatusNotPresent: return L"Not Present"; + case IfOperStatusLowerLayerDown: return L"Lower Layer Down"; + } + return L"Unknown"; +} + +void NetworkUtil::DisposeInterfaceTable() +{ + if (s_InterfaceTable) + { + FreeMibTable(s_InterfaceTable); + s_InterfaceTable = nullptr; + } + s_InterfaceCount = 0UL; +} diff --git a/Common/NetworkUtil.h b/Common/NetworkUtil.h new file mode 100644 index 000000000..6722fff93 --- /dev/null +++ b/Common/NetworkUtil.h @@ -0,0 +1,46 @@ +/* Copyright (C) 2021 Rainmeter Project Developers + * + * This Source Code Form is subject to the terms of the GNU General Public + * License; either version 2 of the License, or (at your option) any later + * version. If a copy of the GPL was not distributed with this file, You can + * obtain one at . */ + +#ifndef RM_COMMON_NETWORKUTIL_H_ +#define RM_COMMON_NETWORKUTIL_H_ + +#include +#include +#include + +class __declspec(novtable) NetworkUtil final +{ +public: + static void Initialize(); + static void Finalize(); + + static bool UpdateInterfaceTable(); + + static const ULONG GetInterfaceCount() { return s_InterfaceCount; } + static MIB_IF_ROW2* GetInterfaceTable() { if (s_InterfaceTable) { return s_InterfaceTable->Table; } return nullptr; } + + static ULONG FindBestInterface(LPCWSTR interfaceName); + static ULONG GetIndexFromIfIndex(const ULONG ifIndex); + + static LPCWSTR GetInterfaceTypeString(const ULONG ifIndex); + static LPCWSTR GetInterfaceMediaConnectionString(const NET_IF_MEDIA_CONNECT_STATE state); + static LPCWSTR GetInterfaceOperStatusString(const IF_OPER_STATUS status); + +private: + NetworkUtil() = delete; + ~NetworkUtil() = delete; + + NetworkUtil(const NetworkUtil& other) = delete; + NetworkUtil& operator=(NetworkUtil other) = delete; + + static void DisposeInterfaceTable(); + + static ULONG s_InterfaceCount; + static MIB_IF_TABLE2* s_InterfaceTable; +}; + +#endif diff --git a/Library/MeasureNet.cpp b/Library/MeasureNet.cpp index 0a46f979b..aec2bf21f 100644 --- a/Library/MeasureNet.cpp +++ b/Library/MeasureNet.cpp @@ -9,16 +9,15 @@ #include "MeasureNet.h" #include "Rainmeter.h" #include "System.h" +#include "../Common/NetworkUtil.h" -MIB_IF_TABLE2* MeasureNet::c_Table = nullptr; -UINT MeasureNet::c_NumOfTables = 0; std::vector MeasureNet::c_StatValues; std::vector MeasureNet::c_OldStatValues; MeasureNet::MeasureNet(Skin* skin, const WCHAR* name, NET type) : Measure(skin, name), m_Net(type), - m_Interface(), - m_Octets(), + m_Interface(0U), + m_Octets(0Ui64), m_FirstTime(true), m_Cumulative(false), m_UseBits(false) @@ -35,67 +34,42 @@ MeasureNet::~MeasureNet() */ void MeasureNet::UpdateIFTable() { - bool logging = false; + const ULONG oldCount = NetworkUtil::GetInterfaceCount(); - if (c_Table) - { - FreeMibTable(c_Table); - c_Table = nullptr; - } + if (!NetworkUtil::UpdateInterfaceTable()) return; - if (GetIfTable2(&c_Table) == NO_ERROR) + MIB_IF_ROW2* table = NetworkUtil::GetInterfaceTable(); + const ULONG newCount = NetworkUtil::GetInterfaceCount(); + if (table && GetRainmeter().GetDebug() && oldCount != newCount) { - if (c_NumOfTables != c_Table->NumEntries) - { - c_NumOfTables = c_Table->NumEntries; - logging = true; - } + LogDebug(L"------------------------------"); + LogDebugF(L"* NETWORK-INTERFACE: Count=%i", newCount); - if (GetRainmeter().GetDebug() && logging) + for (size_t i = 0; i < newCount; ++i) { - LogDebug(L"------------------------------"); - LogDebugF(L"* NETWORK-INTERFACE: Count=%i", c_NumOfTables); + LPCWSTR type = NetworkUtil::GetInterfaceTypeString(table[i].Type); + LPCWSTR state = NetworkUtil::GetInterfaceMediaConnectionString(table[i].MediaConnectState); + LPCWSTR status = NetworkUtil::GetInterfaceOperStatusString(table[i].OperStatus); - for (size_t i = 0; i < c_NumOfTables; ++i) - { - const WCHAR* type = L"Other"; - switch (c_Table->Table[i].Type) - { - case IF_TYPE_ETHERNET_CSMACD: - type = L"Ethernet"; - break; - case IF_TYPE_PPP: - type = L"PPP"; - break; - case IF_TYPE_SOFTWARE_LOOPBACK: - type = L"Loopback"; - break; - case IF_TYPE_IEEE80211: - type = L"IEEE802.11"; - break; - case IF_TYPE_TUNNEL: - type = L"Tunnel"; - break; - case IF_TYPE_IEEE1394: - type = L"IEEE1394"; - break; - } + LogDebugF(L"%3i: Name: %s", (int)i + 1, table[i].Description); + LogDebugF(L" Alias: %s", table[i].Alias); - LogDebugF(L"%i: %s", (int)i + 1, c_Table->Table[i].Description); - LogDebugF(L" Alias: %s", c_Table->Table[i].Alias); - LogDebugF(L" Type=%s(%i), Hardware=%s, Filter=%s", - type, c_Table->Table[i].Type, - (c_Table->Table[i].InterfaceAndOperStatusFlags.HardwareInterface == 1) ? L"Yes" : L"No", - (c_Table->Table[i].InterfaceAndOperStatusFlags.FilterInterface == 1) ? L"Yes" : L"No"); + WCHAR guid[64] = { 0 }; + if (StringFromGUID2(table[i].InterfaceGuid, guid, 64) > 0) + { + LogDebugF(L" GUID: %s", guid); } - LogDebug(L"------------------------------"); + + LogDebugF(L" Type=%s(%i), Hardware=%s, Filter=%s", + type, table[i].Type, + (table[i].InterfaceAndOperStatusFlags.HardwareInterface == 1) ? L"Yes" : L"No", + (table[i].InterfaceAndOperStatusFlags.FilterInterface == 1) ? L"Yes" : L"No"); + LogDebugF(L" IfIndex=%i, State=%s, Status=%s(%i)", + table[i].InterfaceIndex, + state, + status, table[i].OperStatus); } - } - else - { - // Something's wrong. Unable to get the table. - c_Table = nullptr; - c_NumOfTables = 0; + LogDebug(L"------------------------------"); } } @@ -107,11 +81,14 @@ void MeasureNet::UpdateIFTable() ULONG64 MeasureNet::GetNetOctets(NET net) { ULONG64 value = 0; - MIB_IF_ROW2* table = (MIB_IF_ROW2*)c_Table->Table; + MIB_IF_ROW2* table = NetworkUtil::GetInterfaceTable(); + if (!table) return value; + + const ULONG interfaceCount = NetworkUtil::GetInterfaceCount(); if (m_Interface == 0) { // Get all interfaces - for (UINT i = 0; i < c_NumOfTables; ++i) + for (UINT i = 0; i < interfaceCount; ++i) { // Ignore the loopback and filter interfaces if (table[i].Type == IF_TYPE_SOFTWARE_LOOPBACK || @@ -136,22 +113,22 @@ ULONG64 MeasureNet::GetNetOctets(NET net) } else { - // Get the selected interface - if (m_Interface <= c_NumOfTables) + if (m_Interface <= interfaceCount) { + ULONG index = NetworkUtil::GetIndexFromIfIndex(m_Interface); switch (net) { case NET_IN: - value += table[m_Interface - 1].InOctets; + value += table[index].InOctets; break; case NET_OUT: - value += table[m_Interface - 1].OutOctets; + value += table[index].OutOctets; break; case NET_TOTAL: - value += table[m_Interface - 1].InOctets; - value += table[m_Interface - 1].OutOctets; + value += table[index].InOctets; + value += table[index].OutOctets; break; } } @@ -169,16 +146,20 @@ ULONG64 MeasureNet::GetNetStatsValue(NET net) ULONG64 value = 0; size_t statsSize = c_StatValues.size() / 2; + MIB_IF_ROW2* table = NetworkUtil::GetInterfaceTable(); + if (!table) return value; + + const ULONG interfaceCount = NetworkUtil::GetInterfaceCount(); if (m_Interface == 0) { // Get all interfaces for (size_t i = 0; i < statsSize; ++i) { // Ignore the loopback and filter interfaces - if (c_NumOfTables == statsSize) + if (interfaceCount == statsSize) { - if (c_Table->Table[i].Type == IF_TYPE_SOFTWARE_LOOPBACK || - c_Table->Table[i].InterfaceAndOperStatusFlags.FilterInterface == 1) continue; + if (table[i].Type == IF_TYPE_SOFTWARE_LOOPBACK || + table[i].InterfaceAndOperStatusFlags.FilterInterface == 1) continue; } switch (net) @@ -203,19 +184,20 @@ ULONG64 MeasureNet::GetNetStatsValue(NET net) // Get the selected interface if (m_Interface <= statsSize) { + ULONG index = NetworkUtil::GetIndexFromIfIndex(m_Interface); switch (net) { case NET_IN: - value += c_StatValues[(m_Interface - 1) * 2 + 0]; + value += c_StatValues[index * 2 + 0]; break; case NET_OUT: - value += c_StatValues[(m_Interface - 1) * 2 + 1]; + value += c_StatValues[index * 2 + 1]; break; case NET_TOTAL: - value += c_StatValues[(m_Interface - 1) * 2 + 0]; - value += c_StatValues[(m_Interface - 1) * 2 + 1]; + value += c_StatValues[index * 2 + 0]; + value += c_StatValues[index * 2 + 1]; break; } } @@ -226,9 +208,9 @@ ULONG64 MeasureNet::GetNetStatsValue(NET net) void MeasureNet::UpdateValue() { - if (c_Table == nullptr) return; + if (!NetworkUtil::GetInterfaceTable()) return; - const ULONG64 bits = m_UseBits ? 8Ui64 : 1Ui64; + const ULONG64 bits = m_UseBits ? 8ULL : 1ULL; if (m_Cumulative) { @@ -302,11 +284,23 @@ void MeasureNet::ReadOptions(ConfigParser& parser, const WCHAR* section) std::wstring iface = parser.ReadString(section, L"Interface", L""); if (!iface.empty() && !std::all_of(iface.begin(), iface.end(), iswdigit)) { - m_Interface = GetBestInterfaceOrByName(iface.c_str()); + m_Interface = NetworkUtil::FindBestInterface(iface.c_str()); + if (GetRainmeter().GetDebug()) + { + MIB_IF_ROW2* table = NetworkUtil::GetInterfaceTable(); + for (size_t i = 0; i < NetworkUtil::GetInterfaceCount(); ++i) + { + if (table[i].InterfaceIndex == m_Interface) + { + LogDebugF(this, L"Using network interface: %s (IfIndex=%i)", table[i].Description, m_Interface); + break; + } + } + } } else { - m_Interface = parser.ReadInt(section, L"Interface", 0); + m_Interface = parser.ReadUInt(section, L"Interface", 0U); } m_Cumulative = parser.ReadBool(section, L"Cumulative", false); @@ -333,86 +327,47 @@ void MeasureNet::ReadOptions(ConfigParser& parser, const WCHAR* section) } } -UINT MeasureNet::GetBestInterfaceOrByName(const WCHAR* iface) +void MeasureNet::UpdateStats() { - if (c_Table == nullptr) return 0; + MIB_IF_ROW2* table = NetworkUtil::GetInterfaceTable(); + if (!table) return; - if (_wcsicmp(iface, L"BEST") == 0) - { - DWORD dwBestIndex; - if (NO_ERROR == GetBestInterface(INADDR_ANY, &dwBestIndex)) - { - MIB_IF_ROW2* table = (MIB_IF_ROW2*)c_Table->Table; - for (size_t i = 0; i < c_NumOfTables; ++i) - { - if (table[i].InterfaceIndex == (NET_IFINDEX)dwBestIndex) - { - if (GetRainmeter().GetDebug()) - { - LogDebugF(this, L"Using network interface: Number=(%i), Name=\"%s\"", i + 1, table[i].Description); - } + const ULONG interfaceCount = NetworkUtil::GetInterfaceCount(); + size_t statsSize = interfaceCount * 2; - return (UINT)(i + 1); - } - } - } - } - else + // Fill the vectors + if (c_StatValues.size() < statsSize) { - MIB_IF_ROW2* table = (MIB_IF_ROW2*)c_Table->Table; - for (size_t i = 0; i < c_NumOfTables; ++i) - { - if (_wcsicmp(iface, table[i].Description) == 0) - { - return (UINT)(i + 1); - } - } + c_StatValues.resize(statsSize, 0); } - LogErrorF(this, L"Cannot find interface: \"%s\"", iface); - return 0; -} - -void MeasureNet::UpdateStats() -{ - if (c_Table) + if (c_OldStatValues.size() < statsSize) { - size_t statsSize = c_NumOfTables * 2; - - // Fill the vectors - if (c_StatValues.size() < statsSize) - { - c_StatValues.resize(statsSize, 0); - } - - if (c_OldStatValues.size() < statsSize) - { - c_OldStatValues.resize(statsSize, 0); - } + c_OldStatValues.resize(statsSize, 0); + } - for (UINT i = 0; i < c_NumOfTables; ++i) + for (size_t i = 0; i < interfaceCount; ++i) + { + ULONG64 in = table[i].InOctets; + ULONG64 out = table[i].OutOctets; + if (c_OldStatValues[i * 2 + 0] != 0) { - ULONG64 in = c_Table->Table[i].InOctets; - ULONG64 out = c_Table->Table[i].OutOctets; - if (c_OldStatValues[i * 2 + 0] != 0) + if (in > c_OldStatValues[i * 2 + 0]) { - if (in > c_OldStatValues[i * 2 + 0]) - { - c_StatValues[i * 2 + 0] += in - c_OldStatValues[i * 2 + 0]; - } + c_StatValues[i * 2 + 0] += in - c_OldStatValues[i * 2 + 0]; } + } - if (c_OldStatValues[i * 2 + 1] != 0) + if (c_OldStatValues[i * 2 + 1] != 0) + { + if (out > c_OldStatValues[i * 2 + 1]) { - if (out > c_OldStatValues[i * 2 + 1]) - { - c_StatValues[i * 2 + 1] += out - c_OldStatValues[i * 2 + 1]; - } + c_StatValues[i * 2 + 1] += out - c_OldStatValues[i * 2 + 1]; } - - c_OldStatValues[i * 2 + 0] = in; - c_OldStatValues[i * 2 + 1] = out; } + + c_OldStatValues[i * 2 + 0] = in; + c_OldStatValues[i * 2 + 1] = out; } } @@ -529,10 +484,5 @@ void MeasureNet::InitializeStatic() void MeasureNet::FinalizeStatic() { - if (c_Table) - { - FreeMibTable(c_Table); - } - c_Table = nullptr; - c_NumOfTables = 0; + NetworkUtil::Finalize(); } diff --git a/Library/MeasureNet.h b/Library/MeasureNet.h index b648bf9d5..a5078dd15 100644 --- a/Library/MeasureNet.h +++ b/Library/MeasureNet.h @@ -48,10 +48,9 @@ class MeasureNet : public Measure private: ULONG64 GetNetOctets(NET net); ULONG64 GetNetStatsValue(NET net); - UINT GetBestInterfaceOrByName(const WCHAR* iface); NET m_Net; - UINT m_Interface; + ULONG m_Interface; ULONG64 m_Octets; bool m_FirstTime; @@ -60,8 +59,6 @@ class MeasureNet : public Measure static std::vector c_OldStatValues; static std::vector c_StatValues; - static MIB_IF_TABLE2* c_Table; - static UINT c_NumOfTables; }; #endif