locked
File System watcher service only runs once. RRS feed

  • Question

  • I have a windows service running with a file system watcher to do a database import. It works great the first time (although it fires twice). But then it stops firing for additional files. the service runs fine but the files do not trigger processing.

    someone said make the watcher  instance variable but i don't see how?

    Its all mess code because i have tried everything...

    Code below..

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Diagnostics;
    using System.IO;  
    using System.Linq;
    using System.ServiceProcess;
    using System.Text;
    using System.Threading.Tasks;
    using System.Data.SqlClient;
    using Microsoft.VisualBasic.FileIO;
    using System.Threading;

    namespace PayrollUpdateService
    {

        public partial class Service1 : ServiceBase
        {
            //started yet?
           //static bool transflag = false;
          // static int trips = 0;
            static string lastfile = "";
            

            public Service1()
            {
                InitializeComponent();
                WriteToFile("Payroll Update Service was initialized at " + DateTime.Now); 


            }

            protected override void OnStart(string[] args)
            {
                WriteToFile("Payroll Update Service is started at " + DateTime.Now);
                //  FileProcesser fp = new FileProcesser(ConfigurationManager.AppSettings["FromPath"]);
                FileProcesser fp = new FileProcesser(@"\\fileserver\Folders\IT\test");
                fp.Watch();

              
            }

            protected override void OnStop()
            {
                WriteToFile("Payroll Update Service is stopped at " + DateTime.Now);

            }
            ///////////////////////////////////////////////////////////////////////////////////

          


            public static void WriteToFile(string Message)
            {
                string path = AppDomain.CurrentDomain.BaseDirectory + "\\Logs";
                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }
                string filepath = AppDomain.CurrentDomain.BaseDirectory + "\\Logs\\ServiceLog_" + DateTime.Now.Date.ToShortDateString().Replace('/', '_') + ".txt";
                if (!File.Exists(filepath))
                {
                    // Create a file to write to.   
                    using (StreamWriter sw = File.CreateText(filepath))
                    {
                        sw.WriteLine(Message);
                    }
                }
                else
                {
                    using (StreamWriter sw = File.AppendText(filepath))
                    {
                        sw.WriteLine(Message);
                    }
                }
            }
            ///////////////////////////////////////////////////////////////////////////////// write to file end


            ////////////////////////////////////////////////////////////////////////////////////////
            public class FileProcesser
            {
                FileSystemWatcher watcher;
                string directoryToWatch;
                public FileProcesser(string path)
                {
                    this.watcher = new FileSystemWatcher();
                    this.directoryToWatch = path;
                }
                public void Watch()
                {
                    watcher.Path = directoryToWatch;
                    watcher.NotifyFilter = NotifyFilters.LastAccess |
                                 NotifyFilters.LastWrite |
                                 NotifyFilters.FileName |
                                 NotifyFilters.DirectoryName;
                    watcher.Filter = "*.*";
                    watcher.Changed += new FileSystemEventHandler(OnChanged);
                    watcher.Created += new FileSystemEventHandler(OnCreated);
                    watcher.EnableRaisingEvents = true;
                }

                private void OnChanged(object sender, FileSystemEventArgs e)
                {


                  //  if (lastfile = e.FullPath)
                   // {   //only process once
                    //    return;
                    //}
                     int timeout = 5000;
                     while (timeout > 0)
                           {
                               try {
                                     File.ReadAllText(e.FullPath);
                                     //all copied..jump out and process it.
                                     WriteToFile("Copy finished, process file.." + DateTime.Now);
                                     timeout = 0;
                                    }
                               catch (IOException) 
                                     {
                                       WriteToFile("waiting for copy to finish.." + DateTime.Now);
                                     }

                                        Thread.Sleep(1000);  
                                         timeout -= 100;
                          }//while

                    

                    //File.Copy(e.FullPath, @"\\fileserver\Folders\IT\test\processing\"+ Path.GetFileName(e.FullPath), true);


                            //Create the csv file and parce it...
                            string csvname = DateTime.Now.Ticks+"_payroll.csv";
                            WriteToFile(csvname + " sanatize txt file... " + DateTime.Now);

                            string text = File.ReadAllText(e.FullPath);
                            text = text.Replace("\",", "\"|");
                            text = text.Replace("\"\"|", "null|");
                            File.WriteAllText(@"\\fileserver\Folders\IT\test\processing\" + csvname, text);

                            WriteToFile("csv parsed...." + DateTime.Now);

                            WriteToFile(e.FullPath + ".....delete file ...." + DateTime.Now);
                            //delete text file..no longer needed
                            //File.Delete(e.FullPath);


                            ///////////////////////////////////////////////////////////////////////
                          //  timeout = 5000;
                          //  while (timeout > 0)
                          //  {
                            //    try
                            //    {
                            //        File.ReadAllText(e.FullPath);
                            //        File.Delete(e.FullPath);
                                    //all copied..jump out and process it.
                           //         WriteToFile("deleting file.." + DateTime.Now);
                            //        timeout = 0;
                            //    }
                           //     catch (IOException)
                           //     {
                            //        WriteToFile("waiting to delete file.." + DateTime.Now);
                            //    }

                            //    Thread.Sleep(1000);
                            //    timeout -= 100;
                          //  }//while


                            ///////////////////////////////////////////////////////////////////////////////


                       //     String name = Path.GetFileName(e.FullPath).ToString();
                        //    WriteToFile(name + " File Changed " + DateTime.Now);

                    //Truncate the Table
                     // TruncateTable();
                      WriteToFile("Temp table truncate done... " + DateTime.Now);

                    //Parce the CSV
                     // DataTable ProcessedcsvData = GetDataTabletFromCSVFile(@"\\fileserver\Folders\IT\test\processing\"+ csvname);
                      WriteToFile("CSV processed... " + DateTime.Now);

                    //Bulk insert the Datatable
                    //  InsertDataIntoSQLServerUsingSQLBulkCopy(ProcessedcsvData);
                       WriteToFile("Done! " + DateTime.Now);

                       //remember last file
                      // lastfile = e.FullPath;

                       FileProcesser fp = new FileProcesser(@"\\fileserver\Folders\IT\test");
                       fp.Watch();

                }

                private void OnCreated(object sender, FileSystemEventArgs e)
                {

                    String name = Path.GetFileName(e.FullPath).ToString();
                    WriteToFile(name + " New file payroll received! " + DateTime.Now);

                  

                    //do not nitify when working
                   // watcher.EnableRaisingEvents = false;
                    // File.Copy(e.FullPath, ConfigurationManager.AppSettings["ToPath"] + "\\" + Path.GetFileName(e.FullPath), true);
                    // File.Delete(e.FullPath);
                   // if (transflag == false)
                   // {
                    

                    
                        //File.Copy(@"\\fileserver\Folders\IT\test\payroll (78).txt", @"\\fileserver\Folders\IT\test\processing\payroll (78).txt",true);

                        //transflag = true;
                        //Sanatize the textfile
                       // string text = File.ReadAllText(@"\\fileserver\Folders\IT\test\payroll (78).txt");
                       // text = text.Replace("\",", "\"|");
                       // text = text.Replace("\"\"|", "null|");
                       // File.WriteAllText(@"\\fileserver\Folders\IT\test\payroll.csv", text);
                       // WriteToFile("csv parsed" + DateTime.Now);
                    
                   

                     //  }

               


                  
                }

            }   
      
       
      
            /////////////////////////////////////////////////////////////////////////////////////file watcher end

            //////////////////////////////////////////////////////////////////////////////////////////////
            private static DataTable GetDataTabletFromCSVFile(string csv_file_path)
            {   //Parce the CSV to a databale for insert
                DataTable csvData = new DataTable();
                try
                {
                    using (TextFieldParser csvReader = new TextFieldParser(csv_file_path))
                    {
                        csvReader.SetDelimiters(new string[] { "|" });
                        csvReader.HasFieldsEnclosedInQuotes = true;
                        string[] colFields = csvReader.ReadFields();
                        foreach (string column in colFields)
                        {
                            DataColumn datecolumn = new DataColumn(column);
                            datecolumn.AllowDBNull = true;
                            csvData.Columns.Add(datecolumn);
                        }
                        while (!csvReader.EndOfData)
                        {
                            string[] fieldData = csvReader.ReadFields();
                            //Making empty value as null
                            for (int i = 0; i < fieldData.Length; i++)
                            {
                                if (fieldData[i] == "")
                                {
                                    fieldData[i] = null;
                                }
                            }
                            csvData.Rows.Add(fieldData);
                        }
                    }
                }
                catch (Exception ex)
                {
                    return null;
                }
                return csvData;

            }
            ////////////////////////////////////////////////////GetDataTabletFromCSVFile end

            ////////////////////////////////////////////////////////////////////////////////////
            static void InsertDataIntoSQLServerUsingSQLBulkCopy(DataTable csvFileData)
            {//Insert the datatable into the database.
                //Create Connection
                var c = new SqlConnection();
                c.ConnectionString = "Server = Donald;Database =hehehehe;User Id=sa;Password=blabla;";
                c.Close();



                using (c)
                {
                    c.Open();
                    using (SqlBulkCopy s = new SqlBulkCopy(c))
                    {
                        s.DestinationTableName = "NewTempEtime";
                        foreach (var column in csvFileData.Columns)
                            s.ColumnMappings.Add(column.ToString(), column.ToString());
                        s.WriteToServer(csvFileData);
                    }
                    c.Close();
                }

            }
            //////////////////////////////////////////////////////////////InsertDataIntoSQLServerUsingSQLBulkCopy end

            ///////////////////////////////////////////////////////////////////////////////////////////////////////////

            static void TruncateTable()
            {//Truncate the table for the new insert of data

                var c = new SqlConnection(); // Your Connection String here
                c.ConnectionString = "Server = Donald;Database =heheheh;User Id=sa;Password=blabbla;";
                c.Close();

                SqlCommand command1 = new SqlCommand("Truncate Table  NewTempEtime;", c);



                try
                {   //connect to the database
                    c.Close();
                    c.Open();

                }
                catch
                {
                    //Could not connect to database.;
                    return;
                }
                //ok add the record
                try
                {
                    command1.ExecuteNonQuery();
                }
                catch
                {
                    //failed to execute
                    c.Close();
                    return;
                }

                c.Close();
                return;

            }
            //////////////////////////////////////////////////////////////////////////////////////TruncateTable end



        }
    }

    Friday, February 22, 2019 4:33 PM

