基于C#的Windows服务 – 尝试在生产中进行JIT调试

我在我投入生产的服务的事件日志中收到此错误:

RivWorks.FeedHandler.exe [5496]中发生了未处理的win32exception。 即时调试此exception失败,并出现以下错误:无法启动调试器,因为没有用户登录。

我在Win NT全局帐户下安装并运行它。 我不知道它为什么试图进入调试模式。 它是在Release模型下构建的。 在4.0 Framework上运行。

当我在我的开发机器上运行时,通过EXE入口点而不是WinSvc入口点,一切运行正常 – 但是 – 我已经处于“调试”模式。

有什么想法的想法?


2010-10-21 – 注意 – 更改了代码库。

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Diagnostics; using System.Linq; using System.ServiceProcess; using System.Text; using System.IO; using sysIO = System.IO; using RivWorks.FeedHandler; using System.Collections; namespace RivWorks.FeedHandler.Service { public partial class FeedListener : ServiceBase { #region Declarations private List _keys = new List(); private System.Threading.Timer _clock = null; private FileSystemWatcher _watcher; private BackgroundWorker _worker; private Queue _queue = new Queue(); private bool _isDequeueing = false; #endregion #region Constructor public FeedListener() { InitializeComponent(); } #endregion #region Start/Stop protected override void OnStart(string[] args) { try { WriteToEventLog("Enter Start", EventLogEntryType.Information); _keys.AddRange(new string[] { "csv", "xml", "zip", "rivx" }); _worker = new BackgroundWorker(); _worker.WorkerReportsProgress = true; _worker.WorkerSupportsCancellation = true; _worker.DoWork += new DoWorkEventHandler(BackgroundWorkerDoWork); _worker.ProgressChanged += new ProgressChangedEventHandler(BackgroundWorkerProgressChanged); _worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(BackgroundWorkerRunWorkerCompleted); _watcher = new FileSystemWatcher(AppSettings.Default.FTPRootPath, "*.*"); _watcher.IncludeSubdirectories = true; _watcher.NotifyFilter = sysIO.NotifyFilters.DirectoryName | sysIO.NotifyFilters.FileName | sysIO.NotifyFilters.LastAccess | sysIO.NotifyFilters.CreationTime | sysIO.NotifyFilters.LastWrite; _watcher.Created += new sysIO.FileSystemEventHandler(fileCreatedOrChanged); _watcher.Changed += new sysIO.FileSystemEventHandler(fileCreatedOrChanged); _watcher.EnableRaisingEvents = true; // check every 15 minutes... _clock = new System.Threading.Timer(Tick, null, 0, 900000); WriteToEventLog("Exit Start", EventLogEntryType.Information); } catch (Exception ex) { WriteToEventLog(ex, EventLogEntryType.Error); this.Stop(); } } protected override void OnStop() { try { _watcher.Dispose(); _watcher = null; _clock.Dispose(); _clock = null; _worker.Dispose(); _worker = null; } catch (Exception ex) { WriteToEventLog(ex, EventLogEntryType.Error); } } #endregion #region Event Handlers void fileCreatedOrChanged(object sender, sysIO.FileSystemEventArgs e) { try { WriteToEventLog("Enter fileCreatedOrChanged", EventLogEntryType.Information); if (!_queue.Contains(e.FullPath)) _queue.Enqueue(e.FullPath); if (!_isDequeueing) DeQueue(); } catch (Exception ex) { WriteToEventLog(ex, EventLogEntryType.Error); } } #endregion #region Do work on another Thread void BackgroundWorkerDoWork(object sender, DoWorkEventArgs e) { try { WriteToEventLog("Enter BackgroundWorkerDoWork", EventLogEntryType.Information); BackgroundWorker bw = sender as BackgroundWorker; WriteToEventLog("Create Handler", EventLogEntryType.Information); RivWorks.FeedHandler.Library.Handler handler = new RivWorks.FeedHandler.Library.Handler(Convert.ToBoolean(AppSettings.Default.InProduction), AppSettings.Default.ArchivePath); WriteToEventLog("Setup Handler", EventLogEntryType.Information); handler.Keys = _keys; handler.RootDirectory = AppSettings.Default.RootDirectory; handler.FtpPath = AppSettings.Default.FTPRootPath; handler.WorkPath = AppSettings.Default.WorkPath; handler.ArchivePath = AppSettings.Default.ArchivePath; handler.EmailHost = AppSettings.Default.EmailHost; handler.EmailPassword = AppSettings.Default.EmailPassword; handler.EmailUser = AppSettings.Default.EmailUser; handler.ErrorNotificationRecipients = AppSettings.Default.ErrorNotificationRecipients; handler.InProduction = Convert.ToBoolean(AppSettings.Default.InProduction); Library.DTO.FileHandler fileHandler = new Library.DTO.FileHandler(handler.FtpPath, handler.WorkPath, handler.ArchivePath, (string)e.Argument); WriteToEventLog("Call Handler.Execute", EventLogEntryType.Information); handler.Execute(bw, e, fileHandler); } catch (Exception ex) { WriteToEventLog(ex, EventLogEntryType.Error); } finally { WriteToEventLog("Exit BackgroundWorkerDoWork", EventLogEntryType.Information); } } void BackgroundWorkerProgressChanged(object sender, ProgressChangedEventArgs e) { try { if (e.ProgressPercentage >= 100) { } } catch (Exception ex) { WriteToEventLog(ex, EventLogEntryType.Error); } } void BackgroundWorkerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { try { if (e.Cancelled) { WriteToEventLog("Cancelled.", EventLogEntryType.Warning); if (e.Error != null) { WriteToEventLog(e.Error, EventLogEntryType.Error); } } else if (e.Error != null) { WriteToEventLog(e.Error, EventLogEntryType.Error); } else { WriteToEventLog("Successfully completed.", EventLogEntryType.Information); } } catch (Exception ex) { WriteToEventLog(ex, EventLogEntryType.Error); } } #endregion #region Private Methods private void Tick(object data) { try { if (!_isDequeueing) { WriteToEventLog("Enter Tick. FTP Root = " + AppSettings.Default.FTPRootPath, EventLogEntryType.Information); foreach (string key in _keys) { List files = Directory.GetFiles(Path.Combine(AppSettings.Default.FTPRootPath), "*." + key, SearchOption.AllDirectories).ToList(); foreach (string fileName in files) { if (File.Exists(fileName)) { // Toss this file name into the Queue... WriteToEventLog("Call _queue.Enqueue(" + fileName + ")", EventLogEntryType.Information); if (!_queue.Contains(fileName)) _queue.Enqueue(fileName); } } } // Now, start handling the list of files... DeQueue(); } } catch (Exception ex) { WriteToEventLog(ex, EventLogEntryType.Error); } finally { WriteToEventLog("Exit Tick", EventLogEntryType.Information); } } private void DeQueue() { try { _isDequeueing = true; WriteToEventLog("Enter DeQueue", EventLogEntryType.Information); while (_queue.Count > 0) { string queuedFile = _queue.Dequeue(); WriteToEventLog("DeQueued " + queuedFile, EventLogEntryType.Information); bool isValid = false; foreach (string key in _keys) { if (Path.GetExtension(queuedFile).Replace(".", "").Equals(key, StringComparison.CurrentCultureIgnoreCase)) isValid = true; } if (isValid) { // Now, spin up a new thread and do the work on the file, based on file type... WriteToEventLog("Call RunWorkerAsync", EventLogEntryType.Information); string UserName = Path.GetDirectoryName(queuedFile).Replace(AppSettings.Default.FTPRootPath, "").Replace("\\", ""); int i = 0; DateTime sTime = DateTime.Now; DateTime eTime = DateTime.Now; _worker.RunWorkerAsync(queuedFile); // goes to BackgroundWorkerDoWork(object sender, DoWorkEventArgs e) // while(_worker.IsBusy) { System.Threading.Thread.Sleep(5000); i++; } eTime = DateTime.Now; TimeSpan ts = new TimeSpan(eTime.Ticks - sTime.Ticks); string msg = String.Format("Import for {0} started at {1} and ended at {2}. It took {3} cycles and the elapsed time was {4}:{5}:{6}.", UserName, sTime, eTime, i, ts.Hours, ts.Minutes, ts.Seconds); WriteToEventLog(msg, EventLogEntryType.Information); } } } catch (Exception ex) { WriteToEventLog(ex, EventLogEntryType.Error); } finally { _isDequeueing = false; WriteToEventLog("Exit DeQueue", EventLogEntryType.Information); } } private void WriteToEventLog(Exception ex, EventLogEntryType eventLogEntryType) { try { string message = string.Empty; string sTrace = ex.StackTrace; while (ex != null) { message = message + Environment.NewLine + Environment.NewLine + ex.Message; ex = ex.InnerException; } message = message + Environment.NewLine + Environment.NewLine + sTrace; WriteToEventLog(message, eventLogEntryType); } catch (Exception ex2) { WriteToEventLog(ex2.Message, EventLogEntryType.Error); } } private void WriteToEventLog(string message, EventLogEntryType eventLogEntryType) { try { this.EventLog.WriteEntry(message, eventLogEntryType); } catch (Exception ex) { throw ex; } } #endregion } } 

2010-10-20 – 注意 – 添加了服务代码文件。 也许这里有一个基本错误?

 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Diagnostics; using System.Linq; using System.ServiceProcess; using System.Text; using System.IO; using sysIO = System.IO; using RivWorks.FeedHandler; namespace RivWorks.FeedHandler.Service { public partial class FeedListener : ServiceBase { #region Declarations private List _keys = new List(); private System.Threading.Timer _clock = null; private FileSystemWatcher _watcher; private BackgroundWorker _worker; static private bool _isBusy = false; #endregion #region Constructor public FeedListener() { InitializeComponent(); } #endregion #region Start/Stop protected override void OnStart(string[] args) { try { _keys.AddRange(new string[] { "csv", "xml", "zip", "rivx" }); _worker = new BackgroundWorker(); _worker.WorkerReportsProgress = true; _worker.WorkerSupportsCancellation = true; _worker.DoWork += new DoWorkEventHandler(BackgroundWorkerDoWork); _worker.ProgressChanged += new ProgressChangedEventHandler(BackgroundWorkerProgressChanged); _worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(BackgroundWorkerRunWorkerCompleted); _watcher = new FileSystemWatcher(AppSettings.Default.FTPRootPath, "*.*"); _watcher.IncludeSubdirectories = true; _watcher.NotifyFilter = sysIO.NotifyFilters.DirectoryName | sysIO.NotifyFilters.FileName | sysIO.NotifyFilters.LastAccess | sysIO.NotifyFilters.CreationTime | sysIO.NotifyFilters.LastWrite; _watcher.Created += new sysIO.FileSystemEventHandler(fileCreatedOrChanged); _watcher.Changed += new sysIO.FileSystemEventHandler(fileCreatedOrChanged); _watcher.EnableRaisingEvents = true; // check every 5 minutes... _clock = new System.Threading.Timer(Tick, null, 0, 300000); } catch (Exception ex) { WriteToEventLog(ex, EventLogEntryType.Error); this.Stop(); } } protected override void OnStop() { try { _watcher.Dispose(); _watcher = null; _clock.Dispose(); _clock = null; _worker.Dispose(); _worker = null; } catch (Exception ex) { WriteToEventLog(ex, EventLogEntryType.Error); } } #endregion #region Event Handlers void fileCreatedOrChanged(object sender, sysIO.FileSystemEventArgs e) { try { DTO.BackgroundWorkerEventArgs eventArgs = new DTO.BackgroundWorkerEventArgs(); sysIO.WatcherChangeTypes myType = e.ChangeType; bool isValid = false; foreach (string key in _keys) { if (Path.GetExtension(e.FullPath).Replace(".", "").Equals(key, StringComparison.CurrentCultureIgnoreCase)) isValid = true; } if (isValid) { eventArgs.PathAndFile = e.FullPath; eventArgs.Key = Path.GetExtension(e.FullPath).ToLower().Replace(".", ""); eventArgs.FileName = Path.GetFileName(e.FullPath); eventArgs.Path = Path.GetDirectoryName(e.FullPath); eventArgs.UserName = Path.GetDirectoryName(e.FullPath).Replace(AppSettings.Default.FTPRootPath, "").Replace("\\", ""); eventArgs.IsRunning = true; System.Threading.Thread.Sleep(30000); // Now, spin up a new thread and do the work on the file, based on file type... _isBusy = true; _worker.RunWorkerAsync(eventArgs); // goes to BackgroundWorkerDoWork(object sender, DoWorkEventArgs e) // int i = 0; DateTime sTime = DateTime.Now; DateTime eTime = DateTime.Now; while (_isBusy) { System.Threading.Thread.Sleep(5000); i++; } eTime = DateTime.Now; TimeSpan ts = new TimeSpan(eTime.Ticks - sTime.Ticks); string msg = String.Format("Import for {0} started at {1} and ended at {2}. It took {3} cycles and the elapsed time was {4}:{5}:{6}.", eventArgs.UserName, sTime, eTime, i, ts.Hours, ts.Minutes, ts.Seconds); WriteToEventLog(msg, EventLogEntryType.Information); } } catch (Exception ex) { WriteToEventLog(ex, EventLogEntryType.Error); } } #endregion #region Do work on another Thread void BackgroundWorkerDoWork(object sender, DoWorkEventArgs e) { try { RivWorks.FeedHandler.Handler handler = new RivWorks.FeedHandler.Handler(); BackgroundWorker bw = sender as BackgroundWorker; handler.Execute(bw, e); } catch (Exception ex) { WriteToEventLog(ex, EventLogEntryType.Error); } finally { _isBusy = false; } } void BackgroundWorkerProgressChanged(object sender, ProgressChangedEventArgs e) { try { if (e.ProgressPercentage >= 100) { _isBusy = false; } } catch (Exception ex) { WriteToEventLog(ex, EventLogEntryType.Error); } } void BackgroundWorkerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { try { if (e.Cancelled) { WriteToEventLog("Cancelled.", EventLogEntryType.Warning); if (e.Error != null) { WriteToEventLog(e.Error, EventLogEntryType.Error); } } else if (e.Error != null) { WriteToEventLog(e.Error, EventLogEntryType.Error); } else { WriteToEventLog("Successfully completed.", EventLogEntryType.Information); } _isBusy = false; } catch (Exception ex) { WriteToEventLog(ex, EventLogEntryType.Error); } } #endregion #region Private Methods private void Tick(object data) { try { foreach (string key in _keys) { List files = Directory.GetFiles(Path.Combine(AppSettings.Default.FTPRootPath), "*." + key, SearchOption.AllDirectories).ToList(); foreach (string fileName in files) { System.Threading.Thread.Sleep(5000); if (File.Exists(fileName)) { DateTime lat = File.GetLastWriteTime(fileName); try { File.SetLastWriteTime(fileName, DateTime.Now); } catch { // just catch and ignore with a short pause... System.Threading.Thread.Sleep(5000); } } } } } catch (Exception ex) { WriteToEventLog(ex, EventLogEntryType.Error); } } private void WriteToEventLog(Exception ex, EventLogEntryType eventLogEntryType) { try { string message = string.Empty; string sTrace = ex.StackTrace; while (ex != null) { message = message + Environment.NewLine + Environment.NewLine + ex.Message; ex = ex.InnerException; } message = message + Environment.NewLine + Environment.NewLine + sTrace; this.EventLog.WriteEntry(message, eventLogEntryType); } catch (Exception ex2) { WriteToEventLog(ex2, EventLogEntryType.Error); } } private void WriteToEventLog(string message, EventLogEntryType eventLogEntryType) { try { this.EventLog.WriteEntry(message, eventLogEntryType); } catch (Exception ex) { WriteToEventLog(ex, EventLogEntryType.Error); } } #endregion } } 

即使它作为发布exe运行,当应用程序崩溃时你仍然可以选择附加到调试器…你只是看不到调试符号,只是汇编:)

我相信Dr. Watson流程可以捕获应用程序错误以进行调试…因为您的应用程序是一项服务,Watson博士无法与桌面交互,从而为您提供错误。 您可以转到服务属性并在LogOn选项卡上标记“允许服务与桌面交互”,然后在应用程序崩溃时应该为您提供Dr. Watson弹出窗口。

禁用Dr. Watson的步骤如下:
http://support.microsoft.com/kb/188296

如果要在服务器上调试应用程序,可以在服务器上启用远程调试,并将Visual Studio附加到进程…如果您想尝试此操作,我可以为您提供有关远程调试Windows服务的更多提示。

HTH,詹姆斯

*编辑*根据您提供的代码,我将看看以下几个方面:

  1. 是否在App.Config正确设置了AppSettings.Default.FTPRootPath

  2. 服务启动时是否立即对该目录进行更改? 你有一个计时器评论为“每五分钟检查一次”,这有点令人困惑,因为FileSystemWatcher会在你将EnableRaisingEvents设置为true后立即开始接收事件。 所以问题实际上可能在于fileCreatedOrChanged

  3. 沿着这些方向,您有一个BackgroundWorker为多个事件提供服务,更糟糕的是,您正在异步触发处理程序。 这是我最可能的怀疑,因为如果在第一个作业运行时再次调用_worker.RunWorkerAsync() ,您将获得InvalidOperationException 。 虽然我不确定为什么你不会在日志中看到它

  4. 您正在使用计时器更新监视目录中所有文件的上次写入时间,并且每五秒执行一次。 这似乎是一个非常糟糕的主意……我不确定你想要完成什么。 这将触发你的FileSystemWatcher的更改事件,这可以解释为什么你在启动后不到10秒就崩溃了(计时器的初始刻度被设置为立即触发,这意味着五秒后你将改变所有文件时间,触发不久后的FileSystemWatcher多次)

所以我最好的猜测是,在五秒钟内,你已经开始在同一个BackgroundWorkerRunWorkAsync()多个RunWorkAsync()调用,这是RunWorkAsync()

将静态变量_isBusy设置为true / false是不可靠的,因为你使用BackgroundWorkersmultithreading…你需要使用Mutex或其他一些锁,但这并不能真正破坏使用BackgroundWorker的目的?

此外,如果你想使用类似isBusy标志的东西,它必须看起来更像:

 while (_isBusy) { System.Threading.Thread.Sleep(5000); } _isBusy = true; _worker.RunWorkerAsync(eventArgs); 

在尝试启动后台工作程序之前,你需要_isBusy是假的…你拥有它的方式,如果事件触发100次,你将进行100次调用。

解决问题的最简单方法是在每次事件触发时在fileCreatedOrChanged方法中创建一个新的BackgroundWorker …创建这么多新线程会产生开销,但如果在这个方法中完成的工作很重要,那么它将是值得的开销。

您可能可以依赖内置的BackgroundWorker.IsBusy属性,但是,如果您要阻止后台工作程序完成,我将不得不质疑异步线程的好处。

**编辑**

我现在明白你要用初始文件时间戳更改来完成什么…我认为你最好单独保留时间戳,但只需要通过启动循环来处理现有文件。 您可以为每个线程生成后台工作线程,就像在FileSystemWatcher上进行操作一样。 你处理它的方式是刻意创建一个副作用来触发你想要的结果。

我在日益复杂的情况下失去了一点……整个队列/出队的事情可能是不必要的。 或者也许我只是没有看到真正存在的需求。 同样,让我感到震惊的是,您正在异步启动后台工作程序,然后将主线程置于睡眠状态直到完成。

当您将主线程置于hibernate状态时,不会处理任何事件,因此您真正将multithreading限制为一个线程。 我看到你要写一个事件日志,一个线程完成需要多长时间。 如果我有机会,我将开始第二个答案来解决这个问题,但它的要点是将Stopwatch传递一个Stopwatch类(它将为您提供在操作期间经过的毫秒或CPU滴答的精确计数) .Result property。

但是你要的代码! 基本上,无论您何时决定调用_worker.RunWorkerAsync(queuedFile) ,而不是运行一个类级别的BackgroundWorker每次都要创建一个新的。 传递事件处理程序等所有相同的参数。您的服务入口点将删除所有BGW引用,如下所示:

 protected override void OnStart(string[] args) { try { _keys.AddRange(new string[] { "csv", "xml", "zip", "rivx" }); _watcher = new FileSystemWatcher(AppSettings.Default.FTPRootPath, "*.*"); _watcher.IncludeSubdirectories = true; _watcher.NotifyFilter = sysIO.NotifyFilters.DirectoryName | sysIO.NotifyFilters.FileName | sysIO.NotifyFilters.LastAccess | sysIO.NotifyFilters.CreationTime | sysIO.NotifyFilters.LastWrite; _watcher.Created += new sysIO.FileSystemEventHandler(fileCreatedOrChanged); _watcher.Changed += new sysIO.FileSystemEventHandler(fileCreatedOrChanged); _watcher.EnableRaisingEvents = true; WriteToEventLog("Exit Start", EventLogEntryType.Information); } 

以及异步运行BGW的代码将变为:

 BackgroundWorker worker = new BackgroundWorker(); worker.WorkerReportsProgress = true; worker.WorkerSupportsCancellation = true; worker.DoWork += new DoWorkEventHandler(BackgroundWorkerDoWork); worker.ProgressChanged += BackgroundWorkerProgressChanged; // Note you don't need worker.RunWorkerCompleted += BackgroundWorkerRunWorkerCompleted; // the 'new' here worker.RunWorkerAsync(queuedFile); // goes to BackgroundWorkerDoWork(object sender, DoWorkEventArgs e) // 

该错误消息告诉您它无法附加调试器以允许您检查exception。 这与发布版本完全无关。 发布版本和调试版本都可以调试(幸运的是!)。

调试服务与调试常规应用程序略有不同。 请查看本指南以获取一些建议。