Как служба Windows может определить свое имя службы? - PullRequest
33 голосов
/ 03 декабря 2009

Я посмотрел и не смог найти простой вопрос:

Как служба Windows может определить имя службы, для которой она была запущена?

Я знаю, что установка может взломать реестр и добавить аргумент командной строки, но логически кажется, что должно быть ненужным, поэтому этот вопрос.

Я надеюсь запустить несколько копий одного двоичного файла более аккуратно, чем взломать реестр.

Редактировать

Это написано на C #. Мои приложения Main () точка входа делает разные вещи, в зависимости от аргументы командной строки:

  • Установка или удаление службы. Командная строка может предоставлять не по умолчанию ServiceName и может изменять количество рабочих потоков.
  • Запустить как исполняемый файл командной строки (для отладки),
  • Запускать как «Служба Windows». Здесь он создает экземпляр моего ServiceBase -приобретенного затем вызывает System.ServiceProcess.ServiceBase.Run (экземпляр);

В настоящее время шаг установки добавляет имя службы и число потоков к ImagePath в реестре, чтобы приложение могло определить его ServiceName.

Ответы [ 7 ]

27 голосов
/ 10 декабря 2009

От: 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");
    } 
6 голосов
/ 13 июня 2012

ServiceBase.ServiceName свойство дает имя сервиса во время компиляции. Если при установке службы вы указали другое имя, атрибут ServiceName не даст правильное имя. Поэтому мне пришлось использовать приведенный ниже код, чтобы получить название службы моей службы.

Это альтернатива (без использования LINQ) методу NVRAM:

/**
 * 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";
}

Я неправильно пытался получить имя службы Windows в качестве первой строки в main () без первого вызова ServiceBase.Run () . Мы должны зарегистрировать наш исполняемый файл как сервис, используя ServiceBase.Run (), прежде чем получить его имя.

Ссылка: http://msdn.microsoft.com/en-us/library/hde9d63a.aspx#Y320

2 голосов
/ 19 августа 2013

Короткая версия с 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"];
1 голос
/ 04 декабря 2009

Точка входа ServiceMain (), которую должен реализовать каждый исполняемый файл службы, получает ServiceName в качестве первого входного аргумента.

Если вы пишете свой сервис с использованием .NET, точка входа ServiceMain () реализована для вас .NET. Имя ServiceName назначается при установке службы с использованием свойства ServiceProcess.ServiceBase.ServiceName. Если вы пытаетесь настроить службу .NET для поддержки динамических значений ServiceName, я понятия не имею, как получить доступ к фактическому ServiceName во время выполнения.

0 голосов
/ 22 сентября 2016

У меня была проблема типа "курица и яйцо", когда мне нужно было узнать местоположение службы до завершения Service.Run () (Service может быть частью установки клиента или сервера, установщик назвал их соответствующим образом, и мне нужно было определить, какая это было при запуске)

Я полагался на реестр, чтобы получить мне имя.

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;
}
0 голосов
/ 07 июля 2016

Что не так с этим. ServiceName, если вы находитесь внутри service.cs?

т.е:.

protected override void OnStart(string[] args)
    {
        Logger.Info($"{this.ServiceName} started on {Environment.MachineName}...");  
    }
0 голосов
/ 13 октября 2015

В поисках лучшего решения я попробовал это:

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

Я передал имя сервера и файл конфигурации в 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;
}
...