2009-12-03 29 views
27

我已經看了,但沒有找到什麼應該是一個簡單的問題:Windows服務如何確定其ServiceName?

Windows服務如何能確定它被啓動的服務名稱?

我知道的安裝可以在註冊表破解並添加命令行參數,但在邏輯上似乎像它應該是不必要的,因此這個問題。

我希望能夠比註冊表黑客更乾淨地運行單個二進制文件的多個副本。

編輯

這是用C#編寫。我的應用程序的Main()切入點做不同的事情,這取決於 命令行參數:

  • 安裝或卸載該服務。命令行可以提供非默認的 ServiceName並可以更改工作線程的數量。
  • 作爲命令行可執行文件運行(用於調試),
  • 作爲「Windows服務」運行。在這裏,它創建一個我的ServiceBase衍生的 類的實例,然後調用System.ServiceProcess.ServiceBase.Run(instance);

目前,安裝步驟的附加服務名稱和線程計數的的ImagePath在註冊表,以便應用程序能確定它的服務名稱。

+0

你沒有提到什麼語言你寫的服務,。 – Walter 2009-12-03 19:09:08

回答

24

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

這裏是一個WMI溶液。重寫ServiceBase.ServiceMainCallback()也可以工作,但是這似乎爲我工作...

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

對於64位服務將「選擇*從Win32_Service」仍然工作? – shindigo 2014-04-22 15:59:13

+1

回答我自己的意見:是的,這個查詢在WIN7上使用64位服務。確保在調用Service.Run()之前不要嘗試執行此操作!此外 - 工作文件夾是Windows \ System32 – shindigo 2014-04-22 20:51:02

+0

感謝您抽出時間發佈此信息,您爲我節省了很多痛苦。 – Waltzy 2014-12-10 16:30:31

1

每個服務可執行文件必須實現的ServiceMain()入口點接收ServiceName作爲其第一個輸入參數。

如果您使用.NET編寫服務,則ServiceMain()入口點由.NET爲您實現。 ServiceName是在使用ServiceProcess.ServiceBase.ServiceName屬性安裝服務時分配的。如果您嘗試自定義.NET服務以支持動態ServiceName值,我不知道如何在運行時訪問實際的ServiceName。

+0

在運行時識別使用命令SC QUERYEX – lsalamon 2009-12-04 18:17:29

+0

@Remy - 它看起來像ServiceMain是用於C++代碼(http://msdn.microsoft.com/en-us/library/ms685138%28VS.85%29.aspx),I正在使用C#。 @Isalamon - 我相信你錯了,但是如果你能展示** SC **可以如何使用,請發表一個詳細的答案。 – NVRAM 2009-12-08 18:02:05

+0

即使.NET服務也有一個ServiceMain()入口點,這是.NET爲您實現的一個ServiceBase.ServiceMainCallback()方法)。我只注意到ServiceBase.OnStart事件有一個args參數。我沒有在.NET中編寫服務,但假設ServiceBase在構建args參數時沒有去除由ServiceMain()提供的第一個參數(ServiceName),那麼您應該能夠從中取出ServiceName。 – 2009-12-09 00:15:01

5

ServiceBase.ServiceName屬性使服務的編譯時的名字。如果在安裝服務時指定了不同的名稱,則ServiceName屬性將不會提供正確的名稱。所以,我不得不使用下面的代碼來獲取我的服務的服務名稱。

這是一個替代(不使用LINQ)到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"; 
} 

我錯誤地試圖獲取Windows服務的名稱作爲第一行的main()前未調用ServiceBase.Run()。我們必須先使用ServiceBase.Run()將我們的可執行文件註冊爲服務,然後才能獲取其名稱。

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

+1

+1,用於指出在調用ServiceBase.Run()之前服務名稱不可用。回想起來,似乎很明顯,但我正在檢查構造函數,並且仍然獲得編譯時的名稱。 – msulis 2014-02-19 20:55:58

+1

如果我們在同一個exe文件中託管多個服務,該怎麼辦?他們會在不同的過程中執行嗎?如果沒有,那麼我們需要告訴當前線程的服務名稱而不是當前進程。那樣的話我們該怎麼做? – drowa 2014-12-02 20:06:44

2

短版使用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"]; 
0

通過搜索更好的解決方案我想這:

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

我通過服務名和一個配置文件複製到binpath。 安裝該服務使用的SC.exe(我不使用installutil!)

在服務你可以得到命令行論點

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

有什麼不好this.ServiceName,如果你在service.cs裏面嗎?

即:

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

這與Windows Services列表中顯示的服務名稱不同:( – fabriciorissetto 2016-11-09 18:23:08

+0

ServiceBase。ServiceName和ServiceInstaller.ServiceName是兩個不同的屬性,但它們應該是相同的。 https://docs.microsoft.com/en-us/dotnet/api/system.serviceprocess.serviceinstaller?view=netframework-4.7.1 – 2018-01-23 14:48:11

0

我有,我需要知道完成Service.Run()(服務可能是一個客戶端或服務器安裝的一部分服務之前位置的雞和蛋的問題,安裝程序命名他們適當,我需要檢測它是在啓動)

我依靠註冊表讓我的名字。

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; 
} 
相關問題