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