Windows服务如何确定其ServiceName?

时间:2009-12-03 18:11:06

标签: c# windows-services service

我看了,找不到一个简单的问题:

Windows服务如何确定启动它的ServiceName?

我知道安装可以破解注册表并添加一个命令行参数,但从逻辑上讲,似乎应该是不必要的,因此这个问题。

我希望比注册表黑客更干净地运行单个二进制文件的多个副本。

修改

这是用C#编写的。我的应用 Main()入口点可以执行不同的操作,具体取决于 命令行参数:

  • 安装或卸载服务。命令行可以提供非默认值 ServiceName,可以更改工作线程数。
  • 作为命令行可执行文件运行(用于调试),
  • 作为“Windows服务”运行。在这里,它创建了我的 ServiceBase -derived的实例 class,然后调用 System.ServiceProcess.ServiceBase.Run(instance);

目前,安装步骤会将服务名称和线程计数附加到注册表中的 ImagePath ,以便应用程序可以确定它的ServiceName。

7 个答案:

答案 0 :(得分:27)

来自:https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=387024

这是一个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");
    } 

答案 1 :(得分:6)

ServiceBase.ServiceName属性给出了编译时的服务名称。如果在安装服务时指定其他名称,则ServiceName属性将不会提供正确的名称。所以,我必须使用下面的代码来获取我的服务的服务名称。

这是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";
}

我没有在第一次调用ServiceBase.Run()的情况下错误地尝试在main()中获取Windows服务的名称作为第一行。在获取名称之前,我们必须使用ServiceBase.Run()将可执行文件注册为服务。

参考:http://msdn.microsoft.com/en-us/library/hde9d63a.aspx#Y320

答案 2 :(得分:2)

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"];

答案 3 :(得分:1)

每个服务可执行文件必须实现的ServiceMain()入口点接收ServiceName作为其第一个输入参数。

如果您使用.NET编写服务,则ServiceMain()入口点由.NET实现。使用ServiceProcess.ServiceBase.ServiceName属性安装服务时,将分配ServiceName。如果您尝试自定义.NET服务以支持动态ServiceName值,我不知道如何在运行时访问实际的ServiceName。

答案 4 :(得分:1)

this.ServiceName有什么问题,如果你在service.cs里面?

即:

protected override void OnStart(string[] args)
    {
        Logger.Info($"{this.ServiceName} started on {Environment.MachineName}...");  
    }

答案 5 :(得分:0)

通过寻找更好的解决方案,我尝试了这个:

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

我将servicename和配置文件传递给binpath。 该服务是使用SC.exe安装的(我不使用installutil!)

在服务上,您可以获得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;
}

答案 6 :(得分:0)

我有一个鸡与蛋问题,我需要在完成Service.Run()之前知道服务位置(服务可能是客户端或服务器安装的一部分,安装程序将它们命名为适当的,我需要检测哪个它是在启动时)

我依靠注册表为我命名。

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