处理 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)