Windows powershell - написання windows-служб в powershell
Продукти та технології:
Windows Services, Windows PowerShell, C #
У статті розглядаються:
- архітектура Windows-служб;
- управління Windows-службами;
- використання фрагментів коду на C # в скриптах Windows PowerShell;
- написання самокерованої служби в Windows PowerShell.
Windows-служби (Windows Services) - це зазвичай скомпільовані програми на C, C ++, C # або іншими мовами на основі Microsoft .NET Framework, і налагодження таких служб буває досить важким. Кілька місяців тому, надихнувшись тим, що інші операційні системи допускають написання служб як простих скриптів оболонки, я почав цікавитися, чи є і в Windows простіший спосіб їх створення.
У цій статті представлений кінцевий результат цих зусиль: новий і простий спосіб створення Windows-служб. Їх можна писати на скриптовій мовою в Windows PowerShell. Більше ніякої компіляції, просто швидкі цикли «правка-тест», які можна виконувати в будь-якій системі, а не тільки на комп'ютері розробника.
Що таке Windows-служба?
Windows-служби - це програми, які виконуються в тлі без взаємодії з користувачем. Наприклад, веб-сервер, який «мовчки» відповідає на HTTP-запити для веб-сторінок з мережі, є службою, так само як і моніторингове додаток, просто записуючий показники продуктивності або фіксує апаратні події від датчиків.
Служби можна запускати автоматично при завантаженні системи. Або запускати на вимогу, коли вони запитуються додатками, які покладаються на них. Служби виконуються у власному сеансі Windows, відмінному від UI-сеансу. Вони працюють в ряді системних процесів з ретельно вибраними правами для обмеження ризиків, пов'язаних з безпекою.
Windows Service Control Manager
Служби управляються Windows Service Control Manager (SCM). SCM відповідає за конфігурацію служб, їх запуск і зупинку і т. Д.
Панель управління SCM доступна через Control Panel | System and Security | Administrative Tools | Services. Як показано на рис. 1. вона відображає список всіх сконфігурованих служб з їх назвами, описами, станом, типом запуску і ім'ям користувача.

