web-dev-qa-db-ger.com

Wie kann ein Windows-Dienst seinen Dienstnamen ermitteln?

Ich habe gesucht und konnte keine einfache Frage finden:

Wie kann ein Windows-Dienst den Dienstnamen ermitteln, für den er gestartet wurde?

Ich weiß, dass die Installation die Registrierung hacken und ein Befehlszeilenargument hinzufügen kann, aber logischerweise scheint dies sollte unnötig zu sein, daher diese Frage.

Ich hoffe, dass ich mehrere Kopien einer einzelnen Binärdatei sauberer ausführen kann als der Registry-Hack.

Bearbeiten :

Dies ist in C # geschrieben. Meine Apps Main () entry point haben je nach Befehlszeilenargument unterschiedliche Funktionen:

  • Installieren oder deinstallieren Sie den Dienst. Die Befehlszeile kann einen nicht standardmäßigen ServiceName bereitstellen und die Anzahl der Arbeitsthreads ändern.
  • Als ausführbare Befehlszeilen-Datei ausführen (zum Debuggen),
  • Führen Sie als "Windows-Dienst" aus. Hier erstellt es eine Instanz meiner ServiceBase - abgeleiteten Klasse und ruft dann System.ServiceProcess.ServiceBase.Run (Instanz) auf;

Gegenwärtig hängt der Installationsschritt den Dienstnamen und die Thread-Anzahl an ImagePath in der Registrierung an, damit die App ihren Dienstnamen ermitteln kann.

31
NVRAM

Von: https://connect.Microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=387024

Hier ist eine WMI-Lösung. Das Überschreiben von ServiceBase.ServiceMainCallback () könnte auch funktionieren, aber das scheint für mich zu funktionieren ...

    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");
    } 
26
NVRAM

Die Eigenschaft ServiceBase.ServiceName gibt den Namen des Dienstes zur Kompilierzeit an. Wenn Sie bei der Installation des Dienstes einen anderen Namen angeben, gibt das ServiceName-Attribut keinen korrekten Namen an. Daher musste ich den Code unten verwenden, um den Dienstnamen meines Dienstes zu erhalten.

Es ist eine Alternative (ohne LINQ zu verwenden) zur NVRAM-Methode:

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

Ich habe fälschlicherweise versucht, den Namen des Windows-Dienstes als erste Zeile in main () zu erhalten, ohne zuerst ServiceBase.Run () aufzurufen. Wir müssen unsere ausführbare Datei mit ServiceBase.Run () als Dienst registrieren, bevor Sie ihren Namen erhalten.

Ref .: http://msdn.Microsoft.com/de-de/library/hde9d63a.aspx#Y320

6
vivek.m

Kurzversion mit 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"];
2
Mahesh

Der ServiceMain () - Einstiegspunkt, den jede ausführbare Dienstdatei implementieren muss, erhält den ServiceName als erstes Eingabeargument.

Wenn Sie Ihren Dienst mit .NET schreiben, wird der Einstiegspunkt ServiceMain () von .NET für Sie implementiert. Der ServiceName wird zugewiesen, wenn der Dienst mit der Eigenschaft ServiceProcess.ServiceBase.ServiceName installiert wird. Wenn Sie versuchen, einen .NET-Dienst zur Unterstützung dynamischer ServiceName-Werte anzupassen, habe ich keine Ahnung, wie auf den tatsächlichen ServiceName zur Laufzeit zugegriffen werden kann.

1
Remy Lebeau

Was ist mit this.ServiceName falsch, wenn Sie sich in den service.cs befinden?

d.h .:

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

Auf der Suche nach einer besseren Lösung habe ich folgendes versucht:

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

Ich habe den Dienstnamen und eine Konfigurationsdatei an den Binpath übergeben. Der Dienst wurde mithilfe der SC.exe installiert (ich verwende kein installutil!)

Über den Dienst erhalten Sie die Befehlszeilenargumente

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

Ich hatte ein Problem mit dem Henne-und-Ei-Problem, bei dem ich vor dem Abschluss von Service.Run () den Service-Speicherort kennen musste. Der Service könnte Teil einer Client- oder Server-Installation sein Anlaufen)

Ich habe mich auf die Registrierung verlassen, um mir den Namen zu holen.

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
Andy