Windows服务以本地系统帐户启动时卡在“启动”状态

27

我用C#开发了一个控制台应用程序来创建HTTP服务器,现在想将其转换成Windows服务以便不需要登录机器即可初始化。我按照How to create Windows Service中的所有步骤,并选择“Local System”作为账户,但当我在服务器上安装并单击启动按钮时,需要等待一段时间后就会出现以下错误:

Error 1053: The service did not respond to the start or control request in timely fashion.

之后,服务状态停留在“starting”状态,应用程序无法工作,我甚至不能停止该服务。

为尝试解决此问题,我将其更改为“Network Service”,这样它就可以正常启动,但是当我在命令提示符下使用“netstat -an”命令检查端口时,应用程序没有监听我设置的端口。但如果我将其作为控制台应用程序运行,则应用程序正常监听。

因此,我正在寻找以下两个问题的答案:

  1. 我应该怎么做才能确保使用本地系统帐户正确启动服务?
  2. 如果我决定使用网络服务帐户,我应该注意些什么以确保我的服务作为服务器正常工作?

OnStart 应该在 30 秒内完成。请参考此链接:http://stackoverflow.com/questions/2631364/c-sharp-windows-service - Orçun Çolak
6个回答

36

当我将我的控制台应用程序转换为Windows服务时,我只是直接将代码放在OnStart方法中。然而,我意识到OnStart方法应该启动服务,但需要在服务实际启动之前结束一段时间。因此,我创建了一个线程来运行我的服务,并让OnStart方法完成。我测试过,服务完全正常。以下是代码:

protected override void OnStart(string[] args)
{
    Listener(); // this method never returns
}

以下是工作原理:

protected override void OnStart(string[] args)
{
    Thread t = new Thread(new ThreadStart(Listener));
    t.Start();
}

但我仍然不明白为什么使用网络服务帐户时,该服务运行(通过“启动”状态),但没有工作。如果有人知道原因,我会很高兴知道。


2
最好的猜测是Listener()的调用在静默失败,可能是因为网络服务缺少必要的访问权限,而错误检查不够全面。 - Harry Johnston
4
我曾经用一个有意义的无限循环做了类似的事情,它不会返回。最终我使用 Task.Run(() => MyMethod()); 来启动它。 - Kevin Giszewski
1
如果它是一个HTTP服务器,那么它可能正在等待连接,从未返回,这就是为什么Listener()从不返回并且服务始终处于“启动”模式的原因;使用线程是正确的解决方案。 - Pedro Vicente
很奇怪!之前在async OnStart方法中做所有事情时它是可以工作的,但现在我必须按照你的答案才能让它工作。 - Amir Hajiha

20

如果您有一个在Windows服务中未响应或显示挂起的服务,无法停止,请按照以下步骤强制停止该服务。

  • 开始 -> 运行开始 -> 输入 services.msc 并按下 Enter
  • 查找该服务并检查其属性以确定其服务名称
  • 一旦找到,请打开命令提示符。键入 sc queryex [servicename]
  • 确定PID(进程ID)
  • 在同一命令提示符中键入 taskkill /pid [pid number] /f

运行 sc queryex 命令后,如果没有任何输出,您应该在命令提示符中运行该命令,或在 PowerShell 中执行 cmd 命令。 - 我零0七

1

查找服务的PID

sc queryex <SERVICE_NAME>

给出以下结果

SERVICE_NAME:Foo.Services.Bar TYPE:10 WIN32_OWN_PROCESS STATE:2 0 START_PENDING(NOT_STOPPABLE,NOT_PAUSABLE,IGNORES_SHUTDOWN)WIN32_EXIT_CODE:0(0x0)SERVICE_EXIT_CODE:0(0x0)CHECKPOINT:0x0 WAIT_HINT:0x0 PID:3976 FLAGS:

现在杀死该服务:

taskkill /f /pid 3976

SUCCESS:PID为3976的进程已被终止。


0

对我来说,这是一个查看外部队列的 while 循环。while 循环会一直运行,直到队列为空为止。通过仅在 Environment.UserInteractive 时直接调用定时器事件来解决问题。因此,该服务可以很容易地进行调试,但在作为服务运行时,它将等待计时器的 ElapsedEventHandler 事件。

服务:

partial class IntegrationService : ServiceBase
{
    private static Logger logger = LogManager.GetCurrentClassLogger();
    private System.Timers.Timer timer;

    public IntegrationService()
    {
        InitializeComponent();
    }

