【问题标题】:Why Main() method is called on service start?为什么在服务启动时调用 Main() 方法?
【发布时间】:2017-03-28 10:21:50
【问题描述】:

我有一个带有一个主类的应用程序它覆盖 ServiceBase 方法并具有 Main() 静态方法。 我想在命令行调用时使用 Main() 方法,从 Windows 服务管理调用时使用 OnStart()/OnStop()。

我使用 installutils 成功安装了这个应用程序作为服务,但是当我启动它时,调用的是 Main() 方法而不是预期的 OnStart()。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
using System.Xml.Serialization;
using System.Runtime.Serialization.Json;
using System.Threading;
using System.ServiceProcess;
using System.Configuration.Install;
using System.Reflection;

namespace Test
{

    class Program : ServiceBase
    {

        static void Main(string[] args)
        {  
            log.Error("Run as App");
        }


        protected override void OnStart(string[] args)
        {
            log.Info("Starting service");
        }

        protected override void OnStop()
        {
            log.Info("Stopping service");
        }
    }


}

【问题讨论】:

    标签: c# service windows-services


    【解决方案1】:

    Windows 服务有一个罪恶的秘密。他们以普通的旧控制台应用程序开始他们的生活。只有在它们开始运行并在服务控制管理器中注册后,它们才会转变为服务。

    因此,是的,Main 是第一个被调用的入口点是正确的,因为此时它只是一个控制台应用程序。

    服务向服务控制管理器注册并成为服务的方式是通过从您的Main 方法(或它调用的方法)调用ServiceBase.Run() 来处理的

    【讨论】:

    • 当 Environment.UserInteractive 为 false 时,我应该运行 ServiceBase.Run() 吗?似乎 ServiceBase.Run() 需要一个服务数组作为参数,我应该传递什么?
    • @Tobia - 通常,您会向它传递一个具有 OnStartOnStop 方法的类的实例。它需要一个数组,因为服务进程实际上可以支持/包含多个服务。如果您在 VS 中创建一个新的服务项目,它应该会自动为您设置。并且以UserInteractive 为中心是一种非常常见的区分方式(但不一定是 100% 万无一失的 - 什么都不是)
    • 我不知道这是否是一个肮脏的秘密,因为它是所有独立可执行程序(与可链接库相反)如何工作的明显且有据可查的表现。编译器将程序的入口点设置为 main() 方法或其重载之一。除此之外,由程序自己注册任何外部上下文(服务控制管理器)并保持自身运行(而不是仅仅退出 main() 方法并终止)。
    • @Craig - 可以想象操作系统具有“本机”支持以不同方式启动不同类型的二进制文件。当然,managed 入口点与实际入口点有很大不同。 .NET 可以将所有服务机制置于幕后,只是假装 OnStart/OnStop 等是服务托管代码部分的实际入口点。
    • 但他们没有,并命名了一个不同的操作系统。 :)。当然,这是可以想象的。任何二进制文件中入口点的唯一魔力是可执行文件中的第一条指令是指向入口点函数地址的 goto (jmp)。当然,在 .NET 中,跳转是到一个 .NET 存根,它在 .NET 运行时环境中包装程序的剩余生命周期,然后调用您的 main(),但它在功能上是相同的,并且值得注意的是,微软并没有假装 OnStart 和 OnStop 是特殊情况,因为这必须在编译器中完成,而且会很愚蠢。 :)
    【解决方案2】:

    这就是答案:

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Net;
    using System.Text;
    using System.Threading.Tasks;
    using System.Timers;
    using System.Xml.Serialization;
    using System.Runtime.Serialization.Json;
    using System.Threading;
    using System.ServiceProcess;
    using System.Configuration.Install;
    using System.Reflection;
    
    namespace Test
    {
    
        class Program : ServiceBase
        {
    
            static void Main(string[] args)
            {  
                    if (System.Environment.UserInteractive)
                    {
                        log.Debug("App");
                    }
                    else
                    {
                        ServiceBase.Run(new ServiceBase[] { new Program() });
                    }
            }
    
    
            protected override void OnStart(string[] args)
            {
                log.Info("Starting service");
            }
    
            protected override void OnStop()
            {
                log.Info("Stopping service");
            }
        }
    
    
    }
    

    【讨论】:

      【解决方案3】:

      Main 方法也像在任何其他程序中一样在服务中被调用。 (见https://msdn.microsoft.com/en-us/library/windows/desktop/ms685477(v=vs.85).aspx)。

      您可以使用属性Environment.UserInteractive (https://msdn.microsoft.com/en-us/library/system.environment.userinteractive.aspx) 来确定应用是由用户调用还是作为服务调用。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-12-06
        • 2012-05-16
        • 2013-03-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-10-26
        • 1970-01-01
        相关资源
        最近更新 更多