不使用InstallUtil.exe安装Windows服务

43

我正在尝试部署一个Windows服务,但不太确定正确的方法。我一开始将其构建为控制台应用程序,现在已将其转换为Windows服务项目,并从服务中的OnStart方法中调用我的类。

现在我需要在没有Visual Studio的服务器上安装它,如果我理解正确,这意味着我不能使用InstallUtil.exe,而必须创建一个安装程序类。这个理解对吗?

我曾经查看过一个之前的问题,Install a .NET windows service without InstallUtil.exe,但我只是想确保我理解得正确。

如果我创建了该问题接受答案链接的类,下一步是什么?上传MyService.exe和MyService.exe.config到服务器,双击exe文件,然后就成功了吗?

这个服务只会安装在一个服务器上。


复制 - https://dev59.com/XnVC5IYBdhLWcg3wjx5d - Adrian Zanescu
@AZ 是的,我知道,我认为问这个问题应该没问题,因为我是在参考之前的问题,而我的问题略有不同,因为它不是一个 .net 服务(它没有任何接口),所以想确保相同的答案适用。 - annelie
8个回答

68

我知道这是一个非常古老的问题,但最好使用新信息来更新它。

您可以使用sc命令安装服务:

InstallService.bat:

@echo OFF
echo Stopping old service version...
net stop "[YOUR SERVICE NAME]"
echo Uninstalling old service version...
sc delete "[YOUR SERVICE NAME]"

echo Installing service...
rem DO NOT remove the space after "binpath="!
sc create "[YOUR SERVICE NAME]" binpath= "[PATH_TO_YOUR_SERVICE_EXE]" start= auto
echo Starting server complete
pause

使用 SC,您还可以做更多的事情:卸载旧服务(如果您之前已经安装过它),检查是否存在具有相同名称的服务......甚至将您的服务设置为自动启动。

其中一个参考:使用 sc.exe 创建服务; 如何传递上下文参数

我已经通过这两种方式和 InstallUtil 来实现。就个人而言,我觉得使用 SC 更加简洁并且更有益于您的健康。


3
这非常简单,应该比其他方法获得更多赞。 - Sam
3
我认为这是安装Windows服务最简单的方法。 - ykh
1
我同意,这绝对是最简单的解决方案,并且应该比其他超级复杂的解决方案得到更多的赞同!干得好! - Robert Green MBA
1
同意,这应该是解决方案。无需安装程序,SC已经包含在Windows Server中了。 - Luke Vo
1
更新2022年:在Windows 10下似乎不再需要保留“binpath=”后面的空格! - 001

34

即使没有安装Visual Studio,您仍然可以使用包含在.NET框架中的installutil。

在服务器上,以管理员身份打开命令提示符,然后执行以下操作:

CD C:\Windows\Microsoft.NET\Framework\v4.0.version (insert your version)

installutil "C:\Program Files\YourWindowsService\YourWindowsService.exe" (insert your service name/location)

卸载:

installutil /u "C:\Program Files\YourWindowsService\YourWindowsService.exe" (insert your service name/location)

21
InstallUtil.exe工具只是对您的服务中的安装程序组件进行一些反射调用的包装器。因此,它实际上并没有做太多事情,只是运行这些安装程序组件提供的功能。马克·格拉维尔(Marc Gravell)的解决方案只是提供了一种从命令行执行此操作的方法,因此您不再需要依赖目标计算机上的InstallUtil.exe
以下是基于Marc Gravell的解决方案的逐步说明。 如何使.NET Windows服务在安装后立即启动?

谢谢,我已经接近完成了。我收到了一个安全异常,说“找不到源,但是无法搜索某些或所有事件日志。不可访问的日志:安全性”。我猜这可能与它正在运行的帐户有关,就像@ho在他的评论中所说的那样。我已将其设置为LocalService,但我会更改并再次尝试。顺便说一下,在您的代码中有一个小拼写错误,对于InstallService(),它说“IDictionary state = new Hasttable();”而不是Hashtable。 - annelie
没问题。好的,我已经尝试了所有不同的账户,除了用户之外,其他所有账户都会出现相同的异常情况,而对于用户,我必须输入用户名和密码。那应该是哪个用户名和密码呢?是我用来登录服务器的用户吗? - annelie
我成功安装了它!我使用LocalSystem账户,在打开命令提示符时必须以管理员身份运行才能正常工作。现在我需要弄清楚为什么服务一启动就停止,但这是另一个问题。 :) 谢谢! - annelie
在您的Windows服务类构造函数中(或在OnStart()事件处理程序中),调用System.Diagnostics.Debugger.Break()。当您启动服务时,将提示您进入调试会话。您可以从那里进行调试。不过我认为您必须拥有管理员权限才能使此操作生效。 - Matt Davis

6

这是一个基础服务类(ServiceBase子类),可以被子类化以构建一个Windows服务,该服务可以轻松地从命令行安装,无需使用installutil.exe。此解决方案源自如何使.NET Windows服务在安装后立即启动? ,添加了一些代码以使用调用StackFrame获取服务类型。

public abstract class InstallableServiceBase:ServiceBase
{

