c# - setup - windows service visual studio 2015
¿Cómo puede un servicio de Windows determinar su ServiceName? (7)
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.
¿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}...");
}
Buscando una mejor solución probé 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 de servicio y un archivo de configuración al binpath. 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;
}
De: https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=387024
Aquí hay una solución 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");
}
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 dinámicos, no tengo idea de cómo acceder al ServiceName real en tiempo de ejecución.
La propiedad ServiceBase.ServiceName proporciona el nombre de servicio en tiempo de compilació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
Tuve un problema de gallina y huevo en el que necesitaba conocer la ubicación del servicio antes de completar Service.Run () (El servicio podría ser parte de la instalación de un cliente o servidor, el instalador los nombró adecuadamente y necesitaba detectar cuál era el problema). puesta en marcha)
Confié en el registro para darme 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;
}
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<ManagementBaseObject>().First()["Name"];