如何从服务本身获取Windows服务的名称

时间:2021-02-10 18:11:40

I have a bunch of win services written in .NET that use same exact executable with different configs. All services write to the same log file. However since I use the same .exe the service doesn't know its own service name to put in the log file.

我有一堆用.NET编写的win服务,它们使用相同的可执行文件和不同的配置。所有服务都写入同一个日志文件。但是,由于我使用相同的.exe,服务不知道自己的服务名称要放在日志文件中。

Is there a way my service can programatically retrieve its own name?

有没有办法我的服务可以以编程方式检索自己的名字?

2 个解决方案

#1


Insight can be gained by looking at how Microsoft does this for the SQL Server service. In the Services control panel, we see:

通过查看Microsoft如何为SQL Server服务执行此操作,可以获得洞察力。在“服务”控制面板中,我们看到:

Service name: MSSQLServer

服务名称:MSSQLServer

Path to executable: "C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Binn\sqlservr.exe" -sMSSQLSERVER

可执行文件的路径:“C:\ Program Files \ Microsoft SQL Server \ MSSQL.1 \ MSSQL \ Binn \ sqlservr.exe”-sMSSQLSERVER

Notice that the name of the service is included as a command line argument. This is how it is made available to the service at run time. With some work, we can accomplish the same thing in .NET.

请注意,服务名称包含在命令行参数中。这是它在运行时可用于服务的方式。通过一些工作,我们可以在.NET中完成同样的事情。

Basic steps:

  1. Have the installer take the service name as an installer parameter.
  2. 让安装程序将服务名称作为安装程序参数。

  3. Make API calls to set the command line for the service to include the service name.
  4. 进行API调用以将服务的命令行设置为包括服务名称。

  5. Modify the Main method to examine the command line and set the ServiceBase.ServiceName property. The Main method is typically in a file called Program.cs.
  6. 修改Main方法以检查命令行并设置ServiceBase.ServiceName属性。 Main方法通常位于名为Program.cs的文件中。

Install/uninstall commands

To install the service (can omit /Name to use DEFAULT_SERVICE_NAME):

要安装该服务(可以省略/ Name以使用DEFAULT_SERVICE_NAME):

installutil.exe /Name=YourServiceName YourService.exe

To uninstall the service (/Name is never required since it is stored in the stateSaver):

要卸载服务(/ Name永远不需要,因为它存储在stateSaver中):

installutil.exe /u YourService.exe

Installer code sample:

using System;
using System.Collections;
using System.Configuration.Install;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.ServiceProcess;

namespace TestService
{
    [RunInstaller(true)]
    public class ProjectInstaller : Installer
    {
        private const string DEFAULT_SERVICE_NAME = "TestService";
        private const string DISPLAY_BASE_NAME = "Test Service";

        private ServiceProcessInstaller _ServiceProcessInstaller;
        private ServiceInstaller _ServiceInstaller;

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

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

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

        public override void Install(IDictionary stateSaver)
        {
            if (this.Context != null && this.Context.Parameters.ContainsKey("Name"))
                stateSaver["Name"] = this.Context.Parameters["Name"];
            else
                stateSaver["Name"] = DEFAULT_SERVICE_NAME;

            ConfigureInstaller(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 + " /s:" + (string)stateSaver["Name"];

                    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();
            }
        }

        public override void Rollback(IDictionary savedState)
        {
            ConfigureInstaller(savedState);
            base.Rollback(savedState);
        }

        public override void Uninstall(IDictionary savedState)
        {
            ConfigureInstaller(savedState);
            base.Uninstall(savedState);
        }

        private void ConfigureInstaller(IDictionary savedState)
        {
            _ServiceInstaller.ServiceName = (string)savedState["Name"];
            _ServiceInstaller.DisplayName = DISPLAY_BASE_NAME + " (" + _ServiceInstaller.ServiceName + ")";
        }

        [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;
    }
}

Main code sample:

using System;
using System.ServiceProcess;

namespace TestService
{
    class Program
    {
        static void Main(string[] args)
        {
            string serviceName = null;
            foreach (string s in args)
            {
                if (s.StartsWith("/s:", StringComparison.OrdinalIgnoreCase))
                {
                    serviceName = s.Substring("/s:".Length);
                }
            }

            if (serviceName == null)
                throw new InvalidOperationException("Service name not specified on command line.");

            // Substitute the name of your class that inherits from ServiceBase.

            TestServiceImplementation impl = new TestServiceImplementation();
            impl.ServiceName = serviceName;
            ServiceBase.Run(impl);
        }
    }

