C# based Windows Service - Tries to do JIT Debugging in production

坚强是说给别人听的谎言 提交于 2019-12-04 14:09:46

Even though it's running as a release exe, you'll still be given the option to attach to a debugger when the app crashes... you just won't see debug symbols, just assembly :)

I believe it's the Dr. Watson process that catches app errors for debugging... Because your app is a service, Dr. Watson can't interact with the desktop, giving you the error you see. You can go to the service properties and mark "allow service to interact with the desktop", found on the LogOn tab, which should then give you a Dr. Watson popup when the app crashes.

Steps to disable Dr. Watson are here:
http://support.microsoft.com/kb/188296

If you want to debug the app on the server, you can enable remote debugging on the server, and attach Visual Studio to the process... if you want to try this, I can give you more tips for debugging a windows service remotely.

HTH, James


* Edit * Based on the code you provided, I'd look at the following areas:

  1. Is AppSettings.Default.FTPRootPath set correctly in App.Config?

  2. Are there changes happening to that directory immediately when the service starts? You have a timer commented as "check every five minutes", which is a little confusing, because the FileSystemWatcher will start receiving events as soon as you set EnableRaisingEvents to true. So the issue could actually lie within fileCreatedOrChanged

  3. Along those lines, you have one BackgroundWorker servicing multiple events, and worse, you're firing the handler asynchronously. This is my most likely suspect, because if you call _worker.RunWorkerAsync() again while the first job is running, you'll get an InvalidOperationException. Though I'm not sure why you wouldn't see that in the log

  4. You're using the timer to update the last write time for all files in the watched directory, and you do this every five seconds. This seems like a very bad idea... I'm not sure what you're trying to accomplish. This will fire your FileSystemWatcher's changed event, which would explain why you're crashing less than 10 seconds after you start (the timer's initial tick is set to fire immediately, meaning five seconds later you're changing all the file times, triggering the FileSystemWatcher multiple times shortly after that)

So my best guess is that within five seconds, you've begun firing multiple RunWorkAsync() calls on the same BackgroundWorker, which is a no-no : )

Setting the static variable _isBusy to true/false isn't reliable because you're multi-threading with the BackgroundWorkers... you need to use a Mutex or some other lock, but doesn't that really defeat the purpose of using a BackgroundWorker?

Also, if you wanted to use something like an isBusy flag, it would have to look more like:

while (_isBusy) {
    System.Threading.Thread.Sleep(5000);
}

_isBusy = true;
_worker.RunWorkerAsync(eventArgs);

You need _isBusy to be false before you try to launch the Background worker... the way you have it, if the event fires 100 times, you'll make 100 calls.

The easiest solution to your problem would be to create a new BackgroundWorker in the fileCreatedOrChanged method every time the event fires... there's overhead involved in creating so many new threads, but if the work being done in this method is significant, it will be worth the overhead.

You might be able to rely on the built-in BackgroundWorker.IsBusy property, but again, I'd have to question the benefit of asynchronous threading if you're just going to block until the background worker completes.


** Edit **

I understand now what you're trying to accomplish with the initial file timestamp changes... I think you would do better to leave the timestamps alone, but just run through a startup loop to process existing files. You can spawn a background worker thread for each one, just like you do on the FileSystemWatcher nofications. The way you're handling it is deliberately creating a side-effect to trigger the result you want.

I'm losing track a little bit in the growing complexity... the whole queue/dequeue thing might be unnecessary. Or maybe I just am not seeing a need that is truly there. Again, what strikes me is that you are launching the background worker asynchronously, then putting the main thread to sleep until it finishes.

When you put the main thread to sleep, no events will get processed, so you are truly throttling the multi-threading to one thread. I see that you want to write to the event log how long it took for a thread to finish. I will start a second answer to address that if I get a chance to, but the gist of it is to pass a Stopwatch class (which will give you an accurate count of either milliseconds or CPU ticks that pass during an operation) to the DoWorkEventArgs.Result property.

But the code you requested! Basically, wherever you decide to call _worker.RunWorkerAsync(queuedFile), rather than run one class-level BackgroundWorker create a new one each time. Pass all the same parameters for the event handlers, etc. Your service entry point would drop all the BGW references and look like:

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

and the code where you run the BGW asynchronously would become:

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) //

The error message tells you that it couldn't attach a debugger to let you inspect the exception. This is completely unrelated to the fact that this is a release build. Release build and debug builds can both be debugged (fortunately!).

Debugging services is a little different from debugging regular applications. Please check this guide for some advice.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!