前言
有时,可能需要在运行时获取当前进程所属的 Windows 服务(有关创建 Windows 服务的说明,请参阅 https://learn.microsoft.com/zh-cn/dotnet/core/extensions/windows-service)。例如,可能需要在 Windows 服务中使用不同的日志记录器。
本文将讨论如何获取当前进程所属的 Windows 服务。
IHostLifetime
IHostLifetime 接口定义了用于控制主机启动和关闭的方法,它有两个实现:
ConsoleLifetime:控制台应用程序的主机生存期实现。
WindowsServiceLifetime:Windows 服务的主机生存期实现。
WindowsServiceLifetime 类会在其 StartAsync 方法中设置 ServiceName 属性的值,该方法是对应于当前进程的 Windows 服务的名称:
public WindowsServiceLifetime(IHostEnvironment Environment, IHostApplicationLifetime applicationLifetime, ILoggerFactory loggerFactory, IOptions<HostOptions> optionsAccessor, IOptions<WindowsServiceLifetimeOptions> windowsServiceOptionsAccessor)
{
...
ServiceName = windowsServiceOptionsAccessor.Value.ServiceName;
...
}
因此,在 Windows 服务中,可以通过 IHostLifetime 接口获取当前进程对应的 Windows 服务的名称:
var host = builder.Build();
var lifetime = host.Services.GetService(typeof(IHostLifetime)) as IHostLifetime;
var serviceName = lifetime.GetType().GetProperty("ServiceName").GetValue(lifetime);
logger.LogInformation($"The service name is {serviceName}");
await host.RunAsync();
问题
但是,上述方法有一个问题,即获取的 ServiceName 属性的值实际上是 AddWindowsService 代码中设置的名称,而不是 Windows 服务的真实名称。
builder.Services.AddWindowsService(options =>
{
options.ServiceName = ".NET Joke Service";
});
因此,要获取 Windows 服务的真实名称,需要另辟蹊径。
解决方案
Windows 服务启动后,它对应于一个进程。因此,可以通过其 Id 获取与当前进程对应的 Windows 服务的名称。
可以使用 WMI 来获取与当前进程对应的 Windows 服务的名称:
await host.StartAsync();
var processId = Process.GetCurrentProcess().Id;
var query = $"SELECT * FROM Win32_Service WHERE ProcessId = {processId}";
var searcher = new ManagementObjectSearcher(query);
var services = searcher.Get();
var serviceName = services.Cast<ManagementObject>().Select(s => s["Name"]).FirstOrDefault();
logger.LogInformation($"The service name is {serviceName}");
await host.WaitForShutdownAsync();
注意,必须在调用 host.StartAsync 之后才能获取与当前进程对应的 Windows 服务。
结论
本文讨论了如何获取与当前进程对应的 Windows 服务的名称。希望这篇文章对你有所帮助。