¿Cómo puede un servicio de Windows determinar su ServiceName?

Miré y no pude encontrar lo que debería ser una simple pregunta:

¿Cómo puede un Servicio de Windows determinar el ServiceName para el que se inició?

Sé que la instalación puede piratear el registro y agregar un argumento de línea de comando, pero lógicamente parece que no debería ser necesario, de ahí esta pregunta.

Espero ejecutar varias copias de un único archivo binario más limpiamente que el registro.

Editar :

Esto está escrito en C #. El punto de entrada principal () de mis aplicaciones hace cosas diferentes, dependiendo de los argumentos de la línea de comando:

  • Instalar o desinstalar el servicio. La línea de comando puede proporcionar un ServiceName no predeterminado y puede cambiar el número de subprocesos de trabajo.
  • Ejecutar como un ejecutable de línea de comandos (para la depuración),
  • Ejecutar como un “Servicio de Windows”. Aquí, crea una instancia de mi clase derivada de ServiceBase , luego llama a System.ServiceProcess.ServiceBase.Run (instancia);

Actualmente, el paso de instalación agrega el nombre del servicio y el recuento de subprocesos a ImagePath en el registro para que la aplicación pueda determinar su nombre de servicio.

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

Aquí hay una solución de WMI. Anular el ServiceBase.ServiceMainCallback () también podría funcionar, pero esto parece funcionar para mí …

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

La propiedad ServiceBase.ServiceName proporciona el nombre de servicio en tiempo de comstackción. Si especifica un nombre diferente al instalar el servicio, el atributo ServiceName no dará el nombre correcto. Entonces, tuve que usar el código a continuación para obtener el nombre del servicio de mi servicio.

Es una alternativa (sin usar LINQ) al método de 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"; } 

Estaba tratando incorrectamente de obtener el nombre del servicio de Windows como primera línea en main () sin llamar primero a ServiceBase.Run () . Debemos registrar nuestro ejecutable como servicio utilizando ServiceBase.Run () antes de obtener su nombre.

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

Versión corta con 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().First()["Name"]; 

El punto de entrada ServiceMain () que cada ejecutable de servicio debe implementar recibe el ServiceName como su primer argumento de entrada.

Si está escribiendo su servicio usando .NET, el punto de entrada ServiceMain () es implementado por .NET por usted. El ServiceName se asigna cuando el servicio se instala utilizando la propiedad ServiceProcess.ServiceBase.ServiceName. Si está intentando personalizar un servicio .NET para admitir valores de ServiceName dynamics, no tengo idea de cómo acceder al ServiceName real en tiempo de ejecución.

¿Qué pasa con esto. ServiceName, si estás dentro del servicio.cs?

es decir:

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

Al buscar una mejor solución, intenté esto:

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

Pasé el nombre del servicio y un archivo de configuración a la ruta de acceso. El servicio se instaló utilizando SC.exe (¡no uso el installutil!)

En el servicio, puede obtener los argumentos de línea de comando

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

Tuve un problema de huevo y gallina en el que necesitaba saber la ubicación del servicio antes de completar Service.Run () (El servicio podría ser parte de una instalación de cliente o servidor, el instalador los nombró apropiadamente y tuve que detectar cuál era puesta en marcha)

Confié en el registro para obtener el nombre.

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