0

I'm developing Windows Service that is using FileSystemWatcher to trigger on creation of a new folder. OnCreated it reads a file insp_board.txt that is located in that new folder, logs data into log.txt and waits for another folder creation trigger. When I run code in console app it works fine, but when I run it as a Windows Service it executes OnCreated function properly but on next OnCreated execution it throws IO Exception: Could not access file because it is being used by another process.

Here is the service starting class:

public partial class SPIHydraImport : ServiceBase
{
    private SourceWatcher sourceWatcher;

    public SPIHydraImport()
    {
        InitializeComponent();
        sourceWatcher = new SourceWatcher();
        var eventLogger = new EventLogger("HydraSPIImport", "Application");
    }

    protected override void OnStart(string[] args)
    {
        try
        {

            sourceWatcher.StartWatching();

        }
        catch (Exception ex)
        {
            EventLog.WriteEntry("An error occurred: " + ex.Message, EventLogEntryType.Error);
            throw;
        }
    }

    protected override void OnStop()
    {
        sourceWatcher.StopWatching();
    }
}

Watcher class:

public class SourceWatcher
   {
       private FileSystemWatcher watcher;
       private Logger logger = new Logger();

       private string sourcePath = ConfigurationManager.AppSettings["SourcePath"];
       public SourceWatcher()
       {
           watcher = new FileSystemWatcher();
           watcher.Path = sourcePath;
           watcher.IncludeSubdirectories = true;
           watcher.Filter = "insp_board.txt";
           watcher.Created += new FileSystemEventHandler(OnCreated);
           watcher.EnableRaisingEvents = true;
       }

       public void StartWatching()
       {
           watcher.EnableRaisingEvents = true;
           this.logger.LogStandard($"Start: watcher");
       }

       private void OnCreated(object source, FileSystemEventArgs e)
       {
           StopWatching();
           BoardData boardData = new BoardData();
           string directoryPath = e.FullPath;

           try
           {
               this.logger.LogStandard($"{directoryPath}");
               boardData = boardData.ParseFile(directoryPath);
               this.logger.LogStandard($"BoardID: {boardData.boardId} , Operation: {boardData.operationId} , PCB ID: {boardData.pcbId} , Inspection time: {boardData.inspectionTime} , Tack time: {boardData.tackTime} , Is pass: {boardData.isPass}");
           }
           catch (Exception exc)
           {
               this.logger.LogError(exc.ToString());
               throw;
           }
           finally
           {
               StartWatching();
           }
       }

       public void StopWatching()
       {
           watcher.EnableRaisingEvents = false;
           this.logger.LogStandard("Stop: watcher");
       }
   }

ParseFile function:

  public  BoardData ParseFile(string inspBoardPath)
  {
      var boardData = new BoardData();
      var lines = File.ReadAllLines(inspBoardPath).Skip(2);

      foreach (var line in lines)
      {
          var parts = line.Split(',');

          boardData.boardId = parts[1];
          boardData.operationId = "0" + parts[3];
          boardData.pcbId = parts[4];
          boardData.inspectionTime = DateTime.ParseExact(parts[5], "yyyyMMddHHmmss", CultureInfo.InvariantCulture).ToString("yyyy-MM-dd HH:mm:ss");
          boardData.tackTime = double.Parse(parts[6], CultureInfo.InvariantCulture);
          if (parts[7].Equals("3"))
          {
              boardData.isPass = 1;
          } else
          {
              boardData.isPass = 0;
          }
      }

      return boardData;
  }

When running console type application the same error occurred bit I've added watcherEnableRaisingEvents = false: function at the beginning of OnCreated and watcherEnableRaisingEvents = true at the end and it worked fine. Even though I've implemented such solution in Windows Service app it is still causing the same problems. I've also tried to dispose watcher but the result was exactly the same. I've added function checking if the file is existing, and it was existing every time.

6
  • public partial class SPIHydraImport : ServiceBase why is this partial and what components does it initialize? Commented Dec 19, 2023 at 16:01
  • private Logger logger = new Logger(); - let me guess: your own creation? Commented Dec 19, 2023 at 16:02
  • catch (Exception exc) { this.logger.LogError(exc.ToString()); throw; } - why rethrow here? Log, Cleanup, Get on with it. Commented Dec 19, 2023 at 16:06
  • 1
    When the OnCreated event is raised, the file it points to not necessarily has been created yet. The process that created it may (you have to assume so) be still writing to it. You should never do anything in those event handlers. Just enqueue the work, so a worker Task / Thread can handle the files it has in the queue (and handles the exceptions) -- Stopping the FSW in the meanwhile is also a bad idea Commented Dec 19, 2023 at 16:08
  • But to the actual problem: When the FSW raises the created event, the FileSystem hasn't necessarily given up all resources, so you can access the files. It's a bit tricky. You can monitor filesize change, last modified date ... and for good measure give it some time and then try to access. Commented Dec 19, 2023 at 16:10

1 Answer 1

0

It looks like your event handler is being called twice (concurrently) because while it is processing one request, another filesystem event occurs. One variant is to use some synchronization: lock some object while you are handling an event. But this is creates a bottleneck. Other options depend on the exact task - you can move away the file for processing.

But there is more to it - I saw situations where a file was not closed in time after high-level functions like File.ReadAllLines. You may want to go for a low-level FileStream and read file bytes using it, then explicitly calling .Close and .Dispose of the object.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.