    class TestServiceImplementation : ServiceBase
    {
        protected override void OnStart(string[] args)
        {
            // Your service implementation here.
        }
    }
}

#2


I use this function in VB

我在VB中使用此功能

Private Function GetServiceName() As String
    Try
        Dim processId = Process.GetCurrentProcess().Id
        Dim query = "SELECT * FROM Win32_Service where ProcessId  = " & processId.ToString
        Dim searcher As New Management.ManagementObjectSearcher(query)
        Dim share As Management.ManagementObject
        For Each share In searcher.Get()
            Return share("Name").ToString()
        Next share
    Catch ex As Exception
        Dim a = 0
    End Try
    Return "DefaultServiceName"
End Function

#1


Insight can be gained by looking at how Microsoft does this for the SQL Server service. In the Services control panel, we see:

通过查看Microsoft如何为SQL Server服务执行此操作,可以获得洞察力。在“服务”控制面板中,我们看到:

Service name: MSSQLServer

服务名称:MSSQLServer

Path to executable: "C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Binn\sqlservr.exe" -sMSSQLSERVER

可执行文件的路径:“C:\ Program Files \ Microsoft SQL Server \ MSSQL.1 \ MSSQL \ Binn \ sqlservr.exe”-sMSSQLSERVER

Notice that the name of the service is included as a command line argument. This is how it is made available to the service at run time. With some work, we can accomplish the same thing in .NET.

请注意,服务名称包含在命令行参数中。这是它在运行时可用于服务的方式。通过一些工作,我们可以在.NET中完成同样的事情。

Basic steps:

  1. Have the installer take the service name as an installer parameter.
  2. 让安装程序将服务名称作为安装程序参数。

  3. Make API calls to set the command line for the service to include the service name.
  4. 进行API调用以将服务的命令行设置为包括服务名称。

  5. Modify the Main method to examine the command line and set the ServiceBase.ServiceName property. The Main method is typically in a file called Program.cs.
  6. 修改Main方法以检查命令行并设置ServiceBase.ServiceName属性。 Main方法通常位于名为Program.cs的文件中。

Install/uninstall commands

To install the service (can omit /Name to use DEFAULT_SERVICE_NAME):

要安装该服务(可以省略/ Name以使用DEFAULT_SERVICE_NAME):

installutil.exe /Name=YourServiceName YourService.exe

To uninstall the service (/Name is never required since it is stored in the stateSaver):

要卸载服务(/ Name永远不需要,因为它存储在stateSaver中):

installutil.exe /u YourService.exe

Installer code sample:

using System;
using System.Collections;
using System.Configuration.Install;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.ServiceProcess;

namespace TestService
{
    [RunInstaller(true)]
    public class ProjectInstaller : Installer
    {
        private const string DEFAULT_SERVICE_NAME = "TestService";
        private const string DISPLAY_BASE_NAME = "Test Service";

        private ServiceProcessInstaller _ServiceProcessInstaller;
        private ServiceInstaller _ServiceInstaller;

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

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

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

        public override void Install(IDictionary stateSaver)
        {
            if (this.Context != null && this.Context.Parameters.ContainsKey("Name"))
                stateSaver["Name"] = this.Context.Parameters["Name"];
            else
                stateSaver["Name"] = DEFAULT_SERVICE_NAME;

            ConfigureInstaller(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 + " /s:" + (string)stateSaver["Name"];

                    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();
            }
        }

        public override void Rollback(IDictionary savedState)
        {
            ConfigureInstaller(savedState);
            base.Rollback(savedState);
        }

        public override void Uninstall(IDictionary savedState)
        {
            ConfigureInstaller(savedState);
            base.Uninstall(savedState);
        }

        private void ConfigureInstaller(IDictionary savedState)
        {
            _ServiceInstaller.ServiceName = (string)savedState["Name"];
            _ServiceInstaller.DisplayName = DISPLAY_BASE_NAME + " (" + _ServiceInstaller.ServiceName + ")";
        }

        [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;
    }
}

Main code sample:

using System;
using System.ServiceProcess;

namespace TestService
{
    class Program
    {
        static void Main(string[] args)
        {
            string serviceName = null;
            foreach (string s in args)
            {
                if (s.StartsWith("/s:", StringComparison.OrdinalIgnoreCase))
                {
                    serviceName = s.Substring("/s:".Length);
                }
            }

            if (serviceName == null)
                throw new InvalidOperationException("Service name not specified on command line.");

            // Substitute the name of your class that inherits from ServiceBase.

            TestServiceImplementation impl = new TestServiceImplementation();
            impl.ServiceName = serviceName;
            ServiceBase.Run(impl);
        }
    }

    class TestServiceImplementation : ServiceBase
    {
        protected override void OnStart(string[] args)
        {
            // Your service implementation here.
        }
    }
}

#2


I use this function in VB

我在VB中使用此功能

Private Function GetServiceName() As String
    Try
        Dim processId = Process.GetCurrentProcess().Id
        Dim query = "SELECT * FROM Win32_Service where ProcessId  = " & processId.ToString
        Dim searcher As New Management.ManagementObjectSearcher(query)
        Dim share As Management.ManagementObject
        For Each share In searcher.Get()
            Return share("Name").ToString()
        Next share
    Catch ex As Exception
        Dim a = 0
    End Try
    Return "DefaultServiceName"
End Function