J'ai regardé et n'ai pas trouvé ce qui devrait être une question simple:
Comment un service Windows peut-il déterminer le nom du service pour lequel il a été démarré?
Je sais que l'installation peut pirater le registre et ajouter un argument de ligne de commande, mais logiquement, il semble que devrait soit inutile, d'où cette question.
J'espère pouvoir exécuter plusieurs copies d'un seul binaire plus proprement que le hack du registre.
Modifier :
Ceci est écrit en C #. Mes applications Main () le point d'entrée fait différentes choses, en fonction des arguments de la ligne de commande:
Actuellement, l'étape d'installation ajoute le nom du service et le nombre de threads au ImagePath dans le registre afin que l'application puisse déterminer qu'il s'agit de ServiceName.
De: https://connect.Microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=387024
Voici une solution WMI. Remplacer le ServiceBase.ServiceMainCallback () pourrait également fonctionner, mais cela semble fonctionner pour moi ...
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 propriété ServiceBase.ServiceName donne le nom du service à la compilation. Si vous spécifiez un nom différent lors de l'installation du service, l'attribut ServiceName ne donnera pas le nom correct. Donc, je devais utiliser le code ci-dessous pour obtenir le nom de service de mon service.
C'est une alternative (sans utiliser LINQ) à la méthode 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";
}
J'essayais à tort d'obtenir le nom du service Windows en première ligne de main () sans d'abord appeler ServiceBase.Run () . Nous devons enregistrer notre fichier exécutable en tant que service à l'aide de ServiceBase.Run () avant d'obtenir son nom.
Réf.: http://msdn.Microsoft.com/en-us/library/hde9d63a.aspx#Y320
Version courte avec 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"];
Le point d'entrée ServiceMain () que chaque exécutable de service doit implémenter reçoit le nom de service comme premier argument d'entrée.
Si vous écrivez votre service à l'aide de .NET, le point d'entrée ServiceMain () est implémenté par .NET pour vous. Le serviceName est attribué lors de l'installation du service à l'aide de la propriété ServiceProcess.ServiceBase.ServiceName. Si vous essayez de personnaliser un service .NET pour prendre en charge les valeurs dynamiques ServiceName, je ne sais pas comment accéder au nom de service actuel au moment de l'exécution.
Quel est le problème avec this.ServiceName, si vous êtes dans le service.cs?
c'est à dire.:
protected override void OnStart(string[] args)
{
Logger.Info($"{this.ServiceName} started on {Environment.MachineName}...");
}
En cherchant une meilleure solution, j'ai essayé ceci:
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 );
J'ai passé le nom du service et un fichier de configuration au chemin bin. Le service a été installé à l'aide de SC.exe (je n'utilise pas installutil!).
Sur le service, vous pouvez obtenir les arguments en ligne de commande
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;
}
J'avais un problème de poulet et d'oeufs où je devais connaître l'emplacement du service avant de terminer Service.Run () (le service peut faire partie d'une installation client ou serveur, le programme d'installation les a nommés correctement et j'ai besoin de savoir sur lequel il se trouvait. Commencez)
Je me suis fié au registre pour me donner le nom.
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;
}