views:

128

answers:

8

I have built a Windows Service and for some reason, when I start the service it starts up and then shuts right back down. I’ve tried googling why this is happening. Nothing is appearing in any system logs. Here is my Start/Stop code for the service. I would expect that since I have created a File Listener that it should stay running. What am I missing?

#region Declarations
private List<string> _keys = new List<string>();
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)
{
    _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;

    TouchFiles();
}
protected override void OnStop()
{
    _watcher.Dispose();
    _watcher = null;
    _worker.Dispose();
    _worker = null;
}
#endregion

#region Event Handlers
void fileCreatedOrChanged(object sender, sysIO.FileSystemEventArgs e)
{
    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)
    {
        try
        {
            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("\\", "");

            FileInfo fileInfo = new FileInfo(eventArgs.PathAndFile);

            // 1st attempt at stalling for the file lock due to slow write speeds...
            while (IsFileLocked(fileInfo)) { /* nop */ }

            // Wait until the thread is not busy...
            //while (_worker.IsBusy) { /* nop */ }
            while (_isBusy) { /* nop */ }

            // Now, spin up a new thread and do the work on the file, based on file type...
            _worker.RunWorkerAsync(eventArgs);  // goes to BackgroundWorkerDoWork(object sender, DoWorkEventArgs e) //
        }
        catch (Exception ex)
        {
            string m = ex.Message;
        }
    }
}
void BackgroundWorkerDoWork(object sender, DoWorkEventArgs e)
{
    DTO.BackgroundWorkerEventArgs eventArgs = (DTO.BackgroundWorkerEventArgs)e.Argument;
    RivWorks.FeedHandler.Handler handler = new RivWorks.FeedHandler.Handler();
    _isBusy = true;

    try
    {
        if (eventArgs.Key.Equals("csv", StringComparison.CurrentCultureIgnoreCase))
        {
            handler.ImportCSV(ref eventArgs);
        }
        if (eventArgs.Key.Equals("zip", StringComparison.CurrentCultureIgnoreCase))
        {
            handler.UnZip(ref eventArgs);
            handler.ImportCSV(ref eventArgs);
        }
    }
    catch (Exception ex)
    {
        string m = ex.Message;
        _worker.ReportProgress(0);
    }
    finally
    {
        _isBusy = false;
        _worker.ReportProgress(100);
        if (_worker.CancellationPending)
        {
            e.Cancel = true;
        }
    }
}
void BackgroundWorkerProgressChanged(object sender, ProgressChangedEventArgs e)
{
}
void BackgroundWorkerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if (e.Cancelled)
    {
        Console.WriteLine("Cancelled.");
    }
    else if (e.Error != null)
    {
        Console.WriteLine(e.Error.Message);
    }
    else
    {
        Console.WriteLine("Successfully completed.");
    }

    TouchFiles();
}
#endregion
A: 

Maybe you need to call base.OnStart somewhere in OnStart?

Pieter
A: 

I don't know what TouchFiles does, but a windows service normally runs in loop eq. while(true), which yours does not seem to do.

klausbyskov
+2  A: 

Why dont you just debug it? Add Debugger.Break() in the OnStart method.

leppie
@leppie - this won't work. The SCM will detect lack of responsiveness and kill your process before you have time to do anything.
Steve Townsend
I did so extensively. As Steve said - as a service it is near impossible to debug.
Keith Barrows
First make a console Exe. Run this to make sure your logic is right. *Then* convert it to run as a service. This is how I've debugged similar situations in the past.
Jay
@Steve Townsend: Never had a problem before with that. Maybe I have the location of that wrong. @Keith Barrows try adding the call to `Break` in 'DoWork'. This would allow for OnStart to complete and not cause a 'timeout'.
leppie
I do have a EXE (TestHarness) that I run from while in Dev. Everything works fine there. I am now stripping most everything out and pushing all work down into BackgroundWorker thread...
Keith Barrows
+3  A: 

I don't see the code for TouchFiles() but it sounds like the source of the problem. Like when it iterates all of the directories on a disk drive. That takes a lot of time, longer then the Service Control Manager is willing to put up with for a service to get started (30 seconds, I think).

Start a thread and have it touch the files. Also beware of your BackgroundWorker, the events are not called on the same thread that started it. That takes a synchronization provider that is not available in a service.

Hans Passant
One Minor note: You can get SCM to wait longer with ServiceBase.RequestAdditionalTime buts its a really bad idea.
Conrad Frix
TouchFiles - loops on the extensions provided by the _keys list and sets the LastModifiedTime to now. The directory I am listening to is the FTP drop directory for feed files. Upon startup, there *may* be a file or two still waiting to be processed. So, I "touch" the files to trigger the FileListener. If every file is there this method takes <250ms to run.
Keith Barrows
Well, unlikely then. Add some logging to your code, display time stamps, watch out for exceptions.
Hans Passant
I think it's an exception, like you say, that's crashing this. Based on his other post, TouchFiles() is updating the file times of all the files in a directory, which triggers the fileCreatedOrChanged event multiple times, which is making multiple calls to RunWorkerAsync() on the same BackgroundWorker. See answer below
James B
@James - yeah, his event logging code is crashing :)
Hans Passant
That's why I never use the stuff ;) ...jk
James B
A: 

Try adding a Debugger.Launch() line at the beginning of your OnStart method and step through until you encounter whatever exception is causing the problem.

On another note, I'd refactor the design so the bulk of the work is in a separate and testable class, and just wire up the bare minimum in the service.

Matthew Abbott
A: 

It looks like your OnStart simply executes and exits, you're not keeping the application alive, it's acting like a console app, start run through and then exit. If this is the case, you need to wait for something like an end/shutdown service event, and let your worker background thread process the incoming work requests and not exit this routine until your app gets signaled to terminate, from an event perhaps.

Jason
A: 

Are you sure you are using the correct service startup code that is generated for you? This could happen if it is just running as an app. I've had this happen when I have a separate Main() I use for testing.

Jason Goemaat
A: 

I posted an answer in your other, related post... I believe the issue is that you're firing multiple RunWorkAsync calls on the same BackgroundWorker, and this is probably crashing the service.

Also, you're using an _isBusy flag in the context of multi-threaded background workers... you need to use a multi-threaded locking system, such as a Mutex (though I still say that defeats the point of running BackgroundWorkers asynchronously).

Check the answer here: http://stackoverflow.com/questions/3979946/c-based-windows-service-tries-to-do-jit-debugging-in-production/3979974#3979974

James B