Установить «Параметры запуска» при установке службы с помощью .Net ServiceInstaller? - PullRequest
9 голосов
/ 17 марта 2009

В настоящее время я пишу небольшое приложение-службу Windows и могу успешно удалить / удалить его и т. Д. Примерно так:

        serviceProcessInstaller = new ServiceProcessInstaller();
        serviceInstaller = new System.ServiceProcess.ServiceInstaller();
        serviceProcessInstaller.Account = ServiceAccount.LocalSystem;
        serviceInstaller.ServiceName = "ABC";
        serviceInstaller.StartType = ServiceStartMode.Automatic;
        serviceInstaller.Description = "DEF";
        Installers.AddRange(new Installer[] { serviceProcessInstaller, serviceInstaller });

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

Ответы [ 6 ]

7 голосов
/ 18 марта 2009

Параметры могут быть установлены с помощью P / Invoking API ChangeServiceConfig. Они появляются после указанного в кавычках пути и имени файла для вашего исполняемого файла в аргументе lpBinaryPathName.

Параметры будут доступны вашему сервису при запуске через метод Main:

static void Main(string[] args)

(Main обычно находится в файле Program.cs).

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

using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration.Install;
using System.ComponentModel;
using System.Configuration.Install;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.ServiceProcess;
using System.Text;

namespace ServiceTest
{
    [RunInstaller(true)]
    public class ProjectInstaller : Installer
    {
        private string _Parameters;

        private ServiceProcessInstaller _ServiceProcessInstaller;
        private ServiceInstaller _ServiceInstaller;

        public ProjectInstaller()
        {
            _ServiceProcessInstaller = new ServiceProcessInstaller();
            _ServiceInstaller = new ServiceInstaller();

            _ServiceProcessInstaller.Account = ServiceAccount.LocalService;
            _ServiceProcessInstaller.Password = null;
            _ServiceProcessInstaller.Username = null;

            _ServiceInstaller.ServiceName = "Service1";

            this.Installers.AddRange(new System.Configuration.Install.Installer[] {
                _ServiceProcessInstaller,
                _ServiceInstaller});

            _Parameters = "/ThisIsATest";
        }

        public override void Install(IDictionary stateSaver)
        {
            base.Install(stateSaver);

            IntPtr hScm = OpenSCManager(null, null, SC_MANAGER_ALL_ACCESS);
            if (hScm == IntPtr.Zero)
                throw new Win32Exception();
            try
            {  
                IntPtr hSvc = OpenService(hScm, this._ServiceInstaller.ServiceName, SERVICE_ALL_ACCESS);
                if (hSvc == IntPtr.Zero)
                    throw new Win32Exception();
                try
                {
                    QUERY_SERVICE_CONFIG oldConfig;
                    uint bytesAllocated = 8192; // Per documentation, 8K is max size.
                    IntPtr ptr = Marshal.AllocHGlobal((int)bytesAllocated); 
                    try
                    {
                        uint bytesNeeded;
                        if (!QueryServiceConfig(hSvc, ptr, bytesAllocated, out bytesNeeded))
                        {
                            throw new Win32Exception();
                        }
                        oldConfig = (QUERY_SERVICE_CONFIG) Marshal.PtrToStructure(ptr, typeof(QUERY_SERVICE_CONFIG));
                    }
                    finally
                    {
                        Marshal.FreeHGlobal(ptr);
                    }

                    string newBinaryPathAndParameters = oldConfig.lpBinaryPathName + " " + _Parameters;

                    if (!ChangeServiceConfig(hSvc, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE,
                        newBinaryPathAndParameters, null, IntPtr.Zero, null, null, null, null))
                        throw new Win32Exception();
                }
                finally
                {
                    if (!CloseServiceHandle(hSvc))
                        throw new Win32Exception();
                }
            }
            finally
            {
                if (!CloseServiceHandle(hScm))
                    throw new Win32Exception();
            }
        }

