处理 Windows 服务中的致命错误

Posted

技术标签:

【中文标题】处理 Windows 服务中的致命错误【英文标题】:Handling fatal error in a Windows Service 【发布时间】:2011-11-15 18:08:55 【问题描述】:

我创建了一个 Windows 服务和安装程序,它监视一组文件的更改并将任何更改的文件复制到 WatchlistConfig.xml 文件中指定的目标目录。

我的服务有几个问题: 1. 有一次停止运行。 (不可接受) 2. 我们有时必须尝试多次启动服务才能“启动”。

我认为问题 #1 可能是由于未处理应用程序中的致命错误。我发现了一些我试图合并到 Main() 方法中的代码,但它是为控制台应用程序编写的(应用程序不是一个可识别的类),因此现在被注释掉了。知道哪个是在服务中实现此功能的正确类吗?

我猜问题 #2 很可能是超时。监视列表当前由网络上不同机器上的 9 个不同文件组成。连接到这些源不是立即的(不是全部都在一个域上)。有没有办法为服务启动设置不同的超时值?

这是相关代码。可应要求提供其他课程。 提前致谢。

编辑:错误地发布了我用来调试的测试工具(控制台)中的 Main()。我已将其留在原处并从 WinSvc 项目中添加 Program 类

    //Console Test harness
        class Program
            
                [STAThread]
                static void Main(string[] args)
                
                    //AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
                    //Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
                    //Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
                    //Application.EnableVisualStyles();
                    //Application.SetCompatibleTextRenderingDefault(false);
                    //Application.Run(new Form1());

                    TimedWatchList twl = new TimedWatchList(new PSU_Config(Helpers.GetConfigFile()));

                    Console.WriteLine("Press \'q\' to quit the sample.");
                    while (Console.Read() != 'q') ;
                

                static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
                
                    HandleException((Exception)e.ExceptionObject);
                

                static void HandleException(Exception e)
                
                    //Handle/Log Exception Here
                
                static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
                
                    Logger.Loggit(e.Exception.Message);
                
            

    //Actual Service
       static class Program
        
            /// <summary>
            /// The main entry point for the application.
            /// </summary>
            static void Main()
            
                ServiceBase[] ServicesToRun;
                ServicesToRun = new ServiceBase[] 
                 
                    new Psu() 
                ;
                ServiceBase.Run(ServicesToRun);
            
        

      public partial class Psu : ServiceBase
        
            public Psu()
            
                InitializeComponent();
                TimedWatchList twl = new TimedWatchList(new PSU_Config(Helpers.GetConfigFile()));
            

            protected override void OnStart(string[] args)
            
            

            protected override void OnStop()
            
            
        


        public class TimedWatchList
        
            public static PSU_Config Config  get; set; 
            List<WatchFile> WatchList = new List<WatchFile>();

            public TimedWatchList(PSU_Config config)
            
                Config = config;
                if (Config.PrintDebugMsgs) Logger.Loggit("Attempting to create TimedWatchList object");
                WatchList = WatchListFactory.GetWatchList(Helpers.GetWatchListFile());
                if (Config.PrintDebugMsgs) Logger.Loggit("TimedWatchList created");

                Timer _timer = new Timer();
                _timer.Interval += Config.Interval;
                _timer.Enabled = true;
                // register OnTimedEvent() to fire on each "tick" 
                _timer.Elapsed += OnTimedEvent;
            

            private void OnTimedEvent(object source, ElapsedEventArgs e) 
            
                foreach (WatchFile file in WatchList)
                
                    file.PostOnUpdate();
                
            

        //TimedWatchList class

 internal class WatchFile
    // represents a file that is being watched
    
    #region Props
        public FileInfo SourceFile  get; set; 
        public DirectoryInfo TargetPath  get; set; 
    #endregion //Props

    #region CTOR
        public WatchFile()  
        public WatchFile(string fileName, string sourcePath, string destPath)
        
            SourceFile = new FileInfo(Path.Combine(sourcePath, fileName));
            TargetPath = new DirectoryInfo(destPath);
        
        public WatchFile(FileInfo sourceFile, DirectoryInfo targetDirectory)
        
            SourceFile = sourceFile;
            TargetPath = targetDirectory;
        
    #endregion //CTOR

        public void PostOnUpdate()
        
            //if (TimedWatchList.Config.PrintDebugMsgs) Logger.Loggit("WatchFile Post Event called for: " + SourceFile.Name);
            //if (TimedWatchList.Config.PrintDebugMsgs) Logger.Loggit("Stored LastModified datetime: " + LastModified);

            string targetPath = String.Format(@"0\1", TargetPath.FullName, SourceFile.Name);
            
                try
                
                    //ensure directory exists
                    if (!Directory.Exists(TargetPath.FullName)) Directory.CreateDirectory(TargetPath.FullName);

                    //ensure file version is current
                    if (!File.Exists(targetPath) || (File.GetLastWriteTime(targetPath) != File.GetLastWriteTime(SourceFile.FullName)))
                    
                        Logger.Loggit(String.Empty);
                        Logger.Loggit("Attempting to copy: " + SourceFile + " (" + File.GetLastWriteTime(SourceFile.FullName) + ")");
                        SourceFile.CopyTo(targetPath, true);
                        Logger.Loggit("\tCopy posted.\tLastModified: " + File.GetLastWriteTime(targetPath));
                    

                
                catch (IOException ioex)
                
                    Logger.Loggit("Error: " + ioex.Message);
                
                catch (Exception ex)
                
                    Logger.Loggit("Error: " + ex.Message);
                
            
        
    // WatchFile class

