Как сделать службу Windows с параметрами?

Я написал службу Windows, и я хочу, чтобы у каждого клиента был запущен 1 экземпляр. Это связано с тем, что у каждого клиента есть собственная БД с идентичными схемами; единственная разница между службами Windows заключается в том, что у каждой из них будет свой параметр, соответствующий клиентской БД, которую они должны обслуживать. (И у меня не может быть одной службы с несколькими рабочими потоками, потому что соединение с БД использует статическую переменную, с которой я не могу возиться в разных потоках.)

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

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

Как ты делаешь это?


person Shaul Behr    schedule 11.02.2010    source источник
comment
Я удаляю свой ответ, есть люди, которые знают об этом больше, чем я.   -  person Binary Worrier    schedule 11.02.2010
comment
@Binary Worrier - Жаль, что ты не удалил свой ответ! Там были некоторые идеи, которые были очень многообещающими...   -  person Shaul Behr    schedule 11.02.2010
comment
Ответ восстановлен: я пометил его как CW, он неполный, у нас где-то есть шпаргалка с инструкциями по запуску нескольких экземпляров одной и той же службы, но я не могу ее найти и у меня нет времени копаться, извините Шауль.   -  person Binary Worrier    schedule 11.02.2010


Ответы (5)


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

Сказав это, кажется, что было бы проще (и удобнее в сопровождении) изменить код подключения к базе данных, чтобы он мог поддерживать несколько рабочих потоков.

person Jeff Sternal    schedule 11.02.2010
comment
+1 за ссылку, еще раз +1, если можно, за совет иметь несколько рабочих потоков в одном сервисе. У вас всегда может быть графический интерфейс, который удаленно подключается к службе и показывает информацию о запущенных потоках. - person Binary Worrier; 11.02.2010

Все, что я хотел, это отправить один параметр в сервис, который я создал. Как оказалось, все, что вам нужно сделать, это (осторожно!) отредактировать реестр в HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\ и добавить параметр в ImagePath после кавычек.

Например. Данные значения ImagePath: "C:\Program Files\myservice\myservice.exe" param1

Я нашел решение по этой ссылке http://social.msdn.microsoft.com/Forums/is/csharpgeneral/thread/38242afa-7e40-4c06-975e-aa97d3cc782f

person Ricardo Appleton    schedule 07.06.2011

В основном вам нужно установить службу несколько раз и настроить ее с помощью файла exe.config.

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

Обновить

exe.Config — это файл конфигурации приложения.

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

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

person Community    schedule 11.02.2010
comment
В учебнике, на который я ссылался выше, есть класс MyWindowsServiceInstaller, который выполняет установку. Похоже, это должно быть место для настройки цикла для всех клиентов, но я вообще не вижу места для настройки параметров командной строки для каждой службы. Как настроить параметры? простите за невежество, а что такое exe.config? - person Shaul Behr; 11.02.2010
comment
обновил мой вопрос, чтобы прояснить некоторые моменты, которые вы упомянули. - person Shaul Behr; 11.02.2010

Насколько мне известно, невозможно указать параметры запуска, используя ServiceInstaller, ServiceProcessInstaller или installutil. Однако можно указать параметры запуска, используя некоторые COM API из advapi.dll (проверьте левое меню) . Полный набор необходимых вызовов можно найти по адресу здесь. Это класс (также) с именем ServiceInstaller, который содержит необходимые внешние методы и некоторые служебные методы.

Вы бы хотели использовать служебный метод InstallAndStart. Он принимает имя службы, отображаемое имя и путь к исполняемому файлу, который представляет вашу службу Windows. Вы можете назвать это так:

InstallAndStart("MyService", "My Service For User 1",
                "c:\\pathtoexe\MyService.exe user1");

Если у вас есть следующий сервис, параметр startupParam получит значение user1.

class Program : ServiceBase
{
    private string startupParam;

    static void Main(string[] args)
    {
        string arg = args[0];
        ServiceBase.Run(new Program(arg));
    }

    public Program(string startupParam)
    {
        this.ServiceName = "MyService";
        this.startupParam = startupParam;
    }
    ...
}
person Ronald Wildenberg    schedule 11.02.2010
comment
ммм... только что попробовал, и не похоже, что installutil позволяет передавать параметры...? Я что-то пропустил? - person Shaul Behr; 11.02.2010
comment
Хм, кажется, я не прав. Извините за это и забудьте о части installutil. Я сам использую собственный класс ServiceInstaller, который использует COM API для запуска и остановки служб. Этот класс позволяет передавать параметры, и я думал, что installutil сделает то же самое. Я обновлю свой ответ... - person Ronald Wildenberg; 11.02.2010
comment
Рональд, у меня есть клиент, которому требуется служба Windows для выполнения действий в системе с использованием учетной записи SYSTEM, потому что у моего клиента нет разрешений. Если я хочу, чтобы клиент сообщал службе Windows, что делать, должен ли я каждый раз останавливаться и запускаться, передавая такие аргументы? Я нашел функцию OnCustomCommand, но она бесполезна, потому что вы можете передавать только целые числа. Или я должен использовать базу данных или ключи реестра для временной установки аргументов в зависимости от запущенного клиента? - person MacGyver; 27.01.2016
comment
В этом случае я бы использовал WCF для предоставления конечной точки в вашей службе Windows, которая может получать команды от вашего клиента. - person Ronald Wildenberg; 27.01.2016

Вы можете передать параметры вашему установщику, используя installutil, например, ServiceName и DisplayName.

ProjectInstaller.cs

public partial class ProjectInstaller : Installer
{
    protected override void OnBeforeInstall(IDictionary savedState)
    {
        SetServiceName();
        base.OnBeforeInstall(savedState);
    }

    protected override void OnBeforeUninstall(IDictionary savedState)
    {
        SetServiceName();
        base.OnBeforeUninstall(savedState);
    }

    private string AppendParameter(string path, char parameter, string value)
    {
        if (!path.StartsWith("\""))
            path = $"\"{path}\"";

        if (value.Contains(" "))
            value = $"\"{value}\"";

        return $"{path} -{parameter}{value}";
    }

    private void SetServiceName()
    {
        if (Context.Parameters.ContainsKey("ServiceName"))
            serviceInstaller.ServiceName = Context.Parameters["ServiceName"];

        if (Context.Parameters.ContainsKey("DisplayName"))
            serviceInstaller.DisplayName = Context.Parameters["DisplayName"];

        Context.Parameters["assemblypath"] = AppendParameter(Context.Parameters["assemblypath"], 's', serviceInstaller.ServiceName);
    }
}

Это добавит параметр к пути, хранящемуся в службе, например:

Раньше: "C:\Service.exe"

После: "C:\Service.exe" -s"Экземпляр 1"

Затем вы можете прочитать этот параметр при запуске службы и перейти к конструктору служб.

Program.cs

static void Main(string[] args)
{
    string serviceName = args.Single(x => x.StartsWith("-s")).Substring("-s".Length);

    ServiceBase service = new Service(serviceName);
    ServiceBase.Run(service);
}

Service.cs

public partial class Service : ServiceBase
{
    public Service(string serviceName)
    {
        InitializeComponent();

        ServiceName = serviceName;
    }
}

Использование

installutil /ServiceName="Instance 1" /DisplayName="Instance 1 Service" "C:\Service.exe"
installutil /ServiceName="Instance 2" /DisplayName="Instance 2 Service" "C:\Service.exe"
person Derrick Moeller    schedule 26.09.2018