C# Windows 服务如何确定其服务名称?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1841790/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can a Windows Service determine its ServiceName?
提问by NVRAM
I've looked and couldn't find what should be a simple question:
我看过了,找不到应该是一个简单的问题:
How can a Windows Service determine the ServiceName for which it was started?
Windows 服务如何确定为其启动的 ServiceName?
I know the installation can hack at the registry and add a command line argument, but logically that seems like it shouldbe unnecessary, hence this question.
我知道的安装可以在注册表破解并添加命令行参数,但在逻辑上似乎像它应该是不必要的,因此这个问题。
I'm hoping to run multiple copies of a single binary more cleanly than the registry hack.
我希望比注册表黑客更干净地运行单个二进制文件的多个副本。
Edit:
编辑:
This is written in C#. My apps Main()entry point does different things, depending on command line arguments:
这是用 C# 编写的。我的应用程序Main()入口点执行不同的操作,具体取决于命令行参数:
- Install or Uninstall the service. The command line can provide a non-default ServiceName and can change the number of worker threads.
- Run as a command-line executable (for debugging),
- Run as a "Windows Service". Here, it creates an instance of my ServiceBase-derived class, then calls System.ServiceProcess.ServiceBase.Run(instance);
- 安装或卸载服务。命令行可以提供非默认的 ServiceName,并且可以更改工作线程的数量。
- 作为命令行可执行文件运行(用于调试),
- 作为“Windows 服务”运行。在这里,它创建了我的ServiceBase派生类的实例,然后调用System.ServiceProcess.ServiceBase.Run(instance);
Currently, the installation step appends the service name and thread count to the ImagePathin the registry so the app can determine it's ServiceName.
目前,安装步骤将服务名称和线程计数附加到注册表中的ImagePath,以便应用程序可以确定它的 ServiceName。
采纳答案by NVRAM
From: https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=387024
来自:https: //connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=387024
Here is a WMI solution. Overriding the ServiceBase.ServiceMainCallback()might also work, but this seems to work for me...
这是一个 WMI 解决方案。覆盖ServiceBase.ServiceMainCallback()也可能有效,但这似乎对我有用......
protected String GetServiceName()
{
// Calling System.ServiceProcess.ServiceBase::ServiceNamea allways returns
// an empty string,
// see https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=387024
// So we have to do some more work to find out our service name, this only works if
// the process contains a single service, if there are more than one services hosted
// in the process you will have to do something else
int processId = System.Diagnostics.Process.GetCurrentProcess().Id;
String query = "SELECT * FROM Win32_Service where ProcessId = " + processId;
System.Management.ManagementObjectSearcher searcher =
new System.Management.ManagementObjectSearcher(query);
foreach (System.Management.ManagementObject queryObj in searcher.Get()) {
return queryObj["Name"].ToString();
}
throw new Exception("Can not get the ServiceName");
}
回答by Remy Lebeau
The ServiceMain() entry point that every service executable must implement receives the ServiceName as its first input argument.
每个服务可执行文件必须实现的 ServiceMain() 入口点接收 ServiceName 作为其第一个输入参数。
If you are writing your service using .NET, the ServiceMain() entry point is implemented by .NET for you. The ServiceName is assigned when the service is installed using the ServiceProcess.ServiceBase.ServiceName property. If you are trying to customize a .NET service to support dynamic ServiceName values, I have no clue how to access the actual ServiceName at runtime.
如果您使用 .NET 编写服务,ServiceMain() 入口点由 .NET 为您实现。ServiceName 是在使用 ServiceProcess.ServiceBase.ServiceName 属性安装服务时分配的。如果您尝试自定义 .NET 服务以支持动态 ServiceName 值,我不知道如何在运行时访问实际的 ServiceName。
回答by vivek.m
ServiceBase.ServiceName property gives the compile-time name of service. If you specify a different name when installing the service, then ServiceName attribute will not give correct name. So, I had to use below code to obtain the service name of my service.
ServiceBase.ServiceName 属性给出了服务的编译时名称。如果在安装服务时指定不同的名称,则 ServiceName 属性将不会给出正确的名称。所以,我不得不使用下面的代码来获取我的服务的服务名称。
It's an alternative (without using LINQ) to NVRAM's method:
这是 NVRAM 方法的替代方法(不使用 LINQ):
/**
* Returns the service name of currently running windows service.
*/
static String getServiceName()
{
ServiceController[] scServices;
scServices = ServiceController.GetServices();
// Display the list of services currently running on this computer.
int my_pid = System.Diagnostics.Process.GetCurrentProcess().Id;
foreach (ServiceController scTemp in scServices)
{
// Write the service name and the display name
// for each running service.
// Query WMI for additional information about this service.
// Display the start name (LocalSytem, etc) and the service
// description.
ManagementObject wmiService;
wmiService = new ManagementObject("Win32_Service.Name='" + scTemp.ServiceName + "'");
wmiService.Get();
int id = Convert.ToInt32(wmiService["ProcessId"]);
if (id == my_pid)
{
return scTemp.ServiceName;
#if IS_CONSOLE
Console.WriteLine();
Console.WriteLine(" Service : {0}", scTemp.ServiceName);
Console.WriteLine(" Display name: {0}", scTemp.DisplayName);
Console.WriteLine(" Start name: {0}", wmiService["StartName"]);
Console.WriteLine(" Description: {0}", wmiService["Description"]);
Console.WriteLine(" Found.......");
#endif
}
}
return "NotFound";
}
I was incorrectly trying to obtain the name of windows service as first line in main() without first calling ServiceBase.Run(). We must register our executable as service using ServiceBase.Run() before obtaining its name.
我错误地尝试获取 Windows 服务的名称作为 main() 中的第一行,而没有先调用ServiceBase.Run()。在获取它的名称之前,我们必须使用 ServiceBase.Run() 将我们的可执行文件注册为服务。
Ref.: http://msdn.microsoft.com/en-us/library/hde9d63a.aspx#Y320
参考:http: //msdn.microsoft.com/en-us/library/hde9d63a.aspx#Y320
回答by Mahesh
Short version with Linq
使用 Linq 的简短版本
int processId = System.Diagnostics.Process.GetCurrentProcess().Id;
ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT * FROM Win32_Service where ProcessId = " + processId);
ManagementObjectCollection collection = searcher.Get();
var serviceName = (string)collection.Cast<ManagementBaseObject>().First()["Name"];
回答by raiserle
By searching for a better solution i tried this:
通过寻找更好的解决方案,我尝试了这个:
string serviceName = "myDynamicServiceName";
string serviceBin = "path\to\Service.exe";
string configFile = "path\to\myConfig.xml";
string credentials = "obj= .\mytestuser password= test";
string scCommand = string.Format( "sc create {0} start= auto binPath= \"\\"{1}\\" -ini={2} -sn={3}\" type= own{4}", serviceName, serviceBin, configFile , serviceName ,credentials );
I passed the servicename and an configuration file to the binpath. The service was installed by using the SC.exe (i don't use the installutil!)
我将服务名称和配置文件传递给 binpath。该服务是使用 SC.exe 安装的(我不使用 installutil!)
On the service you can get the Commandline-Arguments
在服务上,您可以获得命令行参数
protected override void OnStart(string[] args){
string binpath = new System.IO.FileInfo(System.Reflection.Assembly.GetAssembly(this.GetType()).Location).DirectoryName + "\";
System.IO.StreamWriter sw = new System.IO.StreamWriter( binpath + "test.log");
sw.WriteLine( binpath );
string[] cmdArgs = System.Environment.GetCommandLineArgs();
foreach (string item in cmdArgs) {
sw.WriteLine(item);
}
sw.Flush();
sw.Dispose();
sw = null;
}
回答by simon
What's wrong with this.ServiceName, if you're inside the service.cs?
如果您在 service.cs 中,this.ServiceName 有什么问题?
i.e.:
IE:
protected override void OnStart(string[] args)
{
Logger.Info($"{this.ServiceName} started on {Environment.MachineName}...");
}
回答by Andy
I had a chicken-and-egg problem where I needed to know the service location before completing Service.Run() (Service could be part of a client or server installation, installer named them appropriately, and I needed to detect which it was on startup)
我有一个先有鸡还是先有蛋的问题,我需要在完成 Service.Run() 之前知道服务位置(服务可能是客户端或服务器安装的一部分,安装程序适当地命名它们,我需要检测它在哪个位置启动)
I relied on the registry to get me the name.
我依靠注册表来获取我的名字。
public String IdentifySelfFromRegistry()
{
String executionPath = Assembly.GetEntryAssembly().Location;
Microsoft.Win32.RegistryKey services = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(
@"SYSTEM\CurrentControlSet\services");
if (services != null)
{
foreach(String subkey in services.GetSubKeyNames())
{
if (executionPath.Equals(ServicePathFromServiceKey(services.OpenSubKey(subkey))))
return subkey;
}
}
return String.Empty;
}
protected static String ServicePathFromServiceKey(Microsoft.Win32.RegistryKey serviceKey)
{
if (serviceKey != null)
{
String exec = serviceKey.GetValue(ServicePathEntry) as String;
if (exec != null)
return exec.Trim('\"');
}
return String.Empty;
}