Введение
Однажды я заметил, что большая часть рабочего/учебного времени за компьютером проходит незаметно. Кажется, что ты «немного посидел» в IDE, «на пару минут» открыл браузер, а в итоге половина дня куда-то исчезла.
Существующие решения для отслеживания активности есть, но мне было интересно не просто установить готовую программу, а разобраться, как подобный инструмент работает изнутри.
Так появился проект ProcessTimeWatcher — небольшое WPF-приложение на C#, которое отслеживает активное окно пользователя и показывает, сколько времени было проведено в разных приложениях.
В этой статье расскажу, как устроено приложение, какие проблемы возникли во время разработки и чему удалось научиться.
Идея проекта
Основная задача была простой:
Определять, какое приложение сейчас находится в фокусе, и считать время его использования.
Например:
Visual Studio 02:34:12Google Chrome 01:20:45Telegram 00:45:10Spotify 00:30:05
Но за этой простой задачей скрывается несколько проблем:
-
Windows не предоставляет готовый удобный список «текущего активного приложения»;
-
название процесса не всегда совпадает с названием программы;
-
некоторые процессы нельзя прочитать без дополнительных прав;
-
нужно правильно учитывать время переключения между окнами.
Используемые технологии
В проекте используются:
-
C#
-
WPF (.NET)
-
Windows API
-
Process API
-
XAML для интерфейса
Почему именно WPF?
Для небольшого Windows-приложения WPF оказался удобным вариантом:
-
простой интерфейс через XAML;
-
хорошая работа с окнами Windows;
-
возможность использовать нативные возможности платформы;
-
быстрый результат без лишнего количества зависимостей.
Как приложение получает активное окно
Главная часть программы — определение окна, которое сейчас находится в фокусе.
Для этого используются функции Windows API:
[DllImport("user32.dll")]private static extern IntPtr GetForegroundWindow();[DllImport("user32.dll")]private static extern uint GetWindowThreadProcessId(IntPtr hWnd,out uint processId);
Она возвращает дескриптор активного окна.
После этого через:
GetWindowThreadProcessId()
можно получить идентификатор процесса.
Примерный алгоритм:
Пользователь переключился на приложение | vПолучаем активное окно | vПолучаем ID процесса | vПолучаем информацию о процессе | vДобавляем время активности
Получение информации о приложении
Первоначально я использовал имя процесса.
Например:
Code.exedevenv.exechrome.exe
Но быстро стало понятно, что этого недостаточно.
Пользователь видит не:
devenv
а:
Visual Studio
Поэтому пришлось получать дополнительную информацию:
process.MainModule.FileVersionInfo.FileDescription
Она позволяет получить более привычное название приложения.
Visual Studio
Подсчёт времени
Изначально время отображалось просто:
61 сек
Но такой вариант быстро стал неудобным.
Поэтому формат был изменён:
00:01:01
где:
-
первые два значения — часы;
-
следующие — минуты;
-
последние — секунды.
Так статистика стала гораздо удобнее для чтения.
Работа с историей активности
Каждое приложение получает собственный счётчик.
Например:
{"Visual Studio": 5400, "Chrome": 1800, "Telegram": 600}
При переключении окна:
-
определяется новое приложение;
-
предыдущему добавляется время;
-
запускается отслеживание нового процесса.
Экспорт отчётов
Одной из дополнительных функций стал экспорт результатов.
Сейчас можно сохранить отчёт для дальнейшего анализа.
Идея была сделать данные удобными не только внутри приложения, но и вне его. Большую часть всей информации я храню в Obsidian, поэтому было логично сделать экспорт отчета именно под Markdown файлы. По итогу я добился создание нового .md файла и/или возможность записать данные в уже существующий файл.
Это позволило:
-
хранить историю в Obsidian;
-
анализировать свою продуктивность.
Интерфейс приложения
Основной интерфейс показывает:
-
список приложений;
-
потраченное время;
-
состояние отслеживания;
-
кнопки управления.
В будущем планируется:
-
графики активности;
-
статистика по дням;
-
категории приложений.
P.S. Для создания интерфейса я использовал помощь ИИ при разработке концепции дизайна, после чего адаптировал результат под нужды приложения.
Что оказалось сложнее, чем казалось
1. Получение информации о процессах
На первый взгляд кажется:
«Ну просто получить название программы»
Но на практике:
-
некоторые процессы защищены;
-
некоторые не имеют нормального описания;
-
некоторые запускаются через дополнительные оболочки.
2. Правильный подсчёт времени
Сначала приложение больше напоминало секундомер.
Оно просто увеличивало значение.
Но настоящий трекер времени должен учитывать:
-
переключения;
-
паузы;
-
закрытие приложения;
-
перезапуск программы.
3. Разница между процессом и приложением
Windows мыслит процессами.
Пользователь мыслит программами.
Это две разные вещи.
Например:
chrome.exechrome.exechrome.exe
может означать несколько вкладок и процессов одного браузера.
Что дальше
План развития ProcessTimeWatcher:
-
автозапуск вместе с Windows;
-
сохранение истории между запусками;
-
графики активности;
-
система целей;
-
категории приложений;
-
уведомления при слишком долгом использовании программы;
-
улучшение определения названий приложений;
-
Работа в фоновом режиме
-
Создание таймера, который будет следить, чтобы пользователь оставался в выбранном приложении и напоминал, если пользователь на что-то отвлекаться.
Итоги
ProcessTimeWatcher начинался как небольшая идея:
«А сколько времени я реально провожу в программах?»
Но в процессе разработки он превратился в полноценный учебный проект, который позволил разобраться с:
-
Windows API;
-
процессами;
-
WPF;
-
обработкой событий;
-
работой с пользовательским интерфейсом.
Главный вывод после разработки:
Даже простая идея может оказаться намного сложнее внутри, чем кажется снаружи.
Именно такие проекты лучше всего показывают, как работает разработка настоящего программного обеспечения.
Исходный код проекта доступен на GitHub.
Скачать последнюю актуальную версию приложения можно тут.
Сейчас ProcessTimeWatcher остаётся небольшим open-source проектом, но уже позволил мне лучше понять взаимодействие C# с Windows API и работу приложений под Windows. В дальнейшем планирую развивать проект и добавлять новые возможности.
ссылка на оригинал статьи https://habr.com/ru/articles/1059148/