PortCheck/Form1.cs
2025-05-05 22:58:52 +07:00

425 lines
20 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Drawing;
using System.Windows.Forms;
using System.Net.NetworkInformation;
using System.Linq;
using Microsoft.Win32; // Для работы с реестром
using System.Diagnostics; // Для Process
using EthernetSpeedMonitor;
using System.IO;
namespace PortCheck
{
public partial class Form1 : Form
{
private static readonly string logFilePath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
"EthernetMonitor.log");
private static readonly string speedLogFilePath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
"EthernetSpeed.txt");
private void LogSpeedChange(string oldSpeed, string newSpeed, string interfaceName)
{
try
{
string logEntry = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss}: " +
$"Интерфейс: {interfaceName}, " +
$"Изменение скорости: {oldSpeed} -> {newSpeed}";
// Добавляем запись в файл логирования скорости
File.AppendAllText(speedLogFilePath, logEntry + Environment.NewLine);
// Также добавляем в общий лог
LogMessage($"Зафиксировано изменение скорости Ethernet: {oldSpeed} -> {newSpeed} на интерфейсе {interfaceName}");
}
catch (Exception ex)
{
// Логируем ошибку записи в основной лог
LogMessage($"Ошибка при записи в файл логирования скорости: {ex.Message}");
Debug.WriteLine($"Error writing to speed log: {ex.Message}");
}
}
private string previousSpeedInfo = string.Empty;
private void LogMessage(string message)
{
try
{
File.AppendAllText(logFilePath,
$"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}: {message}{Environment.NewLine}");
}
catch
{
// Игнорируем ошибки записи лога
}
}
// Метод для отображения неотложного уведомления
private void ShowUrgentNotification(string message, string title)
{
// Логируем сообщение
LogMessage($"ВАЖНО: {title} - {message}");
// Настраиваем и показываем уведомление
notifyIconMain.BalloonTipTitle = title;
notifyIconMain.BalloonTipText = message;
notifyIconMain.BalloonTipIcon = ToolTipIcon.Warning; // Используем иконку предупреждения
// Показываем на 10 секунд (10000 мс)
notifyIconMain.ShowBalloonTip(10000);
}
// Ключ реестра для автозапуска
private const string AutorunRegistryKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Run";
// Имя значения в реестре (используем имя приложения)
private readonly string AutorunValueName = Application.ProductName;
public Form1()
{
InitializeComponent();
}
// --- События формы ---
private void Form1_Load(object sender, EventArgs e)
{
try
{
// Скрываем форму при запуске
this.Visible = false;
this.Hide();
// Явно устанавливаем видимость иконки в трее
if (notifyIconMain.Icon == null)
{
// Попытка загрузить иконку из ресурсов
try
{
notifyIconMain.Icon = Properties.Resources.Network_37044; // Если иконка в ресурсах проекта
}
catch
{
// Если не получилось, используем значок приложения
notifyIconMain.Icon = this.Icon;
// Если и это не сработало, используем стандартную иконку
if (notifyIconMain.Icon == null)
notifyIconMain.Icon = SystemIcons.Application;
}
}
notifyIconMain.Visible = true;
// Первоначальная проверка скорости
UpdateNetworkSpeedInfo();
// Запускаем таймер
timerUpdate.Enabled = true;
LogMessage("Приложение запущено");
}
catch (Exception ex)
{
MessageBox.Show($"Ошибка при инициализации: {ex.Message}", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
// Скрытие окна вместо закрытия по кнопке "X" (если бы оно было видимым)
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
// Если закрытие инициировано пользователем (а не системой или Application.Exit())
// Можно добавить логику, чтобы не закрывать, а сворачивать,
// но т.к. форма невидима, лучше обработать выход через меню.
// Важно убрать иконку при реальном выходе!
if (e.CloseReason == CloseReason.UserClosing)
{
// Если хочешь, чтобы крестик просто сворачивал (не актуально для невидимой формы)
// e.Cancel = true;
// this.WindowState = FormWindowState.Minimized;
// this.Hide();
}
else // При Application.Exit() или выключении Windows
{
CleanupNotifyIcon();
}
}
// --- Логика получения скорости ---
private void UpdateNetworkSpeedInfo()
{
try
{
// Получаем информацию о скорости и имя интерфейса
var speedInfo = GetActiveEthernetSpeedInfo();
string currentSpeedInfo = speedInfo.Speed;
string interfaceName = speedInfo.InterfaceName;
// Проверяем, изменилась ли скорость
if (!string.IsNullOrEmpty(previousSpeedInfo) &&
!currentSpeedInfo.Equals(previousSpeedInfo, StringComparison.OrdinalIgnoreCase) &&
currentSpeedInfo != "Disconnected" && previousSpeedInfo != "Disconnected")
{
// Отправляем неотложное уведомление о смене скорости
ShowUrgentNotification(
$"Скорость порта изменилась с {previousSpeedInfo} на {currentSpeedInfo}",
"Изменение скорости Ethernet");
// Записываем изменение скорости в специальный лог
LogSpeedChange(previousSpeedInfo, currentSpeedInfo, interfaceName);
}
// Обновляем предыдущее значение скорости
previousSpeedInfo = currentSpeedInfo;
// Обновляем текст подсказки иконки
notifyIconMain.Text = $"Ethernet: {currentSpeedInfo}";
}
catch (Exception ex)
{
// Логируем ошибку
LogMessage($"Ошибка при обновлении информации о скорости: {ex.Message}");
Debug.WriteLine($"Error updating speed: {ex.Message}");
notifyIconMain.Text = "Ethernet: Error";
}
}
// Создаем структуру для возврата информации об интерфейсе и скорости
private struct EthernetSpeedInfo
{
public string Speed { get; set; }
public string InterfaceName { get; set; }
}
private EthernetSpeedInfo GetActiveEthernetSpeedInfo()
{
var result = new EthernetSpeedInfo
{
Speed = "Disconnected",
InterfaceName = "Unknown"
};
// Ищем активные Ethernet адаптеры
var activeEthernet = NetworkInterface.GetAllNetworkInterfaces()
.FirstOrDefault(ni =>
ni.NetworkInterfaceType == NetworkInterfaceType.Ethernet && // Тип Ethernet
ni.OperationalStatus == OperationalStatus.Up && // Статус "включен"
ni.Supports(NetworkInterfaceComponent.IPv4) && // Поддерживает IPv4
ni.GetIPProperties().GatewayAddresses.Any()); // Имеет шлюз
if (activeEthernet == null)
{
// Если не нашли по шлюзу, попробуем просто первый активный Ethernet
activeEthernet = NetworkInterface.GetAllNetworkInterfaces()
.FirstOrDefault(ni =>
ni.NetworkInterfaceType == NetworkInterfaceType.Ethernet &&
ni.OperationalStatus == OperationalStatus.Up);
}
if (activeEthernet != null)
{
// Сохраняем имя интерфейса
result.InterfaceName = activeEthernet.Name;
// Скорость в битах в секунду
long speedBps = activeEthernet.Speed;
if (speedBps <= 0)
{
result.Speed = "N/A"; // Скорость не определена
return result;
}
// Переводим в Мбит/с
long speedMbps = speedBps / 1_000_000;
// Простое округление до стандартных значений для наглядности
if (speedMbps >= 9500) result.Speed = "10 Gbps";
else if (speedMbps >= 4500) result.Speed = "5 Gbps";
else if (speedMbps >= 2000) result.Speed = "2.5 Gbps";
else if (speedMbps >= 900) result.Speed = "1 Gbps"; // Гигабит
else if (speedMbps >= 90) result.Speed = "100 Mbps"; // 100 Мегабит
else if (speedMbps >= 9) result.Speed = "10 Mbps"; // 10 Мегабит
else result.Speed = $"{speedMbps} Mbps"; // Если нестандартная скорость
}
return result;
}
private string GetActiveEthernetSpeed()
{
// Ищем активные Ethernet адаптеры
var activeEthernet = NetworkInterface.GetAllNetworkInterfaces()
.FirstOrDefault(ni =>
ni.NetworkInterfaceType == NetworkInterfaceType.Ethernet && // Тип Ethernet
ni.OperationalStatus == OperationalStatus.Up && // Статус "включен"
ni.Supports(NetworkInterfaceComponent.IPv4) && // Поддерживает IPv4 (часто хороший индикатор активности)
ni.GetIPProperties().GatewayAddresses.Any()); // Имеет шлюз (очень хороший индикатор основного подключения)
if (activeEthernet == null)
{
// Если не нашли по шлюзу, попробуем просто первый активный Ethernet
activeEthernet = NetworkInterface.GetAllNetworkInterfaces()
.FirstOrDefault(ni =>
ni.NetworkInterfaceType == NetworkInterfaceType.Ethernet &&
ni.OperationalStatus == OperationalStatus.Up);
}
if (activeEthernet != null)
{
// Скорость в битах в секунду
long speedBps = activeEthernet.Speed;
if (speedBps <= 0)
{
return "N/A"; // Скорость не определена
}
// Переводим в Мбит/с
long speedMbps = speedBps / 1_000_000;
// Простое округление до стандартных значений для наглядности
if (speedMbps >= 9500) return "10 Gbps";
if (speedMbps >= 4500) return "5 Gbps";
if (speedMbps >= 2000) return "2.5 Gbps";
if (speedMbps >= 900) return "1 Gbps"; // Гигабит
if (speedMbps >= 90) return "100 Mbps"; // 100 Мегабит
if (speedMbps >= 9) return "10 Mbps"; // 10 Мегабит
return $"{speedMbps} Mbps"; // Если нестандартная скорость
}
else
{
return "Disconnected"; // Не найдено активного Ethernet адаптера
}
}
// --- Обработчики событий компонентов ---
private void timerUpdate_Tick(object sender, EventArgs e)
{
UpdateNetworkSpeedInfo();
}
private void settingsToolStripMenuItem_Click(object sender, EventArgs e)
{
// Открываем форму настроек
using (SettingsForm settingsForm = new SettingsForm())
{
// Передаем текущие настройки в форму
settingsForm.StartWithWindows = IsAutoStartEnabled();
settingsForm.UpdateIntervalSeconds = timerUpdate.Interval / 1000; // Переводим мс в секунды
if (settingsForm.ShowDialog() == DialogResult.OK)
{
// Применяем настройки из формы
SetAutoStart(settingsForm.StartWithWindows);
timerUpdate.Interval = settingsForm.UpdateIntervalSeconds * 1000; // Переводим секунды в мс
}
}
}
private void checkNowToolStripMenuItem_Click(object sender, EventArgs e)
{
UpdateNetworkSpeedInfo(); // Выполняем проверку немедленно
}
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
// Корректный выход из приложения
CleanupNotifyIcon(); // Убираем иконку перед выходом
Application.Exit();
}
// Двойной клик по иконке тоже может открывать настройки
private void notifyIconMain_MouseDoubleClick(object sender, MouseEventArgs e)
{
settingsToolStripMenuItem_Click(sender, e);
}
// --- Логика автозапуска ---
private bool IsAutoStartEnabled()
{
try
{
using (RegistryKey key = Registry.CurrentUser.OpenSubKey(AutorunRegistryKey, false)) // false - только чтение
{
if (key == null) return false;
object value = key.GetValue(AutorunValueName);
// Проверяем, что значение не null и совпадает с путем к нашему приложению
return value != null && value.ToString().Equals(Application.ExecutablePath, StringComparison.OrdinalIgnoreCase);
}
}
catch (Exception ex)
{
Debug.WriteLine($"Error checking autorun: {ex.Message}");
return false; // В случае ошибки считаем, что автозапуск отключен
}
}
private void SetAutoStart(bool enable)
{
try
{
// Открываем ключ с правом записи
using (RegistryKey key = Registry.CurrentUser.OpenSubKey(AutorunRegistryKey, true))
{
if (key == null)
{
// Если ключа нет, возможно, его нужно создать (хотя обычно он есть)
// Registry.CurrentUser.CreateSubKey(AutorunRegistryKey); // Потребует повышенных прав? Скорее всего нет для HKCU
// key = Registry.CurrentUser.OpenSubKey(AutorunRegistryKey, true);
// Если создать не удалось или не хочется, просто выходим
Debug.WriteLine($"Registry key '{AutorunRegistryKey}' not found.");
MessageBox.Show($"Не удалось получить доступ к ключу реестра:\n{AutorunRegistryKey}\n\nАвтозапуск не может быть изменен.", "Ошибка реестра", MessageBoxButtons.OK, MessageBoxIcon.Error);
return; // Не можем продолжить
}
if (enable)
{
// Добавляем или обновляем значение
key.SetValue(AutorunValueName, Application.ExecutablePath);
}
else
{
// Удаляем значение, если оно существует
if (key.GetValue(AutorunValueName) != null)
{
key.DeleteValue(AutorunValueName, false); // false - не вызывать исключение, если значения нет
}
}
}
}
catch (UnauthorizedAccessException)
{
MessageBox.Show("Нет прав для изменения настроек автозапуска в реестре.", "Ошибка доступа", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
catch (Exception ex)
{
Debug.WriteLine($"Error setting autorun: {ex.Message}");
MessageBox.Show($"Произошла ошибка при изменении настроек автозапуска:\n{ex.Message}", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
// --- Очистка ---
private void CleanupNotifyIcon()
{
if (notifyIconMain != null)
{
notifyIconMain.Visible = false;
notifyIconMain.Dispose();
notifyIconMain = null; // Помогает сборщику мусора
}
}
}
}