【问题讨论】:

【参考方案1】:

真的没必要猜;作为一项服务,您应该将错误记录到系统事件日志中。设置一个***处理程序(正如你所做的那样),但不要指望能够处理它。

如果错误未得到处理,您将无法对其进行任何处理。记录并退出。尽快发现您可以处理的错误,测试和设计您的代码,以免出现其他问题。

您可以将服务设置为在崩溃后自动重启,但这应该是最后的手段。拿出你的调试器,找出错误发生的确切位置和原因。我在这里看到很多“它可能是[某物]”和“它可能是[其他东西]”的陈述。同样,没有充分的理由去猜测。您可以使用工具来帮助您弄清楚到底发生了什么。

【讨论】:

糟糕,我从控制台测试工具中粘贴了 Main()。不知道如何调试服务,所以我一直在通过引用程序集并在控制台中运行它来进行调试。有没有办法在本地运行和调试服务? @JM:至少您应该将未处理的异常记录到系统事件日志中。至于调试,是的,您可以将其作为控制台应用程序启动。 Here is another SO Question 关于那个主题。 谢谢版。创建自定义错误/事件日志是不好的做法吗? @JM:自己进行日志记录并不是一个糟糕的做法,但这应该是除了记录到事件日志之外。如果我的系统上的服务或驱动程序出现问题,我希望它会将 something 记录到事件日志中,以帮助我诊断问题。【参考方案2】:

您可能想简单地将您的函数包装在 try / catch 块中,看看您会发现什么。

try

  MainAppFunctionality();

catch (Exception e)

   //Not sure what you are going to do here, it's probably too late

我建议您在应用程序的各个点登录到 Windows 事件日志作为开始,这样您就可以开始缩小错误的位置。

我也不确定您为什么在 Windows 服务上下文中使用 Console.Read()。在 Vista 中,该服务无法与桌面交互。

【讨论】:

嗨,布莱恩。我不得不承认我真的没有使用过 Windows 事件日志。我认为将消息写入自定义错误日志会更加用户友好。这是不好的做法吗? @JM。 : 我不确定它对你的情况是否有帮助。但这就是 Windows 事件日志的设计目的。如果需要,您可以随时写入两者,但通常 Windows 事件日志拥有您所需要的一切。追踪这些东西总比没有好。

以上是关于处理 Windows 服务中的致命错误的主要内容,如果未能解决你的问题,请参考以下文章

为应用程序池“应用程序池”提供服务的进程与 Windows 进程激活服务发生了致命的通信错误

致命错误:require(): laravel 5 (windows 10) 要求打开失败

QTCreator g++.exe:致命错误:没有输入文件(Windows)

错误“致命错误 C1034:windows.h:未设置包含路径”

在 Windows 中编译 Qt 时出现致命错误

Windows 服务启动错误 1069