2013-02-01 52 views
2

我有一個安裝.NET Windows服務的ServiceProcessInstaller安裝Windows服務作爲虛擬用戶運行

安裝過程完美地工作,如果我可以:

  • 設置爲SYSTEM(serviceProcessInstaller1.Account = ServiceAccount.LocalSystem)運行服務。
  • 通過指定UsernamePassword屬性或讓安裝進程提示我,將服務設置爲以普通用戶身份運行(serviceProcessInstaller1.Account = ServiceAccount.User)。

不過,我想作爲一個虛擬用戶,一拉NT Service\ServiceName運行服務。如果您查看一些SQL Server服務,您將看到它們默認以自己的虛擬用戶帳戶登錄。在http://technet.microsoft.com/en-us/library/dd548356.aspx有更多,儘管有限的信息。


我已經嘗試設置serviceProcessInstaller1.Username = @"NT Service\ServiceName",但隨後安裝程序引發以下錯誤不管是什麼我給的密碼(我曾嘗試String.Empty,相同的用戶名,我自己的密碼,null彈出互動對話,甚至是隨機的垃圾):

帳戶名和安全ID之間沒有映射已完成

但是,如果我安裝服務正常(例如,然後我可以從services.msc管理單元中進入服務的屬性,在登錄頁面上將用戶更改爲NT Service\ServiceName,並且它運行得非常漂亮。

我也看過ChangeServiceConfig2 function,但我似乎無法讓它改變任何東西。


我怎樣才能在用戶日誌從代碼設置爲虛擬用戶NT Service\ServiceNameServiceProcessInstaller內?

+0

託管和虛擬服務帳戶是一個非常新的Windows功能。你需要在諸如serverfault.com這樣的站點上提出關於它的問題,在那裏你會發現系統管理員用這個來修補它。 http://technet.microsoft.com/en-us/library/dd548356%28WS.10%29.aspx –

+0

@HansPassant我意識到這有點臨界,但我的問題是關於如何從代碼設置用戶,具體爲我可以清楚地手動設置用戶。因此,我認爲這是一個編程問題而不是系統管理員問題,併發布在這裏。關於安裝服務的SO似乎還有比SF更多的問題 - 並不是說​​它意味着我想的很多。 –

回答

3

您不能直接使用ServiceProcessInstaller對象。但是,您可以使用the Change method in WMIServiceInstaller.Committed事件中設置服務安裝後的用戶名。指定用戶名作爲wmiParams[6]密碼保留爲空

void serviceInstaller1_Committed(object sender, InstallEventArgs e) 
{ 
    using (ManagementObject service = new ManagementObject(new ManagementPath("Win32_Service.Name='ServiceName'"))) 
    { 
     object[] wmiParams = new object[11]; 
     wmiParams[6] = @"NT Service\ServiceName"; 
     service.InvokeMethod("Change", wmiParams); 
    } 
} 

最後,不要忘了給用戶的讀/服務exe文件和配置文件執行權限,或者你會得到拒絕訪問錯誤。

+0

如果你是一個像我這樣的虛擬角色,那麼在wmiParams [6]行中給出的帳戶名稱與你的服務名稱相符是非常重要的。 – Jay

+0

此代碼很適合使用TopShelf: https://github.com/Topshelf/Topshelf/pull/355 –

1

上述建議溶解的替代方法(使用ServiceInstaller.Committed事件)在connect.microsoft.com上描述爲解決方法。這個想法是通過反射來調整私人字段haveLoginInfo以允許null作爲有效的密碼。

const string s_ServiceName = "myservice1"; 
    const string s_DisplayName = "Tell admin what it is"; 
    const string s_Description = "Tell admin what it does"; 

     var procesServiceInstaller = new ServiceProcessInstaller 
     { 
      Account = ServiceAccount.User, 
      Username = string.Format("NT Service\\{0}", s_ServiceName), 
      Password = null, 
     }; 

     //Here comes the hack. 
     // ReSharper disable once PossibleNullReferenceException 
     procesServiceInstaller 
      .GetType() 
      .GetField("haveLoginInfo", BindingFlags.Instance | BindingFlags.NonPublic) 
      .SetValue(procesServiceInstaller, true); 



     var serviceInstaller = new ServiceInstaller(); 
     var path = string.Format(
       "/assemblypath={0}", 
       Assembly.GetExecutingAssembly().Location); 
     string[] cmdline = { path }; 

     var context = new InstallContext("", cmdline); 
     serviceInstaller.Context = context; 
     serviceInstaller.DisplayName = s_DisplayName; 
     serviceInstaller.ServiceName = s_ServiceName; 
     serviceInstaller.Description = s_Description; 
     serviceInstaller.StartType = ServiceStartMode.Manual; 
     serviceInstaller.Parent = procesServiceInstaller; 

     try 
     { 
      var state = new ListDictionary(); 
      serviceInstaller.Install(state); 
     } 
     catch (Win32Exception win32Exception) 
     { 
      //TODO: HandleException(win32Exception); 
     } 
     catch (InvalidOperationException ex) 
     { 
      //TODO: HandleException(ex); 
     } 

即使這個解決方案並不少,至少在視覺上不那麼難看。

注意:在connect.microsoft.com的變通方法說明中存在錯誤。這裏提到的私人字段名稱是hasLoginInfo,但必須是haveLoginInfo

相關問題