Initial version
This commit is contained in:
9
EonaCat.PortMonitor/App.xaml
Normal file
9
EonaCat.PortMonitor/App.xaml
Normal file
@@ -0,0 +1,9 @@
|
||||
<Application x:Class="EonaCat.PortMonitor.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
StartupUri="MainWindow.xaml">
|
||||
<Application.Resources>
|
||||
<SolidColorBrush x:Key="DynamicBackgroundColor" Color="White"/>
|
||||
<SolidColorBrush x:Key="DynamicTextColor" Color="Black"/>
|
||||
</Application.Resources>
|
||||
</Application>
|
13
EonaCat.PortMonitor/App.xaml.cs
Normal file
13
EonaCat.PortMonitor/App.xaml.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System.Configuration;
|
||||
using System.Data;
|
||||
using System.Windows;
|
||||
|
||||
namespace EonaCat.PortMonitor;
|
||||
|
||||
/// <summary>
|
||||
/// Interaction logic for App.xaml
|
||||
/// </summary>
|
||||
public partial class App : System.Windows.Application
|
||||
{
|
||||
}
|
||||
|
10
EonaCat.PortMonitor/AssemblyInfo.cs
Normal file
10
EonaCat.PortMonitor/AssemblyInfo.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System.Windows;
|
||||
|
||||
[assembly:ThemeInfo(
|
||||
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||
//(used if a resource is not found in the page,
|
||||
// or application resource dictionaries)
|
||||
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||
//(used if a resource is not found in the page,
|
||||
// app, or any theme specific resource dictionaries)
|
||||
)]
|
81
EonaCat.PortMonitor/ConnectionDetailsWindow.xaml
Normal file
81
EonaCat.PortMonitor/ConnectionDetailsWindow.xaml
Normal file
@@ -0,0 +1,81 @@
|
||||
<Window x:Class="EonaCat.PortMonitor.ConnectionDetailsWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="Connection Details"
|
||||
Height="430" Width="400"
|
||||
ResizeMode="NoResize"
|
||||
ShowInTaskbar="False"
|
||||
WindowStyle="SingleBorderWindow"
|
||||
WindowStartupLocation="CenterScreen">
|
||||
|
||||
<Window.Resources>
|
||||
<!-- Dynamic Resource for the theme -->
|
||||
<ResourceDictionary Source="/Themes.xaml" />
|
||||
</Window.Resources>
|
||||
|
||||
<Grid Margin="10">
|
||||
<!-- Main Content Area -->
|
||||
<Border Background="{DynamicResource DynamicBackgroundColor}" CornerRadius="10" BorderBrush="#DCDCDC" BorderThickness="1" Padding="10">
|
||||
<Grid>
|
||||
<!-- Define Rows and Columns -->
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="20"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Title -->
|
||||
<TextBlock Text="Connection Details" FontSize="20" FontWeight="Bold" Foreground="{DynamicResource DynamicTextColor}" HorizontalAlignment="Center" Grid.Row="0" Grid.ColumnSpan="2" Margin="0,0,0,10"/>
|
||||
|
||||
<!-- Protocol -->
|
||||
<TextBlock Text="Protocol:" FontSize="14" Foreground="{DynamicResource DynamicTextColor}" VerticalAlignment="Center" Grid.Row="1" Grid.Column="0" Margin="0,5"/>
|
||||
<TextBox Name="ProtocolText" FontSize="14" Foreground="{DynamicResource DynamicTextColor}" VerticalAlignment="Center" Grid.Row="1" Grid.Column="2" Margin="0,5" IsReadOnly="True" Background="Transparent" BorderBrush="Transparent"/>
|
||||
|
||||
<!-- Local Address -->
|
||||
<TextBlock Text="Local Address:" FontSize="14" Foreground="{DynamicResource DynamicTextColor}" VerticalAlignment="Center" Grid.Row="2" Grid.Column="0" Margin="0,5"/>
|
||||
<TextBox Name="LocalAddressText" FontSize="14" Foreground="{DynamicResource DynamicTextColor}" VerticalAlignment="Center" Grid.Row="2" Grid.Column="2" Margin="0,5" IsReadOnly="True" Background="Transparent" BorderBrush="Transparent"/>
|
||||
|
||||
<!-- Local Port -->
|
||||
<TextBlock Text="Local Port:" FontSize="14" Foreground="{DynamicResource DynamicTextColor}" VerticalAlignment="Center" Grid.Row="3" Grid.Column="0" Margin="0,5"/>
|
||||
<TextBox Name="LocalPortText" FontSize="14" Foreground="{DynamicResource DynamicTextColor}" VerticalAlignment="Center" Grid.Row="3" Grid.Column="2" Margin="0,5" IsReadOnly="True" Background="Transparent" BorderBrush="Transparent"/>
|
||||
|
||||
<!-- Remote Address -->
|
||||
<TextBlock Text="Remote Address:" FontSize="14" Foreground="{DynamicResource DynamicTextColor}" VerticalAlignment="Center" Grid.Row="4" Grid.Column="0" Margin="0,5"/>
|
||||
<TextBox Name="RemoteAddressText" FontSize="14" Foreground="{DynamicResource DynamicTextColor}" VerticalAlignment="Center" Grid.Row="4" Grid.Column="2" Margin="0,5" IsReadOnly="True" Background="Transparent" BorderBrush="Transparent"/>
|
||||
|
||||
<!-- Remote Port -->
|
||||
<TextBlock Text="Remote Port:" FontSize="14" Foreground="{DynamicResource DynamicTextColor}" VerticalAlignment="Center" Grid.Row="5" Grid.Column="0" Margin="0,5"/>
|
||||
<TextBox Name="RemotePortText" FontSize="14" Foreground="{DynamicResource DynamicTextColor}" VerticalAlignment="Center" Grid.Row="5" Grid.Column="2" Margin="0,5" IsReadOnly="True" Background="Transparent" BorderBrush="Transparent"/>
|
||||
|
||||
<!-- State -->
|
||||
<TextBlock Text="State:" FontSize="14" Foreground="{DynamicResource DynamicTextColor}" VerticalAlignment="Center" Grid.Row="6" Grid.Column="0" Margin="0,5"/>
|
||||
<TextBox Name="StateText" FontSize="14" Foreground="{DynamicResource DynamicTextColor}" VerticalAlignment="Center" Grid.Row="6" Grid.Column="2" Margin="0,5" IsReadOnly="True" Background="Transparent" BorderBrush="Transparent"/>
|
||||
|
||||
<!-- Process Name -->
|
||||
<TextBlock Text="Process Name:" FontSize="14" Foreground="{DynamicResource DynamicTextColor}" VerticalAlignment="Center" Grid.Row="7" Grid.Column="0" Margin="0,5"/>
|
||||
<TextBox Name="ProcessNameText" FontSize="14" Foreground="{DynamicResource DynamicTextColor}" VerticalAlignment="Center" Grid.Row="7" Grid.Column="2" Margin="0,5" IsReadOnly="True" Background="Transparent" BorderBrush="Transparent"/>
|
||||
|
||||
<!-- Process Id -->
|
||||
<TextBlock Text="Process Id:" FontSize="14" Foreground="{DynamicResource DynamicTextColor}" VerticalAlignment="Center" Grid.Row="8" Grid.Column="0" Margin="0,5"/>
|
||||
<TextBox Name="ProcessIdText" FontSize="14" Foreground="{DynamicResource DynamicTextColor}" VerticalAlignment="Center" Grid.Row="8" Grid.Column="2" Margin="0,5" IsReadOnly="True" Background="Transparent" BorderBrush="Transparent"/>
|
||||
|
||||
<!-- Connection duration -->
|
||||
<TextBlock Text="Connection duration:" FontSize="14" Foreground="{DynamicResource DynamicTextColor}" VerticalAlignment="Center" Grid.Row="9" Grid.Column="0" Margin="0,5"/>
|
||||
<TextBox Name="ConnectionDurationText" FontSize="14" Foreground="{DynamicResource DynamicTextColor}" VerticalAlignment="Center" Grid.Row="9" Grid.Column="2" Margin="0,5" IsReadOnly="True" Background="Transparent" BorderBrush="Transparent"/>
|
||||
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Window>
|
49
EonaCat.PortMonitor/ConnectionDetailsWindow.xaml.cs
Normal file
49
EonaCat.PortMonitor/ConnectionDetailsWindow.xaml.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using System.Windows;
|
||||
|
||||
namespace EonaCat.PortMonitor
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for ConnectionDetailsWindow.xaml
|
||||
/// </summary>
|
||||
public partial class ConnectionDetailsWindow : Window
|
||||
{
|
||||
public ConnectionDetailsWindow(ConnectionInfo conn)
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
ProtocolText.Text = conn.Protocol;
|
||||
LocalAddressText.Text = conn.LocalAddress;
|
||||
LocalPortText.Text = conn.LocalPort.ToString();
|
||||
RemoteAddressText.Text = conn.RemoteAddress;
|
||||
RemotePortText.Text = conn.RemotePort.ToString();
|
||||
|
||||
StateText.Text = conn.State;
|
||||
|
||||
// Update the state text every second
|
||||
System.Windows.Threading.DispatcherTimer stateTimer = new System.Windows.Threading.DispatcherTimer();
|
||||
stateTimer.Interval = TimeSpan.FromSeconds(1);
|
||||
stateTimer.Tick += (sender, e) => UpdateStateText(conn);
|
||||
stateTimer.Start();
|
||||
|
||||
ProcessIdText.Text = Convert.ToString(conn.ProcessId);
|
||||
ProcessNameText.Text = conn.ProcessName;
|
||||
ConnectionDurationText.Text = Convert.ToString(conn.FormattedConnectionDuration);
|
||||
|
||||
// Update the connection duration text every second
|
||||
System.Windows.Threading.DispatcherTimer timer = new System.Windows.Threading.DispatcherTimer();
|
||||
timer.Interval = TimeSpan.FromSeconds(1);
|
||||
timer.Tick += (sender, e) => UpdateConnectionDurationText(conn);
|
||||
timer.Start();
|
||||
}
|
||||
|
||||
private void UpdateStateText(ConnectionInfo conn)
|
||||
{
|
||||
Dispatcher.Invoke(() => StateText.Text = conn.State);
|
||||
}
|
||||
|
||||
private void UpdateConnectionDurationText(ConnectionInfo conn)
|
||||
{
|
||||
Dispatcher.Invoke(() => ConnectionDurationText.Text = Convert.ToString(conn.FormattedConnectionDuration));
|
||||
}
|
||||
}
|
||||
}
|
32
EonaCat.PortMonitor/EonaCat.PortMonitor.csproj
Normal file
32
EonaCat.PortMonitor/EonaCat.PortMonitor.csproj
Normal file
@@ -0,0 +1,32 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<UseWPF>true</UseWPF>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<ApplicationIcon>icon.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="icon.png" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="icon.ico" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="icon.png" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="EonaCat.Commands" Version="1.0.0" />
|
||||
<PackageReference Include="EonaCat.Json" Version="1.1.9" />
|
||||
<PackageReference Include="EonaCat.WPF.Popup" Version="1.0.4" />
|
||||
<PackageReference Include="System.Management" Version="9.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
25
EonaCat.PortMonitor/EonaCat.PortMonitor.sln
Normal file
25
EonaCat.PortMonitor/EonaCat.PortMonitor.sln
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.13.35825.156 d17.13
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EonaCat.PortMonitor", "EonaCat.PortMonitor.csproj", "{4D76BA36-0C8F-4335-A792-3A38D50B502C}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{4D76BA36-0C8F-4335-A792-3A38D50B502C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4D76BA36-0C8F-4335-A792-3A38D50B502C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4D76BA36-0C8F-4335-A792-3A38D50B502C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4D76BA36-0C8F-4335-A792-3A38D50B502C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {FC0B8B1C-7CA4-47E3-88C3-57703E061393}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
108
EonaCat.PortMonitor/MainWindow.xaml
Normal file
108
EonaCat.PortMonitor/MainWindow.xaml
Normal file
@@ -0,0 +1,108 @@
|
||||
<Window x:Class="EonaCat.PortMonitor.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="EonaCat Port Monitor" Height="600" Width="1500" WindowStartupLocation="CenterScreen">
|
||||
|
||||
<Window.Resources>
|
||||
<ResourceDictionary Source="/Themes.xaml" />
|
||||
</Window.Resources>
|
||||
|
||||
<Grid Background="{DynamicResource DynamicBackgroundColor}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Define a Grid inside the first Row (to contain the StackPanel and the button) -->
|
||||
<Grid Grid.Row="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<!-- Flexible space -->
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<!-- Fixed width for the button -->
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Filter and other elements go in the first column -->
|
||||
<StackPanel Grid.Column="0" Orientation="Horizontal" Margin="10">
|
||||
<TextBlock Text="Filter by Keyword:" VerticalAlignment="Center" Margin="5" />
|
||||
<TextBox Name="KeywordFilter" Width="200" Margin="5" KeyUp="FilterConnections_KeyUp"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Theme switch button goes in the second column (aligned to the right) -->
|
||||
<Button x:Name="btnTheme" Content="🌙" Width="40" Height="30" Margin="10,0" VerticalAlignment="Center" Grid.Column="1" Click="btnTheme_Click" />
|
||||
</Grid>
|
||||
|
||||
<!-- Network Statistics Section -->
|
||||
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="10">
|
||||
<TextBlock Name="TcpUdpStatsText" VerticalAlignment="Center" FontSize="14" Margin="5" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- DataGrid for Connections -->
|
||||
<TabControl Margin="10" Grid.Row="2" >
|
||||
<TabItem Header="Connections" >
|
||||
<DataGrid Name="NetworkDataGrid" AutoGenerateColumns="False" IsReadOnly="True" Margin="10" PreviewMouseDoubleClick="DataGrid_PreviewMouseDoubleClick">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="Protocol" Binding="{Binding Protocol}" Width="100"/>
|
||||
<DataGridTextColumn Header="Local Address" Binding="{Binding LocalAddress}" Width="200"/>
|
||||
<DataGridTextColumn Header="Local Port" Binding="{Binding LocalPort}" Width="100"/>
|
||||
<DataGridTextColumn Header="Remote Address" Binding="{Binding RemoteAddress}" Width="200"/>
|
||||
<DataGridTextColumn Header="Remote Port" Binding="{Binding RemotePort}" Width="100"/>
|
||||
<DataGridTextColumn Header="State" Binding="{Binding State}" Width="150"/>
|
||||
<DataGridTextColumn Header="Process Name" Binding="{Binding ProcessName}" Width="200"/>
|
||||
<DataGridTextColumn Header="Process Id" Binding="{Binding ProcessId}" Width="100"/>
|
||||
<DataGridTextColumn Header="Connection Duration" Binding="{Binding ConnectionDuration}" Width="150"/>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</TabItem>
|
||||
<TabItem Header="Inbound Connections" >
|
||||
<DataGrid Name="InboundDataGrid" AutoGenerateColumns="False" IsReadOnly="True" Margin="10" PreviewMouseDoubleClick="DataGrid_PreviewMouseDoubleClick">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="Protocol" Binding="{Binding Protocol}" Width="100"/>
|
||||
<DataGridTextColumn Header="Local Address" Binding="{Binding LocalAddress}" Width="200"/>
|
||||
<DataGridTextColumn Header="Local Port" Binding="{Binding LocalPort}" Width="100"/>
|
||||
<DataGridTextColumn Header="Remote Address" Binding="{Binding RemoteAddress}" Width="200"/>
|
||||
<DataGridTextColumn Header="Remote Port" Binding="{Binding RemotePort}" Width="100"/>
|
||||
<DataGridTextColumn Header="State" Binding="{Binding State}" Width="150"/>
|
||||
<DataGridTextColumn Header="Process Name" Binding="{Binding ProcessName}" Width="200"/>
|
||||
<DataGridTextColumn Header="Process Id" Binding="{Binding ProcessId}" Width="100"/>
|
||||
<DataGridTextColumn Header="Connection Duration" Binding="{Binding ConnectionDuration}" Width="150"/>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</TabItem>
|
||||
<TabItem Header="Outbound Connections" >
|
||||
<DataGrid Name="OutboundDataGrid" AutoGenerateColumns="False" IsReadOnly="True" Margin="10" PreviewMouseDoubleClick="DataGrid_PreviewMouseDoubleClick">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="Protocol" Binding="{Binding Protocol}" Width="100"/>
|
||||
<DataGridTextColumn Header="Local Address" Binding="{Binding LocalAddress}" Width="200"/>
|
||||
<DataGridTextColumn Header="Local Port" Binding="{Binding LocalPort}" Width="100"/>
|
||||
<DataGridTextColumn Header="Remote Address" Binding="{Binding RemoteAddress}" Width="200"/>
|
||||
<DataGridTextColumn Header="Remote Port" Binding="{Binding RemotePort}" Width="100"/>
|
||||
<DataGridTextColumn Header="State" Binding="{Binding State}" Width="150"/>
|
||||
<DataGridTextColumn Header="Process Name" Binding="{Binding ProcessName}" Width="200"/>
|
||||
<DataGridTextColumn Header="Process Id" Binding="{Binding ProcessId}" Width="100"/>
|
||||
<DataGridTextColumn Header="Connection Duration" Binding="{Binding ConnectionDuration}" Width="150"/>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
|
||||
<!-- Export Section -->
|
||||
<StackPanel Grid.Row="3" Orientation="Horizontal" Margin="10">
|
||||
<Button Content="Export to JSON" Width="120" Height="30" Margin="10,0" VerticalAlignment="Center" Click="ExportToJsonButton_Click" />
|
||||
<Button Content="Export to XML" Width="120" Height="30" Margin="10,0" VerticalAlignment="Center" Click="ExportToXmlButton_Click" />
|
||||
<Button Content="Export to CSV" Width="120" Height="30" Margin="10,0" VerticalAlignment="Center" Click="ExportToCsvButton_Click" />
|
||||
<Button Content="Export to HTML" Width="120" Height="30" Margin="10,0" VerticalAlignment="Center" Click="ExportToHtmlButton_Click" />
|
||||
<Button Content="Save Filter Preset" Width="120" Height="30" Margin="10,0" VerticalAlignment="Center" Click="SaveFilterPresetButton_Click" />
|
||||
<Button Content="Load Filter Preset" Width="120" Height="30" Margin="10,0" VerticalAlignment="Center" Click="LoadFilterPresetButton_Click" />
|
||||
<Button Content="Kill Process" Width="120" Height="30" Margin="10,0" VerticalAlignment="Center" Click="KillProcessButton_Click" />
|
||||
<Button Content="IP Lookup" Width="120" Height="30" Margin="10,0" VerticalAlignment="Center" Click="IpLookupButton_Click" />
|
||||
<Button Content="Block IP" Width="120" Height="30" Margin="10,0" VerticalAlignment="Center" Click="BlockIpButton_Click" />
|
||||
<StackPanel>
|
||||
<CheckBox x:Name="chkMinimize" Content="Minimize on start" VerticalContentAlignment="Center" Width="120" Height="30" Margin="10,0" VerticalAlignment="Center" Checked="chkMinimize_Checked" />
|
||||
<CheckBox x:Name="chkShowPopups" Content="Show notifications" VerticalContentAlignment="Center" Width="120" Height="30" Margin="10,0" VerticalAlignment="Center" IsChecked="True" Checked="chkShowPopups_Checked" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
668
EonaCat.PortMonitor/MainWindow.xaml.cs
Normal file
668
EonaCat.PortMonitor/MainWindow.xaml.cs
Normal file
@@ -0,0 +1,668 @@
|
||||
using EonaCat.Json;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Xml.Serialization;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.ComponentModel;
|
||||
using System.Windows.Input;
|
||||
using EonaCat.Helpers.Commands;
|
||||
using System.Text;
|
||||
using MessageBox = System.Windows.MessageBox;
|
||||
using System.Net.Http;
|
||||
using System.Windows.Media;
|
||||
using Application = System.Windows.Application;
|
||||
using Brushes = System.Drawing.Brushes;
|
||||
using Brush = System.Drawing.Brush;
|
||||
using System.Windows.Threading;
|
||||
|
||||
namespace EonaCat.PortMonitor
|
||||
{
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
public ObservableCollection<ConnectionInfo> Connections { get; set; } = new ObservableCollection<ConnectionInfo>();
|
||||
private List<ConnectionInfo> _previousConnections = new List<ConnectionInfo>();
|
||||
private BackgroundWorker _monitorWorker;
|
||||
private int _interval = 1 * 1000;
|
||||
WPF.Tray.TrayIcon _trayIcon = new WPF.Tray.TrayIcon();
|
||||
|
||||
public bool MinimizeToTray { get; private set; }
|
||||
public bool MinimizeOnStartup { get; private set; }
|
||||
public bool ShowPopups { get; private set; } = true;
|
||||
public bool FirstRun { get; private set; } = true;
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = this;
|
||||
|
||||
NetworkDataGrid.Items.Clear();
|
||||
NetworkDataGrid.ItemsSource = Connections;
|
||||
|
||||
this.StateChanged += MainWindow_StateChanged;
|
||||
|
||||
SetupTrayIcon();
|
||||
|
||||
LoadSettings();
|
||||
|
||||
_monitorWorker = new BackgroundWorker();
|
||||
_monitorWorker.DoWork += MonitorWorker_DoWork;
|
||||
_monitorWorker.RunWorkerAsync();
|
||||
}
|
||||
|
||||
protected override void OnActivated(EventArgs e)
|
||||
{
|
||||
base.OnActivated(e);
|
||||
if (FirstRun)
|
||||
{
|
||||
if (MinimizeToTray || MinimizeOnStartup)
|
||||
{
|
||||
// Minimize to tray on first run
|
||||
Hide();
|
||||
FirstRun = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadSettings()
|
||||
{
|
||||
if (File.Exists("settings.json"))
|
||||
{
|
||||
var json = File.ReadAllText("settings.json");
|
||||
var settings = JsonHelper.ToObject<dynamic>(json);
|
||||
if (settings != null)
|
||||
{
|
||||
if (settings.MinimizeToTray != null)
|
||||
{
|
||||
MinimizeToTray = (bool)settings.MinimizeToTray;
|
||||
}
|
||||
|
||||
if (settings.MinimizeOnStartup != null)
|
||||
{
|
||||
MinimizeOnStartup = (bool)settings.MinimizeOnStartup;
|
||||
chkMinimize.IsChecked = MinimizeOnStartup;
|
||||
}
|
||||
|
||||
if (settings.ShowPopups != null)
|
||||
{
|
||||
ShowPopups = (bool)settings.ShowPopups;
|
||||
chkShowPopups.IsChecked = ShowPopups;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void MainWindow_StateChanged(object? sender, EventArgs e)
|
||||
{
|
||||
if (WindowState == WindowState.Minimized)
|
||||
{
|
||||
// Minimize to tray and hide the window
|
||||
this.Hide();
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupTrayIcon()
|
||||
{
|
||||
// Load the icon from embedded resource
|
||||
var icon = System.Drawing.Icon.ExtractAssociatedIcon(System.Reflection.Assembly.GetExecutingAssembly().Location);
|
||||
_trayIcon.Icon = icon;
|
||||
_trayIcon.TrayLeftMouseUp += (sender, args) =>
|
||||
{
|
||||
// Show the main window when the tray icon is clicked
|
||||
WindowState = WindowState.Normal;
|
||||
Show();
|
||||
Activate();
|
||||
};
|
||||
|
||||
_trayIcon.TrayRightMouseUp += (sender, args) =>
|
||||
{
|
||||
// Generate a context menu for the tray icon
|
||||
var contextMenu = new System.Windows.Controls.ContextMenu();
|
||||
contextMenu.Items.Add(new MenuItem { Header = "Show", Command = new GiveCommand(() => { Show(); WindowState = WindowState.Normal; }) });
|
||||
contextMenu.Items.Add(new MenuItem
|
||||
{
|
||||
Header = "Exit",
|
||||
Command = new GiveCommand(() =>
|
||||
{
|
||||
Close();
|
||||
})
|
||||
});
|
||||
_trayIcon.ContextMenu = contextMenu;
|
||||
};
|
||||
}
|
||||
|
||||
protected override void OnClosing(CancelEventArgs e)
|
||||
{
|
||||
// Save settings before closing
|
||||
var settings = new
|
||||
{
|
||||
MinimizeToTray = this.WindowState == WindowState.Minimized,
|
||||
MinimizeOnStartup = chkMinimize.IsChecked == true,
|
||||
ShowPopups = chkShowPopups.IsChecked == true
|
||||
};
|
||||
File.WriteAllBytes("settings.json", Encoding.UTF8.GetBytes(JsonHelper.ToJson(settings)));
|
||||
|
||||
base.OnClosing(e);
|
||||
}
|
||||
|
||||
private void MonitorWorker_DoWork(object sender, DoWorkEventArgs e)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
MonitorConnections(null);
|
||||
Thread.Sleep(_interval);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task NotifyNewConnectionAsync(ConnectionInfo conn)
|
||||
{
|
||||
if (!ShowPopups)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a new notification manager
|
||||
var notificationManager = new WPF.Popup.NotificationManager();
|
||||
|
||||
// Set up the popup's title and content
|
||||
string popupTitle = $"New Connection: {conn.Protocol} {conn.LocalAddress}:{conn.LocalPort} -> {conn.RemoteAddress}:{conn.RemotePort}";
|
||||
string popupContent = $"Process: {conn.ProcessName}\nState: {conn.State}";
|
||||
|
||||
// Set up the popup
|
||||
var notification = new WPF.Popup.NotificationContent
|
||||
{
|
||||
Title = popupTitle,
|
||||
Message = popupContent,
|
||||
Type = WPF.Popup.NotificationType.Information,
|
||||
};
|
||||
|
||||
// Show the popup
|
||||
await notificationManager.ShowAsync(notification, expirationTime: TimeSpan.FromSeconds(3), onClick: new Action(() => { ShowConnectionDetails(conn); }));
|
||||
}
|
||||
|
||||
|
||||
private void ShowConnectionDetails(ConnectionInfo conn)
|
||||
{
|
||||
// Play a sound when the notification is clicked
|
||||
System.Media.SystemSounds.Asterisk.Play();
|
||||
|
||||
var detailsWindow = new ConnectionDetailsWindow(conn);
|
||||
detailsWindow.Show();
|
||||
}
|
||||
|
||||
private void DataGrid_PreviewMouseDoubleClick(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
// Get the DataGrid that triggered the event
|
||||
DataGrid dataGrid = sender as DataGrid;
|
||||
|
||||
if (dataGrid.SelectedItem != null)
|
||||
{
|
||||
ShowConnectionDetails(dataGrid.SelectedItem as ConnectionInfo);
|
||||
}
|
||||
}
|
||||
|
||||
private List<ConnectionInfo> GetActiveConnections()
|
||||
{
|
||||
var connectionList = new List<ConnectionInfo>();
|
||||
foreach (var conn in RunNetstatCommand())
|
||||
{
|
||||
if (string.IsNullOrEmpty(conn)) continue;
|
||||
|
||||
var match = ParseNetstatLine(conn);
|
||||
if (match.Success)
|
||||
{
|
||||
var connInfo = new ConnectionInfo
|
||||
{
|
||||
Protocol = match.Groups["protocol"].Value,
|
||||
LocalAddress = match.Groups["localAddress"].Value,
|
||||
LocalPort = int.Parse(match.Groups["localPort"].Value),
|
||||
RemoteAddress = match.Groups["remoteAddress"].Value,
|
||||
RemotePort = int.Parse(match.Groups["remotePort"].Value),
|
||||
State = string.IsNullOrEmpty(match.Groups["state"].Value) ? string.Empty : match.Groups["state"].Value, // Handle missing state for UDP
|
||||
ProcessId = string.IsNullOrEmpty(match.Groups["pid"].Value) ? 0 : int.Parse(match.Groups["pid"].Value), // Default PID to 0 if missing
|
||||
ProcessName = string.IsNullOrEmpty(match.Groups["pid"].Value) ? string.Empty : GetProcessName(int.Parse(match.Groups["pid"].Value)),
|
||||
ConnectionTime = DateTime.Now,
|
||||
ConnectionStatus = string.Empty
|
||||
};
|
||||
|
||||
connectionList.Add(connInfo);
|
||||
}
|
||||
}
|
||||
return connectionList;
|
||||
}
|
||||
|
||||
private async void MonitorConnections(object state)
|
||||
{
|
||||
// Store the currently selected connection before the update
|
||||
ConnectionInfo selectedConnection = null;
|
||||
|
||||
// Store the selected item from the DataGrid
|
||||
Dispatcher.Invoke(new Action(() =>
|
||||
{
|
||||
selectedConnection = NetworkDataGrid.SelectedItem as ConnectionInfo;
|
||||
}));
|
||||
|
||||
var currentConnections = await Task.Run(() => GetActiveConnections());
|
||||
var inboundConnections = currentConnections.Where(c => IsInboundConnection(c)).ToList();
|
||||
var outboundConnections = currentConnections.Where(c => IsOutboundConnection(c)).ToList();
|
||||
int totalConnections = currentConnections.Count;
|
||||
|
||||
// Compare and update connections with the existing ones
|
||||
var newConnections = currentConnections.Where(c => !_previousConnections.Any(p => p.LocalAddress == c.LocalAddress && p.LocalPort == c.LocalPort)).ToList();
|
||||
var closedConnections = _previousConnections.Where(p => !currentConnections.Any(c => c.LocalAddress == p.LocalAddress && c.LocalPort == p.LocalPort)).ToList();
|
||||
var stateChangedConnections = currentConnections.Where(c => _previousConnections.Any(p => p.LocalAddress == c.LocalAddress && p.LocalPort == c.LocalPort && p.State != c.State)).ToList();
|
||||
|
||||
// Add new connections or update existing ones
|
||||
foreach (var newConn in newConnections)
|
||||
{
|
||||
newConn.ConnectionStatus = "New";
|
||||
if (newConn.FirstConnectionTime == DateTime.MinValue)
|
||||
{
|
||||
newConn.FirstConnectionTime = DateTime.Now;
|
||||
}
|
||||
|
||||
// Add the new connection to the collection
|
||||
Dispatcher.Invoke(() => Connections.Add(newConn));
|
||||
await NotifyNewConnectionAsync(newConn);
|
||||
}
|
||||
|
||||
// Handle closed connections
|
||||
foreach (var closedConn in closedConnections)
|
||||
{
|
||||
var conn = Connections.FirstOrDefault(c => c.LocalAddress == closedConn.LocalAddress && c.LocalPort == closedConn.LocalPort);
|
||||
if (conn != null)
|
||||
{
|
||||
conn.ConnectionStatus = "Closed";
|
||||
|
||||
// Remove the connection if the status was set to closed for more than 5 seconds
|
||||
if (conn.ConnectionDuration.TotalSeconds > 5)
|
||||
{
|
||||
await Dispatcher.InvokeAsync(() => Connections.Remove(conn));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle state changes
|
||||
foreach (var stateChangedConn in stateChangedConnections)
|
||||
{
|
||||
var conn = Connections.FirstOrDefault(c => c.LocalAddress == stateChangedConn.LocalAddress && c.LocalPort == stateChangedConn.LocalPort);
|
||||
if (conn != null)
|
||||
{
|
||||
conn.ConnectionStatus = "State Changed";
|
||||
conn.BackgroundColor = Brushes.Yellow;
|
||||
|
||||
var timer = new DispatcherTimer
|
||||
{
|
||||
Interval = TimeSpan.FromSeconds(1)
|
||||
};
|
||||
timer.Tick += (sender, args) =>
|
||||
{
|
||||
conn.BackgroundColor = Brushes.Transparent;
|
||||
timer.Stop();
|
||||
};
|
||||
timer.Start();
|
||||
}
|
||||
}
|
||||
|
||||
await Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
InboundDataGrid.ItemsSource = Connections.Where(x => IsInboundConnection(x));
|
||||
OutboundDataGrid.ItemsSource = Connections.Where(x => IsOutboundConnection(x));
|
||||
|
||||
NetworkDataGrid.Items.Refresh();
|
||||
FilterConnections_KeyUp(null, null);
|
||||
UpdateConnectionStats(totalConnections, inboundConnections.Count, outboundConnections.Count);
|
||||
});
|
||||
|
||||
// Re-select the previously selected item after the refresh
|
||||
if (selectedConnection != null)
|
||||
{
|
||||
var selectedItem = Connections.FirstOrDefault(c => c.LocalAddress == selectedConnection.LocalAddress && c.LocalPort == selectedConnection.LocalPort);
|
||||
if (selectedItem != null)
|
||||
{
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
// Set the previously selected item back to the DataGrid
|
||||
NetworkDataGrid.SelectedItem = selectedItem;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Update the previousConnections list for the next iteration
|
||||
_previousConnections = currentConnections;
|
||||
}
|
||||
|
||||
private void UpdateConnectionStats(int total, int inbound, int outbound)
|
||||
{
|
||||
var tcpConnections = Connections.Count(c => c.Protocol == "TCP");
|
||||
var udpConnections = Connections.Count(c => c.Protocol == "UDP");
|
||||
|
||||
var avgConnectionTime = Connections.Average(c => (DateTime.Now - c.ConnectionTime).TotalSeconds);
|
||||
TcpUdpStatsText.Text = $"TCP Connections: {tcpConnections}\nUDP Connections: {udpConnections}\nAverage Connection Time: {avgConnectionTime:F2} seconds\nTotal: {total}\nInbound: {inbound}\nOutbound: {outbound}";
|
||||
}
|
||||
|
||||
private bool IsInboundConnection(ConnectionInfo connection)
|
||||
{
|
||||
// Inbound: LocalAddress is valid, and the machine is accepting the connection (server-side).
|
||||
return IsValidIpAddress(connection.LocalAddress) &&
|
||||
connection.LocalAddress != "0.0.0.0" &&
|
||||
connection.LocalAddress != "::" &&
|
||||
!string.IsNullOrEmpty(connection.RemoteAddress) &&
|
||||
connection.RemoteAddress != "0.0.0.0" &&
|
||||
connection.RemoteAddress != "::" &&
|
||||
connection.RemoteAddress != "N/A" &&
|
||||
connection.RemotePort != 0;
|
||||
}
|
||||
|
||||
private bool IsOutboundConnection(ConnectionInfo connection)
|
||||
{
|
||||
// Outbound: LocalAddress is valid, and the machine is initiating the connection (client-side).
|
||||
return IsValidIpAddress(connection.LocalAddress) &&
|
||||
connection.LocalAddress != "0.0.0.0" &&
|
||||
connection.LocalAddress != "::" &&
|
||||
IsValidIpAddress(connection.RemoteAddress) &&
|
||||
connection.RemoteAddress != "0.0.0.0" &&
|
||||
connection.RemoteAddress != "::" &&
|
||||
connection.RemoteAddress != "N/A" &&
|
||||
connection.LocalPort != 0;
|
||||
}
|
||||
|
||||
private bool IsValidIpAddress(string ipAddress)
|
||||
{
|
||||
return !string.IsNullOrEmpty(ipAddress) && (IsValidIPv4(ipAddress) || IsValidIPv6(ipAddress));
|
||||
}
|
||||
|
||||
private bool IsValidIPv4(string ipAddress)
|
||||
{
|
||||
return System.Net.IPAddress.TryParse(ipAddress, out var ip) && ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork;
|
||||
}
|
||||
|
||||
private bool IsValidIPv6(string ipAddress)
|
||||
{
|
||||
return System.Net.IPAddress.TryParse(ipAddress, out var ip) && ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6;
|
||||
}
|
||||
|
||||
private List<string> RunNetstatCommand()
|
||||
{
|
||||
var result = new List<string>();
|
||||
try
|
||||
{
|
||||
ProcessStartInfo startInfo = new ProcessStartInfo("netstat", "-ano")
|
||||
{
|
||||
RedirectStandardOutput = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true
|
||||
};
|
||||
|
||||
using (Process process = Process.Start(startInfo))
|
||||
{
|
||||
using (var reader = process.StandardOutput)
|
||||
{
|
||||
string line;
|
||||
while ((line = reader.ReadLine()) != null)
|
||||
{
|
||||
result.Add(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error running netstat: {ex.Message}");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Match ParseNetstatLine(string line)
|
||||
{
|
||||
// Regular expression for both TCP and UDP connections, IPv4 and IPv6.
|
||||
var regex = new Regex(@"(?<protocol>TCP|UDP)\s+(?<localAddress>[\d\.]+|\[?[A-F0-9:]+\]?):(?<localPort>\d+)\s+(?<remoteAddress>[\d\.]+|\[?[A-F0-9:]+\]?):(?<remotePort>\d+)\s*(?<state>\S*)?\s*(?<pid>\d+)?");
|
||||
var match = regex.Match(line);
|
||||
return match;
|
||||
}
|
||||
|
||||
private string GetProcessName(int pid)
|
||||
{
|
||||
try
|
||||
{
|
||||
var process = Process.GetProcessById(pid);
|
||||
return process.ProcessName;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "N/A";
|
||||
}
|
||||
}
|
||||
|
||||
private void FilterConnections_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
|
||||
{
|
||||
// Get the filter keyword from the input box
|
||||
var keywordFilter = KeywordFilter.Text.ToLower();
|
||||
|
||||
// Filter the connections based on the keyword
|
||||
var filteredConnections = Connections?.Where(c =>
|
||||
(string.IsNullOrEmpty(keywordFilter) ||
|
||||
c.ProcessName.ToLower().Contains(keywordFilter) ||
|
||||
c.RemoteAddress.ToLower().Contains(keywordFilter) ||
|
||||
c.LocalAddress.ToLower().Contains(keywordFilter) ||
|
||||
c.State.ToLower().Contains(keywordFilter) ||
|
||||
c.Protocol.ToLower().Contains(keywordFilter) ||
|
||||
c.LocalPort.ToString().Contains(keywordFilter) ||
|
||||
c.RemotePort.ToString().Contains(keywordFilter)
|
||||
)
|
||||
).ToList();
|
||||
|
||||
// Clear the existing items and add filtered ones to the ObservableCollection
|
||||
Connections.Clear();
|
||||
if (filteredConnections != null)
|
||||
{
|
||||
foreach (var conn in filteredConnections)
|
||||
{
|
||||
Connections.Add(conn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private void FilterConnections_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
FilterConnections_KeyUp(sender, null);
|
||||
}
|
||||
|
||||
private void ExportToJsonButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var json = JsonHelper.ToJson(Connections, Json.Formatting.Indented);
|
||||
File.WriteAllText("connections.json", json);
|
||||
MessageBox.Show("Connections exported to connections.json", "Export Successful", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
|
||||
private void ExportToXmlButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var xmlSerializer = new XmlSerializer(typeof(List<ConnectionInfo>));
|
||||
using (var writer = new StreamWriter("connections.xml"))
|
||||
{
|
||||
xmlSerializer.Serialize(writer, Connections);
|
||||
}
|
||||
MessageBox.Show("Connections exported to connections.xml", "Export Successful", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
|
||||
private void ExportToCsvButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var csvLines = new List<string> { "Protocol,LocalAddress,LocalPort,RemoteAddress,RemotePort,State,ProcessName,ConnectionTime" };
|
||||
csvLines.AddRange(Connections.Select(c => $"{c.Protocol},{c.LocalAddress},{c.LocalPort},{c.RemoteAddress},{c.RemotePort},{c.State},{c.ProcessName},{c.ConnectionTime}"));
|
||||
|
||||
File.WriteAllLines("connections.csv", csvLines);
|
||||
MessageBox.Show("Connections exported to connections.csv", "Export Successful", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
|
||||
private void SaveFilterPresetButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var filters = new
|
||||
{
|
||||
Keywords = KeywordFilter.Text,
|
||||
};
|
||||
|
||||
var json = JsonHelper.ToJson(filters);
|
||||
File.WriteAllText("filterPreset.json", json);
|
||||
}
|
||||
|
||||
private void LoadFilterPresetButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (File.Exists("filterPreset.json"))
|
||||
{
|
||||
var json = File.ReadAllText("filterPreset.json");
|
||||
var filters = JsonHelper.ToObject<dynamic>(json);
|
||||
|
||||
KeywordFilter.Text = filters.Keywords;
|
||||
|
||||
FilterConnections_KeyUp(null, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void ExportToHtmlButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var htmlContent = "<html><body><table border='1'><tr><th>Protocol</th><th>LocalAddress</th><th>LocalPort</th><th>RemoteAddress</th><th>RemotePort</th><th>State</th><th>ProcessName</th><th>ConnectionTime</th></tr>";
|
||||
foreach (var conn in Connections)
|
||||
{
|
||||
htmlContent += $"<tr><td>{conn.Protocol}</td><td>{conn.LocalAddress}</td><td>{conn.LocalPort}</td><td>{conn.RemoteAddress}</td><td>{conn.RemotePort}</td><td>{conn.State}</td><td>{conn.ProcessName}</td><td>{conn.ConnectionTime}</td></tr>";
|
||||
}
|
||||
htmlContent += "</table></body></html>";
|
||||
|
||||
File.WriteAllText("connections.html", htmlContent);
|
||||
MessageBox.Show("Connections exported to connections.html", "Export Successful", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
|
||||
private void chkMinimize_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
MinimizeOnStartup = chkMinimize.IsChecked == true;
|
||||
}
|
||||
|
||||
private void chkShowPopups_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ShowPopups = chkShowPopups.IsChecked == true;
|
||||
}
|
||||
|
||||
private void KillProcessButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (NetworkDataGrid.SelectedItem is ConnectionInfo selectedConnection)
|
||||
{
|
||||
try
|
||||
{
|
||||
var process = Process.GetProcessById(selectedConnection.ProcessId);
|
||||
process.Kill();
|
||||
MessageBox.Show($"Process {selectedConnection.ProcessName} (PID: {selectedConnection.ProcessId}) terminated.", "Process Killed", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"Failed to terminate process: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async void IpLookupButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (NetworkDataGrid.SelectedItem is ConnectionInfo selectedConnection)
|
||||
{
|
||||
string apiUrl = $"https://reallyfreegeoip.org/json/{selectedConnection.RemoteAddress}";
|
||||
using (HttpClient client = new HttpClient())
|
||||
{
|
||||
var response = await client.GetStringAsync(apiUrl);
|
||||
MessageBox.Show(response, "IP Lookup", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void BlockIpButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Check if an item is selected in the DataGrid
|
||||
if (NetworkDataGrid.SelectedItem is ConnectionInfo selectedConnection)
|
||||
{
|
||||
// Ask the user for confirmation
|
||||
var result = MessageBox.Show(
|
||||
$"Are you sure you want to block the IP address: {selectedConnection.RemoteAddress}?",
|
||||
"Confirm Block",
|
||||
MessageBoxButton.YesNo,
|
||||
MessageBoxImage.Question
|
||||
);
|
||||
|
||||
// If the user clicks 'Yes', proceed with blocking the IP
|
||||
if (result == MessageBoxResult.Yes)
|
||||
{
|
||||
string command = $"netsh advfirewall firewall add rule name=\"Blocked {selectedConnection.RemoteAddress}\" dir=in action=block remoteip={selectedConnection.RemoteAddress}";
|
||||
Process.Start(new ProcessStartInfo("cmd.exe", "/C " + command) { CreateNoWindow = true });
|
||||
MessageBox.Show($"Blocked IP: {selectedConnection.RemoteAddress}", "Firewall Rule Added", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void btnTheme_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Ensure the resources exist
|
||||
if (!Application.Current.Resources.Contains("DynamicBackgroundColor") ||
|
||||
!Application.Current.Resources.Contains("DynamicTextColor"))
|
||||
{
|
||||
MessageBox.Show("Theme resources are missing!", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
// Retrieve current background color
|
||||
SolidColorBrush currentBg = (SolidColorBrush)Application.Current.Resources["DynamicBackgroundColor"];
|
||||
|
||||
if (currentBg.Color == Colors.Black) // Dark Mode → Light Mode
|
||||
{
|
||||
Application.Current.Resources["DynamicBackgroundColor"] = new SolidColorBrush(Colors.White);
|
||||
Application.Current.Resources["DynamicTextColor"] = new SolidColorBrush(Colors.Black);
|
||||
btnTheme.Content = "🌙"; // Set icon to moon for dark mode
|
||||
}
|
||||
else // Light Mode → Dark Mode
|
||||
{
|
||||
Application.Current.Resources["DynamicBackgroundColor"] = new SolidColorBrush(Colors.Black);
|
||||
Application.Current.Resources["DynamicTextColor"] = new SolidColorBrush(Colors.White);
|
||||
btnTheme.Content = "🌞"; // Set icon to sun for light mode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ConnectionInfo : INotifyPropertyChanged
|
||||
{
|
||||
private Brush _backgroundColor;
|
||||
|
||||
public string Protocol { get; set; }
|
||||
public string LocalAddress { get; set; }
|
||||
public int LocalPort { get; set; }
|
||||
public string RemoteAddress { get; set; }
|
||||
public int RemotePort { get; set; }
|
||||
public string State { get; set; }
|
||||
public int ProcessId { get; set; }
|
||||
public string ProcessName { get; set; }
|
||||
public DateTime ConnectionTime { get; set; }
|
||||
public DateTime FirstConnectionTime { get; set; }
|
||||
public string ConnectionStatus { get; set; }
|
||||
public TimeSpan ConnectionDuration => DateTime.Now - FirstConnectionTime;
|
||||
public string FormattedConnectionDuration => ConnectionDuration.ToString(@"hh\:mm\:ss");
|
||||
|
||||
public Brush BackgroundColor
|
||||
{
|
||||
get { return _backgroundColor; }
|
||||
set
|
||||
{
|
||||
if (_backgroundColor != value)
|
||||
{
|
||||
_backgroundColor = value;
|
||||
OnPropertyChanged(nameof(BackgroundColor));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
protected virtual void OnPropertyChanged(string propertyName)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
}
|
||||
}
|
93
EonaCat.PortMonitor/Themes.xaml
Normal file
93
EonaCat.PortMonitor/Themes.xaml
Normal file
@@ -0,0 +1,93 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
|
||||
<!-- Global Styles -->
|
||||
<Style TargetType="Window">
|
||||
<Setter Property="Background" Value="{DynamicResource DynamicBackgroundColor}"/>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="Button">
|
||||
<Setter Property="Foreground" Value="{DynamicResource DynamicTextColor}"/>
|
||||
<Setter Property="Background" Value="{DynamicResource DynamicBackgroundColor}"/>
|
||||
<Setter Property="Padding" Value="5"/>
|
||||
<Setter Property="Margin" Value="5"/>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="Foreground" Value="{DynamicResource DynamicTextColor}"/>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="CheckBox">
|
||||
<Setter Property="Foreground" Value="{DynamicResource DynamicTextColor}"/>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="TextBox">
|
||||
<Setter Property="Foreground" Value="{DynamicResource DynamicTextColor}"/>
|
||||
<Setter Property="Background" Value="{DynamicResource DynamicBackgroundColor}"/>
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource DynamicTextColor}"/>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="DataGrid">
|
||||
<Setter Property="Foreground" Value="{DynamicResource DynamicTextColor}"/>
|
||||
<Setter Property="Background" Value="{DynamicResource DynamicBackgroundColor}"/>
|
||||
<Setter Property="RowBackground" Value="{DynamicResource DynamicBackgroundColor}"/>
|
||||
<Setter Property="SelectionMode" Value="Single" />
|
||||
<Setter Property="GridLinesVisibility" Value="All"/>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="DataGridColumnHeader">
|
||||
<Setter Property="Background" Value="{DynamicResource DynamicBackgroundColor}" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource DynamicTextColor}" />
|
||||
<Setter Property="Height" Value="30"/>
|
||||
<Setter Property="Padding" Value="5"/>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="DataGridCell">
|
||||
<Setter Property="Foreground" Value="{DynamicResource DynamicTextColor}" />
|
||||
<Setter Property="Background" Value="{DynamicResource DynamicBackgroundColor}" />
|
||||
<Setter Property="Padding" Value="5" />
|
||||
|
||||
<Setter Property="BorderBrush" Value="Transparent"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="True">
|
||||
<Setter Property="Background" Value="Green"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
|
||||
<!-- TabControl Theming -->
|
||||
<Style TargetType="TabControl">
|
||||
<Setter Property="Background" Value="{DynamicResource DynamicBackgroundColor}" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource DynamicTextColor}" />
|
||||
<Setter Property="Margin" Value="10" />
|
||||
<Setter Property="Padding" Value="5" />
|
||||
</Style>
|
||||
|
||||
<!-- TabItem Theming -->
|
||||
<Style TargetType="TabItem">
|
||||
<Setter Property="Background" Value="{DynamicResource DynamicBackgroundColor}" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource DynamicTextColor}" />
|
||||
<Setter Property="Height" Value="30"/>
|
||||
<Setter Property="Padding" Value="5"/>
|
||||
<Style.Triggers>
|
||||
<!-- Change color when TabItem is selected -->
|
||||
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="True">
|
||||
<Setter Property="Background" Value="{DynamicResource DynamicTextColor}" />
|
||||
<Setter Property="Foreground" Value="Green" />
|
||||
</DataTrigger>
|
||||
|
||||
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="True">
|
||||
<Setter Property="Background" Value="{DynamicResource DynamicTextColor}" />
|
||||
<Setter Property="Foreground" Value="Green" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="TabPanel">
|
||||
<Setter Property="Background" Value="{DynamicResource DynamicBackgroundColor}" />
|
||||
</Style>
|
||||
</ResourceDictionary>
|
BIN
EonaCat.PortMonitor/icon.ico
Normal file
BIN
EonaCat.PortMonitor/icon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 248 KiB |
BIN
EonaCat.PortMonitor/icon.png
Normal file
BIN
EonaCat.PortMonitor/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 88 KiB |
Reference in New Issue
Block a user