Подтвердить что ты не робот

Как служба Windows может определить свое имя службы?

Я посмотрел и не мог найти, что должно быть простым вопросом:

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

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

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

Edit

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

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

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

4b9b3361

Ответ 1

От: 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");
    } 

Ответ 2

Свойство 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(), прежде чем получить его имя.

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

Ответ 3

Краткая версия с 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"];

Ответ 4

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

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

Ответ 5

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

то есть:.

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

Ответ 6

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

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

Я передал имя сервиса и файл конфигурации в путь bin. Служба была установлена ​​с помощью 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;
}

Ответ 7

У меня была проблема с курицей и яйцом, где мне нужно было знать местоположение службы до завершения 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;
}