425 lines
20 KiB
C#
425 lines
20 KiB
C#
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; // Помогает сборщику мусора
|
||
}
|
||
}
|
||
}
|
||
} |