Мал. 1. Windows Service Control Manager GUI в Windows 10
Для SCM також є інтерфейси командного рядка.
- Стара утиліта net.exe з добре відомими командами net start і net stop, корінням йде аж в MS-DOS! Незважаючи на таку назву, її можна використовувати для запуску і зупинки будь-якої служби, а не тільки мережевих служб. Введіть net help, щоб отримати докладний опис.
- Більш потужна утиліта, sc.exe, введена в Windows NT, дає тонкий контроль над усіма аспектами управління службами. Введіть sc /. щоб отримати докладний опис.
Ці утиліти командного рядка, хоч і присутні в Windows 10, тепер вважаються застарілими, і рекомендується використовувати функції управління службами Windows PowerShell, як буде описано далі.
Підступ Як net.exe, так і sc.exe використовують «короткі» імена служб в одне слово, які, на жаль, не збігаються з більш описовими назвами, відображеними в панелі управління SCM. Щоб отримати відповідність між двома іменами, використовуйте команду get-service з Windows PowerShell.
стану служби
Служби можуть перебувати в різноманітних станах. Деякі стану обов'язкові, інші не обов'язкові. Два базових стану, які повинні підтримуватися всіма службами: зупинена (stopped) і запущена (started). Ці стану відповідно відображаються як порожня клітинка або Running (Виконується) в стовпці Status на рис. 1.
Третє, не обов'язкова стан - Paused (Припинено). І ще одне неявне стан, підтримуване кожною службою, навіть якщо воно ніде не згадується, - Uninstalled (Вилучена).
Служба може переходити між цими станами, як показано на рис. 2.
Мал. 2. Стани служби
Нарешті, існує кілька перехідних станів, які служби можуть підтримувати (не обов'язково): StartPending, StopPending, PausePending, ContinuePending. Вони корисні, тільки якщо переходи між станами займають чимало часу.
Функції управління службами в Windows PowerShell
- узгоджені імена функцій;
- повністю об'єктно-орієнтована;
- просте управління будь-яким .NET-об'єктом.
Windows PowerShell надає багато функцій управління службами, відомих як командлети (cmdlets). Деякі приклади показані в табл. 1.
Табл. 1. Функції управління службами в Windows PowerShell
Реєструє виконуваний файл служби в SCM
Реалізуючи ці методи, служба буде керованою SCM і зможе запускатися автоматично при завантаженні системи або на вимогу; крім того, таку службу можна буде запускати або зупиняти вручну через панель управління SCM, старими командами net.exe / sc.exe або новими функціями Windows PowerShell для керування службами.
Всі служби повинні створювати .NET-об'єкт, похідний від класу ServiceBase.
Створення виконуваного файлу з вихідного коду на C #, вбудованого в скрипт Windows PowerShell
PowerShell спрощує використання .NET-об'єктів в скриптах. За замовчуванням пропонується вбудована підтримка багатьох типів .NET-об'єктів, достатня для більшості завдань. Ще краще те, що вона є розширюваної і дозволяє вбудовувати короткі фрагменти C #-коду в скрипт Windows PowerShell для додавання підтримки будь-якої іншої .NET-функціональності. Ця можливість забезпечується командою Add-Type, яка, незважаючи на свою назву, може робити набагато більше, ніж просто додавати підтримку нових типів .NET-об'єктів в Windows PowerShell. Вона дозволяє навіть компілювати і пов'язувати повне C #-додаток в новий виконуваний файл. Наприклад, наступний скрипт Windows PowerShell, hello.ps1:
Збираємо все воєдино
Можливості PSService.ps1 На основі усього того, що ми обговорили на даний момент, я можу створити ту службу Windows PowerShell, про яку я мріяв, - скрипт PSService.ps1, який:
- може сам себе встановлювати і видаляти (використовуючи функції Windows PowerShell для керування службами);
- може сам себе запускати і зупиняти (використовуючи той же набір функцій);
- включає короткий фрагмент C #-коду, який створює PSService.exe для SCM (за допомогою команди Add-Type);
- робить зворотний виклик з заглушки PSService.exe в скрипт PSService.ps1 для виконання реальної операції служби (у відповідь на події OnStart, OnStop і ін.);
- управляється з панелі SCM і всіх утиліт командного рядка (завдяки заглушці PSService.exe);
- є ВІДМОВОСТІЙКО і успішно обробляє будь-яку команду, перебуваючи в будь-якому стані. Наприклад, він може автоматично зупинити службу перед її видаленням або нічого не робити, якщо йому надійде запит запустити вже виконувану службу;
- підтримує Windows 7 і все більш пізні версії Windows (використовуючи функціональність тільки Windows PowerShell v2).
Зауважте, що в цій статті я торкнувся лише критично важливі частини проекту і реалізації PSService.ps1. Скрипт-приклад також містить оцінний код і в якійсь мірі підтримує необов'язкову функціональність служб, але їх опис тут ускладнило б пояснення без будь-якої необхідності.
Архітектура PSService.ps1 Скрипт організовується в серію розділів:
Глобальні змінні
Безпосередньо за блоком Param скрипт PSService.ps1 містить глобальні змінні, що визначають загальні налаштування, які при необхідності можна змінювати. Їх значення за замовчуванням наведені в табл. 3.
Табл. 3. Значення за замовчуванням глобальних змінних
Значення за замовчуванням
Однослівне ім'я, яке використовується командами net start і іншими
Базове ім'я скрипта
Більш описову назву служби
Sample PowerShell Service
Куди встановлюються файли служби
Файл, в який записуються повідомлення служби
Ім'я журналу подій, в який записуються події служби
Аргументи командного рядка
Щоб спростити використання, скрипт підтримує аргументи командного рядка, які відповідають переходам у їхні капітали, як показано в табл. 4.
Табл. 4. Аргументи командного рядка для переходів станів
За керування службами відповідає SCM. Кожна операція запуску повинна проходити через SCM, щоб він міг відстежувати стану служб. Тому, навіть якщо користувач хоче вручну ініціювати запуск через скрипт служби, цей запуск повинен бути виконаний як запит до SCM. У цьому випадку послідовність операція перерахована нижче.
- Користувач (адміністратор) запускає перший екземпляр: PSService.ps1 -Start.
- Цей перший екземпляр повідомляє SCM запустити службу: Start-Service $ serviceName.
- SCM виконує PSService.exe. Її процедура Main створює об'єкт служби, а потім викликає його метод Run.
- SCM викликає метод OnStart об'єкта служби.
- C # -метод OnStart запускає другий примірник скрипта: PSService.ps1 -Start.
- Цей другий примірник, тепер виконується в тлі як системний користувач, запускає третій примірник, який залишиться в пам'яті як справжня служба: PSService.ps1 -Service. Цей екземпляр і працює як служба.
В результаті будуть виконуватися два завдання: PSService.exe і екземпляр PowerShell.exe, що виконує PSService.ps1 -Service.
За керування службами відповідає SCM.
Все це реалізується трьома частинами коду в скрипті.
- Визначення ключа -Start в блоці Param на початку скрипта:
- У процедурі Main в кінці скрипта блок if обробляє ключ -Start:
- Фрагмент вихідного коду на C #, процедура Main і обробник для OnStart, який виконує команду PSService.ps1 -Start, як показано на рис. 4.
Мал. 4. Оброблювач стартового коду
Отримання стану служби
Оброблювач -Status просто запитує у SCM стан служби та передає його в конвеєр виведення:
Але на етапі налагодження ви можете зіткнутися зі збоями скрипта, наприклад через синтаксичних помилок в скрипті і т. П. У таких випадках стан SCM може виявитися в кінцевому рахунку некоректним. Я потрапляв у таку ситуацію кілька разів, коли готував цю статтю. Щоб допомогти в діагностиці такого роду проблем, доцільно все перевіряти ще раз і провести пошук примірників -Service:
Зупинка і видалення
Операції Stop і Remove, по суті, скасовують то, що було зроблено операціями Start і Setup відповідно:
- -Stop (при запуску користувачем) повідомляє SCM зупинити службу;
- при запуску системою екземпляр PSService.ps1 -Service просто знищується;
- -Remove зупиняє службу, скасовує її реєстрацію, використовуючи sc.exe delete $ serviceName, а потім видаляє файли в каталозі установки.
Крім того, їх реалізація дуже схожа на таку для Setup і Start.
- Визначення кожного ключа в блоці Param на початку скрипта.
- Блок if обробляє ключ в процедурі Main в кінці скрипта.
- Для операції Stop в C #-фрагменти є обробник для OnStop, який запускає PSService.ps1 -Stop. Операція Stop працює по-різному в залежності від типу користувача - реального або системного.
Запис подій у журнал
Служби виконуються у фоновому режимі без UI. Це ускладнює їх налагодження: як діагностувати, що саме дає збої, коли за своєю природою вони нічого не відображають? Звичайний метод - вести запис всіх повідомлень про помилки з тимчасовими мітками, а також протоколювати важливі події, що пройшли успішно, наприклад переходи станів.
Служби виконуються у фоновому режимі без UI. Це ускладнює їх налагодження: як діагностувати, що саме дає збої, коли за своєю природою вони нічого не відображають?
Скрипт-приклад PSService.ps1 реалізує два різних методу протоколювання і використовує їх обох в стратегічно важливих точках (включаючи раніше показані фрагменти коду, де ці методи були прибрані, щоб зробити зрозумілішими базові операції).
- Один з методів записує в журнал Application об'єкти подій з ім'ям служби в якості імені джерела, як показано на рис. 5. Ці об'єкти подій видно в Event Viewer, і їх можна фільтрувати і шукати, використовуючи всі засоби цієї утиліти. Ви також можете отримати ці записи за допомогою командлета Get-Eventlog:
адаптація служби
Щоб створити власну службу, просто зробіть наступне.
- Скопіюйте службу-приклад в новий файл з новим базовим ім'ям, наприклад C: \ Temp \ MyService.ps1.
- Змініть довгу назву служби в розділі глобальних змінних.
- Замініть блок TO DO в обробнику -Service в кінці скрипта. На даний момент блок while ($ true) просто містить модельний код, який пробуджується через кожні 10 секунд і записує в файл журналу одне повідомлення:
Детальнішу інформацію. В урізанні «Посилання».
Очевидно, такий скрипт служби, як цей, не може зрівнятися за продуктивністю з компілювати програмою. Скрипт служби, написаний в Windows PowerShell, дуже зручний для перевірки концепції та для завдань з низькими витратами щодо продуктивності на кшталт моніторингу системи, кластеризації служб і т. Д. Але для будь-якої високопродуктивної завдання рекомендується переписати службу на C ++ або C #.
Крім того, обсяг займаної пам'яті перевищує такий у компільованою програми, так як вимагає завантаження повнофункціонального інтерпретатора Windows PowerShell в сеансі System. Але в сучасних комп'ютерах з багатьма гігабайтами пам'яті це не має особливого значення.
Цей скрипт не має абсолютно ніякого відношення до Ps-Service.exe від Марка Руссиновича (Mark Russinovich). Я вибрав ім'я PSService.ps1 до того, як дізнався про збіг імен. В кінцевому рахунку я зберіг ім'я скрипта-прикладу таким, оскільки воно робить зрозумілим призначення скрипта. Звичайно, якщо ви плануєте поекспериментувати зі своєю службою на основі Windows PowerShell, то повинні перейменувати його, щоб отримати унікальне ім'я служби з унікального базового імені скрипта!
Висловлюю подяку за рецензування статті експерту JDH IT Solutions Джеффрі Хиксу (Jeffery Hicks).
I had made a windows service following this article. All works fine and I felt like I'm a really programmer :). But I have a problem - this service consumes too much memory. In fact, for about 6 hours service consumed 3 Gb memory and I think it's no.
Looks good, runs great, and yes, I run it under a different service account, with no bad side effects.
So you wrote a sample service that is 1000 lines long. Thanks, but I'll skip.
I recently used this as the basis for a customer need, and really appreciated this. I needed to make a slight modification to allow the service to run under a service account other than localsystem, in order to perform some network operations, som.
Interesting article. Is there any article that shows how Powershell can be used to call a REST based service?
This article presents a novel and easy way to create Windows services, by writing them in the PowerShell scripting language rather than C # or C ++. No more compilation, just a quick edit / test cycle that can be done on any system. Read this article in.