安装Windows服务以作为虚拟用户运行

时间:2013-02-01 14:41:19

标签: .net windows-services installation

我有一个安装.NET Windows服务的ServiceProcessInstaller

如果我要么安装过程完美无缺:

  • 将服务设置为以SYSTEM(serviceProcessInstaller1.Account = ServiceAccount.LocalSystem)运行。
  • 通过指定serviceProcessInstaller1.Account = ServiceAccount.UserUsername属性或让安装过程提示我来将服务设置为以普通用户(Password)运行。

但是,我希望该服务以虚拟用户身份运行, a la NT Service\ServiceName。如果查看某些SQL Server服务,您将看到它们默认以自己的虚拟用户帐户登录。 http://technet.microsoft.com/en-us/library/dd548356.aspx上有更多(尽管有限)的信息。


我已尝试设置serviceProcessInstaller1.Username = @"NT Service\ServiceName",但安装程序会抛出以下错误,无论我提供什么密码(我已尝试String.Empty,与用户名相同,我自己的密码,{ {1}}调出交互式对话框,甚至是随机垃圾邮件:

  

帐户名和安全ID之间没有映射

但是,如果我正常安装服务(例如以SYSTEM身份运行),我可以从null管理单元转到服务的属性,在登录页面上将用户更改为{{1它很漂亮。

我也调查了ChangeServiceConfig2 function,但我似乎无法改变它。


如何从我services.msc中的代码中将用户登录设置为虚拟用户NT Service\ServiceName

2 个答案:

答案 0 :(得分: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和配置文件授予用户读/写权限,否则您将收到Access Denied错误。

答案 1 :(得分: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