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:
- Have the installer take the service name as an installer parameter.
- Make API calls to set the command line for the service to include the service name.
- 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.
让安装程序将服务名称作为安装程序参数。
进行API调用以将服务的命令行设置为包括服务名称。
修改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:
- Have the installer take the service name as an installer parameter.
- Make API calls to set the command line for the service to include the service name.
- 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.
让安装程序将服务名称作为安装程序参数。
进行API调用以将服务的命令行设置为包括服务名称。
修改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