    protected override void OnStart(string[] args)
    {
        try
        {
            // Add code here to start your service.
            logger.Info($"Starting IntegrationService");

            var updateIntervalString = ConfigurationManager.AppSettings["UpdateInterval"];
            var updateInterval = 60000;
            Int32.TryParse(updateIntervalString, out updateInterval);

            var projectHost = ConfigurationManager.AppSettings["ProjectIntegrationServiceHost"];
            var projectIntegrationApiService = new ProjectIntegrationApiService(new Uri(projectHost));
            var projectDbContext = new ProjectDbContext();
            var projectIntegrationService = new ProjectIntegrationService(projectIntegrationApiService, projectDbContext);
            timer = new System.Timers.Timer();
            timer.AutoReset = true;
            var integrationProcessor = new IntegrationProcessor(updateInterval, projectIntegrationService, timer);
            timer.Start();
        }
        catch (Exception e)
        {
            logger.Fatal(e);
        }
    }

    protected override void OnStop()
    {
        try
        {
            // Add code here to perform any tear-down necessary to stop your service.
            timer.Enabled = false;
            timer.Dispose();
            timer = null;
        }
        catch (Exception e)
        {
            logger.Fatal(e);
        }
    }
}

处理器:

public class IntegrationProcessor
{
    private static Logger _logger = LogManager.GetCurrentClassLogger();
    private static volatile bool _workerIsRunning;
    private int _updateInterval;
    private ProjectIntegrationService _projectIntegrationService;

    public IntegrationProcessor(int updateInterval, ProjectIntegrationService projectIntegrationService, Timer timer)
    {
        _updateInterval = updateInterval;
        _projectIntegrationService = projectIntegrationService;

        timer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
        timer.Interval = _updateInterval;

        //Don't wait for first elapsed time - Should not be used when running as a service due to that Starting will hang up until the queue is empty
        if (Environment.UserInteractive)
        {
            OnTimedEvent(null, null);
        }
        _workerIsRunning = false;
    }

    private void OnTimedEvent(object source, ElapsedEventArgs e)
    {
        try
        {
            if (_workerIsRunning == false)
            {
                _workerIsRunning = true;

                ProjectInformationToGet infoToGet = null;
                _logger.Info($"Started looking for information to get");
                //Run until queue is empty
                while ((infoToGet = _projectIntegrationService.GetInformationToGet()) != null)
                {
                    //Set debugger on logger below to control how many cycles the service should run while debugging.
                    var watch = System.Diagnostics.Stopwatch.StartNew();
                    _logger.Info($"Started Stopwatch");
                    _logger.Info($"Found new information, updating values");
                    _projectIntegrationService.AddOrUpdateNewInformation(infoToGet);
                    _logger.Info($"Completed updating values");
                    watch.Stop();
                    _logger.Info($"Stopwatch stopped. Elapsed seconds: {watch.ElapsedMilliseconds / 1000}. " +
                                 $"Name queue items: {infoToGet.NameQueueItems.Count} " +
                                 $"Case queue items: {infoToGet.CaseQueueItems.Count} " +
                                 $"Fee calculation queue items: {infoToGet.FeeCalculationQueueItems.Count} " +
                                 $"Updated foreign keys: {infoToGet.ShouldUpdateKeys}");
                }

                _logger.Info($"Nothing more to get from integration service right now");

                _workerIsRunning = false;
            }
            else
            {
                _logger.Info($"Worker is already running! Will check back again after {_updateInterval / 1000} seconds");
            }
        }
        catch (DbEntityValidationException exception)
        {
            var newException = new FormattedDbEntityValidationException(exception);
            HandelException(newException);
            throw newException;
        }
        catch (Exception exception)
        {
            HandelException(exception);
            //If an exception occurs when running as a service, the service will restart and run again
            if (Environment.UserInteractive)
            {
                throw;
            }
        }
    }

    private void HandelException(Exception exception)
    {
        _logger.Fatal(exception);
        _workerIsRunning = false;
    }
}

0

检查Windows应用程序事件日志,它可能包含一些来自您的服务自动生成的事件源的条目(该事件源应具有与服务相同的名称)。


我检查了一下,但是没有找到与我的服务相关的日志。我在我的问题中添加了更多信息:当我尝试启动服务时,我收到以下错误消息:错误1053:服务未能及时响应启动或控制请求。我搜索了这个错误,但主要与Windows Server 2003中包含的一个错误有关。我的Windows服务器是2012,所以我认为这个错误修复不适用于我的情况。你怎么看? - joaocarlospf

0

您可以尝试在注册表中使用一个键来增加Windows服务的超时时间

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control

"ServicesPipeTimeout"=dword:300000 (300秒或5分钟)
如果不存在,则必须创建。

该值以毫秒为单位,因此您指定的300000表示300秒(5分钟),而不是30秒。 (请参见https://support.microsoft.com/en-us/help/922918/a-service-does-not-start-and-events-7000-and-7011-are-logged-in-window) - Richard II

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