Answers

  • public partial class Service1 : ServiceBase
    {
       public Service1()
       {
          InitializeComponent();
          WriteToFile("Payroll Update Service was initialized at " + DateTime.Now); 
    
          _fp = new FileProcessor(…);
       }
    
       protected override void OnStart(string[] args)
       {
           WriteToFile("Payroll Update Service is started at " + DateTime.Now);
       
           _fp.Watch();
       }
    
       protected override void OnStop()
       {
          WriteToFile("Payroll Update Service is stopped at " + DateTime.Now);
          //TODO: Stop the FSW        
       }
    
       private readonly FileProcessor _fp;
    }


    Michael Taylor http://www.michaeltaylorp3.net

    • Marked as answer by Devhelpplease Monday, February 25, 2019 8:38 PM
    Friday, February 22, 2019 7:54 PM

All replies

  • In OnStart you declare the FileProcessor instance. That variable needs to stay around until you're done with the FileSystemWatcher. Move that variable declaration to a field in your service. OnStart can still new up the object but the declaration needs to be in the class. In your OnStop method disable the FSW so it stops raising events.

    "although it fires twice"

    The FSW doesn't work the way most people think it does. It raises events as file system APIs are called. For example if you have a file being written by an app the app may delete the old file, create the new file and then write it in chunks. This results in multiple events being raised. Even an app that simply opens the file and starts overwriting will probably generate a couple of change events. Code that relies on FSW has to handle the fact that the file being notified may still be locked by the remote app and may trigger multiple events. 


    Michael Taylor http://www.michaeltaylorp3.net

    Friday, February 22, 2019 7:12 PM
  • In OnStart you declare the FileProcessor instance. That variable needs to stay around until you're done with the FileSystemWatcher. Move that variable declaration to a field in your service. OnStart can still new up the object but the declaration needs to be in the class. In your OnStop method disable the FSW so it stops raising events.

    "although it fires twice"

    The FSW doesn't work the way most people think it does. It raises events as file system APIs are called. For example if you have a file being written by an app the app may delete the old file, create the new file and then write it in chunks. This results in multiple events being raised. Even an app that simply opens the file and starts overwriting will probably generate a couple of change events. Code that relies on FSW has to handle the fact that the file being notified may still be locked by the remote app and may trigger multiple events. 


    Michael Taylor http://www.michaeltaylorp3.net

    Thank you, I will disable on stop like you suggest. Could you please show me a code example of how i need to declare the variable in the class?

    This person also had the same issue /  solution but no example:

    https://stackoverflow.com/questions/30830565/using-a-filesystemwatcher-with-windows-service

    Friday, February 22, 2019 7:32 PM
  • public partial class Service1 : ServiceBase
    {
       public Service1()
       {
          InitializeComponent();
          WriteToFile("Payroll Update Service was initialized at " + DateTime.Now); 
    
          _fp = new FileProcessor(…);
       }
    
       protected override void OnStart(string[] args)
       {
           WriteToFile("Payroll Update Service is started at " + DateTime.Now);
       
           _fp.Watch();
       }
    
       protected override void OnStop()
       {
          WriteToFile("Payroll Update Service is stopped at " + DateTime.Now);
          //TODO: Stop the FSW        
       }
    
       private readonly FileProcessor _fp;
    }


    Michael Taylor http://www.michaeltaylorp3.net

    • Marked as answer by Devhelpplease Monday, February 25, 2019 8:38 PM
    Friday, February 22, 2019 7:54 PM
  • Thank you I will give this a try,

    So far I get this : 

    Error 1 The name 'fp' does not exist in the current context


            public Service1()
            {
                InitializeComponent();
                FileProcesser fp = new FileProcesser(@"\\fileserver\Folders\IT\test");

                WriteToFile("Payroll Update Service was initialized at " + DateTime.Now); 


            }

            protected override void OnStart(string[] args)
            {
                WriteToFile("Payroll Update Service is started at " + DateTime.Now);
                //  FileProcesser fp = new FileProcesser(ConfigurationManager.AppSettings["FromPath"]);
                //FileProcesser fp = new FileProcesser(@"\\fileserver\Folders\IT\test");
                 
                
                fp.Watch();
             
              
            }


    Friday, February 22, 2019 8:09 PM
  • Notice the code I posted. FileProcessor, the field, is `_fp` not `fp`. Fields are normally delineated with an underscore. All references to `fp` need to be converted to the field `_fp`. The declaration of `fp` in your constructor needs to go away, it is the field now. The ctor only needs to have the `new` expression to create an instance of the processor.

    Michael Taylor http://www.michaeltaylorp3.net

    Friday, February 22, 2019 8:27 PM
  • I have a blog post about sub-classing FileSystemWatcher, and adding some improvements to the way it works. It may help and/or give you some ideas:

    https://geek-goddess-bonnie.blogspot.com/2014/08/filesystemwatcher-improvements.html


    ~~Bonnie DeWitt [C# MVP]

    http://geek-goddess-bonnie.blogspot.com

    Saturday, February 23, 2019 9:21 PM
  • Hi

    Is your problem solved? If so, please post "Mark as answer" to the appropriate answer , so that it will help other members to find solution quickly if they faces similar issue.

    Best Regards,

    Jack.


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Monday, February 25, 2019 7:22 AM
  • Notice the code I posted. FileProcessor, the field, is `_fp` not `fp`. Fields are normally delineated with an underscore. All references to `fp` need to be converted to the field `_fp`. The declaration of `fp` in your constructor needs to go away, it is the field now. The ctor only needs to have the `new` expression to create an instance of the processor.

    Michael Taylor http://www.michaeltaylorp3.net

    Hello, Thank you for the help.

    I beleive I am getting closer, but I am still getting errors even using _fp

    it apears to be this line:      public class FileProcesser _fp

    This is the code with you suggestions included.

    Thanks

    public partial class Service1 : ServiceBase
        {
            //started yet?
           //static bool transflag = false;
          // static int trips = 0;
            static string lastfile = "";
            

            public Service1()
            {
                InitializeComponent();
                FileProcesser _fp = new FileProcesser(@"\\fileserver\Folders\IT\test");

                WriteToFile("Payroll Update Service was initialized at " + DateTime.Now); 


            }

            protected override void OnStart(string[] args)
            {
                WriteToFile("Payroll Update Service is started at " + DateTime.Now);
                //  FileProcesser fp = new FileProcesser(ConfigurationManager.AppSettings["FromPath"]);
                //FileProcesser fp = new FileProcesser(@"\\fileserver\Folders\IT\test");
                 
                
                _fp.Watch();
             
              
            }
        

            protected override void OnStop()
            {
                WriteToFile("Payroll Update Service is stopped at " + DateTime.Now);

            }
            ///////////////////////////////////////////////////////////////////////////////////

            private readonly FileProcessor _fp;


            public static void WriteToFile(string Message)
            {
                string path = AppDomain.CurrentDomain.BaseDirectory + "\\Logs";
                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }
                string filepath = AppDomain.CurrentDomain.BaseDirectory + "\\Logs\\ServiceLog_" + DateTime.Now.Date.ToShortDateString().Replace('/', '_') + ".txt";
                if (!File.Exists(filepath))
                {
                    // Create a file to write to.   
                    using (StreamWriter sw = File.CreateText(filepath))
                    {
                        sw.WriteLine(Message);
                    }
                }
                else
                {
                    using (StreamWriter sw = File.AppendText(filepath))
                    {
                        sw.WriteLine(Message);
                    }
                }
            }
            ///////////////////////////////////////////////////////////////////////////////// write to file end


            ////////////////////////////////////////////////////////////////////////////////////////
            public class FileProcesser _fp
            {
                FileSystemWatcher watcher;
                string directoryToWatch;
                public FileProcesser(string path)
                {
                    this.watcher = new FileSystemWatcher();
                    this.directoryToWatch = path;
                }
                public void Watch()
                {
                    watcher.Path = directoryToWatch;
                    watcher.NotifyFilter = NotifyFilters.LastAccess |
                                 NotifyFilters.LastWrite |
                                 NotifyFilters.FileName |
                                 NotifyFilters.DirectoryName;
                    watcher.Filter = "*.*";
                    watcher.Changed += new FileSystemEventHandler(OnChanged);
                    //watcher.Created += new FileSystemEventHandler(OnCreated);
                    watcher.EnableRaisingEvents = true;
                }
           

    Monday, February 25, 2019 1:30 PM
  • also added disable on stop.

                            

            protected override void OnStop()
            {
                WriteToFile("Payroll Update Service is stopped at " + DateTime.Now);
                _fp.EnableRaisingEvents = false;


            }

    Thank you

    Monday, February 25, 2019 1:45 PM
  • Let's talk about this line.

    ```

    public class FileProcessor _fp
    {

    ```

    This is a class declaration. You are defining the new type and the members it will contain. The identifier after the `class` keyword is the type name. After that you're trying to specify a variable name but this is a class definition. Variable declarations can only exist inside a method or class definition so it is invalid here. Remove the `_fp` from the class declaration.


    Michael Taylor http://www.michaeltaylorp3.net

    Monday, February 25, 2019 2:49 PM
  • Thanks for the reply,

    Yes I caught this and tried with the _fp removed, However after that I started getting refrence errors from the line above. 

    Line: private readonly FileProcessor _fp;

    Error 1 The type or namespace name 'FileProcessor' could not be found (are you missing a using directive or an assembly reference?)

    Monday, February 25, 2019 2:54 PM
  • Ah I had a Typo

    FileProcesser

    Monday, February 25, 2019 3:07 PM
  • Is FileProcessor defined in a separate file? is the namespace containing FileProcessor the same namespace as Service1?

    Right click the FileProcessor type in the field declaration line (`private readonly FileProcessor _fp`) and bring up the quick actions. Then select the action that will bring it into scope (generally `using …`).


    Michael Taylor http://www.michaeltaylorp3.net

    Monday, February 25, 2019 3:07 PM
  • I fixed my typo, and it compiles, However now my service will not run at all...

    I am not sure why this change would stop it from even starting.

    Monday, February 25, 2019 3:11 PM
  • Is FileProcessor defined in a separate file? is the namespace containing FileProcessor the same namespace as Service1?

    Right click the FileProcessor type in the field declaration line (`private readonly FileProcessor _fp`) and bring up the quick actions. Then select the action that will bring it into scope (generally `using …`).


    Michael Taylor http://www.michaeltaylorp3.net

    Unfortunately I get no error's now. But the service will not start. I guess Ill add more debug code.
    Monday, February 25, 2019 3:36 PM
  • It looks like the service inits, and starts but dies in the "onstart" when it hits   _fp.Watch();

    if i comment that out and run the service as a test it runs just fine.

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Diagnostics;
    using System.IO;  
    using System.Linq;
    using System.ServiceProcess;
    using System.Text;
    using System.Threading.Tasks;
    using System.Data.SqlClient;
    using Microsoft.VisualBasic.FileIO;
    using System.Threading;

    namespace PayrollUpdateService
    {








        public partial class Service1 : ServiceBase
        {
            //started yet?
           //static bool transflag = false;
          // static int trips = 0;
            static string lastfile = "";
            

            public Service1()
            {
                InitializeComponent();
                FileProcesser _fp = new FileProcesser(@"\\fileserver\Folders\IT\test");

                WriteToFile("Payroll Update Service was initialized at " + DateTime.Now); 


            }

            protected override void OnStart(string[] args)
            {
                WriteToFile("Payroll Update Service is started at " + DateTime.Now);
                //  FileProcesser fp = new FileProcesser(ConfigurationManager.AppSettings["FromPath"]);
                //FileProcesser fp = new FileProcesser(@"\\fileserver\Folders\IT\test");
                 
                
                _fp.Watch();
             
              
            }
        

            protected override void OnStop()
            {
                WriteToFile("Payroll Update Service is stopped at " + DateTime.Now);
               // _fp.EnableRaisingEvents = false;
                
                
            }
            ///////////////////////////////////////////////////////////////////////////////////

            private readonly FileProcesser _fp;


            public static void WriteToFile(string Message)
            {
                string path = AppDomain.CurrentDomain.BaseDirectory + "\\Logs";
                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }
                string filepath = AppDomain.CurrentDomain.BaseDirectory + "\\Logs\\ServiceLog_" + DateTime.Now.Date.ToShortDateString().Replace('/', '_') + ".txt";
                if (!File.Exists(filepath))
                {
                    // Create a file to write to.   
                    using (StreamWriter sw = File.CreateText(filepath))
                    {
                        sw.WriteLine(Message);
                    }
                }
                else
                {
                    using (StreamWriter sw = File.AppendText(filepath))
                    {
                        sw.WriteLine(Message);
                    }
                }
            }
            ///////////////////////////////////////////////////////////////////////////////// write to file end


            ////////////////////////////////////////////////////////////////////////////////////////
            public class FileProcesser 
            {

               
                FileSystemWatcher watcher;
                string directoryToWatch;
                public FileProcesser(string path)
               {
                    this.watcher = new FileSystemWatcher();
                    this.directoryToWatch = path;
               }
                public void Watch()
                {
                    watcher.Path = directoryToWatch;
                    watcher.NotifyFilter = NotifyFilters.LastAccess |
                                 NotifyFilters.LastWrite |
                                 NotifyFilters.FileName |
                                 NotifyFilters.DirectoryName;
                    watcher.Filter = "*.*";
                    watcher.Changed += new FileSystemEventHandler(OnChanged);
                    //watcher.Created += new FileSystemEventHandler(OnCreated);
                    watcher.EnableRaisingEvents = true;
                }

    Monday, February 25, 2019 4:12 PM
  • Testing service start code is tricky. You can put a Debug.Assert call in your OnStart to trigger the debugger so you can step into it.

    But I'm looking at the file path you're using in your file processor. Services don't have network access by default and therefore that UNC path isn't going to work. In order to access a UNC path you'll need to run the service under a user account that has permissions to access that path. Alternatively you can run under the NETWORK SERVICE account but you'd need to make sure that the UNC path allows access to the machine running your service.


    Michael Taylor http://www.michaeltaylorp3.net

    Monday, February 25, 2019 4:51 PM
  • Testing service start code is tricky. You can put a Debug.Assert call in your OnStart to trigger the debugger so you can step into it.

    But I'm looking at the file path you're using in your file processor. Services don't have network access by default and therefore that UNC path isn't going to work. In order to access a UNC path you'll need to run the service under a user account that has permissions to access that path. Alternatively you can run under the NETWORK SERVICE account but you'd need to make sure that the UNC path allows access to the machine running your service.


    Michael Taylor http://www.michaeltaylorp3.net

    Yeah, The account does have permission on that folder, infact the service worked, when the filewatcher fired the other day just fine. It just only fired once, and wouldent do it again, but it did actually do what it was supose to do in that folder. I know that I dont have a permissions issue. when i remove all the _fp code and revert to the original it works once again too.
    Monday, February 25, 2019 6:21 PM
  • Do this, remove the init of _fp from your constructor. In OnStart put the new to `_fp` and then call your `Watch` method.

    Michael Taylor http://www.michaeltaylorp3.net

    Monday, February 25, 2019 6:30 PM
  • I bet you are right! Because look at the null ref i am getting in event vwr:

    Log Name:      Application
    Source:        Service1
    Date:          2/25/2019 1:34:29 PM
    Event ID:      0
    Task Category: None
    Level:         Error
    Keywords:      Classic
    User:          N/A
    Computer:      fgfggfgfggg
    Description:
    Service cannot be started. System.NullReferenceException: Object reference not set to an instance of an object.
       at PayrollUpdateService.Service1.OnStart(String[] args) in :line 50
       at System.ServiceProcess.ServiceBase.ServiceQueuedMainCallback(Object state)

    Monday, February 25, 2019 6:41 PM
  • You sure you aren't declaring a local variable called `_fp` inside OnStart that is hiding the field defined in the class?

    Michael Taylor http://www.michaeltaylorp3.net

    Monday, February 25, 2019 6:44 PM
  • Do this, remove the init of _fp from your constructor. In OnStart put the new to `_fp` and then call your `Watch` method.

    Michael Taylor http://www.michaeltaylorp3.net

    Well the good news is, that did work. However it did just like the original code and only fires once. When i place the first file all is well and its processed, but consecutive files are not.


    Monday, February 25, 2019 6:46 PM
  • You sure you aren't declaring a local variable called `_fp` inside OnStart that is hiding the field defined in the class?

    Michael Taylor http://www.michaeltaylorp3.net

    Not that I am aware of...This is the current code

           protected override void OnStart(string[] args)
            {
                WriteToFile("Payroll Update Service is started at " + DateTime.Now);
                //  FileProcesser fp = new FileProcesser(ConfigurationManager.AppSettings["FromPath"]);
                //FileProcesser fp = new FileProcesser(@"\\fileserver\Folders\IT\test");

                FileProcesser _fp = new FileProcesser(@"\\fileserver\Folders\IT\test");
                _fp.Watch();
             
              
            }
        

            protected override void OnStop()
            {
                WriteToFile("Payroll Update Service is stopped at " + DateTime.Now);
               // _fp.EnableRaisingEvents = false;
                
                
            }
            ///////////////////////////////////////////////////////////////////////////////////

            private readonly FileProcesser _fp;

    Monday, February 25, 2019 6:50 PM
  • Ok so now that the FSW is going to stick around we can start to figure out why you're seeing it fire "only once". I suspect it is firing multiple times but your code is doing something such that you don't see it. Based upon your description it sounds like you're dropping files into a folder and you're expecting them to get picked up by FSW? If so then you need to handle the create and change events. Furthermore note that a single file operation can trigger multiple events. So your OnChanged method (which is defined in FileProcessor) needs to account for this. Also be aware that the file will still be potentially locked by the app dropping the file(s) so you need to defer doing anything with the file until later. Most FSW apps tend to use a shared queue that the FSW writes "events" to. A separate background worker thread monitors the queue, retrieves the event and then tries to do the work. If the event cannot be processed then it is skipped until next time. However file system events are order dependent so the logic has to account for things like a file being created, deleted and then created again.

    You might start by putting a log message in your OnChange handler and confirm it is being called when you think it should be. You'll need a similar thing (or even use the same method) for OnCreate. If you cannot figure it out then please post the code for OnChange.


    Michael Taylor http://www.michaeltaylorp3.net

    Monday, February 25, 2019 7:04 PM
  • Ok so now that the FSW is going to stick around we can start to figure out why you're seeing it fire "only once". I suspect it is firing multiple times but your code is doing something such that you don't see it. Based upon your description it sounds like you're dropping files into a folder and you're expecting them to get picked up by FSW? If so then you need to handle the create and change events. Furthermore note that a single file operation can trigger multiple events. So your OnChanged method (which is defined in FileProcessor) needs to account for this. Also be aware that the file will still be potentially locked by the app dropping the file(s) so you need to defer doing anything with the file until later. Most FSW apps tend to use a shared queue that the FSW writes "events" to. A separate background worker thread monitors the queue, retrieves the event and then tries to do the work. If the event cannot be processed then it is skipped until next time. However file system events are order dependent so the logic has to account for things like a file being created, deleted and then created again.

    You might start by putting a log message in your OnChange handler and confirm it is being called when you think it should be. You'll need a similar thing (or even use the same method) for OnCreate. If you cannot figure it out then please post the code for OnChange.


    Michael Taylor http://www.michaeltaylorp3.net

    I think i found the issue!!!

    After examining the event log, it looks like it crashes with an IO error after the first run. I need to check for the file in use better!!! After commenting out the IO code it fires every time i drop a file in!

    Thank you so much for all your patience and help!!

    Commented out IO code:

        //Create the csv file and parce it...
                            string csvname = DateTime.Now.Ticks+"_payroll.csv";
                            WriteToFile(csvname + " sanatize txt file... " + DateTime.Now);

                          //  string text = File.ReadAllText(e.FullPath);
                          //  text = text.Replace("\",", "\"|");
                          //  text = text.Replace("\"\"|", "null|");
                          //  File.WriteAllText(@"\\fileserver\Folders\IT\test\processing\" + csvname, text);

                            WriteToFile("csv parsed...." + DateTime.Now);

                            WriteToFile(e.FullPath + ".....delete file ...." + DateTime.Now);
                            //delete text file..no longer needed
                            //File.Delete(e.FullPath);


    Monday, February 25, 2019 7:10 PM
  • Just wanted to say thank you for all the great help and suggestion!
    Tuesday, February 26, 2019 1:07 PM
  • No problem.

    Michael Taylor http://www.michaeltaylorp3.net

    Tuesday, February 26, 2019 2:59 PM
  • You said: >>After examining the event log, it looks like it crashes with an IO error after the first run. I need to check for the file in use better!!! <<

    As I talked about in my blog post that I linked to earlier (maybe you missed it? https://geek-goddess-bonnie.blogspot.com/2014/08/filesystemwatcher-improvements.html), I solved that problem by sub-classing the FileSystemWatcher class and using my sub-class instead. Here's an excerpt from my blog post:

    ...............................................................
    So, I created my own class, sub-classed from FileSystemWatcher and used that instead. The difference being that I create a new event that I call FileReady and fire it when I know the file  copy has completed. How do I know when that happens? In a try/catch, I try to do a File.Open with FileShare set to None. If I can't open the file, then I know it's not done being copied yet and I keep trying until I can open it (at which point I fire the FileReady event). Here's the code for this new class:


    public class MyFileWatcher : FileSystemWatcher
    {
        public event FileSystemEventHandler FileReady;
    
    
        public MyFileWatcher()
        {
            this.Created += new FileSystemEventHandler(Watcher_Created);
        }
        private void Watcher_Created(object sender, FileSystemEventArgs e)
        {
            System.Threading.ThreadPool.QueueUserWorkItem((n) => { WaitForFileReady(e); });
        }
        private void WaitForFileReady(FileSystemEventArgs e)
        {
            while (true)
            {
                try
                {
                    using (FileStream fs = File.Open(e.FullPath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
                    {
                        break;
                    }
                }
                catch (FileNotFoundException ex)
                {
                    return;
                }
                catch (Exception ex)
                {
                    System.Threading.Thread.Sleep(100);
                }
            }
            OnFileReady(e);
        }
        protected virtual void OnFileReady(FileSystemEventArgs e)
        {
            if (this.EnableRaisingEvents && FileReady != null)
                FileReady(this, e);
        }
    }
    
    

     
    In order to utilize this in the MyFileListener class, simply replace the FileSystemWatcher with the MyFileWatcher and handle the FileReady event instead of the Created event (the code inside the event handler will be exactly the same). Here are the changes to make:


    // the declaration
    protected MyFileWatcher Watcher = null;
    // the MyFileListener constructor
    this.Watcher = new MyFileWatcher();
    this.Watcher.FileReady += new FileSystemEventHandler(Watcher_FileReady);
    // the new event handler name
    private void Watcher_FileReady(object sender, FileSystemEventArgs e)

    ...............................................................


    Hope that helps!


    ~~Bonnie DeWitt [C# MVP]

    http://geek-goddess-bonnie.blogspot.com

    Tuesday, February 26, 2019 3:07 PM