ObjBin Cleaner Service

Давно хотел сдеалать программку, которая бы на регулярной основе проверяла папку с проектами и удаляла оттуда автогенеренные папки типа obj и bin, дополнительно все, что нагенерит там ReSharper и папки TestResults. Для кого-то это может и не проблема, но при наличии в разработке нескольких достаточно крупных проектов это становится проблемой, все это может занимать до 2 гигов, к примеру, как у меня. Можете себе еще представить ситуацию, когда на одной машине работает несколько человек, у каждого своя папка с проектами и после завершения сеанса работы не происходит очистки этих папок. Как-то раз, чистя такие вот станции мы обнаружили, что в таких папках было 60 гигов! Таким образом полезность утилиты, которая бы все это каждодневно удаляла – очевидна.

Для этой цели я задумал использовать Windows Service. Некоторые говорят, что легче написать батничек, но я этой магией не владею и ковыряться не хотелось, Windows Service мне показалось более заманчивой перспективой в плане получения новых знаний.

Идея

Основная идея состояла в том, что у меня будет сам сервис и программа настройки работы сервиса. Настройки я решил хранить в обычном XML файле, чтобы можно было подредактировать все и в ручную, если понадобится.

Сам сервис будет знать в каких папках искать другие папки подходящие под заданные параметры поиска и удалять все что нашлось. Сам поиск будет происходить по расписанию. Расписание должно быть простое без всяких хитростей в духе «запускать каждый второй понедельник в декаде».

Подготовка

Создаем новое решение и добавляем туда первым делом проект Windows Service. Пока что можно оставить все как есть. Это так легко только если у вас Visual Studio Ultimate или Enterprise. В противном случае надо будет подключить ссылку на System.ServiceProcess, создать класс и наследовать его от ServiceBase.

Следующим шагом добавляем проект для графического интерфейса задания настроек. Это может быть как WinForms, так и WPF. Я остановился на втором варианте для большей практики. Хотя в данном случае никаких серьезных преимуществ это не дало.

Так как у нас будет общий файл настроек, то и сервис и гуй должны получать к нему доступ. Немного подумав, можно догадаться, что код будет один и тот же для обоих проектов, а это прямая дорога к выделению этого функционала в отдельную библиотеку. Сказано – сделано. Создаем еще проект для динамической библиотеки. В проекты для графической оболочки и для сервиса добаляем ссылки на этот проект.

Начальные данные

Хорошо было бы писать основной код, опираясь уже на известные начальные данные, чтобы не хардкодить их и понимать, что у нас будет в процессе работы, а что придется вычислять на ходу. Поэтому я предлагаю начать с общей библиотеки.

Так как настройки будут хранится в виде XML файла и исходя из природы этого файла, я решил начать с класса агрегата для всех настроек, который будет выступать в файле корневым элементом. Да и нам будет проще.

Помечаем аттрибутом Serializable, чтобы не возиться с сериализацией вручную.

Далее нам потребуются:

  • Списки директорий которые надо отследить,
  • Списки паттернов для поиска
  • Расписание

Классы RootFolder, SearchPattern и Schedule очень простые. Но это все равно лучше сделать классами, так удобнее будет расширять функционал программы, если мы что-то еще добавим к идее.

Не забываем пометить каждый класс как сериализуемый.

Похоже что теперь у нас есть необходимая структура данных  опираясь на которую мы можем разработать основной функционал.

Создание основного функционала

На мой взгляд, написание кода сразу в классе сервиса не очень хорошая идея, так как процесс дебага и исследования будет неоправдано затянут, да и сама процедура установки/удаления сервиса будет напрягать. Все что делает сервис, можно сначала написать в коде для интерфейса, а потом уже все готовое перенести. Я делал это именно так.

Сервис:

Собственно это и будет основным кодом. Естественно не в таком виде, лучше это разделить на небольшие функции, добавить везде логирования в EventLog, чтобы получилось в итоге в духе

Думаю это уже не большая проблема =)

Сохранение и чтение настроек

Для этой цели лучше всего создать класс со статическими методами, чтобы можно было обращаться к ним из любой области приложения. В нашем случае это нормально. Для такого маленького приложения не стоит использовать ServiceLocator или что-то подобное. OOP это не Over Object-oriented Programing.

Так как у нас все настройки содержатся в единственном классе, то он будет приниматся на вход для сохранения и использоваться на выходе для чтения.

Как видно из листинга мы воспользовались стандартным классом XmlSerializer чтобы получить файл XML. Нам это подходит как нельзя лучше. Читать и писать файл в таком случае одинаково легко.

Путь до файла определяется по пути выполняемого кода, что тоже в данном случае отлично сработает для Windows Service. Можно конечно искать в реестре по имени сервиса и оттуда получать путь до установленого сервиса и работать со строкой. Но это слишком много кода.

Теперь для того, чтобы получить настройки достаточно написать

Создание сервиса

По умолчанию в сервисе переопределены 2 метода

Нам надо еще будет переопределить метод OnContinue, для того чтобы реализовать поддержку приостановки сервиса. Хотя это и не обязательно.

Необходимо выделить весь основной код в метод (CleanProjectFolders), для того, чтобы можно было его использовать несколько раз, и прописываем его в методах OnStart & OnContinue.

В методе OnStart еще будет полезно создать EventLog для нашего сервиса, чтобы записывать туда когда, сколько и настколько успешно сервис удаляет заданные папки.

