为什么服务启动时会调用Main()方法?

3

我有一个应用程序,主要类覆盖了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");
        }
    }


}
3个回答

10

Windows服务有一个不为人知的秘密。它们最初是普通的控制台应用程序。只有在它们开始运行并向服务控制管理器注册后,它们才会转变成服务。

因此,没错,Main 是第一个被调用的入口点,因为在这个时候,它只是一个控制台应用程序。

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


我不知道这是否算是一个肮脏的秘密,更多的是一种明显且有充分记录的表现形式,展示了几乎所有独立可执行程序的工作方式(与可链接库相对)。编译器将程序的入口点设置为main()方法或其重载之一。除此之外,程序需要向任何外部上下文(服务控制管理器)注册自己,并保持运行状态(而不仅仅是从main()方法中退出并终止)。 - Craig Tullis
1
@Craig - 可以想象操作系统可以“本地”支持以不同方式启动不同类型的二进制文件。当然,“托管”的入口点与实际的入口点远不相同。.NET可以将所有服务机制放在幕后,并且只是假装OnStart / OnStop等是服务的托管代码部分的实际入口点。 - Damien_The_Unbeliever
但是他们没有这样做,可以提到一个不同的操作系统。当然这是可能的。在任何二进制文件中,入口点的唯一魔法就是可执行文件中的第一条指令是一个跳转(jmp)到入口点函数的地址。当然,在.NET中,这个跳转是到一个.NET存根的,它将程序的其余生命周期包装在.NET运行时环境中,然后调用你的main()函数,但它在功能上是相同的,值得注意的是,微软并没有假装OnStart和OnStop是特殊情况,因为那必须在编译器中完成,这是愚蠢的。 :) - Craig Tullis
@Craig - 在我开始接触的AmigaOS(经典版)上,编译器通常会使从Workbench启动和从控制台启动有很大不同。特别是,您必须处理窗口消息以获取您的“启动参数”,而不是将它们作为参数传递给main()例程。而VB则保持了一个虚构,即您可以拥有一个“启动对象”(一个表单),而不是编写一个Main - Damien_The_Unbeliever
还有谁在使用VB呢? :p. 在那个窗体类中深藏了一个主方法。.NET本身并没有做出任何不同... Amiga真的很棒!可惜康柏高管掠夺了这家公司。在那种情况下,Workbench只是加载exe文件,类似于dll... - Craig Tullis
显示剩余3条评论

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");
        }
    }


}

1

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