【问题标题】:Install windows service without InstallUtil.exe在没有 InstallUtil.exe 的情况下安装 windows 服务
【发布时间】:2011-02-20 20:26:43
【问题描述】:

我正在尝试部署 Windows 服务,但不太确定如何正确执行。我将它构建为一个控制台应用程序开始,我现在将它变成了一个 Windows 服务项目,只需从服务中的 OnStart 方法调用我的类。

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

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

如果我创建问题的已接受答案链接到的类,下一步是什么?上传MyService.exe和MyService.exe.config到服务器,双击exe文件,Bob是我叔叔?

该服务只会安装在一台服务器上。

【问题讨论】:

  • @AZ 是的,我知道,我认为无论如何我都可以问这个问题,因为我指的是上一个问题,而我的略有不同,因为它不是 .net 服务(它不'没有任何界面)所以想确保相同的答案适用。

标签: c# deployment windows-services installation


【解决方案1】:

这是一个基础服务类(ServiceBase 子类),可以对其进行子类化以构建可以从命令行轻松安装的 windows 服务,无需 installutil.exe。本方案来源于How to make a .NET Windows Service start right after the installation?,添加一些代码,使用调用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也是这样吗?
【解决方案2】:

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

您可以使用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,您还可以做更多事情:卸载旧服务(如果您之前已经安装过)、检查是否存在同名服务...甚至将您的服务设置为自动启动。

众多参考之一:creating a service with sc.exe; how to pass in context parameters

我已经通过这种方式和InstallUtil完成了。我个人觉得使用 SC 更清洁,更健康。

【讨论】:

  • 这非常简单,应该比其他方法更受欢迎。
  • 我认为这是安装windows服务最简单的方法
  • 我同意,这完全是最简单的解决方案,应该比其他 UBER COMPLEX 解决方案更受欢迎!好东西!
  • 同意,这应该是解决方案。无需安装程序,并且 SC 已包含在 Windows Server 中。
【解决方案3】:

这个问题是由于安全问题,最好在 RUN AS ADMINISTRATOR 中打开 VS 2012 的开发人员命令提示符并安装您的服务,它肯定会解决您的问题。

【讨论】:

    【解决方案4】:

    Topshelf 是一个 OSS 项目,是在回答了这个问题后开始的,它使 Windows 服务变得更加容易。我强烈建议您研究一下。

    http://topshelf-project.com/

    【讨论】:

      【解决方案5】:

      您仍然可以在没有 Visual Studio 的情况下使用 installutil,它包含在 .net 框架中

      在您的服务器上,然后以管理员身份打开命令提示符:

      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)
      

      【讨论】:

        【解决方案6】:

        InstallUtil.exe 工具只是针对您服务中的安装程序组件的一些反射调用的包装器。因此,它实际上并没有做太多的事情,而是行使了这些安装程序组件提供的功能。 Marc Gravell 的解决方案只是提供了一种从命令行执行此操作的方法,这样您就不必再依赖目标计算机上的 InstallUtil.exe

        这是我基于 Marc Gravell 解决方案的分步说明。

        How to make a .NET Windows Service start right after the installation?

        【讨论】:

        • 谢谢,我快到了。我收到一个安全异常,提示“未找到源,但无法搜索部分或全部事件日志。无法访问的日志:安全性。我猜这可能与@ho 在他的评论中所谈论的帐户有关。我已将其设置为 LocalService,但我会更改它并重试。顺便说一句,您的代码中有微小的拼写错误,对于 InstallService(),它显示为“IDictionary state = new Hasttable();”而不是哈希表。
        • 没有问题。是的,我尝试了所有不同的帐户,除了用户之外的所有帐户都给出了相同的例外,对于用户我必须输入用户名和密码。应该是哪个用户名和密码?对于我登录服务器的用户?
        • 我成功安装了它!我使用 LocalSystem 帐户,在打开命令提示符时必须以管理员身份运行它才能工作。现在我需要弄清楚为什么服务一启动就停止,但这是另一个问题。 :) 谢谢!
        • 在 Windows 服务类的构造函数中(或在OnStart() 事件处理程序中),调用System.Diagnostics.Debugger.Break()。启动服务时,系统会提示您进入调试会话。您可以从那里进行调试。不过,我认为您必须在计算机上拥有管理员权限才能使其正常工作。
        【解决方案7】:

        不是双击,而是使用正确的命令行参数运行它,所以输入类似MyService -i 然后MyService -u 来卸载它。

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

        【讨论】:

          【解决方案8】:

          为什么不直接创建一个安装项目?这真的很容易。

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

          瞧,大功告成。

          更多信息请看这里: http://www.codeproject.com/KB/dotnet/simplewindowsservice.aspx

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

          【讨论】:

          • 谢谢,如果我无法得到其他建议,我会看看这个。不确定您指的是什么凭据?
          • 使用凭据时,他表示运行服务的帐户。
          • @ho 服务应该在哪个帐户下运行?基本上,该服务会监视一些文件夹中的新文件,如果有新文件则将文件上传到数据库,然后将文件移动到不同的文件夹。
          猜你喜欢
          • 2010-09-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多