    /// <summary>
    /// returns Type of the calling service (subclass of InstallableServiceBase)
    /// </summary>
    /// <returns></returns>
    protected static Type getMyType()
    {
        Type t = typeof(InstallableServiceBase);
        MethodBase ret = MethodBase.GetCurrentMethod();
        Type retType = null;
        try
        {
            StackFrame[] frames = new StackTrace().GetFrames();
            foreach (StackFrame x in frames)
            {
                ret = x.GetMethod();

                Type t1 = ret.DeclaringType;

                if (t1 != null && !t1.Equals(t) &&   !t1.IsSubclassOf(t))
                {


                    break;
                }
                retType = t1;
            }
        }
        catch
        {

        }
        return retType;
    }
    /// <summary>
    /// returns AssemblyInstaller for the calling service (subclass of InstallableServiceBase)
    /// </summary>
    /// <returns></returns>
    protected static AssemblyInstaller GetInstaller()
    {
        Type t = getMyType();
        AssemblyInstaller installer = new AssemblyInstaller(
            t.Assembly, null);
        installer.UseNewContext = true;
        return installer;
    }

    private bool IsInstalled()
    {
        using (ServiceController controller =
            new ServiceController(this.ServiceName))
        {
            try
            {
                ServiceControllerStatus status = controller.Status;
            }
            catch
            {
                return false;
            }
            return true;
        }
    }

    private bool IsRunning()
    {
        using (ServiceController controller =
            new ServiceController(this.ServiceName))
        {
            if (!this.IsInstalled()) return false;
            return (controller.Status == ServiceControllerStatus.Running);
        }
    }
    /// <summary>
    /// protected method to be called by a public method within the real service
    /// ie: in the real service
    ///    new internal  void InstallService()
    ///    {
    ///        base.InstallService();
    ///    }
    /// </summary>
    protected void InstallService()
    {
        if (this.IsInstalled()) return;

        try
        {
            using (AssemblyInstaller installer = GetInstaller())
            {

                IDictionary state = new Hashtable();
                try
                {
                    installer.Install(state);
                    installer.Commit(state);
                }
                catch
                {
                    try
                    {
                        installer.Rollback(state);
                    }
                    catch { }
                    throw;
                }
            }
        }
        catch
        {
            throw;
        }
    }
    /// <summary>
    /// protected method to be called by a public method within the real service
    /// ie: in the real service
    ///    new internal  void UninstallService()
    ///    {
    ///        base.UninstallService();
    ///    }
    /// </summary>
    protected void UninstallService()
    {
        if (!this.IsInstalled()) return;

        if (this.IsRunning()) {
            this.StopService();
        }
        try
        {
            using (AssemblyInstaller installer = GetInstaller())
            {
                IDictionary state = new Hashtable();
                try
                {
                    installer.Uninstall(state);
                }
                catch
                {
                    throw;
                }
            }
        }
        catch
        {
            throw;
        }
    }

    private void StartService()
    {
        if (!this.IsInstalled()) return;

        using (ServiceController controller =
            new ServiceController(this.ServiceName))
        {
            try
            {
                if (controller.Status != ServiceControllerStatus.Running)
                {
                    controller.Start();
                    controller.WaitForStatus(ServiceControllerStatus.Running,
                        TimeSpan.FromSeconds(10));
                }
            }
            catch
            {
                throw;
            }
        }
    }

    private void StopService()
    {
        if (!this.IsInstalled()) return;
        using (ServiceController controller =
            new ServiceController(this.ServiceName))
        {
            try
            {
                if (controller.Status != ServiceControllerStatus.Stopped)
                {
                    controller.Stop();
                    controller.WaitForStatus(ServiceControllerStatus.Stopped,
                         TimeSpan.FromSeconds(10));
                }
            }
            catch
            {
                throw;
            }
        }
    }
}

您只需要在真实服务中实现两个公共/内部方法:

    new internal  void InstallService()
    {
        base.InstallService();
    }
    new internal void UninstallService()
    {
        base.UninstallService();
    }

然后在您想要安装服务时调用它们:

    static void Main(string[] args)
    {
        if (Environment.UserInteractive)
        {
            MyService s1 = new MyService();
            if (args.Length == 1)
            {
                switch (args[0])
                {
                    case "-install":
                        s1.InstallService();

                        break;
                    case "-uninstall":

                        s1.UninstallService();
                        break;
                    default:
                        throw new NotImplementedException();
                }
            }


        }
        else {
            ServiceBase[] ServicesToRun;
            ServicesToRun = new ServiceBase[] 
            { 
                new MyService() 
            };
            ServiceBase.Run(MyService);            
        }

    }

这是 sysmon -i/-u 的工作方式吗? - evandrix

5

为什么不创建一个安装程序呢?这很容易。

  1. 向服务添加服务安装程序(在看似无用的服务“设计”界面上进行)
  2. 创建一个安装程序并将服务输出添加到安装程序文件夹中
  3. 最重要的是将服务项目输出添加到所有自定义操作中

完成了,就这么简单。

更多信息请参见: http://www.codeproject.com/KB/dotnet/simplewindowsservice.aspx

还有一种方法可以提示用户输入凭据(或提供您自己的凭据)。


谢谢,如果我无法让其他建议起作用,我会看一下这个。不确定你指的是什么凭据? - annelie
他所说的凭据是指服务运行的帐户。 - Hans Olsson
@ho 服务应该在哪个账户下运行?基本上,该服务会监视一些文件夹以查找新文件,如果有新文件,则将文件上传到数据库,然后将文件移动到另一个文件夹。 - annelie

1

Topshelf是一个开源项目,它在这个问题得到解答后开始,使得Windows服务的编写变得更加容易。我强烈建议您去了解一下。

http://topshelf-project.com/


0

不要双击运行,应该使用正确的命令行参数来运行它,例如输入 MyService -i 然后输入 MyService -u 来卸载它。

你也可以使用 sc.exe 来安装和卸载它(或者复制 InstallUtil.exe)。


0

这个问题是由于安全原因造成的,最好以管理员身份打开 VS 2012 的开发者命令提示符,然后安装您的服务,这一定会解决您的问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接