        [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
        private static extern IntPtr OpenSCManager(
            string lpMachineName,
            string lpDatabaseName,
            uint dwDesiredAccess);

        [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
        private static extern IntPtr OpenService(
            IntPtr hSCManager,
            string lpServiceName,
            uint dwDesiredAccess);

        [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
        private struct QUERY_SERVICE_CONFIG {
            public uint dwServiceType;   
            public uint dwStartType;
            public uint dwErrorControl;
            public string lpBinaryPathName;
            public string lpLoadOrderGroup;
            public uint dwTagId;
            public string lpDependencies;
            public string lpServiceStartName;
            public string lpDisplayName;
        }

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool QueryServiceConfig(
            IntPtr hService,
            IntPtr lpServiceConfig,
            uint cbBufSize,
            out uint pcbBytesNeeded);

        [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool ChangeServiceConfig(
            IntPtr hService,
            uint dwServiceType,
            uint dwStartType,
            uint dwErrorControl,
            string lpBinaryPathName,
            string lpLoadOrderGroup,
            IntPtr lpdwTagId,
            string lpDependencies,
            string lpServiceStartName,
            string lpPassword,
            string lpDisplayName);

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool CloseServiceHandle(
            IntPtr hSCObject);

        private const uint SERVICE_NO_CHANGE = 0xffffffffu;
        private const uint SC_MANAGER_ALL_ACCESS = 0xf003fu;
        private const uint SERVICE_ALL_ACCESS = 0xf01ffu;
    }
}
6 голосов
/ 02 ноября 2011

Вот более краткий ответ:

В вашем классе ServiceInstaller (тот, который использует Установщик в качестве базового класса), добавьте следующие два переопределения:

public partial class ServiceInstaller : System.Configuration.Install.Installer {

    public ServiceInstaller () {
         ...
    }

    protected override void OnBeforeInstall(System.Collections.IDictionary savedState) {
        Context.Parameters["assemblypath"] += "\" /service";
        base.OnBeforeInstall(savedState);
    }

    protected override void OnBeforeUninstall(System.Collections.IDictionary savedState) {
        Context.Parameters["assemblypath"] += "\" /service";
        base.OnBeforeUninstall(savedState);
    }


}
3 голосов
/ 20 ноября 2009

Существует управляемый способ добавления параметров запуска к службам (не в разделе «Параметры запуска» / «Параметры запуска» консоли управления (services.msc), а в «Путь к исполняемому файлу» / «Pfad zur EXE-»). Datei ", как и все нативные сервисы Windows).

Добавьте следующий код к вашему подклассу System.Configuration.Install.Installer: (в VB-коде, дружественном к C #)

'Just as sample
Private _CommandLineArgs As String() = New String() {"/Debug", "/LogSection:Hello World"}

''' <summary>Command line arguments without double-quotes.</summary>
Public Property CommandLineArgs() As String()
  Get
    Return _CommandLineArgs
  End Get
  Set(ByVal value As String())
    _CommandLineArgs = value
  End Set
End Property

Public Overrides Sub Install(ByVal aStateSaver As System.Collections.IDictionary)
  Dim myPath As String = GetPathToExecutable()
  Context.Parameters.Item("assemblypath") = myPath
  MyBase.Install(aStateSaver)
End Sub

Private Function GetPathToExecutable() As String
  'Format as something like 'MyService.exe" "/Test" "/LogSection:Hello World'
  'Hint: The base class (System.ServiceProcess.ServiceInstaller) adds simple-mindedly
  '      a double-quote around this string that's why we have to omit it here.
  Const myDelimiter As String = """ """ 'double-quote space double-quote
  Dim myResult As New StringBuilder(Context.Parameters.Item("assemblypath"))
  myResult.Append(myDelimiter)
  myResult.Append(Microsoft.VisualBasic.Strings.Join(CommandLineArgs, myDelimiter))
  Return myResult.ToString()
End Function

Веселись!

chha

2 голосов
/ 22 января 2010

Я нашел способ добавить параметры запуска при установке службы:

Работаю ли я как сервис

1 голос
/ 26 августа 2009

По какой-то странной причине моя структура QUERY_SERVICE_CONFIG не получала полное значение lpBinaryPathName, только первый символ. Изменение его на класс ниже, казалось, решило проблему. Полный код на http://www.pinvoke.net/default.aspx/advapi32/QueryServiceConfig.html

Редактировать: Также обратите внимание, что это устанавливает «Путь к исполняемому файлу» службы Windows, но не устанавливает «Параметры запуска» службы Windows.

[StructLayout(LayoutKind.Sequential)]
public class QUERY_SERVICE_CONFIG
{
    [MarshalAs(System.Runtime.InteropServices.UnmanagedType.U4)]
    public UInt32 dwServiceType;
    [MarshalAs(System.Runtime.InteropServices.UnmanagedType.U4)]
    public UInt32 dwStartType;
    [MarshalAs(System.Runtime.InteropServices.UnmanagedType.U4)]
    public UInt32 dwErrorControl;
    [MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]
    public String lpBinaryPathName;
    [MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]
    public String lpLoadOrderGroup;
    [MarshalAs(System.Runtime.InteropServices.UnmanagedType.U4)]
    public UInt32 dwTagID;
    [MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]
    public String lpDependencies;
    [MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]
    public String lpServiceStartName;
    [MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]
    public String lpDisplayName;
}
1 голос
/ 18 марта 2009

Это невозможно сделать в управляемом коде.

Но есть одно достойное решение. Если все, что вам нужно, это иметь один и тот же исполняемый файл для службы Windows и GUI (наиболее распространенный сценарий) Вам даже не нужны параметры. Просто проверьте метод Main для свойства System.Environment.UserInteractive и решите, что делать ...

static void Main(string[] args)
{
    if (System.Environment.UserInteractive)
    {
        // start your app normally
    }
    else
    {
        // start your windows sevice
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...