CXWNetwork is a lightweight and portable network monitor tool, designed for professionals.
GitHub Repository: CXWNetwork GitHub Repository
Downloads:
Download CXWNetwork.exe for Windows Download Repos in .zip Download Repos in .tar.gzHere is the C# code for CXWNetwork:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Threading;
namespace CXWNetwork
{
public partial class MainWindow : Window
{
#region Fields
private readonly DispatcherTimer _timer;
private readonly HttpClient _http = new HttpClient();
private bool _isPaused;
private bool _compactMode;
private string _logBuffer = string.Empty;
private int _connectionsThisTick;
private readonly List _connectionsHistory = new();
private AppConfig _currentConfig = AppConfig.CreateDefault();
private readonly Dictionary _profiles = new();
private readonly SpeedTester _speedTester;
private readonly PortScanner _portScanner;
private readonly GraphRenderer _graphRenderer;
private readonly ConfigManager _configManager;
public ObservableCollection Connections { get; } = new();
#endregion
public MainWindow()
{
InitializeComponent();
ConnectionGrid.ItemsSource = Connections;
_speedTester = new SpeedTester(_http);
_portScanner = new PortScanner(Dispatcher);
_graphRenderer = new GraphRenderer();
_configManager = new ConfigManager();
_profiles["Default"] = _currentConfig.Clone();
ProfileCombo.Items.Add("Default");
ProfileCombo.SelectedItem = "Default";
ApplyConfigToUI();
_timer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(_currentConfig.ScanIntervalMs)
};
_timer.Tick += Timer_Tick;
_timer.Start();
}
#region Timer / Scan
private void Timer_Tick(object? sender, EventArgs e)
{
if (_isPaused) return;
_connectionsThisTick = 0;
if (EnableTcpCheck.IsChecked == true)
ScanTcp();
if (EnableUdpCheck.IsChecked == true && !_currentConfig.SkipUdpOnHighCpu)
ScanUdp();
_connectionsHistory.Add(_connectionsThisTick);
if (_connectionsHistory.Count > _currentConfig.MaxGraphHistory)
_connectionsHistory.RemoveAt(0);
_graphRenderer.Draw(ConnectionsCanvas, _connectionsHistory);
EnforceMaxConnections();
}
private void ScanTcp()
{
try
{
var props = IPGlobalProperties.GetIPGlobalProperties();
var conns = props.GetActiveTcpConnections();
foreach (var c in conns)
{
if (c.RemoteEndPoint.Port == 0)
continue;
if (_currentConfig.FilterLocalhost &&
(c.LocalEndPoint.Address.ToString() == "127.0.0.1" ||
c.RemoteEndPoint.Address.ToString() == "127.0.0.1"))
continue;
if (_currentConfig.FilterClosedStates &&
(c.State == TcpState.Closed || c.State == TcpState.TimeWait))
continue;
string processName = "Unknown";
int pid = 0;
if (!_currentConfig.SkipProcessResolve)
{
try
{
foreach (var p in Process.GetProcesses())
{
try
{
// Placeholder: no real port->PID mapping here.
if (p.Id == c.LocalEndPoint.Port)
{
processName = p.ProcessName;
pid = p.Id;
break;
}
}
catch { }
}
}
catch { }
}
if (_currentConfig.FilterSystemProcesses &&
(processName.Equals("System", StringComparison.OrdinalIgnoreCase) ||
processName.Equals("Idle", StringComparison.OrdinalIgnoreCase)))
continue;
string line = $"{DateTime.Now:HH:mm:ss} | TCP | {processName} | {c.LocalEndPoint} → {c.RemoteEndPoint} | {c.State}";
if (_currentConfig.LogOnlyNewConnections && _logBuffer.Contains(line))
continue;
var entry = new ConnectionEntry
{
Time = _currentConfig.ShowTimestamps ? DateTime.Now.ToString("HH:mm:ss") : "",
Protocol = "TCP",
Process = processName,
Local = c.LocalEndPoint.ToString(),
Remote = c.RemoteEndPoint.ToString(),
State = c.State.ToString(),
PID = pid
};
Connections.Add(entry);
_connectionsThisTick++;
_logBuffer += line + Environment.NewLine;
if (_currentConfig.EnableFileLogging)
AppendToLogFile(line);
}
}
catch { }
}
private void ScanUdp()
{
try
{
var props = IPGlobalProperties.GetIPGlobalProperties();
var listeners = props.GetActiveUdpListeners();
foreach (var udp in listeners)
{
if (_currentConfig.FilterLocalhost &&
udp.Address.ToString() == "127.0.0.1")
continue;
string line = $"{DateTime.Now:HH:mm:ss} | UDP | Listening on {udp}";
if (_currentConfig.LogOnlyNewConnections && _logBuffer.Contains(line))
continue;
var entry = new ConnectionEntry
{
Time = _currentConfig.ShowTimestamps ? DateTime.Now.ToString("HH:mm:ss") : "",
Protocol = "UDP",
Process = "N/A",
Local = udp.ToString(),
Remote = "N/A",
State = "Listening",
PID = 0
};
Connections.Add(entry);
_connectionsThisTick++;
_logBuffer += line + Environment.NewLine;
if (_currentConfig.EnableFileLogging)
AppendToLogFile(line);
}
}
catch { }
}
private void EnforceMaxConnections()
{
while (Connections.Count > _currentConfig.MaxConnections)
Connections.RemoveAt(0);
}
#endregion
#region Logging
private void AppendToLogFile(string line)
{
try
{
string path = _currentConfig.LogFilePath;
string? dir = System.IO.Path.GetDirectoryName(path);
if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
Directory.CreateDirectory(dir);
if (!_currentConfig.AppendLogs && File.Exists(path))
File.Delete(path);
File.AppendAllText(path, line + Environment.NewLine);
var fi = new FileInfo(path);
if (fi.Exists && fi.Length > _currentConfig.MaxLogSizeKb * 1024L)
RotateLogs(path);
}
catch { }
}
private void RotateLogs(string path)
{
try
{
int count = _currentConfig.LogRotationCount;
for (int i = count - 1; i >= 1; i--)
{
string src = path + "." + i;
string dst = path + "." + (i + 1);
if (File.Exists(src))
{
if (File.Exists(dst)) File.Delete(dst);
File.Move(src, dst);
}
}
string first = path + ".1";
if (File.Exists(first)) File.Delete(first);
if (File.Exists(path)) File.Move(path, first);
}
catch { }
}
#endregion
#region UI Events: Filter / Selection / Menu
private void FilterBox_TextChanged(object sender, TextChangedEventArgs e)
{
string filter = FilterBox.Text.ToLowerInvariant();
ConnectionGrid.ItemsSource = Connections
.Where(c =>
c.Process.ToLowerInvariant().Contains(filter) ||
c.Local.ToLowerInvariant().Contains(filter) ||
c.Remote.ToLowerInvariant().Contains(filter) ||
c.Protocol.ToLowerInvariant().Contains(filter))
.ToList();
}
private void ConnectionGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (ConnectionGrid.SelectedItem is not ConnectionEntry entry)
return;
DetailProcess.Text = $"Process: {entry.Process}";
DetailPID.Text = $"PID: {entry.PID}";
try
{
if (entry.PID > 0)
{
var p = Process.GetProcessById(entry.PID);
DetailPath.Text = $"Path: {p.MainModule.FileName}";
DetailThreads.Text = $"Threads: {p.Threads.Count}";
}
else
{
DetailPath.Text = "Path: N/A";
DetailThreads.Text = "Threads: N/A";
}
}
catch
{
DetailPath.Text = "Path: Unknown";
DetailThreads.Text = "Threads: Unknown";
}
}
private void SaveAs_Click(object sender, RoutedEventArgs e)
{
var dialog = new Microsoft.Win32.SaveFileDialog
{
Filter = "Text File (*.txt)|*.txt",
FileName = "log.txt"
};
if (dialog.ShowDialog() == true)
{
File.WriteAllText(dialog.FileName, _logBuffer);
MessageBox.Show("Log saved.");
}
}
private void ClearLogs_Click(object sender, RoutedEventArgs e)
{
Connections.Clear();
_logBuffer = string.Empty;
}
private void Pause_Click(object sender, RoutedEventArgs e) => _isPaused = true;
private void Resume_Click(object sender, RoutedEventArgs e) => _isPaused = false;
#endregion
#region SpeedTest
private async void StartSpeedTest_Click(object sender, RoutedEventArgs e)
{
SpeedTestButton.IsEnabled = false;
SpeedTestResult.Text = "Running speed test...";
try
{
var result = await _speedTester.RunAsync(_currentConfig.SpeedTestUrl, _currentConfig.SpeedTestBufferKb);
SpeedTestResult.Text = $"Download: {result.DownloadKbps:F2} kB/s ({result.Bytes} bytes in {result.Seconds:F2}s)";
}
catch (Exception ex)
{
SpeedTestResult.Text = $"Error: {ex.Message}";
}
SpeedTestButton.IsEnabled = true;
}
#endregion
#region Port Scanner
private async void ScanPorts_Click(object sender, RoutedEventArgs e)
{
PortScanResults.Items.Clear();
string host = PortScanHostBox.Text.Trim();
if (string.IsNullOrWhiteSpace(host))
host = _currentConfig.PortScanDefaultHost;
if (!int.TryParse(StartPortBox.Text, out int startPort))
startPort = _currentConfig.PortScanDefaultStartPort;
if (!int.TryParse(EndPortBox.Text, out int endPort))
endPort = _currentConfig.PortScanDefaultEndPort;
if (!int.TryParse(PortScanTimeoutBox.Text, out int timeoutMs))
timeoutMs = _currentConfig.PortScanTimeoutMs;
if (!int.TryParse(PortScanMaxConcurrentBox.Text, out int maxConcurrent))
maxConcurrent = _currentConfig.PortScanMaxConcurrent;
PortScanResults.Items.Add($"Scanning {host} ports {startPort}-{endPort}...");
await _portScanner.ScanAsync(
host,
startPort,
endPort,
timeoutMs,
maxConcurrent,
_currentConfig.PortScanOnlyCommon,
_currentConfig.PortScanSkipCommonClosed,
port => PortScanResults.Items.Add($"OPEN: {port}")
);
PortScanResults.Items.Add("Scan complete.");
}
#endregion
#region Settings / Profiles / Config
private void CompactModeCheck_Checked(object sender, RoutedEventArgs e)
{
_compactMode = true;
DetailsPanel.Visibility = Visibility.Collapsed;
Width = 1000;
}
private void CompactModeCheck_Unchecked(object sender, RoutedEventArgs e)
{
_compactMode = false;
DetailsPanel.Visibility = Visibility.Visible;
Width = 1300;
}
private void ShowDetailsCheck_Checked(object sender, RoutedEventArgs e)
{
if (!_compactMode)
DetailsPanel.Visibility = Visibility.Visible;
}
private void ShowDetailsCheck_Unchecked(object sender, RoutedEventArgs e)
{
DetailsPanel.Visibility = Visibility.Collapsed;
}
private void ApplyConfigToUI()
{
// Display
CompactModeCheck.IsChecked = _currentConfig.CompactMode;
ShowDetailsCheck.IsChecked = _currentConfig.ShowDetails;
ShowTimestampsCheck.IsChecked = _currentConfig.ShowTimestamps;
ShowProtocolColumnCheck.IsChecked = _currentConfig.ShowProtocolColumn;
ShowStateColumnCheck.IsChecked = _currentConfig.ShowStateColumn;
UiFontSizeBox.Text = _currentConfig.UiFontSize.ToString();
RowHeightBox.Text = _currentConfig.RowHeight.ToString();
// Performance
PerformanceModeCheck.IsChecked = _currentConfig.PerformanceMode;
UseAsyncScanCheck.IsChecked = _currentConfig.UseAsyncScan;
SkipProcessResolveCheck.IsChecked = _currentConfig.SkipProcessResolve;
SkipUdpCheck.IsChecked = _currentConfig.SkipUdpOnHighCpu;
ScanIntervalBox.Text = _currentConfig.ScanIntervalMs.ToString();
MaxConnectionsBox.Text = _currentConfig.MaxConnections.ToString();
SettingsGraphMaxHistoryBox.Text = _currentConfig.MaxGraphHistory.ToString();
CpuLimitBox.Text = _currentConfig.CpuLimitPercent.ToString();
// Logging
EnableFileLoggingCheck.IsChecked = _currentConfig.EnableFileLogging;
AppendLogsCheck.IsChecked = _currentConfig.AppendLogs;
LogOnlyNewConnectionsCheck.IsChecked = _currentConfig.LogOnlyNewConnections;
LogFilePathBox.Text = _currentConfig.LogFilePath;
MaxLogSizeKbBox.Text = _currentConfig.MaxLogSizeKb.ToString();
LogRotationCountBox.Text = _currentConfig.LogRotationCount.ToString();
// Filtering
FilterSystemProcessesCheck.IsChecked = _currentConfig.FilterSystemProcesses;
FilterLocalhostCheck.IsChecked = _currentConfig.FilterLocalhost;
FilterClosedStateCheck.IsChecked = _currentConfig.FilterClosedStates;
ProcessIncludeFilterBox.Text = _currentConfig.ProcessIncludeRegex;
ProcessExcludeFilterBox.Text = _currentConfig.ProcessExcludeRegex;
// SpeedTest
SpeedTestUrlBox.Text = _currentConfig.SpeedTestUrl;
SpeedTestBufferKbBox.Text = _currentConfig.SpeedTestBufferKb.ToString();
// PortScan
PortScanSkipCommonClosedCheck.IsChecked = _currentConfig.PortScanSkipCommonClosed;
PortScanOnlyCommonCheck.IsChecked = _currentConfig.PortScanOnlyCommon;
PortScanDefaultHostBox.Text = _currentConfig.PortScanDefaultHost;
PortScanDefaultStartPortBox.Text = _currentConfig.PortScanDefaultStartPort.ToString();
PortScanDefaultEndPortBox.Text = _currentConfig.PortScanDefaultEndPort.ToString();
PortScanTimeoutBox.Text = _currentConfig.PortScanTimeoutMs.ToString();
PortScanMaxConcurrentBox.Text = _currentConfig.PortScanMaxConcurrent.ToString();
}
private void ReadUIToConfig()
{
// Display
_currentConfig.CompactMode = CompactModeCheck.IsChecked == true;
_currentConfig.ShowDetails = ShowDetailsCheck.IsChecked == true;
_currentConfig.ShowTimestamps = ShowTimestampsCheck.IsChecked == true;
_currentConfig.ShowProtocolColumn = ShowProtocolColumnCheck.IsChecked == true;
_currentConfig.ShowStateColumn = ShowStateColumnCheck.IsChecked == true;
if (int.TryParse(UiFontSizeBox.Text, out int uiFontSize))
_currentConfig.UiFontSize = uiFontSize;
if (int.TryParse(RowHeightBox.Text, out int rowHeight))
_currentConfig.RowHeight = rowHeight;
// Performance
_currentConfig.PerformanceMode = PerformanceModeCheck.IsChecked == true;
_currentConfig.UseAsyncScan = UseAsyncScanCheck.IsChecked == true;
_currentConfig.SkipProcessResolve = SkipProcessResolveCheck.IsChecked == true;
_currentConfig.SkipUdpOnHighCpu = SkipUdpCheck.IsChecked == true;
if (int.TryParse(ScanIntervalBox.Text, out int scanInterval))
_currentConfig.ScanIntervalMs = scanInterval;
if (int.TryParse(MaxConnectionsBox.Text, out int maxConnections))
_currentConfig.MaxConnections = maxConnections;
if (int.TryParse(SettingsGraphMaxHistoryBox.Text, out int maxHistory))
_currentConfig.MaxGraphHistory = maxHistory;
if (int.TryParse(CpuLimitBox.Text, out int cpuLimit))
_currentConfig.CpuLimitPercent = cpuLimit;
// Logging
_currentConfig.EnableFileLogging = EnableFileLoggingCheck.IsChecked == true;
_currentConfig.AppendLogs = AppendLogsCheck.IsChecked == true;
_currentConfig.LogOnlyNewConnections = LogOnlyNewConnectionsCheck.IsChecked == true;
_currentConfig.LogFilePath = LogFilePathBox.Text;
if (int.TryParse(MaxLogSizeKbBox.Text, out int maxLogSize))
_currentConfig.MaxLogSizeKb = maxLogSize;
if (int.TryParse(LogRotationCountBox.Text, out int rotationCount))
_currentConfig.LogRotationCount = rotationCount;
// Filtering
_currentConfig.FilterSystemProcesses = FilterSystemProcessesCheck.IsChecked == true;
_currentConfig.FilterLocalhost = FilterLocalhostCheck.IsChecked == true;
_currentConfig.FilterClosedStates = FilterClosedStateCheck.IsChecked == true;
_currentConfig.ProcessIncludeRegex = ProcessIncludeFilterBox.Text;
_currentConfig.ProcessExcludeRegex = ProcessExcludeFilterBox.Text;
// SpeedTest
_currentConfig.SpeedTestUrl = SpeedTestUrlBox.Text;
if (int.TryParse(SpeedTestBufferKbBox.Text, out int speedBuf))
_currentConfig.SpeedTestBufferKb = speedBuf;
// PortScan
_currentConfig.PortScanSkipCommonClosed = PortScanSkipCommonClosedCheck.IsChecked == true;
_currentConfig.PortScanOnlyCommon = PortScanOnlyCommonCheck.IsChecked == true;
_currentConfig.PortScanDefaultHost = PortScanDefaultHostBox.Text;
if (int.TryParse(PortScanDefaultStartPortBox.Text, out int defStart))
_currentConfig.PortScanDefaultStartPort = defStart;
if (int.TryParse(PortScanDefaultEndPortBox.Text, out int defEnd))
_currentConfig.PortScanDefaultEndPort = defEnd;
if (int.TryParse(PortScanTimeoutBox.Text, out int defTimeout))
_currentConfig.PortScanTimeoutMs = defTimeout;
if (int.TryParse(PortScanMaxConcurrentBox.Text, out int defMaxConc))
_currentConfig.PortScanMaxConcurrent = defMaxConc;
}
private void SaveProfile_Click(object sender, RoutedEventArgs e)
{
ReadUIToConfig();
string name = ProfileCombo.Text;
if (string.IsNullOrWhiteSpace(name))
name = "Profile_" + DateTime.Now.ToString("HHmmss");
_currentConfig.ProfileName = name;
_profiles[name] = _currentConfig.Clone();
if (!ProfileCombo.Items.Contains(name))
ProfileCombo.Items.Add(name);
ProfileCombo.SelectedItem = name;
}
private void LoadProfile_Click(object sender, RoutedEventArgs e)
{
if (ProfileCombo.SelectedItem is string name && _profiles.TryGetValue(name, out var cfg))
{
_currentConfig = cfg.Clone();
ApplyConfigToUI();
}
}
private void ExportProfiles_Click(object sender, RoutedEventArgs e)
{
var dialog = new Microsoft.Win32.SaveFileDialog
{
Filter = "JSON (*.json)|*.json",
FileName = "profiles.json"
};
if (dialog.ShowDialog() == true)
{
var list = _profiles.Values.ToList();
_configManager.ExportProfiles(dialog.FileName, list);
MessageBox.Show("Profiles exported.");
}
}
private void ImportProfiles_Click(object sender, RoutedEventArgs e)
{
var dialog = new Microsoft.Win32.OpenFileDialog
{
Filter = "JSON (*.json)|*.json"
};
if (dialog.ShowDialog() == true)
{
var list = _configManager.ImportProfiles(dialog.FileName);
_profiles.Clear();
ProfileCombo.Items.Clear();
foreach (var cfg in list)
{
string name = string.IsNullOrWhiteSpace(cfg.ProfileName)
? "Profile_" + Guid.NewGuid().ToString("N")[..6]
: cfg.ProfileName;
cfg.ProfileName = name;
_profiles[name] = cfg;
ProfileCombo.Items.Add(name);
}
if (_profiles.Count > 0)
{
var first = _profiles.Keys.First();
ProfileCombo.SelectedItem = first;
_currentConfig = _profiles[first].Clone();
ApplyConfigToUI();
}
MessageBox.Show("Profiles imported.");
}
}
#endregion
}
#region Helper classes
public class ConnectionEntry
{
public string Time { get; set; } = "";
public string Protocol { get; set; } = "";
public string Process { get; set; } = "";
public string Local { get; set; } = "";
public string Remote { get; set; } = "";
public string State { get; set; } = "";
public int PID { get; set; }
}
public class AppConfig
{
// Display
public bool CompactMode { get; set; }
public bool ShowDetails { get; set; } = true;
public bool ShowTimestamps { get; set; } = true;
public bool ShowProtocolColumn { get; set; } = true;
public bool ShowStateColumn { get; set; } = true;
public int UiFontSize { get; set; } = 12;
public int RowHeight { get; set; } = 22;
// Performance
public bool PerformanceMode { get; set; }
public bool UseAsyncScan { get; set; } = true;
public bool SkipProcessResolve { get; set; }
public bool SkipUdpOnHighCpu { get; set; }
public int ScanIntervalMs { get; set; } = 1000;
public int MaxConnections { get; set; } = 1000;
public int MaxGraphHistory { get; set; } = 60;
public int CpuLimitPercent { get; set; } = 90;
// Logging
public bool EnableFileLogging { get; set; }
public bool AppendLogs { get; set; } = true;
public bool LogOnlyNewConnections { get; set; }
public string LogFilePath { get; set; } = "logs\\cxwnetwork.log";
public int MaxLogSizeKb { get; set; } = 1024;
public int LogRotationCount { get; set; } = 5;
// Filtering
public bool FilterSystemProcesses { get; set; }
public bool FilterLocalhost { get; set; }
public bool FilterClosedStates { get; set; }
public string ProcessIncludeRegex { get; set; } = "";
public string ProcessExcludeRegex { get; set; } = "";
// SpeedTest
public string SpeedTestUrl { get; set; } = "https://example.com/";
public int SpeedTestBufferKb { get; set; } = 512;
// PortScan
public bool PortScanSkipCommonClosed { get; set; }
public bool PortScanOnlyCommon { get; set; }
public string PortScanDefaultHost { get; set; } = "127.0.0.1";
public int PortScanDefaultStartPort { get; set; } = 1;
public int PortScanDefaultEndPort { get; set; } = 1024;
public int PortScanTimeoutMs { get; set; } = 150;
public int PortScanMaxConcurrent { get; set; } = 50;
// Meta
public string ProfileName { get; set; } = "Default";
public AppConfig Clone() => JsonSerializer.Deserialize(JsonSerializer.Serialize(this))!;
public static AppConfig CreateDefault() => new AppConfig();
}
public class SpeedTestResult
{
public double DownloadKbps { get; set; }
public double Seconds { get; set; }
public long Bytes { get; set; }
}
public class SpeedTester
{
private readonly HttpClient _http;
public SpeedTester(HttpClient http) => _http = http;
public async Task RunAsync(string url, int bufferKb)
{
var sw = Stopwatch.StartNew();
var data = await _http.GetByteArrayAsync(url);
sw.Stop();
double seconds = sw.Elapsed.TotalSeconds;
long bytes = data.Length;
double kbytes = bytes / 1024.0;
double speed = kbytes / Math.Max(seconds, 0.001);
return new SpeedTestResult
{
DownloadKbps = speed,
Seconds = seconds,
Bytes = bytes
};
}
}
public class PortScanner
{
private readonly Dispatcher _dispatcher;
public PortScanner(Dispatcher dispatcher) => _dispatcher = dispatcher;
public async Task ScanAsync(
string host,
int startPort,
int endPort,
int timeoutMs,
int maxConcurrent,
bool onlyCommon,
bool skipCommonClosed,
Action onOpenPort)
{
var ports = Enumerable.Range(startPort, endPort - startPort + 1).ToList();
if (onlyCommon)
{
int[] common = { 20, 21, 22, 25, 53, 80, 110, 143, 443, 3306, 3389 };
ports = ports.Where(p => common.Contains(p)).ToList();
}
using var semaphore = new SemaphoreSlim(maxConcurrent);
var tasks = new List();
foreach (int port in ports)
{
await semaphore.WaitAsync();
tasks.Add(Task.Run(async () =>
{
try
{
using var client = new TcpClient();
var connectTask = client.ConnectAsync(host, port);
if (await Task.WhenAny(connectTask, Task.Delay(timeoutMs)) == connectTask && client.Connected)
{
_dispatcher.Invoke(() => onOpenPort(port));
}
else if (!skipCommonClosed)
{
// closed ports ignored if skipCommonClosed = true
}
}
catch
{
}
finally
{
semaphore.Release();
}
}));
}
await Task.WhenAll(tasks);
}
}
public class GraphRenderer
{
public void Draw(Canvas canvas, List history)
{
canvas.Children.Clear();
if (history.Count == 0) return;
double width = canvas.ActualWidth;
double height = canvas.ActualHeight;
if (width <= 0 || height <= 0)
{
width = 600;
height = 300;
}
int max = history.Max();
if (max == 0) max = 1;
double xStep = width / Math.Max(1, history.Count - 1);
var poly = new Polyline
{
Stroke = Brushes.Lime,
StrokeThickness = 2
};
for (int i = 0; i < history.Count; i++)
{
double x = i * xStep;
double y = height - (history[i] / (double)max) * height;
poly.Points.Add(new Point(x, y));
}
canvas.Children.Add(poly);
}
}
public class ConfigManager
{
public void ExportProfiles(string path, List profiles)
{
var json = JsonSerializer.Serialize(profiles, new JsonSerializerOptions
{
WriteIndented = true
});
File.WriteAllText(path, json);
}
public List ImportProfiles(string path)
{
var json = File.ReadAllText(path);
var list = JsonSerializer.Deserialize>(json);
return list ?? new List();
}
}
#endregion
}