С EventLog работать очень просто, достаточно один раз его создать

Чтобы потом можно было его повсеместно использовать.

И не забывайте его очищать, а то при достижении определенного лимита, сервис будет падать с тем, что не сможет записать данные в лог.

Собственно с функциональностью сервиса все! Просто, да? Мм.. в программировании всё одновременно и просто и сложно.

Однако не все еще сделано для того, чтобы это стало сервисом. Самое простое, на мой взгляд, это сгенерировать сервис установщик для  этого сервиса.

Создание установщика для сервиса

Переходим к классу сервиса, открываем свойства и видим внизу ссылку «Add Installer». После чего появляется еще один класс. Открыв его видим следующую картину:

Выбираем для начала второй пункт, и смотрим опять в свойства:

Меняем Account на LocalService – это самое главное наверно, все остальное можно оставить по умолчанию.

Теперь жмем на serviceInstaller и там меняем свойства по своему усмотрению. Можно поменять имя сервиса и описание его. Остальное можно оставить как есть.

Сохраняем прокт, собираем, все должно собраться без ошибок.

Для того, чтобы установить сервис вручную, необходимо выполнить команду «C:\WINDOWS\Microsoft.NET\Framework\<версия фреймворка>\InstallUtil.exe C:\MyService.exe», для удаления, используйте ключ «/u».

Интерфейс

Интерфейс прост, как программа hello world. В данном случае я даже не стал заморачиваться с паттернами дизайна и просто все написал в code behind. И считаю что это правильно, если вы не согласны, то вы застряли в прошлом, когда платили за строчки кода ;)

Весь интерфейс состоит из одного окна и трех закладок. Я даже сомневаюсь, стоит ли упоминать здесь о том, как это все написано. Единственное что хочу сказать, необходимо запускать приложение с правами администратора, иначе не получится создавать новые файлы в папке с установленной программе, если она будет в Program Files.

Делается это добавление файла app.manifest к проекту (Add > New item) и модификация строчки

На

Это все находится в начале файла, если что.

О! Было бы неплохо знать, есть ли вообще сервис наш в системе и в каком он состоянии. Для того чтобы найти сервис, необходимо выполнить следующий код

Где serviceName и есть имя сервиса заданное на одной из предыдущих картинок выше. ServiceController.GetServices() находится в пространстве имен System.ServiceProcess. Больше ничего интересного на клиенте нет.

Установочный пакет

Для того чтобы создать установочный пакет силами самой студии, необходимо проделать следующие шаги.

Открываем решение, для которого необходимо создать установочный пакет. Добавляем новый проект.

После этого выделяем проект установки, находим иконку «Add custom actions» и жмем на нее. Или можно нажать правой кнопкой мыши на проект View > Custom actions.

В результате откроется окно, с действиями для установки, восстановления или удаления программы.

Опять выделяем корневой элемент в новом окне и нажимаем на нем правой кнопкой мыши. Нажимаем на единственный пункт меню, и появляется новое диалоговое окно. В нем, в выпадающем списке выбираем Application Folder.

Затем жмем на Add File или на Add Assembly, в зависимости от ситуации. Выбираем нужные файлы. После этого можно будет наблюдать примерно следующую картину.

Следующим шагом можно будет поменять названия которые будет видеть пользователь при установке программы. Можете задать имя программы, производителя и все в таком духе. Для этого в обозревателе проектов выделяем проект установщика и жмем на F4 (Свойства). Там меняем все в соответсвии с собственными представлениями о прекрасном =)

В качестве дополнительного удобства при использовании программы, можно добавить папку в меню Start. Для этого открываем окно с настройкам File System. Потом находим пункт User’s Program Menu. Опять с помощью контекстного меню создаем папку, называем ее созвучно сервису или как фантазия подскажет, затем выделяем вновь созданную папку и в панеле левее вызываем контектсное меню снова. Там находим пункт Create New Shortcut

Появляется новое диалоговое окно, где можно выбрать элемент, на который будет ссылка. Открываем Application Folder из выпадаещего списка вверху, и выбираем к чему у нас будет ярлык. Жмем ОК, и затем можно будет переименовать ссылку на что-то более удобоваримое и приятное глазу.

Для установки файлов можно задавать достаточно много других служебных директорий. Чтобы в них что-либо записать действуем по следующей схеме:

Переходим в окно настроек File System, жмем правой кнопкой мыши на корневой элемент File System on Target Machine, в контекстном меню выделяем единственный элемент и видим все многообразие системных папок. Но этим все не ограничивается, внизу виден пункт Custom Folder – а значит можно поставить что угодно, куда угодно.

Итак, компилируем проект и можно установить программу, прямо из студии. Для этого открываем обозреватель решений, находим там проект установщика, вызываем контекстное меню и выбираем пункт Install.

На этом все. Про работу с инсталлятором я расскажу как-нибудь позже.

Скачать готовую программу или исходный код

Hard’n’heavy!

2 комментарий на “ObjBin Cleaner Service

  1. Хорошая, годная статья.

    >И не забывайте его очищать, а то при достижении определенного лимита, сервис будет падать с тем, что не сможет записать данные в лог.

    Может все-таки прилепить еще автоматическую очистку? Чтобы совсем забыть о сервисе, который крутится и крутится себе.

    • Это было написано применительно к коду, чтобы программист очищал логи. Т.е. конечный пользователь не будет делать ничего.
      =)

Оставить комментарий