Come può un servizio di Windows determinare il suo ServiceName?

Ho cercato e non ho potuto trovare quella che dovrebbe essere una semplice domanda:

Come può un servizio Windows determinare il ServiceName per il quale è stato avviato?

So che l’installazione può modificare il registro e aggiungere un argomento da riga di comando, ma logicamente sembra che non sia necessario, quindi questa domanda.

Spero di eseguire più copie di un singolo binario in modo più pulito rispetto alla modifica del registro.

Modifica :

Questo è scritto in C #. Le mie app Il punto di ingresso Main () fa cose diverse, a seconda degli argomenti della riga di comando:

  • Installa o Disinstalla il servizio. La riga di comando può fornire un ServiceName non predefinito e può modificare il numero di thread di lavoro.
  • Esegui come eseguibile da riga di comando (per il debug),
  • Esegui come “Servizio Windows”. Qui crea un’istanza della mia class di ServiceBase , quindi chiama System.ServiceProcess.ServiceBase.Run (instance);

Attualmente, la fase di installazione aggiunge il nome del servizio e il conteggio dei thread a ImagePath nel Registro di sistema, in modo che l’app possa determinare che si tratta di ServiceName.

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

Ecco una soluzione WMI. Anche l’override di ServiceBase.ServiceMainCallback () potrebbe funzionare, ma sembra funzionare per me …

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 proprietà ServiceBase.ServiceName fornisce il nome del servizio in fase di compilazione. Se si specifica un nome diverso durante l’installazione del servizio, l’attributo ServiceName non darà il nome corretto. Quindi, ho dovuto usare sotto il codice per ottenere il nome del servizio del mio servizio.

È un’alternativa (senza usare LINQ) al metodo 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"; } 

Stavo cercando erroneamente di ottenere il nome del servizio Windows come prima riga in main () senza prima chiamare ServiceBase.Run () . Dobbiamo registrare il nostro eseguibile come servizio utilizzando ServiceBase.Run () prima di ottenere il suo nome.

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

Versione breve 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"]; 

Il punto di ingresso ServiceMain () che ogni eseguibile del servizio deve implementare riceve il ServiceName come primo argomento di input.

Se stai scrivendo il tuo servizio utilizzando .NET, il punto di ingresso ServiceMain () è implementato da .NET per te. Il ServiceName viene assegnato quando il servizio viene installato utilizzando la proprietà ServiceProcess.ServiceBase.ServiceName. Se si sta tentando di personalizzare un servizio .NET per supportare valori di ServiceName dinamici, non ho idea di come accedere al ServiceName effettivo in fase di runtime.

Cosa c’è di sbagliato in questo.ServiceName, se sei all’interno del servizio.cs?

vale a dire:

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

Cercando una soluzione migliore ho provato questo:

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

Ho passato il servicename e un file di configurazione al binpath. Il servizio è stato installato utilizzando SC.exe (non utilizzo l’installutil!)

Sul servizio è ansible ottenere gli argomenti della riga di 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; } 

Ho avuto un problema di pollo e uova dove avevo bisogno di conoscere il percorso del servizio prima di completare Service.Run () (il servizio potrebbe essere parte di un’installazione client o server, il programma di installazione li ha denominati in modo appropriato e ho bisogno di rilevare quale era avviare)

Mi sono basato sul registro per ottenere il nome.

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