19 августа 2019 г.

Windows Service создание, изменение и удаление службы из services.msc (на языке C#)

Консольная утилита sc.exe

В составе операционной системы Windows идёт утилита sc, которую можно без проблем вызвать в командной строке sc /?. С её помощью можно изменить любые свойства сервиса. Описание на сайте Microsoft [ссылка].  Примеры:
  1. Создание службы (sc create):
    sc create NewService DisplayName="New Service" binpath=c:\windows\system32\NewServ.exe type=share start=auto
  2. Изменение службы (sc config):
    sc config NewService binpath= "c:\windows\system32\NewServ.exe"
  3. Удаление службы (sc delete):
    sc delete newserv
Для работы с sc следует понимать одну вещь, что у службы есть идентификатор (Service Name) и имя службы (Display name), все изменения делаются используя идентификатор, который можно посмотреть в свойствах службы, пример:




Программа Service Manager


Для упрощение создания, изменения и удаления служб я написал небольшую программу под название Service Manager, которая в простом UI режиме даёт возможность делать то, что умеет sc утилита и даже больше.

Требование: 
Наличие установленного .NET Framework 4.5

Ссылка для скачивания собранной программы: [LINK1]

https://drive.google.com/drive/folders/1NXu810HDv3KYe3LcT8q2gZxQ9zLwuI_n?usp=sharing

Ссылка на исходный код, язык C#: [LINK2]

Для компиляции использовать Visual Studio 2017

Внешний вид окна:


Описание работы
  • В списке можно увидеть установленные службы.
  • При выделении службы, снизу можно видеть дополнительную информацию о ней.
  • Каждый столбец можно сортировать по имени нажимая на него.
  • Список служб и значения их полей обновляется каждые 3 секунды.

После выбора службы доступно контекстное меню:



  • Start - запуск выбранного сервиса
  • Start with arguments - запустить (единожды) службу и передать ей параметры в метод OnStart(string[] args), не путать с аргументами командной строки. (Данной возможности нет в service.msc)
  • Stop - попытка остановки сервиса, при этом он может перейти в состояние StopPadding и придётся вручную его убивать в диспетчере задач.
  • Terminate - принудительная остановка служб без ожидания её завершения. У запущенной службы находится её PID процесса и он убивается. При этом мы гарантированно останавливаем процесс. 
  • Create - диалог создания новой службы.
  • Edit - диалог редактирования выделенной службы.
  • Delete - удаление выделенной службы.
Далее будет демонстрация работу каждого функционала


Запуск сервиса (Start)



Остановка сервиса (Start with parameters)



Остановка сервиса (Stop)




Принудительная остановка сервиса (Terminate)



Создание службы (Create)



Редактирование службы (Edit)



Удаление службы (Delete)



Как это работает?


Работы с сервисами идёт через WinAPI функции реакция на изменение сервиса которыми происходит сразу после вызова, что не скажишь об изменении службы в реестре (для применения потребуется перезагрузка). В качестве UI представления используется WPF с шаблоном MVVM и объекты Prism, а для компиляции Visual Studio 2017. Список WinAPI функций:

/// <summary>
/// Windows native methods.
/// </summary>
internal class NativeMethods
{
    [DllImport("advapi32.dll", EntryPoint = "OpenSCManagerW", ExactSpelling = true, CharSet = CharSet.Unicode,
        SetLastError = true)]
    public static extern IntPtr OpenSCManager(
        string machineName,
        string databaseName,
        ScmAccessRights dwDesiredAccess);
 
    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern bool QueryServiceStatusEx(
        SafeHandle hService,
        int infoLevel,
        IntPtr lpBuffer,
        uint cbBufSize,
        out uint pcbBytesNeeded);
 
    [DllImport("kernel32.dll")]
    public static extern int GetLastError();
 
    [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "QueryServiceConfig2W")]
    public static extern Boolean QueryServiceConfig2(
        IntPtr hService,
        UInt32 dwInfoLevel,
        IntPtr buffer,
        UInt32 cbBufSize,
        out UInt32 pcbBytesNeeded);
 
    [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    [returnMarshalAs(UnmanagedType.Bool)]
    public static extern bool ChangeServiceConfig2(
        IntPtr hService,
        int dwInfoLevel,
        [MarshalAs(UnmanagedType.Struct)] ref ServiceDescriptionStruct lpInfo);
 
    [DllImport("advapi32.dll", SetLastError = true)]
    [returnMarshalAs(UnmanagedType.Bool)]
    public static extern bool CloseServiceHandle(IntPtr hScObject);
 
    [DllImport("advapi32.dll")]
    public static extern int ControlService(
        IntPtr hService,
        ServiceControl dwControl,
        ServiceStatus lpServiceStatus);
 
    // https://docs.microsoft.com/en-us/windows/desktop/api/winsvc/nf-winsvc-createservicea
    [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern IntPtr CreateService(
        IntPtr hScManager,
        string lpServiceName,
        string lpDisplayName,
        ServiceAccessRights dwDesiredAccess,
        ServiceType dwServiceType,
        ServiceBootFlag dwStartType,
        ServiceError dwErrorControl,
        string lpBinaryPathName,
        string lpLoadOrderGroup,
        IntPtr lpdwTagId,
        string lpDependencies,
        string lpServiceStartName,
        string lpPassword);
 
    [DllImport("advapi32.dll", SetLastError = true)]
    [returnMarshalAs(UnmanagedType.Bool)]
    public static extern bool DeleteService(IntPtr hService);
 
    [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern IntPtr OpenService(
        IntPtr hScManager,
        string lpServiceName,
        ServiceAccessRights dwDesiredAccess);
 
    [DllImport("advapi32.dll")]
    public static extern int QueryServiceStatus(IntPtr hService, ServiceStatus lpServiceStatus);
 
    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern int StartService(IntPtr hService, int dwNumServiceArgs, int lpServiceArgVectors);
 
    [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern bool ChangeServiceConfig(
        IntPtr hService,
        ServiceType nServiceType,
        ServiceBootFlag nStartType,
        ServiceError nErrorControl,
        string lpBinaryPathName,
        string lpLoadOrderGroup,
        int lpdwTagId,
        string lpDependencies,
        string lpServiceStartName,
        string lpPassword,
        string lpDisplayName);
 
    [DllImport("shell32.dll", SetLastError = true)]
    public static extern IntPtr CommandLineToArgvW(
        [MarshalAs(UnmanagedType.LPWStr)] string lpCmdLine,
        out int pNumArgs);
 
    [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern Boolean QueryServiceConfig(
        IntPtr hService,
        IntPtr intPtrQueryConfig,
        int cbBufSize,
        out int pcbBytesNeeded);
}

Пример работы с этими функциями можно найти в исходных кода программы, ссылку для скачивания

https://github.com/devowl/winservicemanager

2 комментария:

  1. Спасибо за программку. Отлично справляется со своими функциями.

    ОтветитьУдалить